@erclx/canon 4.120.0 → 4.122.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 (152) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/auto-ship/SKILL.md +29 -115
  3. package/claude/skills/auto-ship/references/draft-mark.md +30 -0
  4. package/claude/skills/auto-ship/references/failure-recovery.md +27 -0
  5. package/claude/skills/auto-ship/references/tier-failures.md +26 -0
  6. package/claude/skills/auto-ship/references/verb-absent.md +37 -0
  7. package/claude/skills/auto-ship/references/worktree-name.md +22 -0
  8. package/claude/skills/backlog-triage/REQUIREMENT.md +47 -0
  9. package/claude/skills/backlog-triage/SKILL.md +139 -0
  10. package/claude/skills/bash-script/SKILL.md +1 -1
  11. package/claude/skills/bash-script/references/patterns.md +0 -99
  12. package/claude/skills/bash-script/references/prompts.md +100 -0
  13. package/claude/skills/canon-rollout/SKILL.md +6 -6
  14. package/claude/skills/context-fold/REQUIREMENT.md +75 -0
  15. package/claude/skills/context-fold/SKILL.md +162 -0
  16. package/claude/skills/{docs-fold → context-fold}/references/anchor-sweep.md +1 -1
  17. package/claude/skills/{docs-fold → context-fold}/references/classify.md +4 -2
  18. package/claude/skills/context-fold/references/context-refresh.md +48 -0
  19. package/claude/skills/context-fold/references/promoted-pages.md +29 -0
  20. package/claude/skills/context-fold/references/receipt-sweep.md +43 -0
  21. package/claude/skills/context-fold/references/unusable-baseline.md +25 -0
  22. package/claude/skills/{docs-fold → context-fold}/references/wireframe-sweep.md +1 -1
  23. package/claude/skills/create-skill/SKILL.md +1 -1
  24. package/claude/skills/create-snippet/SKILL.md +1 -1
  25. package/claude/skills/create-standard/SKILL.md +1 -1
  26. package/claude/skills/design-extract/SKILL.md +1 -1
  27. package/claude/skills/design-taste/REQUIREMENT.md +2 -2
  28. package/claude/skills/docs-fold/REQUIREMENT.md +8 -58
  29. package/claude/skills/docs-fold/SKILL.md +3 -278
  30. package/claude/skills/docs-sync/REQUIREMENT.md +1 -1
  31. package/claude/skills/docs-sync/SKILL.md +2 -2
  32. package/claude/skills/draft-context/REQUIREMENT.md +2 -2
  33. package/claude/skills/draft-context/SKILL.md +4 -4
  34. package/claude/skills/draft-docs/REQUIREMENT.md +1 -1
  35. package/claude/skills/draft-docs/SKILL.md +1 -1
  36. package/claude/skills/draft-figure/SKILL.md +1 -1
  37. package/claude/skills/draft-readme/SKILL.md +1 -1
  38. package/claude/skills/draft-wiki/SKILL.md +1 -1
  39. package/claude/skills/draft-wireframes/REQUIREMENT.md +1 -1
  40. package/claude/skills/draft-wireframes/SKILL.md +3 -3
  41. package/claude/skills/git-issue/SKILL.md +1 -1
  42. package/claude/skills/git-issue/references/issue.md +1 -1
  43. package/claude/skills/git-pr/SKILL.md +9 -147
  44. package/claude/skills/git-pr/references/evidence.md +114 -0
  45. package/claude/skills/git-pr/references/labels.md +20 -0
  46. package/claude/skills/git-pr/references/task-number.md +26 -0
  47. package/claude/skills/git-ship/SKILL.md +1 -1
  48. package/claude/skills/git-stage/SKILL.md +1 -1
  49. package/claude/skills/memory-capture/REQUIREMENT.md +2 -2
  50. package/claude/skills/memory-capture/SKILL.md +6 -6
  51. package/claude/skills/memory-review/REQUIREMENT.md +1 -1
  52. package/claude/skills/memory-review/SKILL.md +5 -101
  53. package/claude/skills/memory-review/references/later-phases.md +108 -0
  54. package/claude/skills/plan-feature/REQUIREMENT.md +1 -1
  55. package/claude/skills/restate-plainly/REQUIREMENT.md +1 -1
  56. package/claude/skills/review-address/REQUIREMENT.md +1 -1
  57. package/claude/skills/review-address/SKILL.md +5 -4
  58. package/claude/skills/review-branch/REQUIREMENT.md +1 -0
  59. package/claude/skills/review-branch/SKILL.md +6 -7
  60. package/claude/skills/review-craft/REQUIREMENT.md +43 -0
  61. package/claude/skills/review-craft/SKILL.md +50 -0
  62. package/claude/skills/review-craft/references/security.md +21 -0
  63. package/claude/skills/review-craft/references/ui.md +72 -0
  64. package/claude/skills/review-pr/REQUIREMENT.md +1 -0
  65. package/claude/skills/review-pr/SKILL.md +1 -1
  66. package/claude/skills/review-pr/references/post.md +1 -1
  67. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -1
  68. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -0
  69. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +2 -2
  70. package/claude/skills/role-planner/REQUIREMENT.md +1 -1
  71. package/claude/skills/session-resume/REQUIREMENT.md +1 -1
  72. package/claude/skills/session-worktree/SKILL.md +4 -10
  73. package/claude/skills/standards-audit/SKILL.md +1 -1
  74. package/claude/skills/task-board/REQUIREMENT.md +1 -1
  75. package/claude/skills/task-board/SKILL.md +6 -80
  76. package/claude/skills/task-board/references/archive.md +54 -0
  77. package/claude/skills/task-board/references/decline.md +40 -0
  78. package/claude/skills/teach-workspace/SKILL.md +8 -105
  79. package/claude/skills/teach-workspace/references/lesson-craft.md +51 -1
  80. package/claude/skills/teach-workspace/references/promotion.md +41 -0
  81. package/claude/skills/write-human/REQUIREMENT.md +7 -4
  82. package/claude/skills/write-human/SKILL.md +4 -2
  83. package/claude/skills/write-human/references/machine-tells.md +1 -1
  84. package/claude/skills/write-human/references/source-material.md +5 -2
  85. package/docs/agents/architecture-record.md +26 -0
  86. package/docs/agents/audits.md +1 -1
  87. package/docs/agents/commands.md +87 -154
  88. package/docs/agents/context-audit-checks.md +2 -20
  89. package/docs/agents/context-audit.md +1 -1
  90. package/docs/agents/context-classify.md +1 -1
  91. package/docs/agents/drift-surfaces.md +192 -0
  92. package/docs/agents/index.md +12 -4
  93. package/docs/agents/install-and-sync.md +2 -181
  94. package/docs/agents/markdown-audit-weight.md +73 -0
  95. package/docs/agents/markdown-audit.md +11 -104
  96. package/docs/agents/migrate.md +34 -0
  97. package/docs/agents/pr-local.md +6 -3
  98. package/docs/agents/records-stale.md +62 -0
  99. package/docs/agents/records.md +5 -1
  100. package/docs/agents/sandbox.md +1 -1
  101. package/docs/agents/tasks-archive.md +81 -0
  102. package/docs/agents/tasks-plans.md +147 -0
  103. package/docs/agents/tasks-validate.md +131 -0
  104. package/docs/agents/tasks.md +6 -333
  105. package/docs/target-projects.md +4 -4
  106. package/docs/workflow/ai-workflow.md +33 -27
  107. package/governance/rules/claude/500-prose.md +1 -1
  108. package/governance/rules/claude/501-markdown.md +3 -3
  109. package/governance/rules/core/045-memory.md +1 -1
  110. package/package.json +1 -1
  111. package/src/autoship/paths.ts +3 -2
  112. package/src/claude/cases/setup.ts +1 -1
  113. package/src/claude/cases/workflow.ts +27 -1
  114. package/src/commands/audits.ts +2 -2
  115. package/src/commands/markdown.ts +7 -9
  116. package/src/commands/pr.ts +11 -4
  117. package/src/commands/records.ts +146 -0
  118. package/src/commands/sandbox.ts +6 -4
  119. package/src/context/classify/extract.ts +1 -1
  120. package/src/context/classify/ollama.ts +1 -1
  121. package/src/context/classify/run.ts +1 -1
  122. package/src/gate/measures.ts +5 -5
  123. package/src/gate/stages.ts +0 -1
  124. package/src/markdown/bans.ts +11 -68
  125. package/src/markdown/gate.ts +1 -1
  126. package/src/markdown/scan.ts +7 -88
  127. package/src/migrate/skill-names.ts +15 -2
  128. package/src/records/stale.ts +243 -0
  129. package/src/sandbox/expect.ts +1 -1
  130. package/src/tasks/archive.ts +28 -4
  131. package/src/tasks/record.ts +1 -1
  132. package/src/tasks/validate.ts +36 -3
  133. package/standards/docs.md +1 -1
  134. package/standards/index.md +1 -1
  135. package/standards/markdown.md +5 -7
  136. package/standards/memory.md +13 -1
  137. package/standards/pr.md +1 -1
  138. package/standards/publish.md +1 -1
  139. package/standards/readme.md +2 -2
  140. package/standards/tasks.md +1 -1
  141. package/tooling/astro/reference.md +1 -1
  142. package/tooling/base/configs/.husky/post-merge +1 -1
  143. package/tooling/base/configs/scripts/verify.sh +2 -2
  144. package/tooling/base/seeds/canon/context/development.md +1 -1
  145. package/tooling/claude/reference.md +1 -1
  146. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +5 -6
  147. package/tooling/go/reference.md +1 -1
  148. package/tooling/nextjs/reference.md +1 -1
  149. package/tooling/php/reference.md +1 -1
  150. package/tooling/python/reference.md +1 -1
  151. package/tooling/vite-react/reference.md +1 -1
  152. package/tooling/web/reference.md +1 -1
@@ -0,0 +1,192 @@
1
+ ---
2
+ title: Drift surfaces
3
+ description: The six sections canon sync --check reports beside the per-domain scan, being seeds, superseded artifacts, unmigrated domains, new rules and skills, and the reverse walk, with the managed-target gate and what counts toward the exit code
4
+ ---
5
+
6
+ # Drift surfaces
7
+
8
+ `canon sync --check` reports six sections outside the per-domain scan that
9
+ `install-and-sync.md` describes, because each names something
10
+ that walk cannot see: `seeds`, `superseded`, `unmigrated`, `newSkills`,
11
+ `newRules`, and `reverse`. None of them produces a change, and no sync command
12
+ applies any of them. The headings below group the first three together and give
13
+ `newSkills` its description under `newRules`, which answers the same question
14
+ about a different corpus.
15
+
16
+ All six report only against a toolkit-managed target, which is one carrying a
17
+ `.claude/` directory, a `CLAUDE.md`, or a domain still at the root layout. The
18
+ report says so through `managed` in the JSON and routes an unmanaged directory to
19
+ `canon init`. Seeds are why the gate exists, since they enumerate from the toolkit
20
+ source rather than from what a target installed, so an unmanaged directory would
21
+ otherwise report every seed as `missing`.
22
+
23
+ A root-layout domain counts as a marker on its own, because the detection fires
24
+ only on root files the toolkit ships and a project in the old layout is one the
25
+ toolkit installed. When `managed` is false every section comes back empty rather
26
+ than the render alone going quiet, so a consumer reading `--json` never acts on a
27
+ finding the rendered half withheld.
28
+
29
+ ## Seeds, superseded artifacts, and unmigrated domains
30
+
31
+ `seeds` classifies every seed the toolkit ships against the target's copy, as
32
+ `matching`, `stale`, `drifted`, or `missing`. `missing` has no per-domain
33
+ equivalent, since the domain walk lists what a target installed and cannot see a
34
+ file that never arrived. There is no `customized` verdict here, because that one
35
+ needs a stamp and seeds carry none, so a file history cannot attribute stays
36
+ `drifted`. Reconcile the section with `seed-sync`, which merges one
37
+ section at a time rather than replacing a file the project edits.
38
+
39
+ A markdown seed installs rewritten rather than copied, since the `stub: true`
40
+ marker the toolkit's own seed gate reads is stripped on the way in. The
41
+ comparison above runs against what the install would write, so a marked seed a
42
+ target never touched still reports `matching`. Every other seed copies byte for
43
+ byte.
44
+
45
+ `superseded` names a file a newer seed folder replaced, such as `.claude/TASKS.md`
46
+ against the `.canon/tasks/` that now ships. The entry carries `replacedBy` and
47
+ nothing else, and the file is never deleted, since the content belongs to the
48
+ project and only its author can decide where it moves. The list derives from the
49
+ seed tree rather than from a fixed set of filenames, so a folder added later is
50
+ covered without a code change. Only an exact stem matches, which leaves a
51
+ suffixed variant such as `TASKS-ARCHIVE.md` unreported.
52
+
53
+ No skill splits the file. The standard governing `replacedBy` is the one the
54
+ `appliesTo` the standards catalog declares, and `canon standards <name>` reads
55
+ its destination shape, so the split is the project's own to make.
56
+
57
+ `unmigrated` names a domain sitting at the root layout an older toolkit installed
58
+ to, with nothing at the path the current one reads. It carries `rootPath`,
59
+ `installPath`, and a file count. Without it a project holding `standards/` at its
60
+ root reports zero entries for that domain and reads as clean, which is the most
61
+ misleading state the report can produce.
62
+
63
+ The field currently names no domain. Standards and snippets are the two the
64
+ toolkit ever installed at a project root, and both closed their install channel,
65
+ so a project still holding a root `standards/` or `snippets/` folder is carrying
66
+ its own authoring surface rather than an unfinished install. Nothing proposes
67
+ moving either, and no command relocates the content. Move it yourself.
68
+
69
+ ## Rules the target never received
70
+
71
+ `newRules` names a rule the target could receive and its tree does not hold. A
72
+ sync refreshes the files a target already holds and adds none, so without this
73
+ section a project's rule set freezes at its install date while every file it
74
+ does hold reports as current. That is the report's most confident wrong
75
+ answer, since a clean result reads as a target holding everything the toolkit
76
+ publishes.
77
+
78
+ The list rides beside `newSkills`, which asks the same question about the plugin
79
+ catalog. Both are names rather than paths, and neither queues a change, because
80
+ the two remedies differ: a skill loads live from the plugin directory and needs
81
+ nothing run, while a rule reaches a target only when someone runs
82
+ `canon gov install`.
83
+
84
+ Read a clean section as reporting rather than as delivering. Nothing here
85
+ installs, nothing counts toward `--exit-code`, and a target can read the list and
86
+ act on none of it. The value stops at an operator reading it, which is the same
87
+ contract the skills list already sets.
88
+
89
+ Since `canon gov install` records the stack it resolved, a target carrying that
90
+ record answers this by comparing its current entitlement against its current
91
+ tree, with no anchor and no git diff involved. That is what lets the section
92
+ name a rule that shipped before the target's last sync: the anchor a sync
93
+ advances plays no part in the read, where an anchor-bound diff can never see a
94
+ rule on the far side of a window a later sync moved past it. The per-file
95
+ `missing` state `canon gov sync` reports comes from the same comparison, so a
96
+ rule the chain lists reaches both surfaces the same way.
97
+
98
+ A target stamped before governance recorded a chain falls back to the older
99
+ band-inference read below. The measurement there anchors on governance's own
100
+ stamp rather than on the oldest anchor across domains, since rules are
101
+ domain-scoped and a shared anchor would let another domain's sync move the
102
+ revision rules are measured from. A target carrying no chain and no governance anchor
103
+ reports nothing at all: it has no date to measure against, and diffing from the
104
+ start of history would read the whole catalog as new.
105
+
106
+ An anchor this toolkit cannot resolve reports nothing by the same route, and that
107
+ one is not visible. A stamp naming a revision the running clone has never seen,
108
+ which is what a registry install without history or a shallow clone produces,
109
+ fails the read and yields an empty list rather than a stated absence. It looks
110
+ identical to a target holding every rule the toolkit publishes. `newSkills`
111
+ behaves the same way, and neither carries the `historyUnavailable` flag the
112
+ per-domain scan uses to separate the two. Treat an empty section on a toolkit
113
+ that is not a full clone as unmeasured rather than clean. This gap does not
114
+ reach the chain-based read above, since it consults no anchor at all.
115
+
116
+ In the fallback, entitlement is filtered, because a stack does not receive
117
+ every rule. The base stack takes the `core` and `claude` folders whole and
118
+ every other stack extends it with individually named rules, so an unfiltered
119
+ list would tell a base consumer about rules it can never receive and train the
120
+ reader to skip the section.
121
+
122
+ The filter accepts a band on either of two grounds. A folder the base stack takes
123
+ whole is entitled to every target, read from the stack file so a folder added to
124
+ base later needs no code change. Every other band is read off the folders the
125
+ target already carries, which is the fallback's only evidence of what a target
126
+ was entitled to before it recorded a chain. One band can be reached by more than
127
+ one stack, so the test over-reports inside a folder the target holds, which
128
+ costs a line where under-reporting would cost the section its point.
129
+
130
+ A rule the target already holds is dropped by name. That is what keeps a rule the
131
+ toolkit moved between band folders out of the list, since a rename reaches this
132
+ read as an addition and only the name tells the two apart.
133
+
134
+ This closes the dangling-citation case as a side effect rather than checking for
135
+ one. The live instance is a rule citing a sibling authored after the target's
136
+ install, and the section names the sibling as new rather than naming the citation
137
+ as broken. That is enough for an operator to act on, and it is not a citation
138
+ check.
139
+
140
+ ## The reverse walk
141
+
142
+ `reverse` is the one section built by walking the target rather than the
143
+ catalog. Every other surface enumerates toolkit-owned keys and asks whether the
144
+ target matches, so a folder the toolkit deleted appears in none of them. It
145
+ carries `unclaimed` and `historyUnavailable`.
146
+
147
+ `unclaimed` names a folder the target holds at a top-level path the toolkit once
148
+ shipped and has since deleted. The candidate roots come from the toolkit's own
149
+ history rather than from a list, so a root dropped later is covered without a
150
+ code change. Scoping to those roots is what keeps the walk useful: walking the
151
+ whole tree reports every project folder as unclaimed, which is true and says
152
+ nothing.
153
+
154
+ The managed gate above applies here too, and it is the one place it surprises.
155
+ A directory holding a dropped folder and nothing else reports an empty `reverse`
156
+ rather than the folder, because it carries none of the three markers. Read an
157
+ empty section on an unmanaged target as a walk that never ran rather than as a
158
+ clean result.
159
+
160
+ Each entry carries `rel`, a file count, and an `attribution` of `dropped`,
161
+ `project`, or `unattributed`. A dropped folder and one the project wrote are the
162
+ same bytes at the same path, so the verdict is traced from history rather than
163
+ guessed from the filesystem.
164
+
165
+ Content matching a version the toolkit published reads as `dropped` and carries
166
+ the `since` commit that published it. Names the toolkit shipped holding content
167
+ it never published read as `unattributed`, which is a state in its own right
168
+ rather than a soft yes. No overlap at all reads as `project`, and the render
169
+ drops those while the JSON keeps them.
170
+
171
+ Only files whose path the toolkit once held are hashed, so a project folder
172
+ colliding on a retired name costs the walk no reads. The cost is that a file the
173
+ toolkit shipped and the target renamed goes unmatched, the same limit the
174
+ `unmigrated` count carries.
175
+
176
+ ## What counts toward the gate
177
+
178
+ `unmigrated` counts toward `--exit-code`, since running the relocation closes it.
179
+ `superseded` and every seed state are excluded, for the reason `orphaned` already
180
+ is: only the user can move content they wrote, so failing a job on it leaves the
181
+ job red with no mechanical remedy.
182
+
183
+ `newRules` is excluded on a different ground, since a command does close it. What
184
+ excludes it is that installing a rule changes what a project is governed by, so
185
+ gating on the count would pressure a target into adopting rules nobody chose.
186
+ `newSkills` is excluded because it needs no command at all.
187
+
188
+ The whole `reverse` section is excluded on the same grounds, and more strongly.
189
+ Every entry in it is a judgment about a file the project may own, and one of its
190
+ three verdicts is a labelled unknown by design. The unmigrated detection shipped
191
+ that exact false positive once, failing a push with no action that cleared it,
192
+ so this section reports and gates nothing.
@@ -8,18 +8,20 @@ category: Agent surface
8
8
 
9
9
  CLI catalog and invocation rules for agents, split by command domain. Start with overview.
10
10
 
11
+ - [Architecture record](architecture-record.md): The four findings canon context audit reads off the architecture record, being the entry cap and length gates the record states for itself, the claim coverage report, and the word counts
11
12
  - [Audits](audits.md): Running every health check as one set, what the single verdict means, the exit code each outcome takes, the retained baseline and the delta it reports, and which corpora are kept out of the record
12
13
  - [Capture](capture.md): Rendering HTML sources to PNG, what the command asserts about fonts, and why the selector has no default
13
14
  - [Census](census.md): Tracked-plus-untracked file count, a breakdown by extension, and a line total that skips whatever reads as binary
14
- - [Command catalog](commands.md): Every project-level command and every domain subcommand, plus the shape each domain exposes
15
+ - [Command catalog](commands.md): Every top-level command with its purpose, each domain's subcommands and the page describing them, the browser commands, and the version skew report
15
16
  - [Comments](comments.md): Comment density by language and kind, the two structural exclusions, and how the degradation sweep finds its vocabulary
16
- - [Context audit checks](context-audit-checks.md): What each non-gating check reports, the unit each checkpoint is measured in, the architecture record's length and entry cap gates and claim coverage, which folders each check reaches, and what moved to the attribute tier
17
+ - [Context audit checks](context-audit-checks.md): What each non-gating check reports, the unit each checkpoint is measured in, where the architecture record's findings are described, which folders each check reaches, and what moved to the attribute tier
17
18
  - [Context audit](context-audit.md): Running the audit, its flags and folder scope, the exit codes, the citation gate, and the widened gate the seed stage runs
18
19
  - [Doc classifier](context-classify.md): Classifying canonical-doc content as keep, replace/rewrite, history, or move in diff and sweep mode, the classifier setting pair, the record shape, and the exit codes
19
20
  - [Self-stated counts](counts.md): Reading a sentence that asserts a closed catalog's size, how a match is decided, the plausibility filter that keeps a generic word from matching a subset, and why the sweep reports rather than gates
20
21
  - [Demo](demo.md): Compiling a screencast draft into a runnable plan, driving a served application to a recording and a still, reading numbered frames back out of a recording, the pointer the recording paints, and what each refusal reports
21
22
  - [Design board](design-board.md): Generating a static index over a project's design surfaces, what each panel reads, and which two stay toolkit-checkout-only
22
23
  - [Docs](docs.md): How canon docs resolves the toolkit's own reference surface from an install root, and how a split domain is named
24
+ - [Drift surfaces](drift-surfaces.md): The six sections canon sync --check reports beside the per-domain scan, being seeds, superseded artifacts, unmigrated domains, new rules and skills, and the reverse walk, with the managed-target gate and what counts toward the exit code
23
25
  - [Driver](driver.md): Walking a page through named interactions, the probe catalog and the false finding each one carries, why viewport heights are never defaulted, and what each refusal reports
24
26
  - [Merge gate](gate.md): Running the gate this repository verifies a branch with, what the stage table holds and what stays a script, how the changed set scopes three stages, and why a stage that cannot read its input reports rather than passing
25
27
  - [Indexes](indexes.md): Flags, exit codes, and JSON shape for canon indexes regen and canon indexes list, plus when regen auto-stages what it rewrote
@@ -27,7 +29,9 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
27
29
  - [Intake](intake.md): Reading intake folder counts and items, the three read states an item can be in, landing a batch of selections in one cluster, the refusal reasons, and why a call is scoped to one file
28
30
  - [Key Changes bijection](key-changes.md): Comparing the files a pull request body's Key Changes names against its own diff, the two directions and the split inside each that decides what is worth raising, the span rules the extractor was measured into, and the three refusals that separate a clean pass from a read that produced nothing
29
31
  - [Label coverage](label-coverage.md): Reading a changed set against the pull request label map, the two tables it matches, how a gap is separated from a decision, and why an absent map is an answer
32
+ - [Markdown audit weight](markdown-audit-weight.md): The structural half 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 why none of them gates
30
33
  - [Markdown audit](markdown-audit.md): 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
34
+ - [Migrate](migrate.md): What each canon migrate verb moves and rewrites, what it refuses, the markers that keep an old spelling, and the idempotence check a second run gives
31
35
  - [Output shape](output-shape.md): Two framed shapes every command renders into, how JSON and --names modes keep stdout clean, and the exit discipline that lets piped output drain
32
36
  - [Overview](overview.md): What this folder covers, the invocation rules every command inherits, and where domain behavior is documented instead
33
37
  - [The pull request evidence comparison](pr-evidence.md): What canon pr evidence compares, the marker that lets it edit its own comment rather than duplicate it, the refusal reasons it names, and why the comparison anchors at the merge base rather than the previous push
@@ -35,8 +39,9 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
35
39
  - [The pull request preview deploy](pr-preview.md): How canon pr preview finds a fenced Cloudflare Pages deploy, dispatches it on a pull request's branch, reads the alias the run prints, the refusal reasons it names, and why an unfenced deploy is refused before anything runs
36
40
  - [Head-sensitive pull request reads](pr-reads.md): Resolving a branch tip from the remote rather than from the pull request object, reading what a review pass covered off its own marker rather than off GitHub's submission stamps, why an empty run list is not a pass, the refusal reasons each verb names, and what the remote read costs
37
41
  - [Records push](records-push.md): Backing the record folders to a private remote and restoring them, which folders the payload carries, the one-time setup, the refusal reasons, and the hook that runs the push
42
+ - [Records stale](records-stale.md): Reading the memory pen as a review queue, what makes an entry due, how a cited path is read and resolved, the order entries come back in, and the exit codes and refusals
38
43
  - [Records validate](records-validate.md): Validating the session records and the standards corpus, the per-kind checks, which root each kind defaults to, the exit codes, and the refusal reasons
39
- - [Records](records.md): The two roots a record folder resolves at, migrating a record a frontmatter change orphaned, claiming the ordinal intake and groundwork share, reading each folder's size and growth, pruning stale scratch, and where validation and the private-remote backup are described
44
+ - [Records](records.md): The two roots a record folder resolves at, migrating a record a frontmatter change orphaned, claiming the ordinal intake and groundwork share, reading each folder's size and growth, pruning stale scratch, and where validation, the memory review queue, and the private-remote backup are described
40
45
  - [Restated instructions](restated.md): Counting the instructions the always-loaded file and every path-scoped rule share with the seed, the shipped skill bodies, and each other, how a match is decided, the three classes, which surface a later edit starts from, and why the sweep reports rather than gates
41
46
  - [Review classification](review-classification.md): Deciding whether a changed set needs the review pass, the two tests it runs, why an empty set refuses rather than skipping, and the written fallback a target on an older binary falls back to
42
47
  - [Routing report](routing.md): Reading per CLAUDE.md section how many bullets name a path, what counts as naming one, when a rule counts as covering it, the two refusals, and why the verb reports rather than gates
@@ -51,7 +56,10 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
51
56
  - [State-scoped risk](state-scoped-risk.md): Reading committed state rather than an arriving change, the shipped-tree corpus the secret scan reads, what it keys on and how a deliberate value is exempted, the advisory check and its network failure mode, and why one gates while the other reports
52
57
  - [Superseded values](superseded.md): Reading where the tree still asserts a value a changed convention no longer produces, why the sweep keys on the value rather than the file, the family stem behind a templated citation, the exemption marker, the blind spots it cannot reach, and why it reports rather than gates
53
58
  - [Targets](targets.md): The projects this toolkit installed into, the record the install writes against the sweep that backs it, what bounds each answer, and the cross-target pull request read
54
- - [Tasks](tasks.md): Selecting a shipped task by stem or pull request, recording a number and closing an outcome, deriving the branch a dispatch and a worker both take, the refusal reasons, the board and backlog checks validate runs, and why the board root defaults to the main worktree
59
+ - [Tasks archive](tasks-archive.md): Archiving a shipped task by stem or pull request, declining one decided against, the refusal reasons each carries, and how the plan and ready folder move with the task
60
+ - [Tasks plans](tasks-plans.md): The five verbs that read or write a task's plan, being where it sits and who cites it, whether it waits on the operator, the branch it derives, what a branch reached against what it declared, and writing its Plan line
61
+ - [Tasks validate](tasks-validate.md): Validating the board against the tree, the seven checks, the untested, claims, and unplaced arrays, the exit codes, and the refusal reasons
62
+ - [Tasks](tasks.md): Claiming the next phase label, recording a pull request number and closing an outcome, listing each task's readiness, and where archiving, the plan verbs, and board validation are described
55
63
  - [Teach authoring](teach-authoring.md): Resolving what the next lesson needs before it is written, rendering a lesson body's blocks to HTML, and rewriting the root listing, a contents page, and each lesson's chrome and quiz stepper from its marker regions
56
64
  - [Teach](teach.md): Listing learning workspaces with what their records schedule next, opening one with its required files, recording sources and glossary terms, where the lesson authoring verbs are described, the refusal reasons, and why every write here runs through a verb
57
65
  - [Test order](test-order.md): Reading where an implementation reached history ahead of its test, how a pair is decided, the three verdicts, the coverage the pairing cannot reach, and why the check reports rather than gates
@@ -228,187 +228,8 @@ edit and a job counting those stays red with no remedy.
228
228
  Six sections sit outside the per-domain scan, because each names something
229
229
  that walk cannot see: `seeds`, `superseded`, `unmigrated`, `newSkills`,
230
230
  `newRules`, and `reverse`. None of them produces a change, and no sync command
231
- applies any of them. The headings below group the first three together and give
232
- `newSkills` its description under `newRules`, which answers the same question
233
- about a different corpus.
234
-
235
- All six report only against a toolkit-managed target, which is one carrying a
236
- `.claude/` directory, a `CLAUDE.md`, or a domain still at the root layout. The
237
- report says so through `managed` in the JSON and routes an unmanaged directory to
238
- `canon init`. Seeds are why the gate exists, since they enumerate from the toolkit
239
- source rather than from what a target installed, so an unmanaged directory would
240
- otherwise report every seed as `missing`.
241
-
242
- A root-layout domain counts as a marker on its own, because the detection fires
243
- only on root files the toolkit ships and a project in the old layout is one the
244
- toolkit installed. When `managed` is false every section comes back empty rather
245
- than the render alone going quiet, so a consumer reading `--json` never acts on a
246
- finding the rendered half withheld.
247
-
248
- #### Seeds, superseded artifacts, and unmigrated domains
249
-
250
- `seeds` classifies every seed the toolkit ships against the target's copy, as
251
- `matching`, `stale`, `drifted`, or `missing`. `missing` has no per-domain
252
- equivalent, since the domain walk lists what a target installed and cannot see a
253
- file that never arrived. There is no `customized` verdict here, because that one
254
- needs a stamp and seeds carry none, so a file history cannot attribute stays
255
- `drifted`. Reconcile the section with `seed-sync`, which merges one
256
- section at a time rather than replacing a file the project edits.
257
-
258
- A markdown seed installs rewritten rather than copied, since the `stub: true`
259
- marker the toolkit's own seed gate reads is stripped on the way in. The
260
- comparison above runs against what the install would write, so a marked seed a
261
- target never touched still reports `matching`. Every other seed copies byte for
262
- byte.
263
-
264
- `superseded` names a file a newer seed folder replaced, such as `.claude/TASKS.md`
265
- against the `.canon/tasks/` that now ships. The entry carries `replacedBy` and
266
- nothing else, and the file is never deleted, since the content belongs to the
267
- project and only its author can decide where it moves. The list derives from the
268
- seed tree rather than from a fixed set of filenames, so a folder added later is
269
- covered without a code change. Only an exact stem matches, which leaves a
270
- suffixed variant such as `TASKS-ARCHIVE.md` unreported.
271
-
272
- No skill splits the file. The standard governing `replacedBy` is the one the
273
- `appliesTo` the standards catalog declares, and `canon standards <name>` reads
274
- its destination shape, so the split is the project's own to make.
275
-
276
- `unmigrated` names a domain sitting at the root layout an older toolkit installed
277
- to, with nothing at the path the current one reads. It carries `rootPath`,
278
- `installPath`, and a file count. Without it a project holding `standards/` at its
279
- root reports zero entries for that domain and reads as clean, which is the most
280
- misleading state the report can produce.
281
-
282
- The field currently names no domain. Standards and snippets are the two the
283
- toolkit ever installed at a project root, and both closed their install channel,
284
- so a project still holding a root `standards/` or `snippets/` folder is carrying
285
- its own authoring surface rather than an unfinished install. Nothing proposes
286
- moving either, and no command relocates the content. Move it yourself.
287
-
288
- #### Rules the target never received
289
-
290
- `newRules` names a rule the target could receive and its tree does not hold. A
291
- sync refreshes the files a target already holds and adds none, so without this
292
- section a project's rule set freezes at its install date while every file it
293
- does hold reports as current. That is the report's most confident wrong
294
- answer, since a clean result reads as a target holding everything the toolkit
295
- publishes.
296
-
297
- The list rides beside `newSkills`, which asks the same question about the plugin
298
- catalog. Both are names rather than paths, and neither queues a change, because
299
- the two remedies differ: a skill loads live from the plugin directory and needs
300
- nothing run, while a rule reaches a target only when someone runs
301
- `canon gov install`.
302
-
303
- Read a clean section as reporting rather than as delivering. Nothing here
304
- installs, nothing counts toward `--exit-code`, and a target can read the list and
305
- act on none of it. The value stops at an operator reading it, which is the same
306
- contract the skills list already sets.
307
-
308
- Since `canon gov install` records the stack it resolved, a target carrying that
309
- record answers this by comparing its current entitlement against its current
310
- tree, with no anchor and no git diff involved. That is what lets the section
311
- name a rule that shipped before the target's last sync: the anchor a sync
312
- advances plays no part in the read, where an anchor-bound diff can never see a
313
- rule on the far side of a window a later sync moved past it. The per-file
314
- `missing` state `canon gov sync` reports comes from the same comparison, so a
315
- rule the chain lists reaches both surfaces the same way.
316
-
317
- A target stamped before governance recorded a chain falls back to the older
318
- band-inference read below. The measurement there anchors on governance's own
319
- stamp rather than on the oldest anchor across domains, since rules are
320
- domain-scoped and a shared anchor would let another domain's sync move the
321
- revision rules are measured from. A target carrying no chain and no governance anchor
322
- reports nothing at all: it has no date to measure against, and diffing from the
323
- start of history would read the whole catalog as new.
324
-
325
- An anchor this toolkit cannot resolve reports nothing by the same route, and that
326
- one is not visible. A stamp naming a revision the running clone has never seen,
327
- which is what a registry install without history or a shallow clone produces,
328
- fails the read and yields an empty list rather than a stated absence. It looks
329
- identical to a target holding every rule the toolkit publishes. `newSkills`
330
- behaves the same way, and neither carries the `historyUnavailable` flag the
331
- per-domain scan uses to separate the two. Treat an empty section on a toolkit
332
- that is not a full clone as unmeasured rather than clean. This gap does not
333
- reach the chain-based read above, since it consults no anchor at all.
334
-
335
- In the fallback, entitlement is filtered, because a stack does not receive
336
- every rule. The base stack takes the `core` and `claude` folders whole and
337
- every other stack extends it with individually named rules, so an unfiltered
338
- list would tell a base consumer about rules it can never receive and train the
339
- reader to skip the section.
340
-
341
- The filter accepts a band on either of two grounds. A folder the base stack takes
342
- whole is entitled to every target, read from the stack file so a folder added to
343
- base later needs no code change. Every other band is read off the folders the
344
- target already carries, which is the fallback's only evidence of what a target
345
- was entitled to before it recorded a chain. One band can be reached by more than
346
- one stack, so the test over-reports inside a folder the target holds, which
347
- costs a line where under-reporting would cost the section its point.
348
-
349
- A rule the target already holds is dropped by name. That is what keeps a rule the
350
- toolkit moved between band folders out of the list, since a rename reaches this
351
- read as an addition and only the name tells the two apart.
352
-
353
- This closes the dangling-citation case as a side effect rather than checking for
354
- one. The live instance is a rule citing a sibling authored after the target's
355
- install, and the section names the sibling as new rather than naming the citation
356
- as broken. That is enough for an operator to act on, and it is not a citation
357
- check.
358
-
359
- #### The reverse walk
360
-
361
- `reverse` is the one section built by walking the target rather than the
362
- catalog. Every other surface enumerates toolkit-owned keys and asks whether the
363
- target matches, so a folder the toolkit deleted appears in none of them. It
364
- carries `unclaimed` and `historyUnavailable`.
365
-
366
- `unclaimed` names a folder the target holds at a top-level path the toolkit once
367
- shipped and has since deleted. The candidate roots come from the toolkit's own
368
- history rather than from a list, so a root dropped later is covered without a
369
- code change. Scoping to those roots is what keeps the walk useful: walking the
370
- whole tree reports every project folder as unclaimed, which is true and says
371
- nothing.
372
-
373
- The managed gate above applies here too, and it is the one place it surprises.
374
- A directory holding a dropped folder and nothing else reports an empty `reverse`
375
- rather than the folder, because it carries none of the three markers. Read an
376
- empty section on an unmanaged target as a walk that never ran rather than as a
377
- clean result.
378
-
379
- Each entry carries `rel`, a file count, and an `attribution` of `dropped`,
380
- `project`, or `unattributed`. A dropped folder and one the project wrote are the
381
- same bytes at the same path, so the verdict is traced from history rather than
382
- guessed from the filesystem.
383
-
384
- Content matching a version the toolkit published reads as `dropped` and carries
385
- the `since` commit that published it. Names the toolkit shipped holding content
386
- it never published read as `unattributed`, which is a state in its own right
387
- rather than a soft yes. No overlap at all reads as `project`, and the render
388
- drops those while the JSON keeps them.
389
-
390
- Only files whose path the toolkit once held are hashed, so a project folder
391
- colliding on a retired name costs the walk no reads. The cost is that a file the
392
- toolkit shipped and the target renamed goes unmatched, the same limit the
393
- `unmigrated` count carries.
394
-
395
- #### What counts toward the gate
396
-
397
- `unmigrated` counts toward `--exit-code`, since running the relocation closes it.
398
- `superseded` and every seed state are excluded, for the reason `orphaned` already
399
- is: only the user can move content they wrote, so failing a job on it leaves the
400
- job red with no mechanical remedy.
401
-
402
- `newRules` is excluded on a different ground, since a command does close it. What
403
- excludes it is that installing a rule changes what a project is governed by, so
404
- gating on the count would pressure a target into adopting rules nobody chose.
405
- `newSkills` is excluded because it needs no command at all.
406
-
407
- The whole `reverse` section is excluded on the same grounds, and more strongly.
408
- Every entry in it is a judgment about a file the project may own, and one of its
409
- three verdicts is a labelled unknown by design. The unmigrated detection shipped
410
- that exact false positive once, failing a push with no action that cleared it,
411
- so this section reports and gates nothing.
231
+ applies any of them. What each reports, the managed-target gate they share, and
232
+ which count toward `--exit-code` are in `drift-surfaces.md`.
412
233
 
413
234
  ## Bootstrap
414
235
 
@@ -0,0 +1,73 @@
1
+ ---
2
+ title: Markdown audit weight
3
+ description: The structural half 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 why none of them gates
4
+ ---
5
+
6
+ # Markdown audit weight
7
+
8
+ ## Bullets, paragraphs, and depth
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 400 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
+
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
+
14
+ A code span is walked around rather than through, so a path quoting link or angle-bracket syntax keeps the width the page gives it. Masking inside one takes back the decision to count it, and the placeholders this toolkit writes are where that shows.
15
+
16
+ The paragraph check measures both halves of one rule. `markdown.md` caps a paragraph at four sentences, and a sentence cap on its own is satisfied by writing fewer and longer ones: 15 paragraphs in this corpus sit inside four sentences and past the weight checkpoint, and the heaviest of those runs 886 characters. The standard therefore states a weight beside the sentence cap, and the verb reads it as its own checkpoint.
17
+
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
+
20
+ The paragraph weight sits at 700 and the bullet weight at 400. Both shipped at 400, because 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.
21
+
22
+ ### The sample behind the paragraph number
23
+
24
+ The checkpoint shipped at 400 as a borrowed number and was decided against a read of the prose it reports. Thirty-six findings were sampled, six from each of six weight bands, drawn at even spacing through each band ordered by path and line, and each was classed as prose a reader wants split or prose the checkpoint should not have reported.
25
+
26
+ Every band below was measured before the scan stopped counting link syntax as prose, so a paragraph sitting in one of these bands is heavier than a paragraph reported at the same number today. The re-sample in the section below re-reads the same range against the corrected measure and reaches the opposite verdict on it, which is the measure moving rather than the reader.
27
+
28
+ | Band | Wants the split | Reads as written |
29
+ | --------- | --------------- | ---------------- |
30
+ | 400 - 425 | 1 | 5 |
31
+ | 425 - 450 | 2 | 4 |
32
+ | 450 - 500 | 2 | 4 |
33
+ | 500 - 600 | 2 | 4 |
34
+ | 600 - 750 | 6 | 0 |
35
+ | Past 750 | 6 | 0 |
36
+
37
+ Precision is what moved the number rather than the finding count. Below 600 the checkpoint was right about seven of twenty-four sampled paragraphs, and past 600 it was right about all twelve. A checkpoint is a prompt to look, and a prompt wrong three times in four teaches a reader to stop looking. The distribution offers no seam to place the number against, with a median of 487 and a seventy-fifth percentile of 563, so the read is the whole of the evidence.
38
+
39
+ Nothing inside the 500 to 600 band separated the two classes by length, which is the reason the number did not land there. The two paragraphs wanting a split ran 543 and 590 characters against four reading well at 515, 532, 555, and 569.
40
+
41
+ The sample is thirty-six paragraphs against a reported population in the hundreds, and one reader classed all of them. Treat a band's rate as the order of magnitude it is rather than as a measured precision, and re-sample before moving the number again.
42
+
43
+ ### The re-sample that moved the number to 700
44
+
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
+
47
+ 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
+
49
+ ## Cadence
50
+
51
+ Uniform cadence is the failure a ban list cannot express. A ban set states negatives, and fragments, verbless clauses, and sentences that all run one length are each the absence of something, so no word added to a ban list reaches any of them. The shape layer already measured a bullet, a paragraph, and a run, and stopped one level above where that failure lives.
52
+
53
+ Cadence measures a paragraph on two numbers. The spread is the words between its longest and shortest sentence, and the opener count is the times one word opens a sentence in it. A spread of five words or under reads as one cadence, and a word opening more than two sentences is a pattern rather than a coincidence. Both come from `## Rhythm` in the `write-human` skill, which states them about prose a person reads, and this measures against that statement rather than setting a threshold of its own.
54
+
55
+ Words are counted off the text a reader is shown. A link contributes its anchor text, an autolink contributes nothing, and the sentence boundaries do not move under that masking, since the boundary pattern requires whitespace after the terminal punctuation and no destination carries any. An opening word is lowercased and stripped of punctuation, so a sentence opening on a backticked command name reports the command.
56
+
57
+ A paragraph carrying fewer than three sentences is skipped rather than scored. A two-sentence configuration note has no spread worth reading, and the opener rule is written about a third sentence turning a coincidence into a pattern, so neither number says anything before the floor. That is the cheap form of a wider exemption: a shape-aware one, exempting a short reference block by what it is rather than by how many sentences it holds, waits on a second case.
58
+
59
+ The unit is the paragraph and each file names its worst on each measure, which follows the depth check rather than setting a precedent. A file's flattest paragraph and its most repetitive one are named only when each crosses its checkpoint, so a file reading healthy names nothing rather than offering its least healthy paragraph as a finding.
60
+
61
+ Neither number gates and neither names a file wrong. This is a weaker claim than the one the weight checkpoints make, because a healthy range differs by surface: a catalog entry is several short sentences carrying one fact each, and a page arguing a decision is not, so one range applied across the corpus would report the surfaces that are correct. The run therefore states where the numbers came from beside them, and the counts are what a reader compares against.
62
+
63
+ A reading of that spread across the whole corpus is what backs the range above, taken once against this repository's own tracked markdown rather than against a target project's.
64
+
65
+ That reading travels with the command rather than staying here. `BASELINE` in `src/markdown/structure.ts` carries the overall share, the per-file range, and the ten-paragraph floor beneath which a file's own rate says nothing, and the run prints all four in the legend beside the rate it measured. A count with no range beside it reads as a finding, and naming that a healthy range differs by surface states that a range exists rather than what it looks like. This page is toolkit-internal, so a reader running the command in a project that installed no standards would otherwise have two counts and nothing to place them against.
66
+
67
+ Two of the rules `write-human` states are deliberately not implemented. A sentence's grammatical shape and whether it carries a finite verb each need a parse rather than a match, and an imperative would read as a defect under a pattern that approximated either. The verbless share is the measure closest to the reported symptom, which is exactly why shipping it wrong would discredit the two that hold.
68
+
69
+ The condition on that was something identifying a finite verb rather than guessing at one, and two parsers have now been run against it over 11,389 paragraph sentences. They disagree by a factor of four. `compromise` reports 2 percent and reads a fronted past participle as a finite verb, so `Measured at <sha> on <date>.` counts as carrying one. `wink-pos-tagger` reports 9 percent, fixes that class, and is still wrong on roughly three in four, because an imperative's verb tags as a proper noun and a noun-ambiguous predicate tags as a noun, which makes `Each maps to a skill.` read verbless. Separating those needs to know which token is the predicate, and that is syntax rather than a tag. The measure stays unimplemented, now against a mechanism rather than against the idea of one.
70
+
71
+ ## Length
72
+
73
+ The Length step sums rendered lines over the whole source, frontmatter and fenced blocks included, and lists each document past the 300-line ceiling, longest first. A changelog and a document carrying a whole-line `<!-- canon-length-exempt: <reason> -->` outside a fence are counted in one line rather than listed. Each `--json` entry carries `renderedLines` and `exempt`, the stated reason or `null`, and `checkpoints` carries `ceiling`.