@erclx/aitk 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # aitk
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@erclx/aitk)](https://www.npmjs.com/package/@erclx/aitk)
4
+ [![CI status](https://img.shields.io/github/actions/workflow/status/erclx/aitk/verify.yml?branch=main)](https://github.com/erclx/aitk/actions/workflows/verify.yml)
5
+ [![License MIT](https://img.shields.io/npm/l/@erclx/aitk)](LICENSE)
6
+
3
7
  One source for your repos' AI conventions. Install once, sync everywhere.
4
8
 
5
9
  ![Adding the aitk marketplace and installing the plugin in Claude Code](assets/install.png)
@@ -39,7 +43,7 @@ Two limits worth knowing before you install. Claude Code is the only agent runti
39
43
 
40
44
  Each domain has a canonical source in this repo and a thin install or sync CLI on your side. The links run to internal narrative, written for someone maintaining the toolkit rather than installing it, so skip them on a first pass.
41
45
 
42
- - [Claude Code plugin](.claude/context/claude-plugin.md): skills for planning, review, docs sync, and the git ship chain
46
+ - [Claude Code plugin](.claude/context/claude-plugin/index.md): skills for planning, review, docs sync, and the git ship chain
43
47
  - [Governance rules](.claude/context/governance.md): Cursor rules and stacks, installable per project
44
48
  - [Standards](.claude/context/standards.md): shared authoring conventions, synced to projects
45
49
  - [Snippets](.claude/context/snippets.md): reusable prompts for Claude and Gemini chat
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aitk",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "0.11.0",
4
+ "version": "0.13.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -35,7 +35,7 @@ Load `${CLAUDE_SKILL_DIR}/references/patterns.md` for the timeline lifecycle, lo
35
35
  - Name functions verb-first: `validate_input`, `deploy_service`, `install_dependencies`.
36
36
  - Do not use global variables except exports from `ask()`.
37
37
  - Do not define unused color variables.
38
- - Do not include comments except the shebang line.
38
+ - Comment only a fact the reader cannot recover from the code, and follow the code-comment rule in `.claude/rules/` when the project installs it.
39
39
  - Use sentence case for section headers and log messages. Proper nouns and product names retain their casing.
40
40
  - Quote variables inside parameter expansions: `"${file#"$dir"/}"` not `"${file#$dir/}"`.
41
41
  - Quote variables in test brackets: `[ "$i" -eq "$cur" ]` not `[ $i -eq $cur ]`.
@@ -55,6 +55,7 @@ One file per kind at `.claude/diagrams/<kind>.md`. Write only the files Step 2 s
55
55
  title: <what the entry answers, sentence case>
56
56
  description: <the question it settles and the signal that drives it>
57
57
  category: <the kind, matching the standard>
58
+ verified: 'TODO: never verified'
58
59
  ---
59
60
 
60
61
  # <Same as title>
@@ -71,8 +72,12 @@ category: <the kind, matching the standard>
71
72
  <one to three explanation paragraphs>
72
73
  ````
73
74
 
75
+ Write `verified` as the literal `TODO: never verified` here, quoted so the colon parses. Step 6 replaces it once a render has been read back, so a SHA written beside the diagram would assert a check that has not run yet. The placeholder is what keeps a render-less project conforming, since Step 5 sends a failed render past Step 6 and nothing stamps the entry. Carry an existing entry's current value across untouched when this pass is rewriting one, and let Step 6 replace that instead.
76
+
74
77
  When sources came from a code scan rather than planning prose, lead the explanation with `Source: code.` and add `Fidelity is lower than prose-driven diagrams. Verify against the project's intent.`
75
78
 
79
+ The code paths the explanation cites are what the `claude-docs` sweep watches for staleness, so cite paths that exist and spell them exactly.
80
+
76
81
  Quote node labels containing spaces or special characters with double quotes (`A["Web shell"]`). Avoid parentheses inside labels, they break some renderers. Use `<br/>` for line breaks inside labels.
77
82
 
78
83
  Run the banned-character scan in `.claude/standards/prose.md` against the whole file, including inside mermaid `subgraph` labels and node text, or `${CLAUDE_SKILL_DIR}/../../standards/prose.md` when the project does not have it. A colon also works as a replacement inside a label. The pedagogical voice the diagram standard asks for is a yield on voice alone and buys no exemption from these bans.
@@ -123,6 +128,20 @@ Fix the source and re-render. Stop after two correction passes on an entry.
123
128
 
124
129
  When a defect survives, keep the entry and name the defect in the chat output. A diagram whose author states the flaw is recoverable. A wrong diagram reported as verified is not.
125
130
 
131
+ ### Stamp the marker
132
+
133
+ Stamp every entry that passed. Set `verified` to the short SHA of `HEAD` and today's ISO date, and delete any `stale` key the `claude-docs` sweep left on it:
134
+
135
+ ```bash
136
+ git rev-parse --short HEAD
137
+ ```
138
+
139
+ The result is one line, `<sha> <date>`, as in `73e9a3f8 2026-08-02`.
140
+
141
+ Stamp nothing else. An entry Step 2 left alone was never re-checked, an entry whose defect survived is known wrong, and an entry whose render was skipped was never seen. All three keep the marker they already carry, and Step 7 names the last two. The field means a render was read back and judged correct, so stamping any of these spends the only signal a reader has on a diagram nobody confirmed.
142
+
143
+ A migration pass stamps on the same rule. Conversion writes entries, Step 5 renders them, and this step reads them back, so the marker records the commit the split was checked against rather than the commit the flat file was written at.
144
+
126
145
  ## Step 7: chat output
127
146
 
128
147
  ```plaintext
@@ -9,11 +9,11 @@ description: Updates `.claude/` planning docs to reflect decisions made during t
9
9
 
10
10
  - If no `.claude/` directory exists, stop: `❌ No .claude/ directory found. Run aitk claude init to set up the workflow.`
11
11
 
12
- The bail on a session that changed nothing lives at the end of Step 2, because it needs the diff to decide.
12
+ The skip for a session that changed nothing lives at the end of Step 2, because it needs the diff to decide. It drops the doc rewrite alone. The diff-driven sweeps in Steps 4 and 5 still run.
13
13
 
14
14
  ## Diff baseline
15
15
 
16
- Steps 2, 4, and 7 share one diff on the usable path. An unusable baseline splits them, per the rule below. Resolve the base ref once and reuse it:
16
+ Steps 2, 4, 5, and 7 share one diff on the usable path. An unusable baseline splits them, per the rule below. Resolve the base ref once and reuse it:
17
17
 
18
18
  ```bash
19
19
  git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null
@@ -32,9 +32,9 @@ An unusable baseline costs only the committed half. `git diff <base> HEAD` is em
32
32
 
33
33
  **Step 2 recovers the committed half.** Read `git log -p -1`, widening to `git log -p -<n>` when the session spans several commits, and read the candidate task files against the working tree. That yields names and content both, which is what lets Step 2 decide on behavior rather than on filenames. A fresh `git init` on `main` with no remote is the ordinary shape of a scaffolded project, so this path carries the evidence rather than covering an edge case.
34
34
 
35
- **Steps 4 and 7 keep the scoped set.** Run them on the working tree and untracked files alone, and skip only when that set comes out empty, each reporting the warning its own step names.
35
+ **Steps 4, 5, and 7 keep the scoped set.** Run them on the working tree and untracked files alone, and skip only when that set comes out empty, each reporting the warning its own step names.
36
36
 
37
- Never substitute the whole tree for a missing baseline, and do not reuse Step 2's commit read in these two for consistency. On a fresh `git init` project the last commit is the scaffold commit, so `git log -p -1` is the whole tree by another route. Step 2 tolerates that because it only reads, and it matches conservatively against outcomes already on the board. These two write, so the same set stubs a wireframe for every uncovered surface in the repository and rewrites every context entry that tree touches.
37
+ Never substitute the whole tree for a missing baseline, and do not reuse Step 2's commit read in these three for consistency. On a fresh `git init` project the last commit is the scaffold commit, so `git log -p -1` is the whole tree by another route. Step 2 tolerates that because it only reads, and it matches conservatively against outcomes already on the board. These three write, so the same set stubs a wireframe for every uncovered surface in the repository, stubs a diagram for every source signal the scaffold introduced, and rewrites every context entry that tree touches.
38
38
 
39
39
  Widening what a step reads is safe. Widening what a step writes is not.
40
40
 
@@ -70,7 +70,17 @@ Keep the match conservative:
70
70
  - Match on the behavior an outcome describes, not on filenames or commit subjects. The path match above only narrowed which task files to open.
71
71
  - Leave an outcome `[ ]` when the diff is ambiguous. An unmarked shipped outcome costs one manual edit, while a wrongly marked one hides work that never happened.
72
72
 
73
- Stop here when the session shows no divergence **and** the diff matches no queued outcome: `✅ No doc updates needed. Session matched the original plan.` Both conditions have to hold. Shipping a queued task exactly as planned is the ordinary case and it reads as no divergence, so a session-only bail would stop the skill before it reaches the marking step.
73
+ Skip Step 3 when the session shows no divergence **and** the diff matches no queued outcome, reporting `✅ No doc updates needed. Session matched the original plan.` Both conditions have to hold. Shipping a queued task exactly as planned is the ordinary case and it reads as no divergence, so a session-only skip would drop the marking step with it.
74
+
75
+ Then run Steps 4 through 8. Step 3 is the only one this skips, because it is the only one driven by the session rather than by the diff or the board. A project with an empty task board making a mechanical change satisfies both conditions above, and stopping here would put an uncovered surface and an uncovered diagram kind out of reach in every such project.
76
+
77
+ Three of the steps that follow write, so each earns the reach separately:
78
+
79
+ - Steps 4 and 5 stub against the diff. These are why the skip is not a stop. A session that changed no docs is exactly when an uncovered surface or diagram kind goes unnoticed.
80
+ - Step 7 rewrites context entries against the diff. The Diff baseline section above already groups it with Steps 4 and 5 as a scoped-set step, so a quiet session is no different from any other for it.
81
+ - Step 8 reads the board rather than the session. Its board-wide scan exists to clear a plan an earlier run stranded, and a run that stops at Step 2 can never reach one.
82
+
83
+ This changes which steps the skill reaches. It does not widen what any of them reads. Steps 4, 5, and 7 still take the same scoped set the Diff baseline section defines, and that section's rule is about the input a write is handed rather than about which writes run.
74
84
 
75
85
  ## Step 3: update
76
86
 
@@ -127,13 +137,59 @@ Output one line per finding:
127
137
 
128
138
  If the sweep finds nothing, skip silently.
129
139
 
130
- ## Step 5: flag diagram staleness
140
+ ## Step 5: diagram staleness sweep
141
+
142
+ Skip this step silently when `.claude/diagrams/` does not exist at `pwd` or holds no entry. An entry is any `*.md` other than `index.md`, so a folder carrying the catalog alone is an empty set. A project that has never run `claude-diagram` is not told on every ship that it has holes. When the baseline is unusable, scope the sweep to the working tree and untracked files, and skip it only when that set is empty, reporting `⚠ No diff to scope against. Skipped the diagram sweep.`
143
+
144
+ This step writes frontmatter and never content. Mermaid bodies and explanation paragraphs are off limits to it. A change that removes a module does not carry the new correct shape of the picture, so rewriting a diagram from it produces a confident wrong diagram, which is worse than the stale one it replaced. The author redraws by running `claude-diagram`.
145
+
146
+ Follow `.claude/standards/diagrams.md` for the marker fields this step writes, or `${CLAUDE_SKILL_DIR}/../../standards/diagrams.md` when the project does not have it.
147
+
148
+ Both findings key on something literally entering or leaving the tree. Anything looser fires on ordinary feature work and rebuilds the ignored warning this sweep replaced.
149
+
150
+ **Contradicted entries.** For each entry, collect the backticked code paths its explanation cites. When a cited path is in the diff as a delete or a rename and no longer exists in the tree, append a `stale` key to that entry's frontmatter naming the path:
151
+
152
+ ```yaml
153
+ stale: 'src/gov/install.ts no longer exists'
154
+ ```
155
+
156
+ Append that key alone. Never edit `verified`, `title`, `description`, or `category`, and never touch the body. When the entry already carries `stale`, extend the existing line rather than adding a second key.
157
+
158
+ **Uncovered kinds.** The standard fixes one source signal per kind. Stub a kind when the diff adds its signal file and no entry covers that kind. The trigger is the signal appearing, never a file under it changing, so a branch editing a component folder that `components.md` already covers produces nothing here.
131
159
 
132
- If `.claude/DIAGRAMS.md` exists at `pwd` and this session edited any source `claude-diagram` reads (planning docs, deploy or infrastructure config, top-level component folders), surface a one-line warning:
160
+ | Signal added by the diff | Kind stubbed when absent |
161
+ | ------------------------------------------------------------------------------------------------------------------ | ------------------------ |
162
+ | `.claude/REQUIREMENTS.md` | `system-context.md` |
163
+ | `.claude/ARCHITECTURE.md` | `components.md` |
164
+ | A deploy or infrastructure config (`Dockerfile`, `.github/workflows/*`, `vercel.json`, `fly.toml`, `compose.yaml`) | `deployment.md` |
133
165
 
134
- `⚠ DIAGRAMS.md may be stale. Run /claude-diagram`
166
+ Leave `request-flow.md` and `data-pipeline.md` out. Neither has a source signal a diff can point at, so a rule covering them would guess at when they went stale.
135
167
 
136
- Do not regenerate inline. The author decides when to re-run the diagram skill. Skip the step silently when `.claude/DIAGRAMS.md` does not exist.
168
+ Write the stub at `.claude/diagrams/<kind>.md`:
169
+
170
+ ```markdown
171
+ ---
172
+ title: <Kind as title case>
173
+ description: 'TODO: name the question this entry settles.'
174
+ category: <the category the standard fixes for this kind>
175
+ verified: 'TODO: never verified'
176
+ ---
177
+
178
+ # <Kind as title case>
179
+
180
+ TODO: draw this. `<signal path>` entered the tree with no entry covering this kind.
181
+
182
+ Run `/claude-diagram <kind>` to replace the stub.
183
+ ```
184
+
185
+ No mermaid fence. An empty stub is visible debt that reaches review through the branch diff, while a generated diagram nobody rendered is invisible debt that reads as verified. A fence here invites the next session to fill it in without a render.
186
+
187
+ Output one line per finding:
188
+
189
+ - `⚠ Diagram stale: .claude/diagrams/<kind>.md cites <path>, which left the tree`
190
+ - `📝 Stubbed: .claude/diagrams/<kind>.md`
191
+
192
+ If the sweep finds nothing, skip silently. An ordinary change that adds no signal and deletes no cited path produces no output at all.
137
193
 
138
194
  ## Step 6: flag CLAUDE.md drift
139
195
 
@@ -202,3 +258,5 @@ Output one line per file updated:
202
258
  If no files were updated and nothing was swept, output:
203
259
 
204
260
  `✅ No changes needed.`
261
+
262
+ Suppress that line when Step 2 already reported no doc updates. It closes the run on its own, and emitting both leaves a quiet session reporting success twice for one outcome.
@@ -85,6 +85,8 @@ Flag only issues that will definitely cause incorrect behavior or break a docume
85
85
  - Linter territory
86
86
  - Issues that depend on unverified state
87
87
 
88
+ A comment that makes a false claim about the code is a correctness finding, not a style one. Flag it past the exclusions above and let the severity ladder rank it.
89
+
88
90
  If uncertain, do not flag. False positives erode trust.
89
91
 
90
92
  ### Severity
@@ -33,7 +33,7 @@ Load `${CLAUDE_SKILL_DIR}/references/template.md` for the base skeleton. Copy it
33
33
  - Decompose by responsibility. Each function does one thing, `main()` orchestrates.
34
34
  - Name functions verb-first: `parse_args`, `fetch_data`, `validate_input`.
35
35
  - Quote variables in expansions and test brackets.
36
- - Do not include comments except the shebang line.
36
+ - Comment only a fact the reader cannot recover from the code, and follow the code-comment rule in `.claude/rules/` when the project installs it.
37
37
  - Use 2-space indentation.
38
38
 
39
39
  ## Validation
@@ -7,7 +7,7 @@ description: Creates a new standard file in `standards/` (toolkit repo) or `.cla
7
7
 
8
8
  Read these files in parallel:
9
9
 
10
- - `${CLAUDE_SKILL_DIR}/references/standard.md`: the meta-standard for a standard's shape, frontmatter, and structure
10
+ - `.claude/standards/standard.md` from the project root: the meta-standard for a standard's shape, frontmatter, and structure
11
11
  - `.claude/standards/prose.md` from the project root: prose conventions for all generated text
12
12
 
13
13
  Read a standard from `${CLAUDE_SKILL_DIR}/../../standards/` instead when the project does not have it.
@@ -35,4 +35,4 @@ What each `aitk` sync or install command does to existing files in a target proj
35
35
 
36
36
  ## Source of truth
37
37
 
38
- - Full semantics live in the toolkit's `.claude/context/tooling.md`, `standards.md`, and `claude-plugin.md`. This skill is the target-session summary. When they disagree, the context docs win.
38
+ - Full semantics live in the toolkit's `.claude/context/tooling.md`, `standards.md`, and `claude-plugin/`. This skill is the target-session summary. When they disagree, the context docs win.
package/docs/agents.md CHANGED
@@ -204,14 +204,16 @@ aitk sandbox check claude:docs drift --json
204
204
 
205
205
  | Flag | Effect |
206
206
  | ------------------- | ---------------------------------------------------------- |
207
- | `--envelope <file>` | Read `is_error`, `num_turns`, and denials from a run |
207
+ | `--envelope <file>` | Read `is_error`, `num_turns`, denials, and the reply text |
208
208
  | `--writes <file>` | Newline-delimited paths the session wrote, for write scope |
209
209
  | `--json` | Emit the verdict record on stdout |
210
210
  | `--strict` | Exit 1 on `unchecked` instead of 0 |
211
211
 
212
212
  The verdict `state` is `pass`, `fail`, or `unchecked`. An arm with no `expect.toml` is `unchecked` and exits 0, so the harness stays usable while expectations roll out. A declaration that exists but asserts nothing is a failure, since an expectation file that asserts nothing passes every run.
213
213
 
214
- Omitting `--writes` or `--envelope` does not silently drop the assertion kinds that need them. Write scope and the turn ceiling report as unchecked and appear in the count, so the standalone command cannot claim more coverage than it had. A verdict never reports `pass` with zero assertions.
214
+ Omitting `--writes` or `--envelope` does not silently drop the assertion kinds that need them. Write scope, the turn ceiling, and the reply assertion report as unchecked and appear in the count, so the standalone command cannot claim more coverage than it had. A verdict never reports `pass` with zero assertions.
215
+
216
+ An envelope that parses but carries no `result` field skips the reply assertion the same way an absent file does. An envelope carrying an empty `result` fails it, since a run that returned no text is a finding rather than a gap in the input.
215
217
 
216
218
  Exit 0 means `pass` or `unchecked`. Exit 1 means `fail`, or a caller error: a malformed target, or a sandbox that was never provisioned. A missing sandbox reports as an error rather than a failed verdict, because failing every path assertion would read as a skill that did nothing. `--strict` moves `unchecked` to exit 1 for a caller that has finished arming its scenarios.
217
219
 
@@ -239,6 +241,8 @@ The record carries every scenario with the arms that declare, plus `totalScenari
239
241
  - `aitk docs list [--json]` lists the downstream catalog: the consumer-facing `docs/` surface plus per-domain narrative from `.claude/context/` when that root is present. Toolkit-internal context entries (`ci`, `development`, `extensions`, `sandbox`) are dropped. From a registry install the context section is absent rather than empty.
240
242
  - `aitk docs <topic>` prints one doc to stdout, resolved by exact name from `docs/` first, then `.claude/context/`. Any doc the install carries is reachable by name, including the toolkit-internal topics the list omits.
241
243
 
244
+ A domain too large for one file splits into `<domain>/` with a generated `index.md`, and both verbs name it by the folder. `aitk docs <domain>` prints that index, which is the catalog routing to the sub-area files, and the listing describes it from the index's `subtitle` where a sibling file supplies `description`. A sibling file wins over a folder of the same name. A folder carrying no `index.md` is absent from both, since a catalog is what makes the sub-areas reachable.
245
+
242
246
  Data prints to stdout and the frame to stderr, so `aitk docs <topic> > out.md` captures clean markdown. With no topic and no verb, `aitk docs` runs `list`. An unknown topic names the available topics on stderr and exits 1.
243
247
 
244
248
  Only a `---` block opening on the first line counts as frontmatter, so a document body carrying horizontal rules emits whole.
@@ -321,6 +325,8 @@ Two exclusions are structural rather than tuning. Heredoc bodies are dropped fro
321
325
 
322
326
  The degradation sweep reads its vocabulary from whichever rule publishes a `## Degradation vocabulary` heading, preferring `.claude/rules/` over `governance/rules/`, so one definition serves the toolkit and every target. Discovery anchors on the heading rather than a filename, because a renumbered rule would otherwise empty the vocabulary while the sweep still reported clean. With no such rule the sweep reports **skipped** rather than zero hits, since finding nothing and looking for nothing mean opposite things.
323
327
 
328
+ `090-code-comments` is the rule that publishes the list, and it ships on the `base` stack. A project that installs or syncs governance for the first time after that rule landed gets a sweep that previously reported skipped, so hits appear where the command used to stay quiet. Edit the backticked terms in the installed copy to change what that project sweeps for. The sweep matches comment text, so a comment naming a term as an example is a hit, and a hit is a prompt to read the line rather than a verdict on it.
329
+
324
330
  ## Runtime catalogs
325
331
 
326
332
  Use these to discover what's available instead of hardcoding names.
@@ -20,12 +20,13 @@ Project docs live in `.claude/` at the project root.
20
20
  ├── ARCHITECTURE.md ← technical design decisions
21
21
  ├── DESIGN.md ← visual intent and token decisions (UI projects)
22
22
  ├── WIREFRAMES.md ← ASCII wireframes: layout, UI copy, and interaction rules (UI projects)
23
+ ├── diagrams/ ← one Mermaid entry per diagram kind with a generated index.md
23
24
  ├── tasks/ ← one file per task with a generated index.md, gitignored local scratch
24
25
  ├── context/ ← per-domain narrative loaded on demand via index.md
25
26
  └── rules/ ← path-scoped governance rules, written by aitk gov install
26
27
  ```
27
28
 
28
- Three tiers of context load with different cost: always-loaded (root `CLAUDE.md`, `.claude/REQUIREMENTS.md`, `.claude/ARCHITECTURE.md`), path-scoped lazy (`.claude/rules/<scope>.md` with `paths:` glob), and on-demand lookup (`.claude/context/<domain>.md` discovered via `.claude/context/index.md`). See [the context model](../.claude/context/context-model.md) for the full picture.
29
+ Three tiers of context load with different cost: always-loaded (root `CLAUDE.md`, `.claude/REQUIREMENTS.md`, `.claude/ARCHITECTURE.md`), path-scoped lazy (`.claude/rules/<scope>.md` with `paths:` glob), and on-demand lookup (`.claude/context/<domain>.md`, or `.claude/context/<domain>/` once a domain outgrows one file, discovered via `.claude/context/index.md`). See [the context model](../.claude/context/context-model.md) for the full picture.
29
30
 
30
31
  Run `aitk init` to seed the `.claude/` directory, a root `CLAUDE.md` file, and `.claude/rules/` in one pass. `aitk init` chains claude init and governance install. Claude Code auto-loads every file in `.claude/rules/` at session start, applying always-on rules unconditionally and path-scoped rules to files matching their `paths:` glob.
31
32
 
@@ -103,4 +103,4 @@ port-collision detail.
103
103
 
104
104
  - [Claude Code and git worktrees](../wiki/claude-worktrees.md) for the isolation and fan-out mechanics
105
105
  - [Claude Code subagents](../wiki/claude-subagents.md) for in-session parallelism without worktrees
106
- - `.claude/context/claude-plugin.md` for how the skills in the loop are categorized
106
+ - `.claude/context/claude-plugin/skills.md` for how the skills in the loop are categorized
@@ -57,7 +57,7 @@ Scaffold installs tooling and seeds. It does not fill the planning docs or the d
57
57
 
58
58
  1. Fill `.claude/REQUIREMENTS.md` and `.claude/ARCHITECTURE.md`. The seed provides the files, the scope and decisions are yours to write.
59
59
  2. For a UI project, invoke `aitk:claude-design-propose` to draft `.claude/DESIGN.md` from the requirements and a `## Personality` section. Skip for non-UI projects.
60
- 3. Optionally invoke `aitk:claude-diagram` to draft entries under `.claude/diagrams/` from the architecture and the requirements. One file per diagram kind, so a later refresh of one kind leaves the others untouched. It renders each diagram it writes to verify the layout, which downloads the Mermaid CLI on first use and takes about 15 seconds. A machine without a renderer still gets the diagrams and is told which check was skipped.
60
+ 3. Optionally invoke `aitk:claude-diagram` to draft entries under `.claude/diagrams/` from the architecture and the requirements. One file per diagram kind, so a later refresh of one kind leaves the others untouched. It renders each diagram it writes to verify the layout, which downloads the Mermaid CLI on first use and takes about 15 seconds. A machine without a renderer still gets the diagrams and is told which check was skipped. Each entry records the commit and date it was last verified against, and `aitk:claude-docs` maintains that record on every ship: it annotates an entry whose cited code path left the tree and stubs a kind whose source signal arrived uncovered. The sweep writes frontmatter only, so a diagram's picture and prose change when you redraw them and at no other time.
61
61
  4. Start the feature loop. See [AI workflow](ai-workflow.md) for the per-feature sequence.
62
62
 
63
63
  ### Stack decision
@@ -179,5 +179,5 @@ Sync also refuses a target whose working tree is dirty, so commit or stash befor
179
179
 
180
180
  - [agents](agents.md): CLI flags, exit codes, and JSON output shapes
181
181
  - [AI workflow](ai-workflow.md): feature-development loop inside a toolkit-managed project
182
- - [tooling](../.claude/context/tooling.md), [governance](../.claude/context/governance.md), [claude plugin](../.claude/context/claude-plugin.md), [indexes](../.claude/context/indexes.md), [snippets](../.claude/context/snippets.md), [standards](../.claude/context/standards.md): per-domain mechanics
182
+ - [tooling](../.claude/context/tooling.md), [governance](../.claude/context/governance.md), [claude plugin](../.claude/context/claude-plugin/index.md), [indexes](../.claude/context/indexes.md), [snippets](../.claude/context/snippets.md), [standards](../.claude/context/standards.md): per-domain mechanics
183
183
  - [sandbox](../.claude/context/sandbox.md): scenario catalog for verifying domain flows
@@ -130,7 +130,7 @@ Zero or one yes: tier 0. Two or three: tier 1. Four: tier 2. Resist over-tiering
130
130
 
131
131
  - [Stitch](../wiki/stitch.md): Gemini-powered design product with the MCP server that anchors tier 1
132
132
  - [Claude Design](../wiki/claude-design.md): first-party hosted design product and handoff bundle
133
- - `.claude/context/claude-plugin.md`: how to decide between workflow and domain-knowledge skills
133
+ - `.claude/context/claude-plugin/skills.md`: how to decide between workflow and domain-knowledge skills
134
134
  - [Visual wireframes](../wiki/visual-wireframes.md): Excalidraw research and setup for the tier 1 wireframe companion
135
135
  - [Community skills and plugins](../wiki/community-skills.md): catalog of frontend design skills and integrations
136
136
  - [Community MCP servers](../wiki/community-mcp-servers.md): catalog of MCPs referenced across all tiers
@@ -8,4 +8,4 @@ paths:
8
8
 
9
9
  ## Authority
10
10
 
11
- - Follow `.claude/standards/readme.md` for structure, required and optional sections, heading hierarchy, and what to link out rather than inline. It is the single source.
11
+ - Follow `.claude/standards/readme.md` for voice, structure, required and optional sections, heading hierarchy, badge classes, per-project-type content, and what to link out rather than inline. It is the single source.
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: Enforce shape, scope, and success criterion for authoring convention files
3
+ paths:
4
+ - '.claude/standards/**/*.md'
5
+ - 'standards/**/*.md'
6
+ ---
7
+
8
+ # Standard authoring standards
9
+
10
+ ## Authority
11
+
12
+ - Follow `.claude/standards/standard.md` for a standard's frontmatter, shape, scope, and success criterion. It is the single source.
13
+ - Read it before writing or editing a standard. Do not work the shape from memory.
@@ -0,0 +1,39 @@
1
+ ---
2
+ description: Decide when a code comment should exist and what it may claim
3
+ paths:
4
+ - '**/*.ts'
5
+ - '**/*.tsx'
6
+ - '**/*.js'
7
+ - '**/*.jsx'
8
+ - '**/*.sh'
9
+ - '**/*.py'
10
+ ---
11
+
12
+ # Code comment standards
13
+
14
+ ## When a comment should exist
15
+
16
+ - Write a comment only when it records a fact the reader cannot recover from the code: an external contract, a rejected alternative, or the reason a surprising line is correct.
17
+ - Do not restate in prose what the line beside it already says.
18
+ - Do not comment a self-contained function whose signature and body already carry its behavior.
19
+ - Let comment density follow how much of a file's behavior is decided outside that file. Treat density as an outcome, never as a target.
20
+ - Do not add or delete a comment to move a file toward a density figure.
21
+
22
+ ## What a comment may claim
23
+
24
+ - State only what is true of the code as written.
25
+ - Describe a function's contract and its constraints, never its steps line by line.
26
+ - Update or delete an invalidated comment in the same change that invalidates it.
27
+ - Do not name a person, a ticket, or a date in place of the fact itself.
28
+
29
+ ## What never goes in a comment
30
+
31
+ - Delete commented-out code. Do not park it beside the live path.
32
+ - Do not record the edit that produced the code. Version control holds the change history.
33
+ - Do not defer work into a comment. Deferred work belongs in the tracker.
34
+
35
+ ## Degradation vocabulary
36
+
37
+ Do not write a comment carrying any of these terms.
38
+
39
+ - `FIXED`, `BUGFIX`, `HACK`, `XXX`, `NOTE:`, `TODO`, `FIXME`, `don't remove`, `previously`, `used to`, `workaround`
@@ -1,2 +1,2 @@
1
1
  extends = ""
2
- rules = ["000-constitution", "010-testing", "020-concurrency", "030-error-handling", "040-performance", "050-logging", "060-naming", "070-planning", "080-config-comments", "500-prose", "510-context", "520-wireframes", "530-requirements", "540-architecture", "550-design", "555-tasks", "560-diagrams", "570-skill", "580-readme", "590-rule-authoring"]
2
+ rules = ["000-constitution", "010-testing", "020-concurrency", "030-error-handling", "040-performance", "050-logging", "060-naming", "070-planning", "080-config-comments", "090-code-comments", "500-prose", "510-context", "520-wireframes", "530-requirements", "540-architecture", "550-design", "555-tasks", "560-diagrams", "570-skill", "580-readme", "590-rule-authoring", "591-standard-authoring"]
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "0.11.0",
4
+ "version": "0.13.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -38,8 +38,33 @@ is_internal_topic() {
38
38
  esac
39
39
  }
40
40
 
41
+ # Emits `name<TAB>description<TAB>target` per context entry, sorted so a domain
42
+ # split into a folder lands in its alphabetical place rather than after every
43
+ # file. Matches listTopics in src/docs/read.ts, which sorts both together.
44
+ collect_context() {
45
+ local file name description
46
+ {
47
+ while IFS= read -r file; do
48
+ name=$(basename "$file" .md)
49
+ [ "$name" = "index" ] && continue
50
+ is_internal_topic "$name" && continue
51
+ description=$(read_frontmatter_field "$file" "description")
52
+ printf '%s\t%s\t%s\n' "$name" "$description" ".claude/context/$name.md"
53
+ done < <(find "$CONTEXT_DIR" -maxdepth 1 -type f -name "*.md")
54
+
55
+ # A split domain is named by its folder and described by its generated
56
+ # index, which carries subtitle where a sibling file carries description
57
+ while IFS= read -r file; do
58
+ name=$(basename "$(dirname "$file")")
59
+ is_internal_topic "$name" && continue
60
+ description=$(read_frontmatter_field "$file" "subtitle")
61
+ printf '%s\t%s\t%s\n' "$name" "$description" ".claude/context/$name/index.md"
62
+ done < <(find "$CONTEXT_DIR" -mindepth 2 -maxdepth 2 -type f -name "index.md")
63
+ } | sort
64
+ }
65
+
41
66
  list_text() {
42
- local file name description category
67
+ local file name description category target
43
68
  log_step "Docs"
44
69
  while IFS= read -r file; do
45
70
  name=$(basename "$file" .md)
@@ -53,13 +78,9 @@ list_text() {
53
78
  # Absent in a registry install, which ships docs/ without .claude/
54
79
  if [ -d "$CONTEXT_DIR" ]; then
55
80
  log_step "Domain context"
56
- while IFS= read -r file; do
57
- name=$(basename "$file" .md)
58
- [ "$name" = "index" ] && continue
59
- is_internal_topic "$name" && continue
60
- description=$(read_frontmatter_field "$file" "description")
81
+ while IFS=$'\t' read -r name description target; do
61
82
  log_info "$name : $description"
62
- done < <(find "$CONTEXT_DIR" -maxdepth 1 -type f -name "*.md" | sort)
83
+ done < <(collect_context)
63
84
  fi
64
85
  }
65
86
 
@@ -76,7 +97,7 @@ emit_json_entry() {
76
97
  }
77
98
 
78
99
  list_json() {
79
- local file name description category
100
+ local file name description category target
80
101
  JSON_FIRST=1
81
102
  printf '['
82
103
  while IFS= read -r file; do
@@ -89,13 +110,9 @@ list_json() {
89
110
  done < <(find "$DOCS_DIR" -maxdepth 1 -type f -name "*.md" | sort)
90
111
 
91
112
  if [ -d "$CONTEXT_DIR" ]; then
92
- while IFS= read -r file; do
93
- name=$(basename "$file" .md)
94
- [ "$name" = "index" ] && continue
95
- is_internal_topic "$name" && continue
96
- description=$(read_frontmatter_field "$file" "description")
97
- emit_json_entry "$name" "$description" "" ".claude/context/$(basename "$file")"
98
- done < <(find "$CONTEXT_DIR" -maxdepth 1 -type f -name "*.md" | sort)
113
+ while IFS=$'\t' read -r name description target; do
114
+ emit_json_entry "$name" "$description" "" "$target"
115
+ done < <(collect_context)
99
116
  fi
100
117
  printf ']'
101
118
  }
@@ -4,7 +4,7 @@
4
4
  #
5
5
  # Kept in bash because the list commands call it once per field per file
6
6
  # inside a loop, where shelling into the CLI would cost a process per read.
7
- # The index engine that used to live here is TypeScript, in src/indexes/.
7
+ # Index logic is TypeScript, in src/indexes/, and does not belong here.
8
8
  read_frontmatter_field() {
9
9
  local file="$1"
10
10
  local key="$2"
@@ -86,6 +86,11 @@ async function interactivePicker(): Promise<string> {
86
86
  * file was given, so the turn ceiling reports as skipped rather than passing on a
87
87
  * fabricated zero. A file that exists but does not parse falls back to clean,
88
88
  * since decision 8 lets the envelope fail a run but never pass one.
89
+ *
90
+ * `result` carries the reply text and is left undefined on both fallbacks, which
91
+ * routes a reply assertion to skipped. Fabricating an empty string there would
92
+ * fail every reply assertion on a run that supplied no envelope, turning a gap in
93
+ * the input into a red arm.
89
94
  */
90
95
  function readEnvelope(path: string | undefined): RunEnvelope | undefined {
91
96
  if (path === undefined) return undefined
@@ -102,6 +107,7 @@ function readEnvelope(path: string | undefined): RunEnvelope | undefined {
102
107
  isError: parsed.is_error === true,
103
108
  turns: typeof parsed.num_turns === 'number' ? parsed.num_turns : 0,
104
109
  denials: Array.isArray(denials) ? denials.length : 0,
110
+ reply: typeof parsed.result === 'string' ? parsed.result : undefined,
105
111
  }
106
112
  } catch {
107
113
  return CLEAN_ENVELOPE
package/src/docs/read.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { existsSync, readFileSync } from 'node:fs'
2
- import { basename, join } from 'node:path'
2
+ import { basename, dirname, join } from 'node:path'
3
3
  import { stripFrontmatter } from '@/frontmatter'
4
4
 
5
5
  const INDEX_TOPIC = 'index'
@@ -16,14 +16,25 @@ export interface ResolvedTopic {
16
16
  readonly rel: string
17
17
  }
18
18
 
19
+ /**
20
+ * A domain too large for one file splits into `<domain>/` with a generated
21
+ * `index.md`, so a topic names either a sibling file or such a folder. Both
22
+ * spellings resolve to the same name a caller types.
23
+ */
19
24
  export function resolveTopic(
20
25
  root: string,
21
26
  topic: string,
22
27
  ): ResolvedTopic | undefined {
23
28
  for (const dir of ROOTS) {
24
- const rel = join(dir, `${topic}.md`)
25
- const path = join(root, rel)
26
- if (existsSync(path)) return { path, rel }
29
+ const candidates = [
30
+ join(dir, `${topic}.md`),
31
+ join(dir, topic, `${INDEX_TOPIC}.md`),
32
+ ]
33
+
34
+ for (const rel of candidates) {
35
+ const path = join(root, rel)
36
+ if (existsSync(path)) return { path, rel }
37
+ }
27
38
  }
28
39
 
29
40
  return undefined
@@ -41,14 +52,21 @@ export function listTopics(root: string): string[] {
41
52
  const cwd = join(root, dir)
42
53
  if (!existsSync(cwd)) continue
43
54
 
44
- const names = [
55
+ const files = [
45
56
  ...new Bun.Glob('*.md').scanSync({ cwd, onlyFiles: true, dot: true }),
46
57
  ]
47
58
  .map((name) => basename(name, '.md'))
48
59
  .filter((name) => name !== INDEX_TOPIC)
49
- .sort()
50
60
 
51
- topics.push(...names)
61
+ const folders = [
62
+ ...new Bun.Glob(`*/${INDEX_TOPIC}.md`).scanSync({
63
+ cwd,
64
+ onlyFiles: true,
65
+ dot: true,
66
+ }),
67
+ ].map((name) => dirname(name))
68
+
69
+ topics.push(...[...files, ...folders].sort())
52
70
  }
53
71
 
54
72
  return topics
@@ -161,21 +161,41 @@ function renderIndex(
161
161
  }
162
162
  })
163
163
  } else {
164
- for (const entry of entries) {
165
- out += formatEntry(entry)
166
- }
164
+ /**
165
+ * Flat mode sorts sub-catalogs among the files rather than after them. A
166
+ * folder and a file are both one domain to a reader scanning the catalog,
167
+ * so a trailing folder reads as absent from the alphabetical run it belongs
168
+ * in. Grouped mode keeps its own heading below, where a category is the
169
+ * organizing key and alphabetical position carries no meaning.
170
+ */
171
+ const lines = [
172
+ ...entries.map((entry) => ({
173
+ key: entry.name,
174
+ text: formatEntry(entry),
175
+ })),
176
+ ...subCatalogs.map((catalog) => ({
177
+ key: catalog.name,
178
+ text: formatSubCatalog(catalog),
179
+ })),
180
+ ].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0))
181
+
182
+ for (const line of lines) out += line.text
167
183
  }
168
184
 
169
- if (subCatalogs.length > 0) {
170
- if (hasCategories) out += '\n## Sub-catalogs\n\n'
185
+ if (hasCategories && subCatalogs.length > 0) {
186
+ out += '\n## Sub-catalogs\n\n'
171
187
  for (const catalog of subCatalogs) {
172
- out += `- [${catalog.title}](${catalog.name}/${INDEX_FILE}): ${catalog.subtitle}\n`
188
+ out += formatSubCatalog(catalog)
173
189
  }
174
190
  }
175
191
 
176
192
  return out
177
193
  }
178
194
 
195
+ function formatSubCatalog(catalog: SubCatalog): string {
196
+ return `- [${catalog.title}](${catalog.name}/${INDEX_FILE}): ${catalog.subtitle}\n`
197
+ }
198
+
179
199
  function formatEntry(entry: IndexEntry): string {
180
200
  return `- [${entry.title}](${entry.name}): ${entry.description}\n`
181
201
  }
@@ -19,6 +19,7 @@ export interface Expectation {
19
19
  readonly absent: readonly string[]
20
20
  readonly content: readonly ContentAssertion[]
21
21
  readonly writeScope: readonly string[]
22
+ readonly reply: readonly string[]
22
23
  readonly manual: readonly string[]
23
24
  readonly maxTurns?: number
24
25
  }
@@ -28,10 +29,17 @@ export interface AssertionResult {
28
29
  readonly message: string
29
30
  }
30
31
 
32
+ /**
33
+ * `reply` is optional because an absent reply and an empty one mean different
34
+ * things. A run whose envelope was never supplied has nothing to assert against
35
+ * and skips. A run that genuinely returned no text carries the empty string and
36
+ * fails every reply assertion, which is the finding.
37
+ */
31
38
  export interface RunEnvelope {
32
39
  readonly isError: boolean
33
40
  readonly turns: number
34
41
  readonly denials: number
42
+ readonly reply?: string
35
43
  }
36
44
 
37
45
  export interface Verdict {
@@ -155,6 +163,7 @@ export function parseExpectation(source: string): Expectation {
155
163
  absent: stringArray(parsed.absent),
156
164
  content: contentArray(parsed.content),
157
165
  writeScope: stringArray(parsed.write_scope),
166
+ reply: stringArray(parsed.reply),
158
167
  manual: stringArray(parsed.manual),
159
168
  maxTurns:
160
169
  typeof parsed.max_turns === 'number' ? parsed.max_turns : undefined,
@@ -171,7 +180,8 @@ export function countMechanicalAssertions(expectation: Expectation): number {
171
180
  expectation.paths.length +
172
181
  expectation.absent.length +
173
182
  expectation.content.length +
174
- expectation.writeScope.length
183
+ expectation.writeScope.length +
184
+ expectation.reply.length
175
185
  )
176
186
  }
177
187
 
@@ -259,6 +269,42 @@ function checkWriteScope(
259
269
  }
260
270
  }
261
271
 
272
+ /**
273
+ * Plain substrings, matched case-sensitively, against the text the run replied
274
+ * with. A substring rather than a regex because the pattern a reply assertion
275
+ * wants is a load-bearing token, a path or a command, and a regex invites the
276
+ * anchored sentence that goes red on any rewording.
277
+ *
278
+ * Declare only positives. A negative substring passes on every reply that
279
+ * phrases the thing differently, which is the vacuous pass
280
+ * `countMechanicalAssertions` excludes `manual` to prevent. An entry asserting
281
+ * what a run must not have said stays in `manual` with its reason.
282
+ */
283
+ function checkReply(
284
+ expectation: Expectation,
285
+ envelope: RunEnvelope | undefined,
286
+ ): KindOutcome {
287
+ if (expectation.reply.length === 0) return { results: [], skipped: [] }
288
+
289
+ if (envelope?.reply === undefined) {
290
+ return {
291
+ results: [],
292
+ skipped: ['reply: no reply text supplied, pass --envelope'],
293
+ }
294
+ }
295
+
296
+ const reply = envelope.reply
297
+
298
+ return {
299
+ results: expectation.reply.map((fragment) =>
300
+ reply.includes(fragment)
301
+ ? { ok: true, message: `reply says: ${fragment}` }
302
+ : { ok: false, message: `reply never says: ${fragment}` },
303
+ ),
304
+ skipped: [],
305
+ }
306
+ }
307
+
262
308
  /**
263
309
  * The envelope never determines a pass. It can only fail a run the expectations
264
310
  * would otherwise have passed. Under `bypassPermissions` the denial count is
@@ -306,16 +352,18 @@ export function checkExpectation(
306
352
  input: CheckInput,
307
353
  ): Verdict {
308
354
  const scope = checkWriteScope(expectation, input.writes)
355
+ const reply = checkReply(expectation, input.envelope)
309
356
  const envelope = checkEnvelope(expectation, input.envelope)
310
357
 
311
358
  const results = [
312
359
  ...checkPaths(expectation, input.sandboxDir),
313
360
  ...checkAbsent(expectation, input.sandboxDir),
314
361
  ...checkContent(expectation, input.sandboxDir),
362
+ ...reply.results,
315
363
  ...scope.results,
316
364
  ...envelope.results,
317
365
  ]
318
- const skipped = [...scope.skipped, ...envelope.skipped]
366
+ const skipped = [...scope.skipped, ...reply.skipped, ...envelope.skipped]
319
367
 
320
368
  const failed = results.filter((result) => !result.ok).length
321
369
 
@@ -17,7 +17,7 @@ Applies to `.claude/ARCHITECTURE.md`. Describes the system shape and the decisio
17
17
 
18
18
  - Per-domain structure and narrative. That belongs in `.claude/context/<domain>.md`, one file per domain.
19
19
  - Setup commands and install instructions. Those live in the README.
20
- - How individual functions work line by line. Those go in code comments.
20
+ - How individual functions work line by line. The code carries its own behavior.
21
21
  - Full type definitions. They live in code. Reference the shape conceptually if needed.
22
22
 
23
23
  ## Sections
@@ -90,6 +90,8 @@ Only the `development` entry carries this section. It is not a general-purpose h
90
90
  - Past roughly 150 lines, check two things before adding more: whether the entry still covers a single domain, and whether it has filled with content `ls` or `--help` reproduces. Fix whichever is true rather than trimming to hit a number.
91
91
  - Never cut a `## Decisions` or `## Gotchas` entry to shorten a file. Cut a `## Layout` or `## CLI` section instead.
92
92
  - Split into a folder (`.claude/context/<domain>/<sub-area>.md`) when a domain has three or more sub-areas that do not fit cleanly in one file. That split is the natural ceiling.
93
+ - Keep a split domain's `index.md` generated. The catalog body is rewritten on every regen, so the domain's own overview and layout belong in a sibling file rather than in it, and the `subtitle` is what names the file to start with.
94
+ - Author a catalog that grows a row per shipped thing as a bullet list, never a markdown table. A table pads its columns to a shared width, so one row outgrowing its column reflows every row and turns a one-line edit into a whole-file rewrite that conflicts with any sibling branch. This is what forced the split above.
93
95
 
94
96
  ## Examples
95
97
 
@@ -24,8 +24,14 @@ An entry that fails these is non-conforming regardless of whether it satisfies e
24
24
  - `title` (required): sentence case, names what the entry answers (`System context`, `Request flow`), not what it draws.
25
25
  - `description` (required): one line on which question the entry settles and which source signal drives it.
26
26
  - `category` (required): the diagram kind, one of the five in Entry kinds. It is the grouping field `aitk indexes regen` renders headings from.
27
+ - `verified` (required): the short commit SHA an entry was last checked against and the ISO date of that check, separated by a space (`73e9a3f8 2026-08-02`). A stub nobody has drawn yet carries the literal `TODO: never verified` instead, which is the one other accepted value.
28
+ - `stale` (optional): one line naming what changed under the entry since that check. Absent on an entry nothing has flagged.
27
29
 
28
- All three feed `.claude/diagrams/index.md` when regenerated. The catalog sorts categories alphabetically rather than in narrative order, so an entry cannot rely on its position to introduce another. Each entry names its own starting point, and the catalog's subtitle routes a first-time reader to the system context entry.
30
+ The first three feed `.claude/diagrams/index.md` when regenerated. The catalog sorts categories alphabetically rather than in narrative order, so an entry cannot rely on its position to introduce another. Each entry names its own starting point, and the catalog's subtitle routes a first-time reader to the system context entry.
31
+
32
+ The marker fields reach the catalog through neither route. `aitk indexes regen` reads `title`, `description`, and `category` and ignores every other key, so a marker changes no generated file. A reader picks it up by opening the entry, which is where it sits above the diagram and where anyone deciding whether to trust the picture is already standing.
33
+
34
+ Two writers share the marker and neither touches the other's field. A pass that renders an entry and reads the picture back sets `verified` and clears `stale`. The `claude-docs` sweep appends `stale` and never edits `verified`. Keeping them separate is what lets a reader tell a diagram nobody has checked since the code moved from one that was checked and found correct.
29
35
 
30
36
  ## Entry kinds
31
37
 
@@ -72,7 +78,7 @@ A second entry for one kind takes a suffixed name (`request-flow-admin.md`) and
72
78
 
73
79
  - One to three short paragraphs below each diagram. Plain English and pedagogical.
74
80
  - Lead with what the diagram shows. Follow with why this shape was chosen and what alternative was rejected, when the choice was non-obvious.
75
- - Reference one or two specific code paths the reader can open. Do not enumerate every file.
81
+ - Reference one or two specific code paths the reader can open. Do not enumerate every file. Backticked paths here are also the set the `claude-docs` sweep watches, so a path cited in this section is one a later session gets told about when it leaves the tree.
76
82
  - Do not duplicate prose across entries. An entry that restates its neighbor has taken the neighbor's job.
77
83
  - The audience is mixed, so vocabulary runs as a gradient across the set. `System context` assumes no knowledge of the repository. `Deployment` may assume the reader has read the others.
78
84
 
@@ -101,5 +107,7 @@ Reference the context entry by path when a reader needs the mechanism. The diagr
101
107
  - When the system changes, update the entries whose source signal changed and leave the rest alone. Rewriting the folder wholesale reproduces the defect the per-kind split exists to end.
102
108
  - A diagram showing a defunct host or library is worse than no diagram. Audit the affected entry in the same PR.
103
109
  - `System context` has no named source signal beyond `.claude/REQUIREMENTS.md`, so nothing tells a session it went stale. Re-read it when the boundary or the set of external dependencies moves.
110
+ - The `claude-docs` sweep watches two things and writes frontmatter only. It appends `stale` when a path an entry cites leaves the tree, and it stubs a kind when a diff adds the source signal that kind is drawn from. Diagram bodies and explanation paragraphs are off limits to it, because a change that removes a module does not carry the new correct shape of the picture.
111
+ - That watch samples thinly. It sees the one or two paths an entry happened to cite and nothing else, so a change elsewhere leaves the entry looking current. `verified` is what covers the gap, and an entry whose date sits far behind the branch is due a read whether or not anything flagged it.
104
112
  - Mermaid blocks are inside fenced code, so the prose-standards hook ignores them. The explanation paragraphs around them are still prose. Follow `standards/prose.md`.
105
113
  - The punctuation bans still apply to node and subgraph labels, and nothing checks them there. An em dash in a label passes every gate the repository has, so read the labels before shipping the entry.
@@ -16,6 +16,7 @@ Reference docs for consistent authoring across the toolkit and target projects.
16
16
  - [Requirements reference](requirements.md): Shape and content rules for .claude/REQUIREMENTS.md
17
17
  - [Governance rule reference](rule.md): Rule frontmatter, body shape, and voice for .claude/rules files
18
18
  - [Claude skill reference](skill.md): Claude skill structure and authoring rules
19
+ - [Standard reference](standard.md): Shape and content rules for authoring a standard
19
20
  - [Tasks reference](tasks.md): Folder layout, filename convention, and content rules for .claude/tasks/
20
21
  - [Versioning reference](versioning.md): Phase label vs semver discipline across tasks, PRs, commits, and tags
21
22
  - [Wireframe reference](wireframes.md): Shape and content rules for .claude/wireframes/<surface>.md files
@@ -36,6 +36,23 @@ Scoped to the README at a repository root. A nested README documenting a folder,
36
36
  - Do not include detailed contribution guidelines. Reference `CONTRIBUTING.md`.
37
37
  - Do not include extensive troubleshooting guides. Use a wiki or separate documentation.
38
38
 
39
+ ### Badges
40
+
41
+ A badge earns its place by carrying a fact the reader cannot get from the page they are already on. Three classes pass that test.
42
+
43
+ - Package version, which reports what the registry actually published rather than what the working tree claims
44
+ - Build or CI status, which reports whether the checks are currently passing
45
+ - License, which passes because a README rendered on a registry page has no host sidebar stating it
46
+
47
+ Stars, forks, download counts, language chips, and "made with" badges fail. The first three restate what a repository host already renders above the README, and the rest decorate.
48
+
49
+ - Cap the block at four. A fifth costs more scanning than any badge repays.
50
+ - Give each badge alt text naming what it reports rather than the service that renders it. Write `npm version`, not `shields.io`.
51
+ - Link each badge to the page a doubting reader would check next, which is the registry listing for a version badge and the run history for a status badge. A red badge with no route to the failing run reports a problem and withholds the only thing the reader wants.
52
+ - Pin a status badge to the branch whose health it claims to report, and confirm the workflow actually runs on that branch. A workflow triggered by pull requests alone never runs on the default branch, so an unpinned badge reports whichever branch ran last and a pinned one reports nothing at all. Fix the trigger, or drop the badge under the rule below.
53
+ - Verify a badge by the value it renders, not by its status code. A badge service answers 200 for a query it cannot satisfy, so a blank badge and a working one look identical to a status check.
54
+ - Zero badges is a correct answer. A private project with no registry presence and no CI has nothing that passes the test, and an empty block beats a padded one.
55
+
39
56
  ## Content
40
57
 
41
58
  - Open public-facing READMEs with universal problems any reader recognizes, not repo-specific artifact names. Save artifact names for feature or "What is inside" sections.
@@ -44,6 +61,10 @@ Scoped to the README at a repository root. A nested README documenting a folder,
44
61
  - For libraries/tools: include API quickstart
45
62
  - For applications/products: include usage instructions and configuration options
46
63
  - For CLI tools: include command examples with flags
64
+ - For agent-facing repositories: name the file an agent loads first and the command that lists what the project exposes
65
+ - For marketplace-distributed plugins: give the install command for every channel the project publishes to
66
+
67
+ A project is often several of these at once. Cover every type that applies rather than picking the closest one. A repository that ships a CLI, distributes a plugin, and holds agent conventions owes its reader all three.
47
68
 
48
69
  ## Examples
49
70
 
@@ -1,33 +1,52 @@
1
1
  ---
2
2
  title: Standard reference
3
3
  description: Shape and content rules for authoring a standard
4
- consumers: create-standard
5
4
  ---
6
5
 
7
6
  # Standard reference
8
7
 
8
+ Applies to each authored standard in the folder. Skip for `index.md`, which is generated rather than authored.
9
+
9
10
  ## Overview
10
11
 
11
- A standard is a target-facing authoring convention for one document type. It installs into a project under `.claude/standards/` and is consumed by skills and developers alike. One standard governs one doc type. Split unrelated conventions into separate files.
12
+ A standard is a target-facing authoring convention for one document type. It installs into a project under `.claude/standards/` and is consumed by skills and developers alike. This file governs itself, so every rule below applies to it.
13
+
14
+ ## What a working standard looks like
15
+
16
+ A standard answers these questions. Each can be answered wrong, which is what makes them a test rather than a preamble.
17
+
18
+ - Which single document type does this govern, and where does that document live?
19
+ - Can an author who has seen no example produce a conforming document from this file alone?
20
+ - Does every rule state a shape the document must have, rather than a fact about the repository that happens to store it?
21
+ - What does a conforming document achieve, stated so a reviewer can call one non-conforming without appealing to taste?
22
+
23
+ A standard failing these questions is non-conforming even when it satisfies every shape rule below.
24
+
25
+ ## Scope
26
+
27
+ - Govern one document type per standard. Split unrelated conventions into separate files.
28
+ - Name no path, filename, or folder outside the document type the standard governs. A standard reaches projects whose layout is their own, so a path borrowed from the authoring repository is wrong in a target and nothing reports it.
29
+ - State the rule, never the mechanism enforcing it. Hooks, scripts, checks, and skill catalogs are facts about one repository. Name the condition the document must meet and let the enforcing surface name its own case.
30
+ - Invent inline examples rather than citing a real file elsewhere in the project. A cited file moves or is deleted and the standard goes stale in silence.
12
31
 
13
32
  ## Frontmatter
14
33
 
15
- - Start the file with a frontmatter block carrying `title` and `description`.
16
- - `title`: names the doc type in sentence case, suffixed `reference` (`Commit reference`, `Branch reference`).
34
+ - Start the file with a frontmatter block carrying at least `title` and `description`. A consuming surface may require more.
35
+ - `title`: names the doc type in sentence case, suffixed `reference` (`Commit reference`, `Branch reference`)
17
36
  - `description`: one line naming what the standard covers. It becomes the index link label on install.
18
37
 
19
38
  ## Structure
20
39
 
21
- - Use sentence case for every heading.
40
+ - Use sentence case for every heading
22
41
  - Flat `##` rule groups for a single-topic standard. Group `##` headers by concern for a multi-topic one.
23
42
  - When one `##` section covers more than one sub-concern, split its bullets into `###` subgroups, one subgroup per sub-concern. A flat bullet list under an `##` covers a single sub-concern. Roughly seven bullets is a signal to split, not a hard cap.
24
- - Order groups from the most-used rule down to the edge case.
43
+ - Order groups from the most-used rule down to the edge case
25
44
 
26
45
  ## Rules
27
46
 
28
- - Write rules as imperative bullets: one rule per bullet, one concern per group.
29
- - State the forbidden shape rather than enumerating allowed options, so a rule survives new categories.
30
- - Cut any rule that resists a crisp one-line phrasing.
47
+ - Write rules as imperative bullets: one rule per bullet, one concern per group
48
+ - State the forbidden shape rather than enumerating allowed options, so a rule survives new categories
49
+ - Cut any rule that resists a crisp one-line phrasing
31
50
  - Do not pad with filler prose. Every line earns its place as a usable reference entry.
32
51
 
33
52
  ## Success criterion
@@ -47,6 +66,6 @@ A standard is a target-facing authoring convention for one document type. It ins
47
66
  ## Examples
48
67
 
49
68
  - Include examples only where a rule is non-obvious. A self-evident rule needs none.
50
- - When shown, label them `### Correct` and `### Incorrect` with an inline `# reason` on each entry.
69
+ - When shown, label them `### Correct` and `### Incorrect` with an inline `# reason` on each entry
51
70
  - Keep to two or three entries. Show the pattern, not a catalog.
52
71
  - Keep each entry a short one-liner or command. Do not write multi-line correct and incorrect function blocks.
@@ -1,52 +0,0 @@
1
- ---
2
- title: Standard reference
3
- description: Shape and content rules for authoring a standard
4
- consumers: create-standard
5
- ---
6
-
7
- # Standard reference
8
-
9
- ## Overview
10
-
11
- A standard is a target-facing authoring convention for one document type. It installs into a project under `.claude/standards/` and is consumed by skills and developers alike. One standard governs one doc type. Split unrelated conventions into separate files.
12
-
13
- ## Frontmatter
14
-
15
- - Start the file with a frontmatter block carrying `title` and `description`.
16
- - `title`: names the doc type in sentence case, suffixed `reference` (`Commit reference`, `Branch reference`).
17
- - `description`: one line naming what the standard covers. It becomes the index link label on install.
18
-
19
- ## Structure
20
-
21
- - Use sentence case for every heading.
22
- - Flat `##` rule groups for a single-topic standard. Group `##` headers by concern for a multi-topic one.
23
- - When one `##` section covers more than one sub-concern, split its bullets into `###` subgroups, one subgroup per sub-concern. A flat bullet list under an `##` covers a single sub-concern. Roughly seven bullets is a signal to split, not a hard cap.
24
- - Order groups from the most-used rule down to the edge case.
25
-
26
- ## Rules
27
-
28
- - Write rules as imperative bullets: one rule per bullet, one concern per group.
29
- - State the forbidden shape rather than enumerating allowed options, so a rule survives new categories.
30
- - Cut any rule that resists a crisp one-line phrasing.
31
- - Do not pad with filler prose. Every line earns its place as a usable reference entry.
32
-
33
- ## Success criterion
34
-
35
- - State what a conforming artifact achieves, not only what shape it takes. A standard that specifies structure exhaustively and success nowhere cannot be argued against, only edited on taste.
36
- - Write the criterion as a small set of questions the artifact must answer, or a task a reader must be able to complete from it. Keep it checkable by a person in one sitting.
37
- - Place it near the top, above the shape rules it governs. The shape rules are the means and the criterion is the test.
38
- - Say that an artifact failing the criterion is non-conforming even when it satisfies every shape rule. Without that line the criterion reads as advice.
39
- - Add a criterion to an existing standard when that standard is next exercised, not in a sweep. A criterion written without a failure to point at is the taste-based edit this section exists to prevent, so a standard with no criterion yet is a known gap rather than a violation.
40
-
41
- ## Changing a standard
42
-
43
- - Change a standard on a failure, not on a finding. A finding is that the docs say X or a paper suggests Y. A failure is a conforming artifact that satisfied every shape rule and still missed the success criterion.
44
- - Park findings wherever the project tracks pending work, or in the standard's own backlog section when it tracks none. They are hypotheses to test, not instructions to apply.
45
- - Cite the failing artifact in the change that fixes it, so the next reader can tell which rules were paid for by evidence.
46
-
47
- ## Examples
48
-
49
- - Include examples only where a rule is non-obvious. A self-evident rule needs none.
50
- - When shown, label them `### Correct` and `### Incorrect` with an inline `# reason` on each entry.
51
- - Keep to two or three entries. Show the pattern, not a catalog.
52
- - Keep each entry a short one-liner or command. Do not write multi-line correct and incorrect function blocks.