tools: command records, Types, instructions and docs group - one line per cause, examples, prohibitions (#142)
CI / verify (push) Successful in 1m12s
Release / release (push) Successful in 37s

Files changed:
- CHANGES.md
- VERSION
- tools/CONTRACT.md
- tools/chemenu/commands/docs_verify.py
- tools/chemenu/commands/instructions_cmd.py
- tools/chemenu/commands/types_cmd.py
This commit is contained in:
torben committed 2026-09-26 09:22:16 +02:00
1 parent b9c22f783f
commit 9617d722de
6 files changed
+428 -137

No files matched your search

+13 -1
View File
@@ -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
<!-- /wikitool:bumps -->
### 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
+1 -1
View File
@@ -1 +1 @@
7.1.0-beta.15
7.1.0-beta.16
+163 -19
View File
@@ -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 `<!-- wikitool:commands -->` 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 <name>` - 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/<name>.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/<name>.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 <type>` - scaffolds a page of the type
#### `instructions sync`
@@ -1968,18 +1995,37 @@ Publish every `instructions/<name>/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/<name>/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/<name>/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 `<!-- dist:strip-start/end -->` 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 `<!-- dist:strip-start/end -->` 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 `<!-- wikitool:commands -->` 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 `<!-- dist:strip-start/end -->` 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 `<!-- wikitool:commands -->` 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 `<!-- dist:strip-start/end -->` 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 `<!-- wikitool:commands -->` 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 `<!-- dist:strip-start/end -->` 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 `<!-- wikitool:commands -->` 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 `<!-- dist:strip-start/end -->` 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 `<name>.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 `<name>.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 `<!-- wikitool:commands -->` 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 `#### <path>` 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 `#### <path>` 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
+120 -68
View File
@@ -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 `<!-- wikitool:commands -->` 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 "
"`<!-- dist:strip-start/end -->` 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 `<!-- dist:strip-start/end -->` 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 "
"`<!-- wikitool:commands -->` 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 "
"`<!-- dist:strip-start/end -->` 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 `<!-- wikitool:commands -->` 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 `<!-- dist:strip-start/end -->` 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 `<name>.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 "
"`<name>.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 `#### <path>` "
"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 `#### <path>` "
"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)"),
+96 -36
View File
@@ -378,19 +378,38 @@ def check_skill_reference_paths() -> list[str]:
"partial failure",
budget=cli_contract.Budget.COUNTED,
),
notes="Publish every `instructions/<name>/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/<name>/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 "
"`<!-- dist:strip-start/end -->` 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 "
"`<!-- dist:strip-start/end -->` 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"),
+35 -12
View File
@@ -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 <name>` - 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/<name>.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/<name>.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 <type>` - scaffolds a page of the type",
),
))
def describe_type_command(
name: str = typer.Argument(..., help="Type name, e.g. 'entity' (see `types list`)"),