tools: command records, Catalog and log group - bullets, examples, prohibitions (#142)
CI / verify (push) Successful in 1m17s
Release / release (push) Successful in 37s

Files changed:
- CHANGES.md
- VERSION
- tools/CONTRACT.md
- tools/chemenu/commands/index_build.py
- tools/chemenu/commands/log_append.py
This commit is contained in:
torben committed 2026-09-26 08:28:38 +02:00
1 parent fd0f60b2e7
commit df8ff2fa22
5 files changed
+139 -26

No files matched your search

+11 -1
View File
@@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse.
--- ---
## 7.1.0-beta.7 - 2026-09-26 - Command records, Git group: NOTES as bullets, one exit line per cause, examples and prohibitions ## 7.1.0-beta.8 - 2026-09-26 - Command records, Catalog and log group: bullets, examples, prohibitions
**Author:** Torben Nehmer **Author:** Torben Nehmer
@@ -76,6 +76,7 @@ concern - readable here, never shipped as something to parse.
- stack-close: wait for CI through the authenticated Gitea connection, with timings and a give-up point - stack-close: wait for CI through the authenticated Gitea connection, with timings and a give-up point
- wikitool: usage lines name wikitool, and the -h acceptance checks become tests - wikitool: usage lines name wikitool, and the -h acceptance checks become tests
- Command records, Git group: NOTES as bullets, one exit line per cause, examples and prohibitions - Command records, Git group: NOTES as bullets, one exit line per cause, examples and prohibitions
- Command records, Catalog and log group: bullets, examples, prohibitions
<!-- /wikitool:bumps --> <!-- /wikitool:bumps -->
### CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them ### CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them
@@ -196,6 +197,15 @@ at the code that implements it. `publish`'s `atomic` property says "every gate"
"both gates" - it has three, and all run before staging. How a record's prose is written is now "both gates" - it has three, and all run before staging. How a record's prose is written is now
stated once, in the `CommandRecord` docstring, and `tools/README.md` points there. stated once, in the `CommandRecord` docstring, and `tools/README.md` points there.
### Command records, Catalog and log group: bullets, examples, prohibitions
`index rebuild`, `log append` and `log status` rewritten the same way; text only. `log append`
now lists its two exit-1 causes separately and states in NEVER what its retry policy said in
prose: check the tail of `kb/log.md` before re-running after an uncertain outcome.
`index rebuild`'s NOTES name the nested-page warning and what `--dry-run` prints, both of which
the command already did. `log status` no longer claims it "reports 0" for a missing log - it
reports that nothing is logged yet, which is what it always printed.
--- ---
## 7.0.0 - 2026-09-22 - Task-Tracker-Anbindung: Vorhaben als Seitenart, Verpflichtungsschicht, Weekly Review als Read-Time-Join ## 7.0.0 - 2026-09-22 - Task-Tracker-Anbindung: Vorhaben als Seitenart, Verpflichtungsschicht, Weekly Review als Read-Time-Join
+1 -1
View File
@@ -1 +1 @@
7.1.0-beta.7 7.1.0-beta.8
+56 -7
View File
@@ -603,18 +603,37 @@ Regenerate the catalog from every page's frontmatter.
- budget: counted - budget: counted
- network: no - network: no
**EXAMPLES**
- `tools/wikitool index rebuild`
- `tools/wikitool index rebuild --dry-run`
**EXIT STATUS** **EXIT STATUS**
- 0 success - 0 success
- 1 Rare I/O error only - 1 An I/O error while writing or removing a catalog file (rare)
**ON FAILURE** **ON FAILURE**
- Rare I/O error only -> Safe to retry freely - the plan is always recomputed from the pages currently on disk, so a re-run converges - An I/O error while writing or removing a catalog file (rare) -> Safe to retry freely - the plan is always recomputed from the pages currently on disk, so a re-run converges
**NEVER**
- Never hand-edit `kb/index.md` or an `INDEX.md` - re-run this command instead.
**NOTES** **NOTES**
`kb/index.md` becomes a map (statistics, one row per collection and per area, links to the shards) and the page tables are written to a generated `INDEX.md` in each collection. An area past 50 rows gets its own shard. Stale shards from removed collections/areas are deleted in the same pass - Rewrites `kb/index.md` as a map: statistics, one row per collection and per area, and links to the shards - no page rows.
- Writes the page tables to a generated `INDEX.md` in each collection. An area with more than 50 rows gets its own `INDEX.md` in its directory.
- Deletes stale shards - an `INDEX.md` of a collection or area that no longer exists - in the same pass.
- Warns about every page nested more than one directory below its collection; it is still catalogued, folded into its area.
- `--dry-run` prints every file it would write and every stale shard it would remove, and writes nothing.
**SEE ALSO**
- `wikitool search` - finds a page without reading the catalog
- `wikitool lint` - its Nested Pages finding is what the warning previews
- `instructions/publish-cycle.md` - where a write session runs this
#### `log append` #### `log append`
@@ -632,18 +651,35 @@ Append a formatted entry to `kb/log.md`.
- budget: counted - budget: counted
- network: no - network: no
**EXAMPLES**
- `tools/wikitool log append --op ingest --title "raw/articles/docker-cheatsheet.md" --body "Created [[Docker]]; updated [[Container]]."`
- `tools/wikitool log append --op lint --title "2026-09-26" --body-file lint-summary.md`
**EXIT STATUS** **EXIT STATUS**
- 0 success - 0 success
- 1 Invalid `--op` or unreadable `--body-file` - 1 `--op` is not one of ingest, query, lint, create, update, delete, rename, move
- 1 `--body-file` cannot be read
**ON FAILURE** **ON FAILURE**
- Invalid `--op` or unreadable `--body-file` -> **Not idempotent.** If the previous run's outcome is uncertain, check the tail of `kb/log.md` before retrying - `--op` is not one of ingest, query, lint, create, update, delete, rename, move -> Nothing was written - fix the argument and retry once
- `--body-file` cannot be read -> Nothing was written - fix the path and retry once
**NEVER**
- Never re-run after an uncertain outcome without first checking the tail of `kb/log.md` - a second run appends a second entry.
**NOTES** **NOTES**
Append a formatted entry to `kb/log.md`. - Appends one entry to `kb/log.md`: a `## [YYYY-MM-DD] <op> | <title>` heading, the body if one is given, and a `---` separator.
- Not idempotent: every successful run appends a new entry, including a repeated one.
**SEE ALSO**
- `wikitool log status` - counts the ingests logged since the last lint
- `instructions/publish-cycle.md` - where a write session runs this
#### `log status` #### `log status`
@@ -661,13 +697,26 @@ Read-only: count `ingest` entries logged since the last `lint` entry.
- budget: counted - budget: counted
- network: no - network: no
**EXAMPLES**
- `tools/wikitool log status`
**EXIT STATUS** **EXIT STATUS**
- 0 success - 0 success
- 0 `kb/log.md` is missing or empty - reported as nothing logged, not a failure
**NOTES** **NOTES**
The deterministic trigger behind the Maintenance Schedule's "every 10 sources" full-lint cadence. Never fails (reports 0 if `kb/log.md` is missing or empty). Safe to retry freely. - Counts the `ingest` entries in `kb/log.md` after the most recent `lint` entry, or since the start of the log if it was never linted, and the total number of entries.
- At 10 or more it prints that the `wiki-lint` skill is due next - the every-10-sources full lint of the Maintenance Schedule.
- Read-only; safe to retry freely.
**SEE ALSO**
- `wikitool log append` - writes the entries this counts
- `wiki-lint` skill - what the threshold asks for
- `tools/CONTRACT.md` § Maintenance schedule - the cadence this reports on
### Finding and checking ### Finding and checking
+25 -6
View File
@@ -229,16 +229,35 @@ def build_index(kb_dir: Path) -> str:
"some regenerated and others not", "some regenerated and others not",
budget=cli_contract.Budget.COUNTED, budget=cli_contract.Budget.COUNTED,
), ),
notes="`kb/index.md` becomes a map (statistics, one row per collection and per area, links " notes=(
"to the shards) and the page tables are written to a generated `INDEX.md` in each " "Rewrites `kb/index.md` as a map: statistics, one row per collection and per area, and "
"collection. An area past 50 rows gets its own shard. Stale shards from removed " "links to the shards - no page rows.",
"collections/areas are deleted in the same pass", "Writes the page tables to a generated `INDEX.md` in each collection. An area with more "
"than 50 rows gets its own `INDEX.md` in its directory.",
"Deletes stale shards - an `INDEX.md` of a collection or area that no longer exists - "
"in the same pass.",
"Warns about every page nested more than one directory below its collection; it is "
"still catalogued, folded into its area.",
"`--dry-run` prints every file it would write and every stale shard it would remove, "
"and writes nothing.",
),
failures=(cli_contract.Failure( failures=(cli_contract.Failure(
label="", cause="An I/O error while writing or removing a catalog file (rare)",
cause="Rare I/O error only",
reaction="Safe to retry freely - the plan is always recomputed from the pages currently " reaction="Safe to retry freely - the plan is always recomputed from the pages currently "
"on disk, so a re-run converges", "on disk, so a re-run converges",
),), ),),
examples=(
"tools/wikitool index rebuild",
"tools/wikitool index rebuild --dry-run",
),
never=(
"Never hand-edit `kb/index.md` or an `INDEX.md` - re-run this command instead.",
),
see_also=(
"`wikitool search` - finds a page without reading the catalog",
"`wikitool lint` - its Nested Pages finding is what the warning previews",
"`instructions/publish-cycle.md` - where a write session runs this",
),
)) ))
def index_rebuild( def index_rebuild(
dry_run: bool = typer.Option( dry_run: bool = typer.Option(
+46 -11
View File
@@ -64,13 +64,34 @@ def ingests_since_last_lint(entries: list[tuple[str, str, str]]) -> int:
atomic="Yes - single append", atomic="Yes - single append",
budget=cli_contract.Budget.COUNTED, budget=cli_contract.Budget.COUNTED,
), ),
notes="Append a formatted entry to `kb/log.md`.", notes=(
failures=(cli_contract.Failure( "Appends one entry to `kb/log.md`: a `## [YYYY-MM-DD] <op> | <title>` heading, the body "
label="", "if one is given, and a `---` separator.",
cause="Invalid `--op` or unreadable `--body-file`", "Not idempotent: every successful run appends a new entry, including a repeated one.",
reaction="**Not idempotent.** If the previous run's outcome is uncertain, check the tail " ),
"of `kb/log.md` before retrying", failures=(
),), cli_contract.Failure(
cause="`--op` is not one of ingest, query, lint, create, update, delete, rename, move",
reaction="Nothing was written - fix the argument and retry once",
),
cli_contract.Failure(
cause="`--body-file` cannot be read",
reaction="Nothing was written - fix the path and retry once",
),
),
examples=(
'tools/wikitool log append --op ingest --title "raw/articles/docker-cheatsheet.md" '
'--body "Created [[Docker]]; updated [[Container]]."',
'tools/wikitool log append --op lint --title "2026-09-26" --body-file lint-summary.md',
),
never=(
"Never re-run after an uncertain outcome without first checking the tail of "
"`kb/log.md` - a second run appends a second entry.",
),
see_also=(
"`wikitool log status` - counts the ingests logged since the last lint",
"`instructions/publish-cycle.md` - where a write session runs this",
),
)) ))
def log_append( def log_append(
op: str = typer.Option(..., "--op", help="|".join(VALID_OPS)), op: str = typer.Option(..., "--op", help="|".join(VALID_OPS)),
@@ -101,10 +122,24 @@ def log_append(
atomic="Read-only", atomic="Read-only",
budget=cli_contract.Budget.COUNTED, budget=cli_contract.Budget.COUNTED,
), ),
notes="The deterministic trigger behind the Maintenance Schedule's \"every 10 sources\" " notes=(
"full-lint cadence. Never fails (reports 0 if `kb/log.md` is missing or empty). Safe to " "Counts the `ingest` entries in `kb/log.md` after the most recent `lint` entry, or "
"retry freely.", "since the start of the log if it was never linted, and the total number of entries.",
failures=(), "At 10 or more it prints that the `wiki-lint` skill is due next - the every-10-sources "
"full lint of the Maintenance Schedule.",
"Read-only; safe to retry freely.",
),
failures=(cli_contract.Failure(
cause="`kb/log.md` is missing or empty - reported as nothing logged, not a failure",
reaction="",
code=0,
),),
examples=("tools/wikitool log status",),
see_also=(
"`wikitool log append` - writes the entries this counts",
"`wiki-lint` skill - what the threshold asks for",
"`tools/CONTRACT.md` § Maintenance schedule - the cadence this reports on",
),
)) ))
def log_status(): def log_status():
"""Report how many `ingest` operations have been logged since the last """Report how many `ingest` operations have been logged since the last