From df8ff2fa223676a5186023f4595fa6e77ae25bc9 Mon Sep 17 00:00:00 2001 From: Torben Nehmer Date: Sat, 26 Sep 2026 08:28:38 +0200 Subject: [PATCH] tools: command records, Catalog and log group - bullets, examples, prohibitions (#142) Files changed: - CHANGES.md - VERSION - tools/CONTRACT.md - tools/chemenu/commands/index_build.py - tools/chemenu/commands/log_append.py --- CHANGES.md | 12 ++++- VERSION | 2 +- tools/CONTRACT.md | 63 ++++++++++++++++++++++++--- tools/chemenu/commands/index_build.py | 31 ++++++++++--- tools/chemenu/commands/log_append.py | 57 +++++++++++++++++++----- 5 files changed, 139 insertions(+), 26 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3012a4f..56db803 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 @@ -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 - 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, Catalog and log group: bullets, examples, prohibitions ### 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 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 diff --git a/VERSION b/VERSION index b576c90..d1a304c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0-beta.7 +7.1.0-beta.8 diff --git a/tools/CONTRACT.md b/tools/CONTRACT.md index a6a960f..b554d5b 100644 --- a/tools/CONTRACT.md +++ b/tools/CONTRACT.md @@ -603,18 +603,37 @@ Regenerate the catalog from every page's frontmatter. - budget: counted - network: no +**EXAMPLES** + +- `tools/wikitool index rebuild` +- `tools/wikitool index rebuild --dry-run` + **EXIT STATUS** - 0 success -- 1 Rare I/O error only +- 1 An I/O error while writing or removing a catalog file (rare) **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** -`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` @@ -632,18 +651,35 @@ Append a formatted entry to `kb/log.md`. - budget: counted - 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** - 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** -- 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** -Append a formatted entry to `kb/log.md`. +- Appends one entry to `kb/log.md`: a `## [YYYY-MM-DD] | ` 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` @@ -661,13 +697,26 @@ Read-only: count `ingest` entries logged since the last `lint` entry. - budget: counted - network: no +**EXAMPLES** + +- `tools/wikitool log status` + **EXIT STATUS** - 0 success +- 0 `kb/log.md` is missing or empty - reported as nothing logged, not a failure **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 diff --git a/tools/chemenu/commands/index_build.py b/tools/chemenu/commands/index_build.py index ade9554..e30e52e 100644 --- a/tools/chemenu/commands/index_build.py +++ b/tools/chemenu/commands/index_build.py @@ -229,16 +229,35 @@ def build_index(kb_dir: Path) -> str: "some regenerated and others not", budget=cli_contract.Budget.COUNTED, ), - 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", + notes=( + "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.", + ), failures=(cli_contract.Failure( - label="", - cause="Rare I/O error only", + cause="An I/O error while writing or removing a catalog file (rare)", reaction="Safe to retry freely - the plan is always recomputed from the pages currently " "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( dry_run: bool = typer.Option( diff --git a/tools/chemenu/commands/log_append.py b/tools/chemenu/commands/log_append.py index dffdcca..2a7642d 100644 --- a/tools/chemenu/commands/log_append.py +++ b/tools/chemenu/commands/log_append.py @@ -64,13 +64,34 @@ def ingests_since_last_lint(entries: list[tuple[str, str, str]]) -> int: atomic="Yes - single append", budget=cli_contract.Budget.COUNTED, ), - notes="Append a formatted entry to `kb/log.md`.", - failures=(cli_contract.Failure( - label="", - cause="Invalid `--op` or unreadable `--body-file`", - reaction="**Not idempotent.** If the previous run's outcome is uncertain, check the tail " - "of `kb/log.md` before retrying", - ),), + notes=( + "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.", + ), + 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( op: str = typer.Option(..., "--op", help="|".join(VALID_OPS)), @@ -101,10 +122,24 @@ def log_append( atomic="Read-only", budget=cli_contract.Budget.COUNTED, ), - 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.", - failures=(), + notes=( + "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.", + ), + 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(): """Report how many `ingest` operations have been logged since the last