diff --git a/CHANGES.md b/CHANGES.md index 9f6fd83..0944020 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse. --- -## 7.1.0-beta.15 - 2026-09-26 - Command records, Workshop runs and session budget group: examples, prohibitions +## 7.1.0-beta.16 - 2026-09-26 - Command records, Types, instructions and docs group: one line per cause, examples, prohibitions **Author:** Torben Nehmer @@ -84,6 +84,7 @@ concern - readable here, never shipped as something to parse. - Command records, Provenance group: examples, exit lines per cause - Command records, Raw material and uploads group: one line per cause, examples, prohibitions - Command records, Workshop runs and session budget group: examples, prohibitions +- Command records, Types, instructions and docs group: one line per cause, examples, prohibitions ### CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them @@ -287,6 +288,17 @@ gate's shape itself instead of pointing at the Mass-Update Gate's. own initiative to get past a budget refusal - and `work close` states the caller's side of its `--yes`: the run's conclusions are in `kb/` first. +### Command records, Types, instructions and docs group: one line per cause, examples, prohibitions + +`types list`/`describe`, `instructions sync`/`verify`/`list` and `docs verify`/`toc`/`contract` +rewritten the same way; text only. `docs verify`'s single sentence naming some twenty checks is +now grouped by what it checks (commands, collections, types, ignore canaries, shipped issue +references, tables of contents, links), with six exit-1 causes and a reaction each. `docs toc`'s +"never fails on content" had been carried over as an exit-1 cause, so the index listed it as +`exit:0,1`; it is now a success line and the index says `exit:0`, which is what the command has +always done. The reasoning `docs toc`'s record carried about its scope already lived in +`toc.py`'s module docstring and now lives only there. + --- ## 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 9485aca..dde2e11 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0-beta.15 +7.1.0-beta.16 diff --git a/tools/CONTRACT.md b/tools/CONTRACT.md index 9c43183..ca62ccc 100644 --- a/tools/CONTRACT.md +++ b/tools/CONTRACT.md @@ -119,7 +119,7 @@ instructions sync write idempotent budget:counted exit:0,1 instructions verify read idempotent budget:counted exit:0,1 Check the instruction layer. instructions list read idempotent budget:counted exit:0 List the flat instructions with their descriptions. docs verify read idempotent budget:counted exit:0,1 Check the docs that mirror the code. -docs toc write idempotent budget:counted exit:0,1 Create, refresh or remove the generated table-of-contents region. +docs toc write idempotent budget:counted exit:0 Create, refresh or remove the generated table-of-contents region. docs contract write idempotent budget:counted exit:0 Regenerate `tools/CONTRACT.md`'s `` region. eval sessions read idempotent budget:exempt exit:0 List the sessions that have a trace under `reports/telemetry/`. eval score read idempotent budget:exempt exit:0,1 Score one traced session. @@ -1915,13 +1915,27 @@ List every type-spec under `types/`. - budget: counted - network: no +**EXAMPLES** + +- `tools/wikitool types list` +- `tools/wikitool types list --json` + **EXIT STATUS** - 0 success +**NEVER** + +- Never pick a page's directory by hand - `types describe` and `new` compute it. + **NOTES** -Name, schema path, subtype field, and description - discover what page types exist without reading `types/*.md` directly. Never fails. Safe to retry freely. +- Lists every type-spec under `types/`: name, schema path, subtype field, and description - which page types exist, without reading `types/*.md` directly. +- Never fails; read-only and safe to retry freely. + +**SEE ALSO** + +- `wikitool types describe ` - one type's full contract #### `types describe` @@ -1939,6 +1953,11 @@ Print one type's full contract. - budget: counted - network: no +**EXAMPLES** + +- `tools/wikitool types describe source` +- `tools/wikitool types describe project --json` + **EXIT STATUS** - 0 success @@ -1946,11 +1965,19 @@ Print one type's full contract. **ON FAILURE** -- Unknown type name -> Fix the name and retry +- Unknown type name -> Fix the name (see `types list`) and retry **NOTES** -Required/optional frontmatter fields with enums, its subtype field (if any), and its authoring body - composed with the stack-owned `types/.guidance.md` where the type-spec declares `guidance:` (`--json` reports it separately as `guidance`/`guidance_path`, absent for a type with none), so a `root: kb` type's contract reads as one answer even though it may live in two files. A type-spec (or its guidance file) over the `docs toc` threshold carries a generated table-of-contents region; it is stripped from this output rather than echoed, since the whole body is being handed over and a navigation aid into it would be noise +- Prints one type's full contract: required and optional frontmatter fields with enums, its subtype field (if any), and its authoring body. +- Where the type-spec declares `guidance:`, the stack-owned `types/.guidance.md` is composed in, so a `root: kb` type's contract reads as one answer even though it may live in two files. `--json` reports it separately as `guidance`/`guidance_path`, absent for a type with none. +- The generated table-of-contents region a long type-spec (or guidance file) carries is stripped from this output. +- Read-only. + +**SEE ALSO** + +- `wikitool types list` - every type +- `wikitool new ` - scaffolds a page of the type #### `instructions sync` @@ -1968,18 +1995,37 @@ Publish every `instructions//SKILL.md` into the harness skill directories. - budget: counted - network: no +**EXAMPLES** + +- `tools/wikitool instructions sync` + **EXIT STATUS** - 0 success -- 1 No skills found under `instructions/`, or a target directory is not a published skill (no `SKILL.md`) and `--force` was not passed +- 1 No skills found under `instructions/` +- 1 A target directory is not a published skill (no `SKILL.md`) and `--force` was not passed **ON FAILURE** -- No skills found under `instructions/`, or a target directory is not a published skill (no `SKILL.md`) and `--force` was not passed -> Check whether the flagged target holds anything worth keeping, then re-run with `--force` if not; otherwise fix the named cause and retry +- No skills found under `instructions/` -> Fix the named cause and retry +- A target directory is not a published skill (no `SKILL.md`) and `--force` was not passed -> Check whether the flagged target holds anything worth keeping, then re-run with `--force` if not + +**NEVER** + +- Never hand-edit a published copy under `.agents/skills/` or `.claude/skills/` - edit the source and re-run this. **NOTES** -Publish every `instructions//SKILL.md` into `.agents/skills/` and `.claude/skills/` as **copies**, and delete published skills whose source is gone. Both targets are gitignored, so a fresh clone runs this once - see `instructions/bootstrap.md`. Re-running is also how a drifted copy is repaired: the source always wins. `--force` is required only to replace a target directory that is not a published skill at all (no `SKILL.md` in it) +- Publishes every `instructions//SKILL.md` into `.agents/skills/` and `.claude/skills/` as **copies**, and deletes published skills whose source is gone. +- Both targets are gitignored, so a fresh clone runs this once. +- Re-running repairs a drifted copy: the source always wins. +- `--force` is required only to replace a target directory that is not a published skill at all (no `SKILL.md` in it). +- Each copy is idempotent, so a re-run converges even after a partial failure. + +**SEE ALSO** + +- `instructions/bootstrap.md` - the fresh-clone procedure that runs this +- `wikitool instructions verify` - checks the copies match #### `instructions verify` @@ -1997,18 +2043,44 @@ Check the instruction layer. - budget: counted - network: no +**EXAMPLES** + +- `tools/wikitool instructions verify` + **EXIT STATUS** - 0 success -- 1 Nothing found under `instructions/` at all, a malformed instruction or `SKILL.md`, a `SKILL.md` carrying a relative markdown link, a published copy that drifted from its source, an instruction nothing references (or, for `manual: true`, one that IS linked from AGENTS.md, CLAUDE.md, or a skill and so risks running implicitly), or something under `instructions/dev/` referenced from outside it and outside a `dist:strip` block +- 1 Nothing found under `instructions/` at all, or a malformed instruction or `SKILL.md` +- 1 A `SKILL.md` carries a relative markdown link +- 1 A published copy drifted from its source +- 1 An instruction nothing references, or a `manual: true` one that IS linked from AGENTS.md, CLAUDE.md or a skill and so risks running implicitly +- 1 Something under `instructions/dev/` is referenced from outside it and outside a `dist:strip` block **ON FAILURE** -- Nothing found under `instructions/` at all, a malformed instruction or `SKILL.md`, a `SKILL.md` carrying a relative markdown link, a published copy that drifted from its source, an instruction nothing references (or, for `manual: true`, one that IS linked from AGENTS.md, CLAUDE.md, or a skill and so risks running implicitly), or something under `instructions/dev/` referenced from outside it and outside a `dist:strip` block -> Fix the flagged file, then re-run. For a relative link in a `SKILL.md`, rewrite it as a repo-root-relative plain path instead. For drift, re-run `sync` instead of hand-editing the published copy - the source under `instructions/` always wins +- Nothing found under `instructions/` at all, or a malformed instruction or `SKILL.md` -> Fix the flagged file, then re-run +- A `SKILL.md` carries a relative markdown link -> Rewrite it as a repo-root-relative plain path, then re-run +- A published copy drifted from its source -> Re-run `instructions sync` - the source under `instructions/` always wins +- An instruction nothing references, or a `manual: true` one that IS linked from AGENTS.md, CLAUDE.md or a skill and so risks running implicitly -> Link it from where it is used, or drop the link to a manual one, then re-run +- Something under `instructions/dev/` is referenced from outside it and outside a `dist:strip` block -> Remove the reference or wrap it in a `dist:strip` block, then re-run + +**NEVER** + +- Never fix drift by hand-editing the published copy. **NOTES** -Flat instructions validate against `types/instruction.schema.yaml`, each `SKILL.md` carries the frontmatter its harness reads, no `SKILL.md` carries a relative markdown link (`sync` copies it to a different depth than the source, so a `SKILL.md` references a target as a repo-root-relative plain path instead - see `instructions/CONTRACT.md` § "A skill's outbound reference is a plain path, not a link"), every published copy is byte-identical to its source, no instruction is left that nothing references, and nothing under `instructions/dev/` is referenced from outside it (a `` block is exempt - see `instructions/CONTRACT.md`). Missing *every* copy is reported as "run sync", not as drift - that is a clean checkout +- Flat instructions validate against `types/instruction.schema.yaml`, and each `SKILL.md` carries the frontmatter its harness reads. +- No `SKILL.md` carries a relative markdown link: `sync` copies it to a different depth than the source, so a `SKILL.md` references a target as a repo-root-relative plain path instead (`instructions/CONTRACT.md` § "A skill's outbound reference is a plain path, not a link"). +- Every published copy is byte-identical to its source. Missing *every* copy is reported as "run sync", not as drift - that is a clean checkout. +- No instruction is left that nothing references; one marked `manual: true` must instead not be linked from AGENTS.md, CLAUDE.md or a skill. +- Nothing under `instructions/dev/` is referenced from outside it; a `` block is exempt (`instructions/CONTRACT.md`). +- Read-only. + +**SEE ALSO** + +- `wikitool instructions sync` - publishes the copies +- `instructions/CONTRACT.md` - the rules this checks #### `instructions list` @@ -2026,13 +2098,23 @@ List the flat instructions with their descriptions. - budget: counted - network: no +**EXAMPLES** + +- `tools/wikitool instructions list` +- `tools/wikitool instructions list --json` + **EXIT STATUS** - 0 success **NOTES** -This is how the layer is discovered; `search` deliberately covers `kb/` only. Never fails - an empty `instructions/` prints "No instructions found." Safe to retry freely. +- Lists the flat instructions with their descriptions - how the instruction layer is discovered; `search` covers `kb/` only. +- Never fails: an empty `instructions/` prints "No instructions found." Read-only and safe to retry freely. + +**SEE ALSO** + +- `wikitool search` - the same question for `kb/` #### `docs verify` @@ -2050,18 +2132,51 @@ Check the docs that mirror the code. - budget: counted - network: no +**EXAMPLES** + +- `tools/wikitool docs verify` + **EXIT STATUS** - 0 success -- 1 A command, contract, or type-form mismatch was found, a type-spec's own frontmatter fails its schema, a shipped `.md`/`.template` cites an issue number, a reference file's table-of-contents region is missing or stale, or a reference file's relative markdown link does not resolve to an existing file +- 1 A command, contract, or type-form mismatch +- 1 The `` region of `tools/CONTRACT.md` is stale +- 1 A type-spec's own frontmatter fails its schema +- 1 A shipped `.md`/`.template` cites an issue number +- 1 A reference file's table-of-contents region is missing or stale +- 1 A reference file's relative markdown link does not resolve to an existing file **ON FAILURE** -- A command, contract, or type-form mismatch was found, a type-spec's own frontmatter fails its schema, a shipped `.md`/`.template` cites an issue number, a reference file's table-of-contents region is missing or stale, or a reference file's relative markdown link does not resolve to an existing file -> Fix the documentation it names, then re-run. For a type-spec's own frontmatter: fix the field, or add a matching line to `types/type-spec.schema.yaml` if the field is legitimately new. For an issue reference: say what was decided instead of pointing at where, or move the pointer behind a `` block. For a table of contents: run `docs toc --apply` - never hand-write the region. For a dead link: fix the `../` count or the target's name +- A command, contract, or type-form mismatch -> Fix the documentation it names, then re-run +- The `` region of `tools/CONTRACT.md` is stale -> Run `docs contract --apply`, then re-run +- A type-spec's own frontmatter fails its schema -> Fix the field, or add a matching line to `types/type-spec.schema.yaml` if the field is legitimately new +- A shipped `.md`/`.template` cites an issue number -> Say what was decided instead of pointing at where, or move the pointer behind a `` block +- A reference file's table-of-contents region is missing or stale -> Run `docs toc --apply`, then re-run +- A reference file's relative markdown link does not resolve to an existing file -> Fix the `../` count or the target's name + +**NEVER** + +- Never hand-write a table-of-contents region or the commands region - regenerate it. **NOTES** -Check the docs that mirror the code: every command has a `cli_contract` record and is listed in `cli_contract.GROUPS` (both directions, so a command dropped from one is not hidden by the other), every command's non-hidden flags appear in its record's SYNOPSIS and vice versa, `tools/CONTRACT.md`'s generated `` region matches what `cli_contract.render_commands_region()` would write, no command's rendered `--help`/`-h` text cites an issue number, every directory under `kb/` has a `COLLECTION.md` and no directory outside it does, every collection declaring `profile:` and a `required_by_stack:` that agrees with the stack's own list, every type the stack lists (currently `source` and `project`) having a type-spec of that name whose schema requires the field the stack list also names (`raw_files:`/`state:`), `kb/CONVENTIONS.md` naming all three tool-owned section headings if it exists at all, every stage contract present, every file under `types/` declaring `type: types/type-spec.md` validating against `types/type-spec.schema.yaml`, no pre-migration `type: entity` blocks left in the contracts, the `.gitignore` canaries clear in both directions (nothing ignored under `raw/`/`kb/`, `incoming/` ignored, everything ignored under `reports/` and the published skill directories), and no `.md`/`.template` file `dist export` would ship citing an issue number - the tracker exists only in the origin repo, so such a number in a distributed instance is a reference its reader can neither resolve nor recognise as unresolvable (a `` region is exempt: it is already gone from the text the check reads, which is the export plan's, not the working tree's), every reference file `docs toc` covers carrying the current table-of-contents region for its own headings - missing and stale are one check, because the generator is idempotent - and every relative markdown link in one of those same reference files resolving to a file that actually exists (a target's `#anchor` suffix is stripped first; code fences and inline code spans are masked before scanning, so a passage showing link syntax as an example is not mistaken for a real reference). The name is about documentation parity, not about the `docs/` directory - it neither reads nor requires one, the same way `kb/` predates the collection it now checks +- Checks the docs that mirror the code. The name is about documentation parity, not the `docs/` directory - it neither reads nor requires one. +- Commands: every command has a `cli_contract` record and is listed in `cli_contract.GROUPS`, in both directions; every command's non-hidden flags appear in its record's SYNOPSIS and vice versa; `tools/CONTRACT.md`'s generated `` region matches what `docs contract` would write; no command's rendered `--help`/`-h` text cites an issue number. +- Collections: every directory under `kb/` has a `COLLECTION.md` and no directory outside it does; every collection declares `profile:` and a `required_by_stack:` that agrees with the stack's own list. +- Types: every type the stack lists (currently `source` and `project`) has a type-spec of that name whose schema requires the field the stack list names (`raw_files:`/`state:`); every file under `types/` declaring `type: types/type-spec.md` validates against `types/type-spec.schema.yaml`; no pre-migration `type: entity` blocks are left in the contracts. +- `kb/CONVENTIONS.md`, if it exists at all, names all three tool-owned section headings; every stage contract is present. +- The `.gitignore` canaries clear in both directions: nothing ignored under `raw/`/`kb/`, `incoming/` ignored, everything ignored under `reports/` and the published skill directories. +- No `.md`/`.template` file `dist export` would ship cites an issue number. A `` region is exempt: the check reads the export plan's text, from which it is already gone. +- Every reference file `docs toc` covers carries the current table-of-contents region for its own headings - missing and stale are one check. +- Every relative markdown link in one of those reference files resolves to an existing file. A target's `#anchor` suffix is stripped first, and code fences and inline code spans are masked before scanning, so link syntax shown as an example is not mistaken for a real reference. +- Read-only. + +**SEE ALSO** + +- `wikitool docs toc` - regenerates tables of contents +- `wikitool docs contract` - regenerates the commands region +- `wikitool instructions verify` - the same kind of check for `instructions/` #### `docs toc` @@ -2079,18 +2194,31 @@ Create, refresh or remove the generated table-of-contents region. - budget: counted - network: no +**EXAMPLES** + +- `tools/wikitool docs toc` +- `tools/wikitool docs toc --apply` + **EXIT STATUS** - 0 success -- 1 Never fails on content: a file with no `##` heading, or one at or under the threshold, is simply left without a region +- 0 Never fails on content: a file with no `##` heading, or one at or under the threshold, is simply left without a region -**ON FAILURE** +**NEVER** -- Never fails on content: a file with no `##` heading, or one at or under the threshold, is simply left without a region -> Nothing to fix - re-run with `--apply` to write what the dry run listed. If `docs verify` still reports a stale region afterwards, the file's `##` headings changed in between; run it again +- Never hand-write or hand-edit a table-of-contents region. **NOTES** -On every reference file over 100 lines, in the scope Anthropic's skill-authoring guidance names for a file previewed rather than read in full: `AGENTS.md`, every stage contract, `kb/CONVENTIONS.md`, every `kb/*/COLLECTION.md`, every flat `instructions/**.md` file, every `types/*.md` type-spec, and every `docs/` page - each together with the `.template` it ships as, where one exists. Computed from those categories rather than listed, so a file added later is in scope without a code change. A template is in scope because it is the same document one step earlier in its life: an instance adopts it by copying it back, so a region missing there is a region missing in the adopted file, which is how `kb/CONVENTIONS.md.template` came to grow past the threshold with no region and left every instance adopting it failing `docs verify` at the end of its own setup. `SKILL.md` is the one exception, and the same guidance is why: it places a skill body on the loading level that is read whole when the skill triggers, and aims its own TOC advice at the bundled reference files a skill points *at*. Human docs (`README.md`, `CHANGES.md`, `EVALS.md`, `INSTALL.md`, `tools/README.md`) are out of scope because AGENTS.md § File naming says no agent loads them as instruction. Dry-run by default (prints which files would change); `--apply` writes. `docs verify` checks the result stays current the same way it checks every other generated-from-code copy +- Creates, refreshes or removes the generated table-of-contents region on every reference file over 100 lines: `AGENTS.md`, every stage contract, `kb/CONVENTIONS.md`, every `kb/*/COLLECTION.md`, every flat `instructions/**.md` file, every `types/*.md` type-spec, and every `docs/` page - each together with the `.template` it ships as, where one exists. +- The scope is computed from those categories rather than listed, so a file added later is in scope without a code change. +- Out of scope: every `SKILL.md`, and the human docs (`README.md`, `CHANGES.md`, `EVALS.md`, `INSTALL.md`, `tools/README.md`). +- Dry-run by default (prints which files would change); `--apply` writes. Idempotent: a re-run after an interruption converges rather than doubling a region. +- If `docs verify` still reports a stale region after `--apply`, the file's `##` headings changed in between; run it again. + +**SEE ALSO** + +- `wikitool docs verify` - checks every region is current #### `docs contract` @@ -2108,13 +2236,29 @@ Regenerate `tools/CONTRACT.md`'s `` region. - budget: counted - network: no +**EXAMPLES** + +- `tools/wikitool docs contract` +- `tools/wikitool docs contract --apply` + **EXIT STATUS** - 0 success +**NEVER** + +- Never hand-edit the region - change the record in code and re-run this. + **NOTES** -Rebuilds the region from `cli_contract.all_records()`: the index (one line per command, `GROUPS` order) followed by each `###`-group's commands as `#### ` man-page-shaped sections. Dry-run by default, like `docs toc`; `--apply` writes. `docs verify`'s `check_commands_region` checks the result stays current the same way it checks every other generated-from-code copy. +- Rebuilds the region from every `cli_contract` record: the index (one line per command, `GROUPS` order) followed by each `###` group's commands as `#### ` man-page-shaped sections. +- Dry-run by default (says whether the file would change); `--apply` writes. +- `docs verify` checks the result stays current. + +**SEE ALSO** + +- `tools/README.md` § Adding a command - how a command gets its record +- `wikitool docs verify` - checks the region is current ### Telemetry diff --git a/tools/chemenu/commands/docs_verify.py b/tools/chemenu/commands/docs_verify.py index 9e42061..c3a4fbc 100644 --- a/tools/chemenu/commands/docs_verify.py +++ b/tools/chemenu/commands/docs_verify.py @@ -259,7 +259,9 @@ def check_command_contracts() -> list[str]: function by `@cli_contract.record(...)`. A record with no matching command, a command with no record, or a `GROUPS` entry appearing twice are the three ways that pairing can drift; each is its own issue so a - session sees exactly which command needs attention. + session sees exactly which command needs attention. Both directions are + checked so that a command dropped from one side is not hidden by the + other. """ registered = sorted(registered_commands()) issues: list[str] = [] @@ -1044,47 +1046,78 @@ def check_breaking_change_for_boundary() -> list[str]: atomic="Read-only", budget=cli_contract.Budget.COUNTED, ), - notes="Check the docs that mirror the code: every command has a `cli_contract` record and " - "is listed in `cli_contract.GROUPS` (both directions, so a command dropped from one is not " - "hidden by the other), every command's non-hidden flags appear in its record's SYNOPSIS and " - "vice versa, `tools/CONTRACT.md`'s generated `` region matches " - "what `cli_contract.render_commands_region()` would write, no command's rendered " - "`--help`/`-h` text cites an issue number, every directory under `kb/` has a " - "`COLLECTION.md` and no directory outside it does, every collection declaring `profile:` " - "and a `required_by_stack:` that agrees with the stack's own list, every type the stack " - "lists (currently `source` and `project`) having a type-spec of that name whose schema " - "requires the field the stack list also names (`raw_files:`/`state:`), `kb/CONVENTIONS.md` " - "naming all three tool-owned section headings if it exists at all, every stage contract " - "present, every file under `types/` declaring `type: types/type-spec.md` validating " - "against `types/type-spec.schema.yaml`, no pre-migration `type: entity` blocks left in the " - "contracts, the `.gitignore` canaries clear in both directions (nothing ignored under " - "`raw/`/`kb/`, `incoming/` ignored, everything ignored under `reports/` and the published " - "skill directories), and no `.md`/`.template` file `dist export` would ship citing an issue " - "number - the tracker exists only in the origin repo, so such a number in a distributed " - "instance is a reference its reader can neither resolve nor recognise as unresolvable (a " - "`` region is exempt: it is already gone from the text the " - "check reads, which is the export plan's, not the working tree's), every reference file " - "`docs toc` covers carrying the current table-of-contents region for its own headings - " - "missing and stale are one check, because the generator is idempotent - and every relative " - "markdown link in one of those same reference files resolving to a file that actually " - "exists (a target's `#anchor` suffix is stripped first; code fences and inline code spans " - "are masked before scanning, so a passage showing link syntax as an example is not mistaken " - "for a real reference). The name is about documentation parity, not about the `docs/` " - "directory - it neither reads nor requires one, the same way `kb/` predates the collection " - "it now checks", - failures=(cli_contract.Failure( - label="", - cause="A command, contract, or type-form mismatch was found, a type-spec's own " - "frontmatter fails its schema, a shipped `.md`/`.template` cites an issue number, a " - "reference file's table-of-contents region is missing or stale, or a reference file's " - "relative markdown link does not resolve to an existing file", - reaction="Fix the documentation it names, then re-run. For a type-spec's own frontmatter: " - "fix the field, or add a matching line to `types/type-spec.schema.yaml` if the field is " - "legitimately new. For an issue reference: say what was decided instead of pointing at " - "where, or move the pointer behind a `` block. For a table " - "of contents: run `docs toc --apply` - never hand-write the region. For a dead link: " - "fix the `../` count or the target's name", - ),), + notes=( + "Checks the docs that mirror the code. The name is about documentation parity, not the " + "`docs/` directory - it neither reads nor requires one.", + "Commands: every command has a `cli_contract` record and is listed in " + "`cli_contract.GROUPS`, in both directions; every command's non-hidden flags appear in " + "its record's SYNOPSIS and vice versa; `tools/CONTRACT.md`'s generated " + "`` region matches what `docs contract` would write; no " + "command's rendered `--help`/`-h` text cites an issue number.", + "Collections: every directory under `kb/` has a `COLLECTION.md` and no directory " + "outside it does; every collection declares `profile:` and a `required_by_stack:` that " + "agrees with the stack's own list.", + "Types: every type the stack lists (currently `source` and `project`) has a type-spec " + "of that name whose schema requires the field the stack list names " + "(`raw_files:`/`state:`); every file under `types/` declaring `type: " + "types/type-spec.md` validates against `types/type-spec.schema.yaml`; no " + "pre-migration `type: entity` blocks are left in the contracts.", + "`kb/CONVENTIONS.md`, if it exists at all, names all three tool-owned section headings; " + "every stage contract is present.", + "The `.gitignore` canaries clear in both directions: nothing ignored under " + "`raw/`/`kb/`, `incoming/` ignored, everything ignored under `reports/` and the " + "published skill directories.", + "No `.md`/`.template` file `dist export` would ship cites an issue number. A " + "`` region is exempt: the check reads the export plan's " + "text, from which it is already gone.", + "Every reference file `docs toc` covers carries the current table-of-contents region " + "for its own headings - missing and stale are one check.", + "Every relative markdown link in one of those reference files resolves to an existing " + "file. A target's `#anchor` suffix is stripped first, and code fences and inline code " + "spans are masked before scanning, so link syntax shown as an example is not mistaken " + "for a real reference.", + "Read-only.", + ), + failures=( + cli_contract.Failure( + cause="A command, contract, or type-form mismatch", + reaction="Fix the documentation it names, then re-run", + ), + cli_contract.Failure( + cause="The `` region of `tools/CONTRACT.md` is stale", + reaction="Run `docs contract --apply`, then re-run", + ), + cli_contract.Failure( + cause="A type-spec's own frontmatter fails its schema", + reaction="Fix the field, or add a matching line to `types/type-spec.schema.yaml` if " + "the field is legitimately new", + ), + cli_contract.Failure( + cause="A shipped `.md`/`.template` cites an issue number", + reaction="Say what was decided instead of pointing at where, or move the pointer " + "behind a `` block", + ), + cli_contract.Failure( + cause="A reference file's table-of-contents region is missing or stale", + reaction="Run `docs toc --apply`, then re-run", + ), + cli_contract.Failure( + cause="A reference file's relative markdown link does not resolve to an existing " + "file", + reaction="Fix the `../` count or the target's name", + ), + ), + examples=( + "tools/wikitool docs verify", + ), + never=( + "Never hand-write a table-of-contents region or the commands region - regenerate it.", + ), + see_also=( + "`wikitool docs toc` - regenerates tables of contents", + "`wikitool docs contract` - regenerates the commands region", + "`wikitool instructions verify` - the same kind of check for `instructions/`", + ), )) def verify(): """Check the docs that mirror the code.""" @@ -1138,31 +1171,37 @@ def verify(): "dry-run form is read-only", budget=cli_contract.Budget.COUNTED, ), - notes="On every reference file over 100 lines, in the scope Anthropic's skill-authoring " - "guidance names for a file previewed rather than read in full: `AGENTS.md`, every stage " - "contract, `kb/CONVENTIONS.md`, every `kb/*/COLLECTION.md`, every flat " - "`instructions/**.md` file, every `types/*.md` type-spec, and every `docs/` page - each " - "together with the `.template` it ships as, where one exists. Computed from those " - "categories rather than listed, so a file added later is in scope without a code change. A " - "template is in scope because it is the same document one step earlier in its life: an " - "instance adopts it by copying it back, so a region missing there is a region missing in " - "the adopted file, which is how `kb/CONVENTIONS.md.template` came to grow past the " - "threshold with no region and left every instance adopting it failing `docs verify` at the " - "end of its own setup. `SKILL.md` is the one exception, and the same guidance is why: it " - "places a skill body on the loading level that is read whole when the skill triggers, and " - "aims its own TOC advice at the bundled reference files a skill points *at*. Human docs " - "(`README.md`, `CHANGES.md`, `EVALS.md`, `INSTALL.md`, `tools/README.md`) are out of scope " - "because AGENTS.md § File naming says no agent loads them as instruction. Dry-run by " - "default (prints which files would change); `--apply` writes. `docs verify` checks the " - "result stays current the same way it checks every other generated-from-code copy", + notes=( + "Creates, refreshes or removes the generated table-of-contents region on every " + "reference file over 100 lines: `AGENTS.md`, every stage contract, " + "`kb/CONVENTIONS.md`, every `kb/*/COLLECTION.md`, every flat `instructions/**.md` file, " + "every `types/*.md` type-spec, and every `docs/` page - each together with the " + "`.template` it ships as, where one exists.", + "The scope is computed from those categories rather than listed, so a file added later " + "is in scope without a code change.", + "Out of scope: every `SKILL.md`, and the human docs (`README.md`, `CHANGES.md`, " + "`EVALS.md`, `INSTALL.md`, `tools/README.md`).", + "Dry-run by default (prints which files would change); `--apply` writes. Idempotent: a " + "re-run after an interruption converges rather than doubling a region.", + "If `docs verify` still reports a stale region after `--apply`, the file's `##` " + "headings changed in between; run it again.", + ), failures=(cli_contract.Failure( - label="", cause="Never fails on content: a file with no `##` heading, or one at or under the " "threshold, is simply left without a region", - reaction="Nothing to fix - re-run with `--apply` to write what the dry run listed. If " - "`docs verify` still reports a stale region afterwards, the file's `##` headings " - "changed in between; run it again", + reaction="", + code=0, ),), + examples=( + "tools/wikitool docs toc", + "tools/wikitool docs toc --apply", + ), + never=( + "Never hand-write or hand-edit a table-of-contents region.", + ), + see_also=( + "`wikitool docs verify` - checks every region is current", + ), )) def toc_command( apply: bool = typer.Option(False, "--apply", help="Write changes; default is dry-run (preview only)"), @@ -1205,12 +1244,25 @@ def toc_command( atomic="Yes - the whole region is rewritten in one file write", budget=cli_contract.Budget.COUNTED, ), - notes="Rebuilds the region from `cli_contract.all_records()`: the index (one line per " - "command, `GROUPS` order) followed by each `###`-group's commands as `#### ` " - "man-page-shaped sections. Dry-run by default, like `docs toc`; `--apply` writes. " - "`docs verify`'s `check_commands_region` checks the result stays current the same way it " - "checks every other generated-from-code copy.", + notes=( + "Rebuilds the region from every `cli_contract` record: the index (one line per " + "command, `GROUPS` order) followed by each `###` group's commands as `#### ` " + "man-page-shaped sections.", + "Dry-run by default (says whether the file would change); `--apply` writes.", + "`docs verify` checks the result stays current.", + ), failures=(), + examples=( + "tools/wikitool docs contract", + "tools/wikitool docs contract --apply", + ), + never=( + "Never hand-edit the region - change the record in code and re-run this.", + ), + see_also=( + "`tools/README.md` § Adding a command - how a command gets its record", + "`wikitool docs verify` - checks the region is current", + ), )) def contract_command( apply: bool = typer.Option(False, "--apply", help="Write changes; default is dry-run (preview only)"), diff --git a/tools/chemenu/commands/instructions_cmd.py b/tools/chemenu/commands/instructions_cmd.py index e9995e6..2f2db66 100644 --- a/tools/chemenu/commands/instructions_cmd.py +++ b/tools/chemenu/commands/instructions_cmd.py @@ -378,19 +378,38 @@ def check_skill_reference_paths() -> list[str]: "partial failure", budget=cli_contract.Budget.COUNTED, ), - notes="Publish every `instructions//SKILL.md` into `.agents/skills/` and " - "`.claude/skills/` as **copies**, and delete published skills whose source is gone. Both " - "targets are gitignored, so a fresh clone runs this once - see `instructions/bootstrap.md`. " - "Re-running is also how a drifted copy is repaired: the source always wins. `--force` is " - "required only to replace a target directory that is not a published skill at all (no " - "`SKILL.md` in it)", - failures=(cli_contract.Failure( - label="", - cause="No skills found under `instructions/`, or a target directory is not a " - "published skill (no `SKILL.md`) and `--force` was not passed", - reaction="Check whether the flagged target holds anything worth keeping, then re-run with " - "`--force` if not; otherwise fix the named cause and retry", - ),), + notes=( + "Publishes every `instructions//SKILL.md` into `.agents/skills/` and " + "`.claude/skills/` as **copies**, and deletes published skills whose source is gone.", + "Both targets are gitignored, so a fresh clone runs this once.", + "Re-running repairs a drifted copy: the source always wins.", + "`--force` is required only to replace a target directory that is not a published " + "skill at all (no `SKILL.md` in it).", + "Each copy is idempotent, so a re-run converges even after a partial failure.", + ), + failures=( + cli_contract.Failure( + cause="No skills found under `instructions/`", + reaction="Fix the named cause and retry", + ), + cli_contract.Failure( + cause="A target directory is not a published skill (no `SKILL.md`) and `--force` was " + "not passed", + reaction="Check whether the flagged target holds anything worth keeping, then re-run " + "with `--force` if not", + ), + ), + examples=( + "tools/wikitool instructions sync", + ), + never=( + "Never hand-edit a published copy under `.agents/skills/` or `.claude/skills/` - edit " + "the source and re-run this.", + ), + see_also=( + "`instructions/bootstrap.md` - the fresh-clone procedure that runs this", + "`wikitool instructions verify` - checks the copies match", + ), )) def sync( force: bool = typer.Option( @@ -438,27 +457,57 @@ def sync( atomic="Read-only", budget=cli_contract.Budget.COUNTED, ), - notes="Flat instructions validate against `types/instruction.schema.yaml`, each `SKILL.md` " - "carries the frontmatter its harness reads, no `SKILL.md` carries a relative markdown link " - "(`sync` copies it to a different depth than the source, so a `SKILL.md` references a " - "target as a repo-root-relative plain path instead - see `instructions/CONTRACT.md` § \"A " - "skill's outbound reference is a plain path, not a link\"), every published copy is " - "byte-identical to its source, no instruction is left that nothing references, and nothing " - "under `instructions/dev/` is referenced from outside it (a " - "`` block is exempt - see `instructions/CONTRACT.md`). Missing " - "*every* copy is reported as \"run sync\", not as drift - that is a clean checkout", - failures=(cli_contract.Failure( - label="", - cause="Nothing found under `instructions/` at all, a malformed instruction or " - "`SKILL.md`, a `SKILL.md` carrying a relative markdown link, a published copy that " - "drifted from its source, an instruction nothing references (or, for `manual: true`, " - "one that IS linked from AGENTS.md, CLAUDE.md, or a skill and so risks running " - "implicitly), or something under `instructions/dev/` referenced from outside it and " - "outside a `dist:strip` block", - reaction="Fix the flagged file, then re-run. For a relative link in a `SKILL.md`, rewrite " - "it as a repo-root-relative plain path instead. For drift, re-run `sync` instead of " - "hand-editing the published copy - the source under `instructions/` always wins", - ),), + notes=( + "Flat instructions validate against `types/instruction.schema.yaml`, and each " + "`SKILL.md` carries the frontmatter its harness reads.", + "No `SKILL.md` carries a relative markdown link: `sync` copies it to a different depth " + "than the source, so a `SKILL.md` references a target as a repo-root-relative plain " + "path instead (`instructions/CONTRACT.md` § \"A skill's outbound reference is a plain " + "path, not a link\").", + "Every published copy is byte-identical to its source. Missing *every* copy is " + "reported as \"run sync\", not as drift - that is a clean checkout.", + "No instruction is left that nothing references; one marked `manual: true` must " + "instead not be linked from AGENTS.md, CLAUDE.md or a skill.", + "Nothing under `instructions/dev/` is referenced from outside it; a " + "`` block is exempt (`instructions/CONTRACT.md`).", + "Read-only.", + ), + failures=( + cli_contract.Failure( + cause="Nothing found under `instructions/` at all, or a malformed instruction or " + "`SKILL.md`", + reaction="Fix the flagged file, then re-run", + ), + cli_contract.Failure( + cause="A `SKILL.md` carries a relative markdown link", + reaction="Rewrite it as a repo-root-relative plain path, then re-run", + ), + cli_contract.Failure( + cause="A published copy drifted from its source", + reaction="Re-run `instructions sync` - the source under `instructions/` always wins", + ), + cli_contract.Failure( + cause="An instruction nothing references, or a `manual: true` one that IS linked " + "from AGENTS.md, CLAUDE.md or a skill and so risks running implicitly", + reaction="Link it from where it is used, or drop the link to a manual one, then " + "re-run", + ), + cli_contract.Failure( + cause="Something under `instructions/dev/` is referenced from outside it and outside " + "a `dist:strip` block", + reaction="Remove the reference or wrap it in a `dist:strip` block, then re-run", + ), + ), + examples=( + "tools/wikitool instructions verify", + ), + never=( + "Never fix drift by hand-editing the published copy.", + ), + see_also=( + "`wikitool instructions sync` - publishes the copies", + "`instructions/CONTRACT.md` - the rules this checks", + ), )) def verify(): """Check instructions/ against its type, that no skill carries a relative markdown link, and every published copy against its source.""" @@ -596,9 +645,20 @@ def verify(): atomic="Read-only", budget=cli_contract.Budget.COUNTED, ), - notes="This is how the layer is discovered; `search` deliberately covers `kb/` only. Never " - "fails - an empty `instructions/` prints \"No instructions found.\" Safe to retry freely.", + notes=( + "Lists the flat instructions with their descriptions - how the instruction layer is " + "discovered; `search` covers `kb/` only.", + "Never fails: an empty `instructions/` prints \"No instructions found.\" Read-only and " + "safe to retry freely.", + ), failures=(), + examples=( + "tools/wikitool instructions list", + "tools/wikitool instructions list --json", + ), + see_also=( + "`wikitool search` - the same question for `kb/`", + ), )) def list_instructions( json_out: bool = typer.Option(False, "--json", help="Print the listing as JSON"), diff --git a/tools/chemenu/commands/types_cmd.py b/tools/chemenu/commands/types_cmd.py index 5fdadf7..1530119 100644 --- a/tools/chemenu/commands/types_cmd.py +++ b/tools/chemenu/commands/types_cmd.py @@ -35,9 +35,22 @@ app = typer.Typer(help="Discover and describe Chemenu type-spec contracts.") atomic="Read-only", budget=cli_contract.Budget.COUNTED, ), - notes="Name, schema path, subtype field, and description - discover what page types exist " - "without reading `types/*.md` directly. Never fails. Safe to retry freely.", + notes=( + "Lists every type-spec under `types/`: name, schema path, subtype field, and " + "description - which page types exist, without reading `types/*.md` directly.", + "Never fails; read-only and safe to retry freely.", + ), failures=(), + examples=( + "tools/wikitool types list", + "tools/wikitool types list --json", + ), + never=( + "Never pick a page's directory by hand - `types describe` and `new` compute it.", + ), + see_also=( + "`wikitool types describe ` - one type's full contract", + ), )) def list_types_command( json_out: bool = typer.Option(False, "--json", help="Print raw findings as JSON") @@ -72,19 +85,29 @@ def list_types_command( atomic="Read-only", budget=cli_contract.Budget.COUNTED, ), - notes="Required/optional frontmatter fields with enums, its subtype field (if any), and its " - "authoring body - composed with the stack-owned `types/.guidance.md` where the " - "type-spec declares `guidance:` (`--json` reports it separately as " - "`guidance`/`guidance_path`, absent for a type with none), so a `root: kb` type's contract " - "reads as one answer even though it may live in two files. A type-spec (or its guidance " - "file) over the `docs toc` threshold carries a generated table-of-contents region; it is " - "stripped from this output rather than echoed, since the whole body is being handed over " - "and a navigation aid into it would be noise", + notes=( + "Prints one type's full contract: required and optional frontmatter fields with enums, " + "its subtype field (if any), and its authoring body.", + "Where the type-spec declares `guidance:`, the stack-owned `types/.guidance.md` is " + "composed in, so a `root: kb` type's contract reads as one answer even though it may " + "live in two files. `--json` reports it separately as `guidance`/`guidance_path`, absent " + "for a type with none.", + "The generated table-of-contents region a long type-spec (or guidance file) carries is " + "stripped from this output.", + "Read-only.", + ), failures=(cli_contract.Failure( - label="", cause="Unknown type name", - reaction="Fix the name and retry", + reaction="Fix the name (see `types list`) and retry", ),), + examples=( + "tools/wikitool types describe source", + "tools/wikitool types describe project --json", + ), + see_also=( + "`wikitool types list` - every type", + "`wikitool new ` - scaffolds a page of the type", + ), )) def describe_type_command( name: str = typer.Argument(..., help="Type name, e.g. 'entity' (see `types list`)"),