@erclx/canon 5.38.0 → 5.39.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.
Files changed (149) hide show
  1. package/README.md +1 -1
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/SKILL.md +12 -9
  4. package/claude/skills/auto-ship/SKILL.md +43 -23
  5. package/claude/skills/backlog-triage/SKILL.md +62 -53
  6. package/claude/skills/backlog-triage/references/apply.md +69 -0
  7. package/claude/skills/build-in-slices/SKILL.md +8 -4
  8. package/claude/skills/canon-cli/SKILL.md +5 -1
  9. package/claude/skills/canon-feedback/SKILL.md +18 -8
  10. package/claude/skills/canon-feedback-triage/SKILL.md +10 -3
  11. package/claude/skills/canvas-board/SKILL.md +36 -17
  12. package/claude/skills/ci-workflow/SKILL.md +5 -4
  13. package/claude/skills/code-craft/SKILL.md +10 -6
  14. package/claude/skills/codebase-layout/SKILL.md +3 -5
  15. package/claude/skills/context-fold/REQUIREMENT.md +4 -4
  16. package/claude/skills/context-fold/SKILL.md +42 -23
  17. package/claude/skills/context-fold/references/context-refresh.md +4 -3
  18. package/claude/skills/context-fold/references/context.md +79 -64
  19. package/claude/skills/create-rule/SKILL.md +7 -3
  20. package/claude/skills/create-skill/SKILL.md +27 -7
  21. package/claude/skills/create-skill/references/skill-reference.md +41 -17
  22. package/claude/skills/decision-escalate/SKILL.md +8 -5
  23. package/claude/skills/deploy-app/SKILL.md +1 -1
  24. package/claude/skills/deprecation-migration/SKILL.md +12 -4
  25. package/claude/skills/design-taste/SKILL.md +17 -11
  26. package/claude/skills/docs-sync/REQUIREMENT.md +1 -0
  27. package/claude/skills/docs-sync/SKILL.md +17 -6
  28. package/claude/skills/docs-sync/references/docs.md +54 -21
  29. package/claude/skills/docs-sync/references/readme.md +32 -8
  30. package/claude/skills/document-health/SKILL.md +3 -4
  31. package/claude/skills/draft-and-pick/SKILL.md +73 -47
  32. package/claude/skills/draft-doc/SKILL.md +9 -4
  33. package/claude/skills/draft-doc/references/context.md +4 -4
  34. package/claude/skills/draft-doc/references/docs.md +1 -0
  35. package/claude/skills/draft-doc/references/wiki.md +3 -2
  36. package/claude/skills/draft-figure/SKILL.md +9 -7
  37. package/claude/skills/draft-identity/SKILL.md +24 -52
  38. package/claude/skills/draft-identity/references/card-route.md +46 -0
  39. package/claude/skills/draft-ready/SKILL.md +19 -7
  40. package/claude/skills/draft-screencast/SKILL.md +6 -112
  41. package/claude/skills/draft-screencast/references/beats-template.md +119 -0
  42. package/claude/skills/draft-slides/SKILL.md +7 -4
  43. package/claude/skills/git-branch/SKILL.md +3 -3
  44. package/claude/skills/git-commit/SKILL.md +5 -3
  45. package/claude/skills/git-followup/SKILL.md +16 -9
  46. package/claude/skills/git-issue/SKILL.md +6 -5
  47. package/claude/skills/git-pr/SKILL.md +56 -44
  48. package/claude/skills/git-ship/SKILL.md +43 -34
  49. package/claude/skills/git-ship/references/ci-watch.md +31 -0
  50. package/claude/skills/git-split/SKILL.md +28 -100
  51. package/claude/skills/git-split/references/independent.md +40 -0
  52. package/claude/skills/git-split/references/stacked.md +73 -0
  53. package/claude/skills/git-stage/SKILL.md +9 -7
  54. package/claude/skills/git-worktree/SKILL.md +27 -49
  55. package/claude/skills/git-worktree/references/cleanup.md +55 -0
  56. package/claude/skills/markdown-craft/SKILL.md +2 -2
  57. package/claude/skills/markdown-craft/references/markdown.md +82 -53
  58. package/claude/skills/plan-feature/SKILL.md +52 -46
  59. package/claude/skills/plan-feature/references/batches.md +46 -0
  60. package/claude/skills/plan-groundwork/SKILL.md +60 -74
  61. package/claude/skills/plan-groundwork/references/output.md +48 -0
  62. package/claude/skills/plan-intake/SKILL.md +28 -21
  63. package/claude/skills/plan-intake-answer/SKILL.md +14 -13
  64. package/claude/skills/read-frames/SKILL.md +2 -0
  65. package/claude/skills/record-screencast/SKILL.md +31 -11
  66. package/claude/skills/review-address/SKILL.md +76 -158
  67. package/claude/skills/review-address/references/rebase-only.md +34 -0
  68. package/claude/skills/review-craft/SKILL.md +27 -11
  69. package/claude/skills/review-pr/SKILL.md +91 -40
  70. package/claude/skills/review-ui/SKILL.md +62 -37
  71. package/claude/skills/review-ui/references/hosted-preview.md +30 -0
  72. package/claude/skills/role-orchestrator/SKILL.md +105 -74
  73. package/claude/skills/role-orchestrator/references/orchestrator-filing.md +26 -0
  74. package/claude/skills/role-planner/SKILL.md +66 -64
  75. package/claude/skills/role-reviewer/SKILL.md +57 -46
  76. package/claude/skills/role-worker/SKILL.md +68 -63
  77. package/claude/skills/search-craft/SKILL.md +7 -5
  78. package/claude/skills/session-compact/SKILL.md +16 -11
  79. package/claude/skills/session-map/SKILL.md +31 -10
  80. package/claude/skills/session-relay/SKILL.md +32 -65
  81. package/claude/skills/session-resume/SKILL.md +18 -13
  82. package/claude/skills/session-worktree/SKILL.md +70 -42
  83. package/claude/skills/systematic-debugging/SKILL.md +6 -1
  84. package/claude/skills/target-check/SKILL.md +13 -6
  85. package/claude/skills/target-setup/SKILL.md +39 -27
  86. package/claude/skills/task-board/SKILL.md +68 -60
  87. package/claude/skills/task-board/references/create-report.md +36 -0
  88. package/claude/skills/task-complete/SKILL.md +20 -18
  89. package/claude/skills/teach-workspace/SKILL.md +61 -33
  90. package/claude/skills/test-craft/SKILL.md +2 -1
  91. package/claude/skills/test-first/SKILL.md +15 -9
  92. package/claude/skills/ui-checklist/SKILL.md +18 -16
  93. package/claude/skills/ux-audit/SKILL.md +9 -10
  94. package/claude/skills/ux-measure/SKILL.md +22 -18
  95. package/claude/skills/video-craft/SKILL.md +15 -7
  96. package/claude/skills/write-human/SKILL.md +9 -1
  97. package/claude/skills/youtube-transcripts/SKILL.md +2 -1
  98. package/docs/agents/context-audit-checks.md +19 -1
  99. package/docs/agents/context-audit.md +2 -2
  100. package/docs/agents/driver.md +1 -1
  101. package/docs/agents/hooks.md +21 -1
  102. package/docs/agents/index.md +10 -14
  103. package/docs/agents/indexes.md +25 -29
  104. package/docs/agents/install-and-sync.md +124 -235
  105. package/docs/agents/markdown-audit-density.md +39 -0
  106. package/docs/agents/markdown-audit-weight.md +9 -7
  107. package/docs/agents/markdown-audit.md +18 -14
  108. package/docs/agents/migrate.md +41 -16
  109. package/docs/agents/output-shape.md +60 -45
  110. package/docs/agents/overview.md +36 -12
  111. package/docs/agents/routing.md +1 -1
  112. package/docs/agents/rule-citations.md +1 -1
  113. package/docs/agents/skills-reach.md +1 -1
  114. package/docs/agents/targets.md +25 -77
  115. package/docs/target/migrations.md +1 -1
  116. package/docs/target/projects.md +2 -2
  117. package/docs/workflow/ai-workflow.md +2 -2
  118. package/docs/workflow/parallel-features.md +2 -2
  119. package/docs/workflow/skill-map.md +1 -1
  120. package/docs/workflow/visual-design-workflow.md +1 -1
  121. package/package.json +1 -1
  122. package/src/commands/claude.ts +1 -1
  123. package/src/commands/context.ts +106 -7
  124. package/src/commands/hooks.ts +63 -0
  125. package/src/commands/markdown.ts +169 -32
  126. package/src/context/audit.ts +256 -5
  127. package/src/context/classify/prompts.ts +1 -1
  128. package/src/context/gate.ts +23 -7
  129. package/src/context/shape-gated.ts +28 -0
  130. package/src/design/document.ts +1 -1
  131. package/src/design/tokens.ts +1 -1
  132. package/src/gate/measures.ts +15 -190
  133. package/src/gate/stages.ts +7 -23
  134. package/src/hooks/reply-links.ts +110 -0
  135. package/src/markdown/density.ts +258 -0
  136. package/src/markdown/gate.ts +57 -18
  137. package/src/markdown/reference-shape.ts +103 -0
  138. package/src/markdown/scope.ts +174 -0
  139. package/src/markdown/structure.ts +9 -59
  140. package/src/shipped/references.ts +1 -1
  141. package/tooling/claude/seeds/.claude/hooks/reply-links.sh +72 -0
  142. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +22 -1
  143. package/tooling/claude/seeds/.claude/settings.json +4 -0
  144. package/docs/agents/commands.md +0 -154
  145. package/docs/agents/docs.md +0 -27
  146. package/docs/agents/drift-surfaces.md +0 -201
  147. package/docs/agents/gov-detect.md +0 -47
  148. package/docs/agents/scripting.md +0 -142
  149. package/src/markdown/growth.ts +0 -43
@@ -1,246 +1,135 @@
1
1
  ---
2
2
  title: Install and sync
3
- description: What each install and sync verb writes, refuses, or leaves alone, and how drift is attributed in a target project
3
+ description: What each install and sync verb writes, refuses, or leaves alone, the drift report and its seven side sections, the rule detection record, and version skew states
4
4
  ---
5
5
 
6
6
  # Install and sync
7
7
 
8
- The behavior notes behind the verbs listed in `commands.md`. Each one records what the verb writes, what it refuses, and what it deliberately leaves alone.
9
-
10
- ## Domain sync
11
-
12
- `canon gov sync` updates only rules already present under
13
- `.claude/rules/canon/` and never adds new ones. That wrapper marks the
14
- toolkit-owned half of the tree, and it is the whole of what the walk reaches:
15
- `.claude/rules/project/` sits outside it by location and is never read,
16
- matched, or reported on, which is how project-authored rules survive. It also
17
- removes a stale `.claude/GOV.md` from the retired build. Use
18
- `canon gov install` to add rules.
19
-
20
- Everything under `.claude/rules/canon/` belongs to the toolkit, so a rule
21
- there that the toolkit no longer ships is deleted on sync, edited or not. That
22
- is how a retired rule stops loading in a target. A renamed rule
23
- the toolkit declares in `governance/renames.toml` moves instead: the sync
24
- installs the rule under its new name and deletes the old file, whether or not
25
- the target's stamp records a stack. Local edits to the old file are not
26
- carried, and the sync says so. The sync lists each change by path before it
27
- applies, and the target's git diff is the record afterwards. `canon sync
28
- --check` lists the same file as `retired` or `renamed` before any sync runs.
29
-
30
- A rule that only routed a path to a standard now names the skill that owns
31
- the guidance. The tasks, plan, context, and skill rules are hand-written at
32
- `canon/` and `claude/skill.md`. A held copy of each syncs in place, and the
33
- groundwork, intake, session, design, readme, and rule copies are deleted as
34
- retired. The markdown rule is hand-written at `writing/markdown.md` and names
35
- the `markdown-craft` skill, and `800-prose` retires.
36
-
37
- A project's own rules belong under `.claude/rules/project/`, where a slug
38
- the toolkit also ships collides with nothing, and a rule placed under `canon/`
39
- instead is lost on the next sync.
40
-
41
- A sync never deletes a rule that a newer canon release installed. The stamp
42
- records the newest release that synced governance, and an older binary holds
43
- every rule it finds no source for, naming both releases and asking for an
44
- upgrade, rather than deleting what it cannot recognize.
45
-
46
- A renamed rule loses its old file and gains nothing under the new name unless
47
- the target's stamp records the stack it installed, in which case the new name
48
- shows as `missing` and `canon gov install <stack>` adds it.
49
-
50
- Design's own orphans are the target's overrides and are meant to stay where
51
- they are, so `canon design sync` deletes nothing it finds no source for.
52
-
53
- `canon design install` copies one toolkit-owned file to
54
- `.claude/design/base.css` and creates no override. A project overrides a value
55
- by writing `.claude/design/project/` itself, which `canon design sync` never
56
- touches, because that subfolder is project-authored by location the way
57
- `.claude/rules/project/` is. An override named exactly like the shipped file is
58
- still the project's.
59
-
60
- A project's faces sit in that subfolder too, under `fonts/` with plain
61
- `@font-face` rules in `fonts.css`. `canon design fonts add <file> --family <name>`
62
- copies a `woff2`, `woff`, `ttf`, or `otf` face in and appends its rule
63
- (`--weight`, `--style`, `--json`), refusing bytes that disagree with the
64
- extension as `invalid-font`. `fonts list --json` emits one row per rule, with a
65
- `problem` where the file is missing or lies outside the subfolder.
66
-
67
- The override ships absent rather than empty. An empty file is one the project
68
- did not ask for and did not write, the reconciliation already handles a missing
69
- side, and an empty override invites a target to fill it before it has an
70
- opinion.
71
-
72
- Nothing arrives on a project that has not run `canon design install`. The
73
- domain is detected by that folder existing, so `canon sync` skips it entirely on
74
- a target that never installed it, and the unstamped line at the end of
75
- `canon sync --check` stays quiet about it for the same reason. Governance is
76
- named there when it is absent and design is not, because a managed project
77
- without governance has yet to install what every project carries, while one
78
- without design chose that.
79
-
80
- When the target's install recorded a stack, `canon gov sync` also reports a
81
- rule that stack lists and `.claude/rules/canon/` does not hold, as a `missing`
82
- entry carrying no change. This is what makes a target whose recorded sync point
83
- postdates a rule joining its stack still see that rule: the report reads the
84
- target's current entitlement against its current tree rather than diffing
85
- from an anchor a later sync could advance past the rule's own commit. A
86
- target whose install predates the recorded chain falls back to the same
87
- rule-folder-inference `newRules` uses in `canon sync --check`.
88
-
89
- ## Tooling diff and sync
90
-
91
- `canon tooling diff <stack> [target]` is the read-only comparison, and a session asking what differs reaches for it by default. It writes nothing and exits 1 when anything differs and 0 when nothing does, so it gates CI. `--json` adds the scan record on stdout with `ok: true`, and the frame goes to stderr. A refusal such as an unknown stack, an excluded stack, or a toolkit-root target still exits 1 and answers `{ ok: false, reason, message }`, so a caller branches on the record and reads a typo'd stack name as a refusal rather than as drift.
92
-
93
- The record carries a `withheld` array beside `configs`, each entry `{ rel, stack, present }`. It fills only when the target sits below its git root: every `.github/` config or seed moves there rather than into `configs` or `seeds`, and none of them counts toward the exit, since GitHub never reads a workflow outside the root. `present` marks a copy an earlier sync already left in the subfolder, which the sync reports and never deletes. At a root, and for a target outside any repository, `withheld` is empty.
94
-
95
- `canon tooling sync <stack> [target] --check` reports the same list and always exits 0. It stays for scripts that already call it. `sync` itself writes only under `--write`, and a headless run with neither flag reports and exits 1.
96
-
97
- ## Install guards
98
-
99
- `canon gov install` requires its first argument under
100
- `CANON_NON_INTERACTIVE=1`. It used to fall back to an interactive picker that
101
- resolved to its first option headlessly, so a call with no stack installed
102
- whichever stack sorted first. It now reports the valid names on stderr and
103
- exits 1.
104
-
105
- Every documented agent path already passes the argument, including
106
- `canon init`. The confirm-then-apply prompt after it still resolves to `Yes`
107
- headlessly, so a call that names its stack is unchanged.
108
-
109
- `--add` with no stack installs the named rules into the one positional, or the
110
- current directory, and keeps the recorded chain. Pass `./node` to force a target
111
- named like a stack. [Rule detection](gov-detect.md) proposes the names.
112
-
113
- `canon gov install` also refuses the toolkit root as a target. It resolves the
114
- target before anything else, so a path that does not exist fails rather than
115
- being scaffolded.
116
-
117
- A target still on the flat installed-rule layout from an older release reports
118
- "No governance surfaces found in target" from `canon gov sync` and exits 0,
119
- since sync reads only the current wrapped destination. `canon gov install`
120
- against that same target writes a second, wrapped copy beside the flat one
121
- rather than reading or clearing it, so the target ends up holding both, and
122
- Claude Code loads both copies of an edited rule at session start. Run `canon
123
- migrate rule-layout` first to move a target off the flat layout, which reports
124
- what it would move and applies it under `--write`.
125
-
126
- ## Governance regen
127
-
128
- `canon gov regen` is the one governance verb that runs against the toolkit root,
129
- because the `.claude/rules/` it writes there is produced output rather than an
130
- operator's working copy. It reads the stack recorded in `internal/governance.toml`
131
- into `.claude/rules/canon/`, installs anything under `internal/rules/` into a
132
- separate `.claude/rules/internal/`, and clears both destinations first so a
133
- rule the record stopped naming disappears. `internal/` is what this repository
134
- alone carries: no `canon gov install` or `canon gov sync` target ever writes it,
135
- since a target has no rule source of its own that ships nowhere.
136
-
137
- It takes `--root <path>` and defaults to the toolkit root, prints nothing on success, and
138
- reports the reason on stderr with exit 1 when the record names a stack or rule
139
- that does not resolve. The Consumed copies stage of `bun run check` calls it and
140
- asserts both the generated rule folders and the consumed copy are committed.
8
+ What each verb writes, refuses, and leaves alone. `canon --help` lists flags.
9
+
10
+ ## Bootstrap
11
+
12
+ `canon init [path]` installs up to five core domains and reports each independently. A failed domain does not abort the run, which finishes the rest and exits 1 naming the failures.
13
+
14
+ Any flag skips the confirmation prompt, but the `base` default of `--stack` does not count, so a bare `canon init` still prompts. `--skip` takes `wiki`, `governance`, and `records`, and warns on any other value.
15
+
16
+ ## Governance install
17
+
18
+ - `canon gov install <stack> [target]` needs the stack under `CANON_NON_INTERACTIVE=1`, else lists the valid names on stderr and exits 1
19
+ - It refuses the toolkit root and a missing path
20
+ - `--add <rules>` with no stack installs into the one positional, or the current directory, keeping the recorded chain. Pass `./node` for a target named like a stack.
21
+
22
+ A flat installed-rule layout makes `gov sync` report "No governance surfaces found in target" and exit 0, and `gov install` write a second, wrapped copy. Run `canon migrate rule-layout` first.
23
+
24
+ ## Governance sync
25
+
26
+ `canon gov sync [target]` updates what `.claude/rules/canon/` holds and adds nothing. `.claude/rules/project/` is never read, so put project rules there.
27
+
28
+ - A rule the toolkit no longer ships is deleted, edited or not
29
+ - A rule renamed in `governance/renames.toml` moves, and its local edits are dropped
30
+ - A rule the recorded stack lists and the tree lacks reports as `missing` with no change
31
+ - A retired `.claude/GOV.md` is removed
32
+ - A binary older than the stamp holds every rule it has no source for and asks for an upgrade
33
+
34
+ After a rename the new name shows as `missing` when the stamp records a stack, and `gov install <stack>` adds it.
35
+
36
+ `canon gov regen [--root <path>]` runs against the toolkit root alone. It rewrites `.claude/rules/canon/` and `.claude/rules/internal/` from `internal/`, and exits 1 when the record names something that does not resolve.
37
+
38
+ ## Rule detection
39
+
40
+ `canon gov detect [target] [--stack <stack>] [--json]` proposes opt-in rules and writes nothing, from `governance/detect.toml` evidence keyed by rule basename.
41
+
42
+ - `deps` match package names in `package.json`, `pyproject.toml`, `go.mod`, and `composer.json`
43
+ - `files` are globs matched against git-listed paths and their folders, to depth two. Install what the operator keeps with `canon gov install --add <rules> <target>`.
44
+
45
+ ```json
46
+ {
47
+ "target": "",
48
+ "stack": "node",
49
+ "proposals": [
50
+ {
51
+ "rule": "react",
52
+ "evidence": [{ "kind": "dep", "match": "react", "path": "package.json" }]
53
+ }
54
+ ]
55
+ }
56
+ ```
57
+
58
+ - `kind` is `dep` or `file`, and `stack` is null without `--stack`, which drops rules that stack resolves
59
+ - A glob without a wildcard is also tested on disk, an unparseable manifest contributes nothing, and outside git the walk skips `.git`, `node_modules`, `vendor`, and `.venv`
60
+ - It exits 0 on any readable target and 1 for a missing target or an unresolved stack
61
+
62
+ ## Tooling
63
+
64
+ - `canon tooling diff <stack> [target]` is the read-only comparison, exiting 1 on any difference
65
+ - A `--json` refusal answers `{ ok: false, reason, message }`, so branch on the record
66
+ - `withheld` entries are `{ rel, stack, present }`, hold every `.github/` file below the git root, and count toward no exit
67
+ - `present` marks a copy an earlier sync left, which is reported and never deleted
68
+ - `canon tooling sync <stack> [target]` writes only under `--write`. `--check` reports and exits 0, and a headless run with neither flag exits 1.
69
+
70
+ `canon tooling inject` and `prune-gitignore` apply one stack with no scan or prompt, and the latter prints the number removed on stdout. Use `sync` unless scripting provisioning, and pass `--nested` inside an open frame.
71
+
72
+ ## Design
73
+
74
+ - `canon design install` copies `base.css` to `.claude/design/` and creates no override
75
+ - Project overrides live in `.claude/design/project/`, which `design sync` never touches
76
+ - `canon design fonts add <file> --family <name>` copies a `woff2`, `woff`, `ttf`, or `otf` face into `project/fonts/` with its `fonts.css` rule, and refuses mismatched bytes as `invalid-font`
77
+ - `fonts list --json` flags a missing or out-of-folder file as a `problem`
78
+ - `canon sync` skips the domain where `design install` never ran
141
79
 
142
80
  ## Whole-project sync
143
81
 
144
- `canon sync` runs every installed domain sync, then offers to commit the result
145
- and open a pull request. Under `CANON_NON_INTERACTIVE=1` it applies the domain
146
- syncs and then refuses the git workflow, reporting the branch and commit it
147
- would have created and exiting 0. Nothing is staged, committed, or pushed
148
- headlessly.
149
-
150
- Run it interactively to reach the commit and pull request options.
151
- It also refuses a target whose working tree is dirty, so commit or stash first.
152
-
153
- ## Drift reporting
154
-
155
- `canon sync --check` reports drift and writes nothing, so it needs no clean tree
156
- and is safe to run at any time. Each file is classified as `stale` when it still
157
- matches what the toolkit installed, `customized` when the project edited it,
158
- `stranded` when it sits at a path the toolkit no longer installs to, `orphaned`
159
- when the project authored it, or `drifted` when no stamp covers it. Governance
160
- also reports `missing`, for a rule the target's recorded stack lists that its
161
- tree does not hold at all, `retired`, for a rule under
162
- `.claude/rules/canon/` the toolkit no longer ships, which the next sync
163
- deletes, and `renamed`, for one the toolkit ships under a new name, which the
164
- next sync moves. A `retired` or `renamed` file counts toward `--exit-code`,
165
- since one sync clears it.
166
-
167
- Use `--json` for the machine-readable report and `--exit-code` to fail a CI job. Orphaned
168
- and missing files are both excluded from that exit code: a project-authored
169
- rule never converges, and a sync that added a missing one silently changes
170
- what the project is governed by, which stays a separate command an operator
171
- chooses to run. Attribution reads `canon/config/config.json` first, which every
172
- install and sync now writes, and falls back to `.claude/canon/config.json` for
173
- a target that has not moved.
174
-
175
- A target installed before stamping shipped has no such file, and neither does
176
- that fallback do anything to migrate it. A target stamped before the file
177
- moved into `.claude/canon/` still carries it at the retired
178
- `.claude/canon.json`, and `canon sync --check` reads that path when the current
179
- one is absent, reporting it rather than moving it. Only a target carrying
180
- neither path falls back to the toolkit's own git history.
181
- Installed content matching any
182
- version that history ever published proves the file is untouched, so it reports
183
- `stale` naming the commit it came from, and content matching no published
184
- version stays `drifted`. A toolkit reached outside a git clone, which is what a
185
- registry install is, cannot run that fallback and reports
186
- `historyUnavailable` alongside the unattributed files.
187
-
188
- Each domain carries its own toolkit anchor in that file, so syncing one domain
189
- never advances the revision another measures from, and each reports the upstream
190
- commits touching its own source path. The `covers` field names the domains a
191
- target has actually stamped, so a domain that was never stamped is legible
192
- rather than reading as a clean one.
193
-
194
- ### Tooling
195
-
196
- Tooling records the stack chain an install resolved rather than per-file hashes,
197
- since `src/tooling/` runs its own inject machinery and has no walk to attribute.
198
- The chain is ordered nearest stack first, which is what a `--skip` run needs:
199
- recording the leaf alone would send the next report measuring against a layer the
200
- target deliberately does not carry. The report loads exactly those stacks, scans
201
- them the way `canon tooling diff` would, and counts what differs per category
202
- under `tooling.counts`.
203
-
204
- `measured` is the field the section exists for. A target carrying no chain
205
- reports `measured: false`, which separates tooling nobody has ever looked at from
206
- tooling that is current. Both produce zero changes otherwise. Every target
207
- installed before the record shipped starts unmeasured and leaves on its next
208
- `canon tooling sync`, since backfilling would mean inferring the chain from
209
- installed files, which is the guess the record replaces.
210
-
211
- A workspace root records nothing, because each package resolves its own chain and
212
- one written at the root would be that same guess. Run the check against a package
213
- to measure it. Tooling never counts toward `--exit-code`, on the grounds seeds
214
- are already excluded on, since it reports golden configs a project is expected to
215
- edit and a job counting those stays red with no remedy.
216
-
217
- ### Surfaces reported beside the domains
218
-
219
- Seven sections sit outside the per-domain scan, because each names something
220
- that walk cannot see: `seeds`, `superseded`, `unmigrated`, `unmovedSurfaces`,
221
- `newSkills`, `newRules`, and `reverse`. None of them produces a change, and no sync command
222
- applies any of them. What each reports, the managed-target gate they share, and
223
- which count toward `--exit-code` are in `drift-surfaces.md`.
82
+ `canon sync [path]` runs every installed domain sync, then offers a commit and pull request. Headlessly it applies the syncs, refuses the git workflow, reports what it would have made, and exits 0. It refuses a dirty working tree.
224
83
 
225
- ## Bootstrap
84
+ ## Drift report
85
+
86
+ `canon sync --check` writes nothing and needs no clean tree. `--json` emits the record, `--exit-code` fails CI.
87
+
88
+ | State | Meaning |
89
+ | ------------ | ------------------------------------------------------------------ |
90
+ | `stale` | Still matches what the toolkit installed |
91
+ | `customized` | The project edited it |
92
+ | `stranded` | Sits at a path the toolkit no longer installs to |
93
+ | `orphaned` | The project authored it |
94
+ | `drifted` | No stamp covers it |
95
+ | `missing` | Governance only. The recorded stack lists it and the tree lacks it |
96
+ | `retired` | Governance only. The toolkit no longer ships it |
97
+ | `renamed` | Governance only. The toolkit ships it under a new name |
98
+
99
+ `retired` and `renamed` count toward `--exit-code`. `orphaned` and `missing` do not.
100
+
101
+ - Attribution reads `canon/config/config.json`, then `.claude/canon/config.json`, then the retired `.claude/canon.json`, and moves none
102
+ - With none it matches the toolkit's git history, where a published version is `stale` and anything else `drifted`. A registry install has none and reports `historyUnavailable`.
103
+ - `covers` names the domains the target stamped, and each domain keeps its own anchor
104
+ - Tooling records its stack chain, nearest first, and never counts toward `--exit-code`
105
+ - `tooling.measured: false` means none was recorded. It clears on the next `canon tooling sync`. A workspace root records nothing, so run the check against a package.
106
+
107
+ ## Side sections
108
+
109
+ Seven sections sit outside the per-domain scan, and no sync applies them. Each returns empty when `managed` is false. A managed target holds `.claude/`, a `CLAUDE.md`, or a root-layout domain, and an unmanaged one is routed to `canon init`.
110
+
111
+ | Section | Reports | Exit |
112
+ | ----------------- | --------------------------------------------------------------------------------------- | ---- |
113
+ | `seeds` | Each seed as `matching`, `stale`, `drifted`, or `missing`. Merge an edited one by hand. | No |
114
+ | `superseded` | A file a newer seed folder replaced, as `replacedBy`. Never deleted. | No |
115
+ | `unmigrated` | A domain at its old root, with `rootPath`, `installPath`, and a count | Yes |
116
+ | `unmovedSurfaces` | A surface at its `.claude/` spelling, as `rel` and `movesTo` | No |
117
+ | `newRules` | A rule the recorded stack lists and the tree lacks | No |
118
+ | `newSkills` | A plugin skill the target has not seen. Needs no command. | No |
119
+ | `reverse` | `unclaimed` folders the toolkit once shipped | No |
120
+
121
+ The Exit column marks what counts toward `--exit-code`. `canon migrate surface-roots` closes `unmovedSurfaces`, and `canon gov install` closes `newRules`. A `reverse` entry is attributed `dropped`, `project`, or `unattributed`.
122
+
123
+ `newRules` and `newSkills` come back empty on a shallow clone or registry install, which is unmeasured rather than clean.
124
+
125
+ ## Version skew
126
+
127
+ `canon sync --check` and `canon claude skills drift` report the installed version against the newest published one in `skew.state`. The report never changes an exit code, so branch on the field.
128
+
129
+ | State | Meaning |
130
+ | --------- | ---------------------------------------------------- |
131
+ | `behind` | Names `canon upgrade` as the remedy |
132
+ | `current` | Installed matches published |
133
+ | `unknown` | Carries the reason the registry could not be reached |
226
134
 
227
- `canon init` installs up to five core domains and reports each one independently. A
228
- domain that fails does not abort the run, so the command finishes the rest and
229
- exits 1 naming the failures. Passing any flag skips the confirmation prompt,
230
- which is what makes it scriptable.
231
-
232
- `--stack` defaults to `base`, and the default
233
- does not read as a passed flag, so a bare `canon init` installs governance and
234
- still prompts. `--skip` takes `wiki`, `governance`, and `records`, and warns
235
- without aborting on any other value. Records has nothing to run non-interactively,
236
- since the private backup repository does not exist yet at scaffold time, so its
237
- step only prints the one-time setup reminder and `--skip records` silences it.
238
-
239
- ## Unguarded tooling primitives
240
-
241
- `canon tooling inject` and `canon tooling prune-gitignore` are the unguarded
242
- primitives beneath `sync`. They apply one stack with no scan and no prompt, and
243
- they deliberately skip the check that rejects `claude`, which is how `canon
244
- claude` drives its own stack through them. Use `sync` unless you are scripting
245
- provisioning. Both frame their own output, so pass `--nested` when calling from
246
- inside an already-open frame.
135
+ `canon upgrade --json` adds `pending`, where the checkout holds a release npm does not serve yet. `checkout` names it, `message` starts `CLI stays at`, and the exit is 0.
@@ -0,0 +1,39 @@
1
+ ---
2
+ title: Markdown audit density
3
+ description: The six layout limits the markdown audit measures, what each one counts, which paths take a finding, and how a folder's gate turns on
4
+ ---
5
+
6
+ # Markdown audit density
7
+
8
+ `canon markdown audit` measures six layout limits beside its bans. Each one is a count against a fixed number, so a finding is a fact rather than a judgment, and a gated path fails the run on it.
9
+
10
+ ## The limits
11
+
12
+ Six limits report on the paths that take them, under `density` in each `--json` entry.
13
+
14
+ | Limit | Finding |
15
+ | ------------------- | ---------------------------------------------------------- |
16
+ | Paragraph length | a prose paragraph past 350 characters |
17
+ | Paragraphs in a row | more than 2 paragraphs with nothing else between them |
18
+ | Bullet length | a top-level bullet past 200 characters, marker excluded |
19
+ | Bullets in a row | more than 5 plain top-level bullets |
20
+ | Heading depth | any heading past H3 |
21
+ | H3 opening | an H3 whose first block is neither a paragraph nor a table |
22
+
23
+ ## What counts as a block
24
+
25
+ A paragraph indented under a list item counts toward that bullet's length and never as a paragraph. A table, a fence, a list, a heading, and a bold section marker each break a paragraph run. Each finding carries `kind`, `line`, `value`, `limit`, and `gating`, and the H3 opening carries `null` for both numbers.
26
+
27
+ ## Which paths take a finding
28
+
29
+ Only context entries, docs pages, skill bodies and references, tooling seeds and stack references, the wiki, `README.md`, and `CONTRIBUTING.md` take a finding. A rule, `CLAUDE.md`, a requirement or design record, the changelog, `examples/`, and every `.canon/` record take none, so a plan never reports one.
30
+
31
+ ## When a finding gates
32
+
33
+ A finding gates when its path sits under a prefix in `GATED_PATHS` in `src/markdown/scope.ts`, and each rewrite batch adds its folders at zero, or one file per entry where it covers part of a folder. A run of more than 5 plain bullets gates in context, docs, and skills from the start, since it failed the push there before this measure existed.
34
+
35
+ Outside a canon checkout every density finding reports and none gates, since the list describes this repository's own rewrite.
36
+
37
+ ## What reads the finding
38
+
39
+ A gating finding sets exit `2`, the same code a ban hit takes, and `markdown-audit.md` carries the full exit contract. The `Markdown` stage of `canon gate run` reads that exit over the whole corpus, and the edit-time hook hands every finding back, gating or not.
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  title: Markdown audit weight
3
- description: The structural half of the markdown audit, being bullet and paragraph weight, bullet stacks, run depth, the two cadence numbers, and the length ceiling, with the samples behind each checkpoint and why none of them gates
3
+ description: The advisory sections of the markdown audit, being bullet and paragraph weight, run depth, the two cadence numbers, and the length ceiling, with the samples behind each checkpoint and which of those numbers gate through density
4
4
  ---
5
5
 
6
6
  # Markdown audit weight
7
7
 
8
8
  ## Bullets, paragraphs, and depth
9
9
 
10
- Bullet weight and depth are the checks that moved off `canon context audit`, carrying what they measured at the time. A top-level bullet reports past roughly 250 characters with continuation lines folded in and nested items left out. A run reports past roughly 40 rendered lines, measured at 80 columns, where a heading breaks one and so does a bold section marker taking the whole line at column zero, either ending in a colon, or holding one whole code span at any width, or running to 20 characters or fewer, skipping fenced blocks and exempting a flat peer list averaging under 130 characters a bullet and a run that is entirely table rows. Each file reports its longest run alone, so a second run past the checkpoint in the same file is never named.
10
+ Bullet weight and depth are the checks that moved off `canon context audit`, carrying what they measured at the time. A top-level bullet reports past roughly 200 characters with continuation lines folded in and nested items left out. A run reports past roughly 40 rendered lines, measured at 80 columns, where a heading breaks one and so does a bold section marker taking the whole line at column zero, either ending in a colon, or holding one whole code span at any width, or running to 20 characters or fewer, skipping fenced blocks and exempting a flat peer list averaging under 130 characters a bullet and a run that is entirely table rows. Each file reports its longest run alone, so a second run past the checkpoint in the same file is never named.
11
11
 
12
12
  Every weight and depth measure counts the text a reader is shown. A link reduces to its anchor text and an autolink drops whole, since no reader is shown either destination. A backticked path stays counted, which is where these measures part from the ban scan `markdown-audit.md` describes: that one blanks a code span so a standard quoting its own banned character does not report itself, and discounting the same span here would under-report a paragraph carrying several. One file holds both span sets and each answers its own question.
13
13
 
@@ -17,7 +17,7 @@ The paragraph check measures both halves of one rule. `markdown.md` caps a parag
17
17
 
18
18
  A sentence boundary closes on terminal punctuation ahead of a capital or a code span. The capital is what keeps a version pin and a decimal from each reading as two sentences, and the code span is admitted beside it because a command name opening a sentence carries no capital to find.
19
19
 
20
- The paragraph weight sits at 700 and the bullet weight at 250. Both shipped at 400, and the bullet number moved to 250 so the audit's warning and the `paragraph-growth` gate stage, which fails a bullet a branch adds or grows past it in a context entry, read one number. The paragraph number was borrowed from the bullet rule when the two checks landed together, and each has since been read against a sample of its own. They are separate checkpoints in the standard and separate patterns in the parser, so a read that moves one leaves the other where it is.
20
+ The paragraph weight sits at 350 and the bullet weight at 200, the two density lengths in `markdown-audit.md`, so this advisory warning and the density finding that gates read one number. Both shipped at 400 and have moved twice, each time against a sample of its own. They are separate checkpoints in the standard and separate patterns in the parser, so a read that moves one leaves the other where it is.
21
21
 
22
22
  ### The sample behind the paragraph number
23
23
 
@@ -44,15 +44,17 @@ The sample is thirty-six paragraphs against a reported population in the hundred
44
44
 
45
45
  That re-sample ran once the scan stopped counting link syntax as prose. Findings at 604, 633, and 677 characters each read as an ordinary four-sentence paragraph on one topic, density arrived around 760 and was plain by 860, and the move cut the weight half of the report roughly in half while leaving the sentence cap untouched.
46
46
 
47
+ ### The read that moved it to 350
48
+
49
+ The operator read sample rewrites at 400 and 350 side by side and picked 350, the length of the reference sample, in groundwork `154`. The bullet number moved to 200 in the same read, since no rewritten one-fact bullet passed 151. Both now gate as density limits, so the earlier verdict that a long paragraph reads well as written no longer holds.
50
+
47
51
  A bullet, a heading, a table row, a blockquote, a blank line, and a fence each end a paragraph, so a heavy bullet is reported by the bullet check alone and never counted twice.
48
52
 
49
53
  ## Bullet stacks
50
54
 
51
- A run of consecutive top-level bullets reports past 5. A blank line, an indented continuation, and a nested item do not break the run, and a paragraph, a heading, or any other column-zero line does. A fenced line is skipped without breaking it. The count is of items rather than weight, so it catches a catalog of short bullets the 250-character checkpoint above passes.
52
-
53
- Two shapes are exempt. A run whose every item opens with a bold label or a link names its own subject per item and reads as a table of entries, and a numbered list states an order, so numbered items never count. `CHANGELOG.md` is exempt by name. Governance rules and `REQUIREMENT.md` sit outside the gate stage's scope rather than being exempted here, since a rule is one imperative per bullet.
55
+ A run of more than 5 plain bullets is one of the six density limits in `markdown-audit.md`, which reports and gates it. This section keeps what counts as a run. A run of consecutive top-level bullets reports past 5. A blank line, an indented continuation, and a nested item do not break the run, and a paragraph, a heading, or any other column-zero line does. A fenced line is skipped without breaking it. The count is of items rather than weight, so it catches a catalog of short bullets the 200-character checkpoint above passes.
54
56
 
55
- The audit reports a stack and never fails on one. The `bullet-stacks` stage in `canon gate run` fails the push, for the reason the length ceiling does: the audit also runs on plans and groundwork records kept out of version control.
57
+ Two shapes are exempt. A run whose every item opens with a bold label or a link names its own subject per item and reads as a table of entries, and a numbered list states an order, so numbered items never count. Governance rules and `REQUIREMENT.md` sit outside the density scope rather than being exempted here, since a rule is one imperative per bullet.
56
58
 
57
59
  ## Cadence
58
60
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Markdown audit
3
- description: Running the audit over any markdown path, where its bans and checkpoints are read from, what each check reports, and why the ban half gates while the structural half reports
3
+ description: Running the audit over any markdown path, where its bans and checkpoints are read from, what each check reports, how a reference page opts into its shape check, and why bans, dead links, and density or reference shape on a gated path fail while the rest reports
4
4
  ---
5
5
 
6
6
  # Markdown audit
@@ -11,13 +11,11 @@ description: Running the audit over any markdown path, where its bans and checkp
11
11
  canon markdown audit
12
12
  canon markdown audit --json
13
13
  canon markdown audit .claude/rules governance
14
- canon markdown audit docs/agents/commands.md
14
+ canon markdown audit docs/agents/install-and-sync.md
15
15
  canon markdown audit 'docs/**/*.md'
16
16
  ```
17
17
 
18
- | Option | Behavior |
19
- | -------- | ---------------------------------------------------------- |
20
- | `--json` | Add a machine-readable record on stdout, keeping the frame |
18
+ Run `canon markdown audit --help` for every flag.
21
19
 
22
20
  ## Scope
23
21
 
@@ -65,15 +63,25 @@ The heading-case measure reports a heading or a frontmatter `title` with a capit
65
63
 
66
64
  `CHANGELOG.md` is exempt from both, since the release tool writes its headings.
67
65
 
66
+ ### Density
67
+
68
+ The six layout limits, their scope, and their gate are in `markdown-audit-density.md`.
69
+
70
+ ### Reference shape
71
+
72
+ A page opts in with `kind: reference` in its frontmatter, and an unmarked page is never measured. On a marked page, outside fences, an H2 other than `Output`, `Refusals`, or `See also` reports, as does one repeated or out of that order, any H3, and a table headed `Flag` or `Option`.
73
+
74
+ Each finding sits under `referenceShape` in the `--json` entry with `kind`, `line`, `text`, and `gating`. It gates under the prefixes density gates on.
75
+
68
76
  ### Weight, cadence, and length
69
77
 
70
- Bullet and paragraph weight, bullet stacks, run depth, the two cadence numbers, and the document length ceiling are each a judgment a reader settles rather than a fact a scan settles. What each measures, the samples behind its checkpoint, and why none of them gates are in `markdown-audit-weight.md`.
78
+ The Bullets, Paragraphs, Cadence, Depth, and Length sections report and never set the exit. The bullet and paragraph numbers gate through the density lengths instead, and depth and cadence stay a judgment a reader settles. What each section measures and the samples behind its checkpoint are in `markdown-audit-weight.md`.
71
79
 
72
80
  ## Exit codes
73
81
 
74
- Exit codes are `0` for a completed run with no gating finding, `1` for a refusal, `2` for a ban hit or a dead relative link, and `3` for a shipped ban set that arrived empty. A banned character and a relative link resolving to nothing on disk each fail the run, both facts a scan settles rather than a reader judging. Bullet, paragraph, and depth weight are judgments a reader settles, and cadence is a distribution whose healthy range moves with the surface, so all four report under every code. A bullet stack, a run of more than 5 plain bullets, reports the same way, and the `bullet-stacks` stage in `canon gate run` owns its verdict. Length reports the same way, since the audit also runs on a plan kept out of version control, and the Document ceiling stage in `canon gate run` owns that verdict instead.
82
+ Exit codes are `0` for a completed run with no gating finding, `1` for a refusal, `2` for a ban hit, a dead relative link, or a density or reference shape finding marked `gating`, and `3` for a shipped ban set that arrived empty. All three are facts a scan settles rather than a reader judging. Depth weight is a judgment a reader settles, and cadence is a distribution whose healthy range moves with the surface, so both report under every code, as do the advisory Bullets and Paragraphs sections. Length reports the same way, since the audit also runs on a plan kept out of version control, and the Document ceiling stage in `canon gate run` owns that verdict instead.
75
83
 
76
- `3` is separate from `1` because the two want different responses from a caller. A refusal means no corpus was built, and the `Markdown bans` stage in `canon gate run` is right to report it as unmeasured rather than as a pass. An empty set means the corpus was walked and nothing was looked for, so that stage fails the push on `3` rather than skipping.
84
+ `3` is separate from `1` because the two want different responses from a caller. A refusal means no corpus was built, and the `Markdown` stage in `canon gate run` is right to report it as unmeasured rather than as a pass. An empty set means the corpus was walked and nothing was looked for, so that stage fails the push on `3` rather than skipping.
77
85
 
78
86
  `2` rather than `1` for the gate keeps a measurement that succeeded and found something distinct from the audit declining to measure at all. A caller reading one as the other sends a reader hunting a defect that does not exist, which is the distinction `canon context audit` and the gate's own seed stage already draw between the same two codes.
79
87
 
@@ -83,8 +91,6 @@ Of the five measures a first full reading took, the ban count is the only one a
83
91
 
84
92
  The ban half reached zero, which was the precondition the gate waited on, and it was re-measured against the same corpus at the moment the gate landed. Three sweep slices then took the structural half, and the corpus reports 1 heavy bullet and 22 heavy paragraphs across 6 files on 2026-08-06, every one on a ground the triage record holds as exempt.
85
93
 
86
- Depth is the one measure no slice triaged, so its figure moved with the sweep rather than with the corpus. The break rule widened to bold section markers on 2026-08-28 and again to the colon-less ones the same day, and each drop came with no edit to any file, so the 41 above reads against a rule the command no longer runs. Read all four structural counts from a run rather than from this paragraph.
87
-
88
94
  ### What a hit asks of an author
89
95
 
90
96
  Rewrite the sentence rather than swapping the banned token for a near-synonym. The rule is about the sense the token carries, so a swap that keeps the sense clears the report without clearing the violation.
@@ -95,7 +101,7 @@ A hit the closed set could not separate from correct prose was the case with no
95
101
 
96
102
  ### Where the rules are enforced
97
103
 
98
- Four surfaces apply the ban set and all four go through this verb. `.claude/hooks/standards-audit.sh` runs it against a single file after each markdown edit, the seed copy a project installs does the same, the `Markdown bans` stage in `canon gate run` runs it across the whole corpus before this repository's own push, and the same-named stage in the one verify runner every stack shares, `scripts/verify.ts`, runs it across a scaffolded project's tracked markdown before its own push. Each hook parsed its own copy of the word bans in awk before that, which left a British spelling passing at edit time and failing the push with nothing in between explaining the difference.
104
+ Four surfaces apply the ban set and all four go through this verb. `.claude/hooks/standards-audit.sh` runs it against a single file after each markdown edit, the seed copy a project installs does the same, the `Markdown` stage in `canon gate run` runs it across the whole corpus before this repository's own push, and the same-named stage in the one verify runner every stack shares, `scripts/verify.ts`, runs it across a scaffolded project's tracked markdown before its own push. Each hook parsed its own copy of the word bans in awk before that, which left a British spelling passing at edit time and failing the push with nothing in between explaining the difference.
99
105
 
100
106
  The seed copy moved onto the verb when the sets became data, since its awk had nothing left to parse. It resolves one runner where the toolkit copy resolves two, looking for no checkout source, and a machine carrying no `canon` gets a report naming the binary to install rather than a silent pass. The Seed independence stage of `bun run check` scopes its walk to markdown and leaves the seed hooks outside it, which its own comment records as deliberate.
101
107
 
@@ -105,7 +111,7 @@ The target push stage excludes `CHANGELOG.md`, unlike every other surface here.
105
111
 
106
112
  The hook prefers a checkout's own `src/cli.ts` over a globally installed binary, so it and the push stage read one build. A published binary lags a branch by whatever has not been released, which would put a ban kind added on the branch into the push and not into the edit. It reads its findings out of the `--json` record rather than off the exit code, so an older binary still reports where the fallback applies. It reads `bans.emptySets` out of the same record, so a set the verb shipped empty reaches the author as a check narrowed to what it could measure rather than as a clean pass.
107
113
 
108
- It also relays the two advisory measures from the same record. A heading finding reports for the whole file, and a colon finding reports only where the word before the colon and the colon appear in the text the write carried, so an edit is never asked to answer for a colon it left untouched.
114
+ It relays every density finding from the same record, gating or not, and the two advisory measures. A heading finding reports for the whole file, and a colon finding reports only where the word before the colon and the colon appear in the text the write carried, so an edit is never asked to answer for a colon it left untouched.
109
115
 
110
116
  That field replaced `bans.missingStandards`, which answered a standard resolving under none of three roots. The set ships with the package now, so the state it named cannot occur and the narrowed check has one cause left, a defect in the build. The hook keeps reading a field either way, since a reader cannot tell a narrowed check from a clean one without it.
111
117
 
@@ -122,5 +128,3 @@ The verb reads the `markdown-craft` skill's reference and the `write-human` cade
122
128
  `publish.md` describes a scan applying the same punctuation bans to finished text on its way out. No code implemented that scan before this command, so nothing is duplicated, and a later surface should call this verb rather than build a second one.
123
129
 
124
130
  That reference's cross-reference rule is out of reach rather than merely unimplemented. It resolves by destination, bare where the destination auto-links a pull request or issue number and backticked where it does not, so this verb sees only the half where the backticked form is correct. A check here would report every compliant reference in the tree and reach none of the published text that goes wrong, which is why the rule holds on reading alone.
125
-
126
- The list-density rule at `claude/skills/markdown-craft/references/markdown.md` is out of scope on purpose, since it carries no number and what a density figure should measure is still open.