@erclx/canon 5.39.0 → 5.41.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 (162) hide show
  1. package/README.md +39 -22
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
  4. package/claude/skills/auto-ship/SKILL.md +6 -0
  5. package/claude/skills/auto-ship/references/fan-in.md +59 -0
  6. package/claude/skills/canvas-board/SKILL.md +1 -1
  7. package/claude/skills/create-skill/references/skill.md +1 -1
  8. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
  9. package/claude/skills/decision-escalate/SKILL.md +1 -2
  10. package/claude/skills/draft-slides/SKILL.md +2 -3
  11. package/claude/skills/draft-slides/references/deck.md +183 -0
  12. package/claude/skills/git-branch/REQUIREMENT.md +17 -6
  13. package/claude/skills/git-branch/SKILL.md +63 -7
  14. package/claude/skills/git-pr/references/evidence.md +2 -2
  15. package/claude/skills/git-pr/references/final-command.md +1 -1
  16. package/claude/skills/git-pr/references/labels.md +1 -1
  17. package/claude/skills/git-pr/references/publish.md +1 -1
  18. package/claude/skills/plan-feature/references/batches.md +48 -1
  19. package/claude/skills/plan-feature/references/plan.md +2 -0
  20. package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
  21. package/claude/skills/plan-intake-answer/SKILL.md +1 -2
  22. package/claude/skills/review-address/references/rebase-conflicts.md +7 -0
  23. package/claude/skills/review-ui/SKILL.md +3 -0
  24. package/claude/skills/review-ui/references/close-out.md +47 -0
  25. package/claude/skills/review-ui/references/post.md +3 -1
  26. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -0
  27. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  28. package/claude/skills/role-worker/REQUIREMENT.md +1 -0
  29. package/claude/skills/role-worker/SKILL.md +9 -0
  30. package/claude/skills/task-board/references/board.md +1 -1
  31. package/claude/skills/task-board/references/tasks.md +1 -1
  32. package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
  33. package/claude/skills/teach-workspace/SKILL.md +1 -2
  34. package/claude/skills/ux-measure/SKILL.md +1 -1
  35. package/docs/agents/audits.md +61 -60
  36. package/docs/agents/context-audit.md +40 -58
  37. package/docs/agents/index.md +5 -51
  38. package/docs/agents/install-and-sync.md +1 -0
  39. package/docs/agents/markdown-audit.md +53 -99
  40. package/docs/agents/records.md +37 -79
  41. package/docs/agents/sessions-transfer.md +26 -70
  42. package/docs/target/index.md +0 -1
  43. package/docs/target/migrations.md +57 -66
  44. package/docs/target/projects.md +36 -78
  45. package/docs/target/scenarios.md +9 -19
  46. package/docs/target/sync.md +27 -68
  47. package/docs/workflow/ai-workflow.md +22 -68
  48. package/docs/workflow/operating-model.md +32 -186
  49. package/docs/workflow/parallel-features.md +30 -66
  50. package/docs/workflow/skill-map-on-request.md +22 -24
  51. package/docs/workflow/skill-map.md +72 -76
  52. package/docs/workflow/visual-design-workflow.md +25 -118
  53. package/docs/workflow/zshrc-aliases.md +18 -37
  54. package/package.json +1 -1
  55. package/src/canvas/client/inspector/fields.css +4 -0
  56. package/src/canvas/index.html +6 -0
  57. package/src/claude/cases/git.ts +4 -0
  58. package/src/claude/skills-reach.ts +2 -2
  59. package/src/cli/ui.ts +3 -1
  60. package/src/commands/canvas.ts +23 -0
  61. package/src/commands/capture.ts +14 -0
  62. package/src/commands/claude.ts +13 -0
  63. package/src/commands/demo.ts +22 -0
  64. package/src/commands/deps.ts +6 -0
  65. package/src/commands/driver.ts +28 -2
  66. package/src/commands/gov.ts +52 -18
  67. package/src/commands/intake.ts +17 -0
  68. package/src/commands/labels.ts +38 -5
  69. package/src/commands/pr.ts +192 -52
  70. package/src/commands/records.ts +19 -3
  71. package/src/commands/secrets.ts +6 -0
  72. package/src/commands/sessions.ts +8 -0
  73. package/src/commands/tasks.ts +52 -0
  74. package/src/commands/teach.ts +13 -0
  75. package/src/commands/upstream.ts +25 -1
  76. package/src/commands/worktrees.ts +5 -0
  77. package/src/context/audit.ts +22 -4
  78. package/src/context/folders.ts +1 -1
  79. package/src/context/shape-gated.ts +5 -2
  80. package/src/counts/scan.ts +1 -1
  81. package/src/design/components.ts +1 -1
  82. package/src/design/document.ts +1 -1
  83. package/src/design/tokens.ts +1 -1
  84. package/src/gate/boundaries.ts +4 -1
  85. package/src/gate/measures.ts +1 -1
  86. package/src/gate/stages.ts +7 -7
  87. package/src/git/env.ts +6 -0
  88. package/src/markdown/scope.ts +11 -19
  89. package/src/pr/frames.ts +53 -9
  90. package/src/pr/lift.ts +153 -19
  91. package/src/pr/painted.ts +87 -0
  92. package/src/records/validate.ts +1 -0
  93. package/src/roots/record.ts +18 -1
  94. package/src/sandbox/census.ts +1 -1
  95. package/src/sandbox/headless/run.ts +3 -2
  96. package/src/sandbox/headless/watch.ts +3 -4
  97. package/src/slides/convert/master.ts +3 -0
  98. package/src/sync/format.ts +46 -0
  99. package/src/sync/stamp.ts +6 -0
  100. package/src/tasks/answers.ts +79 -2
  101. package/src/tasks/fan-in.ts +176 -0
  102. package/src/tasks/stack.ts +1 -1
  103. package/tooling/astro/reference.md +80 -39
  104. package/tooling/base/reference.md +63 -32
  105. package/tooling/base/seeds/canon/context/ci.md +7 -15
  106. package/tooling/base/seeds/canon/context/development.md +14 -14
  107. package/tooling/claude/reference.md +40 -22
  108. package/tooling/cloudflare/reference.md +31 -12
  109. package/tooling/go/reference.md +47 -23
  110. package/tooling/nextjs/reference.md +52 -19
  111. package/tooling/php/reference.md +35 -16
  112. package/tooling/python/reference.md +47 -30
  113. package/tooling/shell/reference.md +6 -5
  114. package/tooling/vite-react/reference.md +49 -21
  115. package/tooling/web/reference.md +131 -71
  116. package/docs/agents/canvas.md +0 -86
  117. package/docs/agents/capture.md +0 -65
  118. package/docs/agents/census.md +0 -23
  119. package/docs/agents/comments.md +0 -34
  120. package/docs/agents/context-audit-checks.md +0 -108
  121. package/docs/agents/context-classify.md +0 -101
  122. package/docs/agents/counts.md +0 -74
  123. package/docs/agents/demo.md +0 -140
  124. package/docs/agents/driver.md +0 -103
  125. package/docs/agents/gate.md +0 -90
  126. package/docs/agents/hooks.md +0 -78
  127. package/docs/agents/intake.md +0 -81
  128. package/docs/agents/key-changes.md +0 -116
  129. package/docs/agents/label-coverage.md +0 -86
  130. package/docs/agents/markdown-audit-density.md +0 -39
  131. package/docs/agents/markdown-audit-weight.md +0 -83
  132. package/docs/agents/pr-evidence-layout.md +0 -70
  133. package/docs/agents/pr-evidence.md +0 -273
  134. package/docs/agents/pr-frames.md +0 -131
  135. package/docs/agents/pr-lift.md +0 -132
  136. package/docs/agents/pr-local.md +0 -111
  137. package/docs/agents/pr-preview.md +0 -155
  138. package/docs/agents/pr-reads.md +0 -111
  139. package/docs/agents/pr-tick.md +0 -72
  140. package/docs/agents/records-push.md +0 -61
  141. package/docs/agents/records-stale.md +0 -40
  142. package/docs/agents/records-validate.md +0 -57
  143. package/docs/agents/restated.md +0 -83
  144. package/docs/agents/routing.md +0 -61
  145. package/docs/agents/rule-citations.md +0 -90
  146. package/docs/agents/sandbox.md +0 -126
  147. package/docs/agents/sessions.md +0 -128
  148. package/docs/agents/skills-audit.md +0 -120
  149. package/docs/agents/skills-reach.md +0 -64
  150. package/docs/agents/slides.md +0 -176
  151. package/docs/agents/state-scoped-risk.md +0 -105
  152. package/docs/agents/superseded.md +0 -85
  153. package/docs/agents/tasks-archive.md +0 -83
  154. package/docs/agents/tasks-plans.md +0 -152
  155. package/docs/agents/tasks-validate.md +0 -133
  156. package/docs/agents/tasks.md +0 -123
  157. package/docs/agents/teach-authoring.md +0 -118
  158. package/docs/agents/teach.md +0 -157
  159. package/docs/agents/test-order.md +0 -58
  160. package/docs/agents/upstream.md +0 -94
  161. package/docs/agents/worktrees.md +0 -118
  162. package/docs/target/standards-moved.md +0 -34
@@ -1,87 +1,69 @@
1
1
  ---
2
2
  title: Context audit
3
- description: Running the audit, its flags and folder scope, the exit codes, the citation gate, and the widened gate the seed stage runs
3
+ description: Running the context audit, its folder scope, the exit codes, every check with the folders it reaches and whether it gates, the seed gate, and the citation exclusions
4
+ kind: reference
4
5
  ---
5
6
 
6
7
  # Context audit
7
8
 
8
- `canon context audit [path]` reports the structural state of the folders following the index-plus-entry contract, meaning a generated `index.md` beside entries carrying frontmatter, and it measures `canon/REQUIREMENTS.md` beside them. It reads and reports. Fixing what it finds is separate work. What each finding means is in `context-audit-checks.md`, including the requirements record's word cap.
9
-
10
- Findings stated over every markdown file rather than over a context entry are measured by `canon markdown audit`, described in `markdown-audit.md`. That command resolves no folder, so it reaches trees this one refuses.
9
+ `canon context audit [path]` reports the structure of the folders following the index-plus-entry contract, meaning a generated `index.md` beside entries carrying frontmatter, and measures `canon/REQUIREMENTS.md` beside them. It reads and reports. Findings over every markdown file belong to `canon markdown audit`, in `markdown-audit.md`.
11
10
 
12
11
  ```bash
13
12
  canon context audit
14
13
  canon context audit --json
15
14
  canon context audit --citations-only
16
15
  canon context audit --folder context,diagrams
17
- canon context audit --folder docs
18
16
  canon context audit tooling/base/seeds --gate
19
17
  ```
20
18
 
21
- | Option | Behavior |
22
- | ------------------ | ------------------------------------------------------------------------- |
23
- | `--json` | Add a machine-readable record on stdout, keeping the frame |
24
- | `--folder <list>` | Comma-separated folder names (default: the three below) |
25
- | `--citations-only` | Run the gating citation check alone, printing nothing when it passes |
26
- | `--gate` | Fail on a missing section, an off-set H2, gated shape break, or drift too |
27
-
28
- `--citations-only` and `--gate` refuse together. The first runs the citation check alone, so the two findings the second adds are never measured, and a run honoring both would exit clean on a file short a required section.
29
-
30
- ## Folder scope
31
-
32
- Scope defaults to `context`, `decisions`, and `diagrams`, and a folder the project does not carry is skipped rather than reported. A domain that outgrew one file and split into `<domain>/` is audited as its own folder, so a split entry measures at the same grain as a flat one.
33
-
34
- A name passed to `--folder` resolves under the record roots first, `.canon/` ahead of `.claude/`, and at the project root last, which is what puts `docs/` and any later corpus in reach of the same engine. The root base is reached only by a name the caller passes, so the default list still resolves under a record root alone and a project holding a root folder of one of those names is not audited against a standard it never adopted. `diagrams` is the one default name that is a session record and moves with them, which is why both record roots are in the list rather than one, and `docs/agents/records.md` states the read order every verb shares. The scope line prints the resolved path, so a project carrying both spellings reads which one was taken. The JSON record carries the base per folder as `folders[].base`.
35
-
36
- A run where no requested name resolves refuses, whichever list it read. Naming the absent ones narrows to `--folder`, since a project carrying one of the three default folders is the ordinary case and a name it never asked for is not a typo. The JSON record carries those names as `unresolvedFolders`.
37
-
38
- ## Exit codes
39
-
40
- Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a gating finding. An unresolved citation gates under every mode. A requirements record stating a word cap gates past it, on any run that measures it, which is every mode except `--citations-only`. A record stating none is reported and never gated.
41
-
42
- `bun run check` in the toolkit repository reads every one of these rules through its own Canonical records stage as well as through this verb, whose gating stage there runs `--gate`.
19
+ `--gate` widens the failing set to the findings marked below. `--citations-only` runs the citation check alone, and the two refuse together.
43
20
 
44
- Entry length, reference form, table, provenance, the record's claim classification, and every word figure no record caps print and return `0` under every mode, because each is a judgment or a weight read alongside one, and failing a push on either would make the check something to route around.
21
+ `--folder` takes comma-separated names, defaulting to `context`, `decisions`, and `diagrams`. A folder the project lacks is skipped, and `folders[].base` names where each resolved.
45
22
 
46
- Required-section, off-set H2, and index findings sit between the two. Both are answerable from the file rather than weighed, so `--gate` promotes them to failing codes while a bare run leaves them advisory. A section shape break, such as a `## Decisions` H2 or a sixth gotcha, gates under `--gate` only in a shape-gated folder. The toolkit runs the bare form against itself and the widened form against the seed tree, described below.
23
+ ## Output
47
24
 
48
- ## The seed gate
25
+ | Check | Reports | Reaches | Finding |
26
+ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | ----------------------------------------------------------- |
27
+ | Citations | A path into an audited folder that resolves nowhere | Every audited folder | Gates always |
28
+ | Requirements record | Words past the cap the record states as `This record holds at most 600 words.` | `canon/REQUIREMENTS.md` | Gates past a stated cap, never under `--citations-only` |
29
+ | Required sections | An entry missing `Overview` or `Layout`. A split folder answers by any sibling | `canon/context/` | Gates under `--gate` |
30
+ | Closed set | An H2 outside `Overview`, `Layout`, and `Gotchas`, under `extraSections` | `canon/context/` | Gates under `--gate` |
31
+ | Shape | `retired-section`, `gotcha-count`, `gotcha-heading`, `gotcha-paragraph`, `layout-heading`, `layout-bullets`, `layout-paragraphs`, `layout-depth` | `canon/context/` | Gates under `--gate` in a `SHAPE_GATED_FOLDERS` folder only |
32
+ | Index drift | An entry its index does not link, or a link resolving to nothing | Every audited folder | Gates under `--gate` |
33
+ | Reference form | A backticked bare filename matching a sibling entry, under `bareReferences` | Split folders | Reports |
34
+ | Length | An entry past about 150 rendered lines at 80 columns, with its `causes` | Every audited folder | Reports |
35
+ | Tables | Six or more body rows whose first column mostly carries a path, command, or link | Every audited folder | Reports |
36
+ | Provenance | A date, change number, or release label. A date after measured, verified, driven, passed, or fired is excluded | `canon/context/` | Reports |
49
37
 
50
- `bun run check` runs `--gate` against every `tooling/<stack>/seeds/` directory carrying a `.claude/` folder, discovered per run so a new stack is covered without a script edit. The seed tree installs into every scaffolded project, so a seed breaking the standard it seeds teaches the wrong shape to each one, and no rule path reaches the tree to report it.
38
+ A shape break is any of these:
51
39
 
52
- The widened gate is correct here and wrong at the project root. A seed is authored once and read by every target, while a context entry in a live project is edited under time pressure by the people who own it. A missing section in the first is a defect shipping outward, and in the second it is a threshold worth reporting and not worth blocking a push over.
53
-
54
- Coverage follows the index-plus-entry contract, so it reaches seeded entries and the indexes beside them. `DESIGN.md` sitting directly under `.claude/` belongs to no audited folder and stays outside it, while `REQUIREMENTS.md` is measured on its own path. The seed record states its own 600-word cap, which gates the same way a project's record does.
55
-
56
- The stage prints the entries it measured per root and warns on a root that measured none. A root can resolve an audited folder and hold no entry in it, which `tooling/claude/seeds` does today, so a single pass line over the set would report coverage of a tree nothing opened.
57
-
58
- It reads the exit code rather than pass against fail, since the two failing codes mean opposite things. A root carrying a `.claude/` that resolves no audited folder refuses at 1 and warns, while a seed short a required section gates at 2 and fails the push. A new stack seeding `.claude/` alone therefore reports what it is rather than a violation it does not have.
59
-
60
- ### Exempting a skeleton
61
-
62
- A seed that deliberately omits a required section sets `stub: true` in its frontmatter, and the section check then skips it. Every other measure still reads the file, since a stub is exempt from owing sections rather than from being well formed.
63
-
64
- ```markdown
65
- ---
66
- title: Architecture
67
- stub: true
68
- ---
69
- ```
40
+ - A `## Decisions` H2, or a `Gotchas` section past five bullets or holding a heading or paragraph
41
+ - A `Layout` H3 titled neither a code-spanned path nor `Other folders`, or a heading below H3 under `Layout`
42
+ - A folder H3 past five bullets or its one-paragraph lead
70
43
 
71
- Only `true` counts. A field holding anything else reads as a seed that meant to turn the exemption off, so a typo cannot silence the gate. Both install paths strip the field on the way into a target, which keeps toolkit bookkeeping out of a project whose own tooling would never read it. A block holding nothing else is dropped whole.
44
+ The development entry may carry a `Scripts` H3 exempt from both folder caps. `SHAPE_GATED_FOLDERS` lives in `src/context/shape-gated.ts`.
72
45
 
73
- The exemption exists because the section check has a false-positive class. A standard may sanction omitting a section, and no measure separates that from a file that forgot it, which is why the finding stays advisory everywhere the widened gate is not running. No seed sets the field today.
46
+ | Code | Meaning |
47
+ | ---- | ------------------------------------------ |
48
+ | `0` | A clean run, or findings that report |
49
+ | `1` | Refused, with the reason on stderr |
50
+ | `2` | A gating finding for the mode the run took |
74
51
 
75
- ## The citation gate
52
+ - `bun run check` runs `--gate` against every `tooling/<stack>/seeds/` directory carrying a `.claude/` folder
53
+ - A root resolving no audited folder refuses with `1` and warns, a root measuring no entry warns, and a seed short a section fails with `2`
54
+ - A seed omitting a required section on purpose sets `stub: true` in its frontmatter. Only the value `true` counts, and both install paths strip the field.
76
55
 
77
- The citation check resolves every path into an audited folder that appears anywhere in the repository, and it is the half wired into `bun run check`. A stale reference has a silent failure mode: the session opens nothing and carries on.
56
+ The citation check skips fenced blocks, fixture and harness trees including `*.test.ts`, and a path into a folder the project lacks.
78
57
 
79
- Three exclusions keep it from firing on prose about paths. Fenced blocks are skipped in markdown, which covers a standard displaying a path as an example. Fixture and harness trees are skipped by location, covering sandbox scenarios that describe their own scratch tree, the eval harness naming its target project, and `*.test.ts`. A path into a folder the project does not carry is skipped, so a skill directing a reader to an optional folder's `index.md` stays valid in a project that carries it and silent in one that does not.
58
+ Append `<!-- audit-ignore-citations: <path> -->` to a line that displays a path without pointing at it. The marker excuses only the paths it names, and a bare `<!-- audit-ignore-citations -->` excuses every citation on the line. In seeds, plugin skill bodies, and stack references, which install into a target, reword the line instead.
80
59
 
81
- Two cases remain, and no syntax separates either from a real reference. One is a sentence naming a hypothetical entry to show the shape of a name. The other is a line instructing a target project about its own tree, which resolves against the target rather than against this repository and passes here only while the two layouts agree. Splitting a seeded domain into a folder ends that agreement and turns every such line red at once.
60
+ ## Refusals
82
61
 
83
- Append `<!-- audit-ignore-citations: <path> -->` to the source line in either case, naming the exact path the sentence displays. The marker excuses only the paths it names, so a line carrying a real reference beside the displayed one keeps that reference checked. A bare `<!-- audit-ignore-citations -->` with no path excuses every citation the line carries instead, and the reference-form check reads either form the same way, since both ask whether a line points at a file and the marker is how a line says it displays a name instead.
62
+ - No requested `--folder` name resolves, with the names under `unresolvedFolders`
63
+ - `--citations-only` together with `--gate`
64
+ - `--citations-only` over folders that all resolved at the project root, which is out of scope
84
65
 
85
- The marker itself stays out of anything that installs. A seed, a plugin skill body, and a stack reference all reach a target, so a marker there lands as toolkit bookkeeping in someone else's tree. Reword those lines to drop the path instead, and where a stop message has to spell it, move that message into a fenced block, which this check already skips.
66
+ ## See also
86
67
 
87
- The pattern spells both record-root prefixes, so a citation into a folder that has moved still resolves and a folder resolved at the project root is measured by every other check while contributing nothing here. A pattern fixed at one root matches nothing after a move and reports nothing, which is a stale reference passing the check written to find it. Widening it to a bare `docs/x.md` would match prose that references nothing, which is a separate decision from where entries come from. A run whose folders all resolved at the root says the check is out of scope rather than reporting that zero paths resolved, and the same run under `--citations-only` refuses, because a gate exiting clean on a scope it could not build is the failure the gate exists to catch.
68
+ - `markdown-audit.md`: depth and bullet weight, which this audit no longer measures
69
+ - `audits.md`: the aggregate that runs this audit
@@ -8,60 +8,14 @@ category: Agent surface
8
8
 
9
9
  CLI catalog and invocation rules for agents, split by command domain. Start with overview.
10
10
 
11
- - [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
- - [Canvas](canvas.md): The canvas server, its content format, and the verbs that list, arrange, fit, select, edit, mark, and capture frames
13
- - [Capture](capture.md): Rendering HTML sources to PNG, what the command asserts about fonts, and why the selector has no default
14
- - [Census](census.md): Tracked-plus-untracked file count, a breakdown by extension, and a line total that skips whatever reads as binary
15
- - [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 requirements record's word cap, which folders each check reaches, and what moved to the attribute tier
17
- - [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
- - [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
- - [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
- - [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
- - [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
22
- - [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
23
- - [Hooks](hooks.md): Running the post-merge steps through one verb, the order and the child call behind each, the root and working directory each step reads, the skip switches, the line each record maps to, and the reply link check
11
+ - [Audits](audits.md): Running every health check as one set, the verdict and exit codes, the corpus states, the retained baseline and its delta, and a catalog of every audit verb with whether a finding gates
12
+ - [Context audit](context-audit.md): Running the context audit, its folder scope, the exit codes, every check with the folders it reaches and whether it gates, the seed gate, and the citation exclusions
24
13
  - [Indexes](indexes.md): How canon indexes regen and canon indexes list resolve paths, what they stage, and the record and exit codes each emits
25
14
  - [Install and sync](install-and-sync.md): 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
26
- - [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
27
- - [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
28
- - [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
29
- - [Markdown audit density](markdown-audit-density.md): 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
30
- - [Markdown audit weight](markdown-audit-weight.md): 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
31
- - [Markdown audit](markdown-audit.md): 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
15
+ - [Markdown audit](markdown-audit.md): Running the markdown audit over any path, each finding it reports with whether it gates, the six density limits, the advisory measures, the exit codes, and where the audit runs
32
16
  - [Migrate](migrate.md): What each canon migrate verb moves, the markers that keep an old spelling, and what each one refuses
33
17
  - [Output shape](output-shape.md): The framed output shape every command renders into, the JSON and color modes, the exit discipline, the runtime catalogs, and a headless invocation per domain
34
18
  - [Overview](overview.md): What this folder covers, the invocation rules every command inherits, and how canon docs resolves a topic name
35
- - [The pull request evidence layout](pr-evidence-layout.md): How canon pr evidence groups screenshots into one block per view with a row per width and a column per theme, when a base column appears, where a name outside the form lands, how the commit line links, and when the blocks open
36
- - [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, what the --check read reports as owed, and why the comparison anchors at the merge base rather than the previous push
37
- - [The pull request frame store](pr-frames.md): How canon pr frames pushes a UI review frame to a never-merged branch through the GitHub API and returns a branch-path link, which ref it writes, the pass segment that keeps an earlier image fixed, the refusal reasons it names, and how --drop and --prune rewrite the branch so a dropped frame leaves history
38
- - [The pull request draft lift](pr-lift.md): How canon pr lift removes the draft mark once the reviews, evidence, standing, and CI all read clear at the head, the gate order and what each reads, the wait on a pending run, the re-read before the write, the refusal reasons it names, and what --check reports
39
- - [The pull request local preview](pr-local.md): How canon pr local finds the server a worktree is running, why a server in another worktree is never reported, the refusal reasons it names, and how a close workflow drops the address line with --remove
40
- - [The pull request preview deploy](pr-preview.md): How canon pr preview finds a fenced Cloudflare Pages deploy, refuses a branch its push filter does not serve, 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
41
- - [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
42
- - [The pull request checklist tick](pr-tick.md): How canon pr tick marks the checklist boxes a UI pass drove, the stamp that names the commit each passed at, why a render at a newer head clears a tick, the refusal reasons it names, and why a taste box is never ticked
43
- - [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
44
- - [Records stale](records-stale.md): Counting the releases since the requirements record was last reviewed, where the review date is read from, and the exit codes and refusals
45
- - [Records validate](records-validate.md): Validating the session records, the per-kind checks, which root they default to, the exit codes, and the refusal reasons
46
- - [Records](records.md): The two roots a record folder resolves at, claiming the ordinal intake and groundwork share, reading each folder's size and growth, pruning stale scratch, and where validation, the canonical review count, and the private-remote backup are described
47
- - [Restated instructions](restated.md): Counting the instructions the always-loaded file and every path-scoped rule share with the seed, the 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
48
- - [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
49
- - [Rule citations](rule-citations.md): Resolving every path a governance rule cites and every frontmatter glob the internal corpus declares, the one form a citation is written in, the shapes that look like citations and are not, the two classes where an absent path is correct, why the glob half reads one corpus, and why this one gates
50
- - [Sandbox](sandbox.md): Scenario routing, the expectation scoring surface, and the coverage census over scenarios and skills
51
- - [Sessions transfer](sessions-transfer.md): Moving a session to another machine, the export bundle and its manifest, placing it with import, the refusal reasons, and what a resume on the target depends on
52
- - [Sessions](sessions.md): Resolving live peer sessions to the worktree and branch each holds, reading which row is the caller, the liveness confidence field, what each session surface can see, and where moving a session to another machine is described
53
- - [Skill audit](skills-audit.md): Measuring both skill corpora against the skill standards, the checks it reads, the requirement gate that is the only failing one, and the drift verb that names bodies rewritten since a ref
54
- - [Citation reach](skills-reach.md): Reporting the skill bodies that cite a path no target project receives, which corpus the verb reads, the ownership key that decides what counts, the one-word qualifier that marks a citation as decided, and why the verb reports instead of gating
55
- - [Slides](slides.md): Where a deck folder lives and what it holds, what it declares for the whole deck in deck.json, the faces it embeds, what each slide declares on its own body, transitions and entrances, the master built from the project's tokens, speaker notes, charts, and what each refusal reports
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
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
19
+ - [Records](records.md): Backing the record folders to a private remote, restoring them on a new machine, reading folder size, and pruning stale scratch
20
+ - [Sessions transfer](sessions-transfer.md): Moving a session to another machine with export and import, the manifest fields, the refusal reasons, and what a resume on the target depends on
58
21
  - [Targets](targets.md): The projects this toolkit installed into, where each answer comes from, and the cross-target pull request read
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 five report 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
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
64
- - [Teach](teach.md): Listing learning workspaces with what their records schedule next, opening one with its required files, recording sources and glossary terms, viewing one with its chrome refreshed, where the lesson authoring verbs are described, the refusal reasons, and why every write here runs through a verb
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
66
- - [Upstream](upstream.md): The fetch, catalog, due, and advance verbs that read Claude Code releases against the toolkit, the cursor record, and each refusal reason
67
- - [Worktrees](worktrees.md): Reporting which worktrees are reclaimable, removing the ones that are, the hidden verb that clears a stranded bare flag, the record a hook reads back, why the reading keys on the pull request rather than on git ancestry, the refusals it names, and the two removal shapes
@@ -16,6 +16,7 @@ Any flag skips the confirmation prompt, but the `base` default of `--stack` does
16
16
  ## Governance install
17
17
 
18
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
+ - `--yes` answers the install confirm with no TTY and no env var. Without it, a headless call refuses and the message names `--yes`. It does not pick a stack.
19
20
  - It refuses the toolkit root and a missing path
20
21
  - `--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
 
@@ -1,11 +1,12 @@
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, 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
3
+ description: Running the markdown audit over any path, each finding it reports with whether it gates, the six density limits, the advisory measures, the exit codes, and where the audit runs
4
+ kind: reference
4
5
  ---
5
6
 
6
7
  # Markdown audit
7
8
 
8
- `canon markdown audit [path...]` reports any markdown file against the `markdown-craft` skill's `markdown.md` reference. That reference governs a file rather than a folder, so this resolves no folder and requires no `index.md`, which is what puts `.claude/rules/` and `governance/` in reach. Folder-shaped findings stay in `canon context audit`, described in `context-audit.md`.
9
+ `canon markdown audit [path...]` reports any markdown file against the `markdown-craft` skill's `markdown.md`. It resolves no folder and needs no `index.md`, so `.claude/rules/` and `governance/` are in reach. Folder-shaped findings belong to `canon context audit`, in `context-audit.md`.
9
10
 
10
11
  ```bash
11
12
  canon markdown audit
@@ -15,116 +16,69 @@ canon markdown audit docs/agents/install-and-sync.md
15
16
  canon markdown audit 'docs/**/*.md'
16
17
  ```
17
18
 
18
- Run `canon markdown audit --help` for every flag.
19
+ An argument is a file, a directory, or a glob, taken against the corpus git lists. A bare run measures every tracked or untracked-and-not-ignored file. An explicit file is taken as given, so a gitignored draft can be measured, and an argument matching nothing is named on the scope line.
19
20
 
20
- ## Scope
21
+ ## Output
21
22
 
22
- An argument is a file, a directory, or a glob. A directory narrows to everything under it and a glob narrows by match, both against the corpus git lists, which is what keeps `node_modules/` and the gitignored session-scratch folders out without naming either. An explicit file path is taken as given, so a gitignored draft can be measured before it is committed. Quote a glob the shell would expand first.
23
+ The ban set and checkpoints ship in `src/markdown/bans.ts` and `src/markdown/structure.ts`. `markdown.md` states each rule and the `write-human` skill states the cadence numbers, so move a number in both places in one change.
23
24
 
24
- A bare run measures every markdown file git lists, tracked plus untracked-and-not-ignored, so a file added on this branch is in scope on the branch that adds it. An argument matching no markdown file is named on the scope line rather than passed over, since a run measuring the paths that did resolve otherwise reads as a pass over one it never opened.
25
+ | Finding | Reports | Gates |
26
+ | --------------- | ------------------------------------------------------------------------------------ | ------------------------- |
27
+ | Bans | The two characters `markdown.md` bans, outside frontmatter, fences, spans, and links | Always |
28
+ | Links | A relative link resolving to nothing on disk | Always |
29
+ | Density | The six limits below, under `density` | On a `GATED_PATHS` prefix |
30
+ | Reference shape | A `kind: reference` page breaking its shape, under `referenceShape` | On a `GATED_PATHS` prefix |
31
+ | Colons | A colon followed by more text in a prose line, under `colons` | Never |
32
+ | Heading case | A capital after the first word of a heading or `title`, under `headingCase` | Never |
25
33
 
26
- ## Where the rules come from
34
+ A page marked `kind: reference` holds its opening, then `Output`, `Refusals`, and `See also` as H2s, each at most once and in that order. It takes no H3 and no table headed `Flag` or `Option`. An unmarked page is never measured.
27
35
 
28
- The ban set and all ten checkpoints ship with the `canon` package as data, in `src/markdown/bans.ts` and `src/markdown/structure.ts`. Every project is measured against the same set whether or not it installed the plugin, and no file has to resolve for a run to mean something.
36
+ | Density limit | Finding |
37
+ | ------------------- | ---------------------------------------------------------- |
38
+ | Paragraph length | A prose paragraph past 350 characters |
39
+ | Paragraphs in a row | More than 2 paragraphs with nothing else between them |
40
+ | Bullet length | A top-level bullet past 200 characters |
41
+ | Bullets in a row | More than 5 plain top-level bullets |
42
+ | Heading depth | Any heading past H3 |
43
+ | H3 opening | An H3 whose first block is neither a paragraph nor a table |
29
44
 
30
- Seven of the ten are stated in `markdown.md` and the three cadence numbers are stated in the `write-human` skill. That split is the content boundary rather than an accident: `markdown.md` carries the enforced rules a scan can decide, and the skill carries the rhythm rules a ban list cannot express. A cadence number moved in the skill and left in the code drifts the same way, so move both.
45
+ - A paragraph under a list item counts toward that bullet, never as a paragraph
46
+ - A table, fence, list, heading, or bold section marker ends a run of paragraphs
47
+ - A run whose every item opens with a bold label or a link is exempt, and numbered items never count
48
+ - Only context entries, docs, skill bodies and references, tooling seeds and stack references, the wiki, `README.md`, and `CONTRIBUTING.md` take a finding
49
+ - A bullet run gates everywhere those paths sit, and the rest gates under `GATED_PATHS` in `src/markdown/scope.ts`. Outside a canon checkout nothing gates.
31
50
 
32
- Reading them out of the reference per run was the original design. It put a parser contract on a document authored for people, and the reference had to carry a paragraph of its own warning an author that a one-word backticked example in a `- Do not use ` bullet would be lifted into a literal ban set and ban that word everywhere. A rule existing to protect a parser from the prose it parses is the argument for separating them.
51
+ Five measures report and never set the exit code on their own.
33
52
 
34
- `markdown.md` still states every ban and every checkpoint, and a reader follows it rather than the code. Nothing compares the two, so a number moved in one place and left in the other drifts silently. Move both in the same change.
53
+ | Measure | Reports | Gate or report |
54
+ | ---------- | ------------------------------------------------------------------------------------ | -------------------------------------- |
55
+ | Bullets | A bullet past 200 characters | Gates through the density length |
56
+ | Paragraphs | A paragraph past 4 sentences or 350 characters | Gates through the density length |
57
+ | Cadence | 3 or more sentences with a spread of 5 words or under, or a word opening more than 2 | Reports |
58
+ | Depth | A run past about 40 rendered lines at 80 columns | Reports |
59
+ | Length | A document past 300 rendered lines, unless it carries `canon-length-exempt` | Reports, and the gate owns the verdict |
35
60
 
36
- The set is closed rather than extensible, so a project cannot add a term by editing a file. It holds the two characters alone, since a character is the one class a literal match settles. The set once carried 13 words and 6 British spellings as well. The words moved to the `write-human` skill as guidance, because a word ban catches the token and misses the habit, and `just`, `allows`, and `very` carry honest uses no literal match separates. Spelling is left to the author, with American spelling carried as guidance in `markdown-craft`.
61
+ | Code | Meaning |
62
+ | ---- | ------------------------------------------------------------------ |
63
+ | `0` | No gating finding |
64
+ | `1` | Refused, with the reason on stderr |
65
+ | `2` | A ban, a dead link, or a gating density or reference shape finding |
66
+ | `3` | The shipped ban set is empty, so nothing was measured |
37
67
 
38
- A set shipped empty is reported rather than passed. It finds nothing and would exit clean, which reports a corpus nobody checked as a corpus carrying no violation, so the run names the empty set and exits `3`. The set ships with the package, so a defect in the build is the only cause left.
68
+ Rewrite the sentence on a ban hit rather than swapping the token. A code span clears the report only where the token is an identifier under discussion.
39
69
 
40
- ## What each check reports
70
+ The audit runs in four places:
41
71
 
42
- ### Bans
72
+ - `.claude/hooks/standards-audit.sh` after each markdown edit, handing back every finding
73
+ - The `Markdown` stage of `canon gate run`, over the whole corpus
74
+ - `scripts/verify.ts` in a scaffolded project, excluding `CHANGELOG.md` and skipping with a logged line when no `canon` is on `PATH`
75
+ - The seeded hook in a target
43
76
 
44
- One closed set reports a hit: the characters `markdown.md` bans under `## Punctuation`.
77
+ ## Refusals
45
78
 
46
- Frontmatter, fenced blocks, inline code spans, and link destinations are excluded. Without the code-span exclusion each reference would report its own backticked examples, and without the link exclusion a semicolon in a query string would report as prose no rewrite can fix.
79
+ - `no-git` or `no-markdown`: no corpus can be built, and the gate reads it as unmeasured
47
80
 
48
- Word choice stays unmeasured and the report says so on every run. Single words travel in the `write-human` skill as guidance a reader applies. A multi-word ban escapes the harvest by width, whether it carries a placeholder standing in for the rest of the sentence or spells the phrase out in full, and every rule under `## Voice` is a judgment. The bans `## Language` states over what a sentence may claim are patterns of the multi-word kind by construction, since a literal match over a pattern reports the compliant text and reaches none of the violations. A report listing hits without naming those would read as a verdict on the whole reference.
81
+ ## See also
49
82
 
50
- ### Links
51
-
52
- A relative link's destination resolves against the filesystem, over the same corpus the ban scan reads. A destination is skipped rather than resolved when it is empty, opens with a URL scheme, opens with `#` as a same-file anchor, opens with `/` as a root-absolute reference, or carries `<` as a template placeholder such as `<slug>` or `<name>`. What remains is split on its first `#`, decoded, and resolved against the linking file's own folder, and a destination resolving to nothing on disk reports.
53
-
54
- The placeholder exemption is the one live case the corpus needs. Every current instance is a genuine illustration rather than a literal path, and a marker-based override is available for the day a real one needs an exception instead.
55
-
56
- ### Colons and heading case
57
-
58
- Two advisory measures report beside the bans and never fail a run.
59
-
60
- The colon measure reports a colon followed by more text in a prose line, outside the label position. A list item's first colon, a bold or code span label, a line-final colon, a time, a URL, and anything inside a code span or a link are left alone. A heading, a table row, and a fenced line are skipped. Each finding carries the line, the column, the line's text, and the word before the colon as `lead` under `colons` in the `--json` entry.
61
-
62
- The heading-case measure reports a heading or a frontmatter `title` with a capitalized word after its first, outside code spans and all-caps tokens, under `headingCase`. A proper noun reports as well and is left as it is.
63
-
64
- `CHANGELOG.md` is exempt from both, since the release tool writes its headings.
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
-
76
- ### Weight, cadence, and length
77
-
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`.
79
-
80
- ## Exit codes
81
-
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.
83
-
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.
85
-
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.
87
-
88
- A banned character is a fact rather than a judgment, which is the test that admits it to a gate. What held it back was that gating on day one against a corpus never checked mechanically fails loudly on work nobody has had a chance to fix. The order was to land the verb reporting, measure the corpus once, fix what it finds, and turn the gate on as its own change, and the gate is the last of the four.
89
-
90
- Of the five measures a first full reading took, the ban count is the only one a gate should ever read.
91
-
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.
93
-
94
- ### What a hit asks of an author
95
-
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.
97
-
98
- A code span clears the report too, since the ban scan walks around one, and it is the answer only where the token is genuinely an identifier under discussion. `## Code and identifiers` in `markdown.md` reserves the span for commands, API names, file paths, and identifiers, so backticking a quoted utterance spends one rule to satisfy another and leaves the corpus no cleaner.
99
-
100
- A hit the closed set could not separate from correct prose was the case with no third option while the set still carried words. The temporal `just` reported as the vague qualifier it happened to spell, and rewriting the sentence is what the toolkit settled on over building an exemption path, since an exemption landing in the verb and not the edit hook leaves the line failing where an author actually meets it. That class is part of why the words left the gate.
101
-
102
- ### Where the rules are enforced
103
-
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.
105
-
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.
107
-
108
- The target push stage reads the same exit codes `canon gate run`'s own stage reads: 0 passes, 1 is a refusal logged as a skip, 2 is a finding, and 3 is a shipped-empty ban set. It skips and logs rather than failing when no `canon` binary is on the machine's PATH, since a scaffolded project is not guaranteed the toolkit is installed, and its skip line names the install command.
109
-
110
- The target push stage excludes `CHANGELOG.md`, unlike every other surface here. A changelog a generator builds from commit subjects carries prose nobody wrote against the ban set, where this repository's own passes only because `release-please` builds it from subjects sessions already wrote to the rule. The stage passes an explicit file list rather than the bare invocation the other surfaces use, so a corpus with no file left once the changelog is set aside reads as a pass rather than falling back to the whole tree.
111
-
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.
113
-
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.
115
-
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.
117
-
118
- Both hooks answer an absent record as well. A completed run always writes the record and a refusal writes none, so an empty one means the verb declined to measure rather than measured and found nothing. The verb needs a git repository to build its corpus and refuses without one, which is a project the seeded hook can be installed into, and reading the findings alone reported that as a clean file.
119
-
120
- A machine with neither runner still blocks no edit, and it says so rather than exiting clean. The push stage holds either way. An edit nobody checked and an edit carrying no violation are one silence to a reader, so the enforcement a machine lacks is reported rather than inferred.
121
-
122
- The stage measures the whole corpus rather than the changed files. A `Do not use` bullet added to a reference bans a token retroactively, and no file in the push that adds the bullet was edited.
123
-
124
- ## What it does not cover
125
-
126
- The verb reads the `markdown-craft` skill's reference and the `write-human` cadence numbers and nothing else. The references the `git-pr`, `git-branch`, and `git-commit` skills own, `publish.md`, `slug.md`, and `versioning.md`, govern an attribute of other artifacts, and none of them is implemented here.
127
-
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.
129
-
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.
83
+ - `context-audit.md`: the folder-shaped findings
84
+ - `audits.md`: the aggregate that runs this audit
@@ -1,103 +1,61 @@
1
1
  ---
2
2
  title: Records
3
- description: The two roots a record folder resolves at, claiming the ordinal intake and groundwork share, reading each folder's size and growth, pruning stale scratch, and where validation, the canonical review count, and the private-remote backup are described
3
+ description: Backing the record folders to a private remote, restoring them on a new machine, reading folder size, and pruning stale scratch
4
+ kind: reference
4
5
  ---
5
6
 
6
7
  # Records
7
8
 
8
- ## Record roots
9
-
10
- Every verb here resolves a record folder at two roots rather than one. `.canon/<folder>` is read first, `.claude/<folder>` second, and a folder neither root carries resolves to the creation default, which is `.canon/`. The scratch folder is the one name that differs by root, spelled `.canon/tmp` and `.claude/.tmp`, since inside a dotted root the leading dot hides nothing already hidden. <!-- canon-keep-record-root -->
11
-
12
- The read order and the creation default agree. While the record folders were moving to a root of their own, creation stayed at `.claude/` so no record landed under a root whose ignore line had yet to reach a project. The ignore line ships now, so a fresh project scaffolds `.canon/` alone, and `.claude/` stays as a read fallback for a project the move has not reached.
13
-
14
- A caller never spells a record root by hand for the same reason. A path written as `.canon/plans/...` resolves against one root and reports nothing when it is wrong, which is the quiet failure this ordering exists to prevent: a stale binary meeting a moved layout, writing to the old path, and reporting success. Read a folder through the verb that owns it, and where a skill needs the path itself, take it from that verb's record rather than composing one.
15
-
16
- A refusal names every root it looked at, so a message reading `no-folder` says where a write would land as well as where the read failed.
17
-
18
- ## Validate
19
-
20
- `canon records validate <kind>` reports where a record folder disagrees with the standard governing it. Its flags, the per-kind checks, and the refusals are in `records-validate.md`.
21
-
22
- ## Ordinal
23
-
24
- `canon records ordinal <kind> <slug>` reports the next ordinal `intake` and `groundwork` share, or claims it with `--claim`. The two kinds share one sequence, per `claude/skills/plan-intake/references/intake.md` and `claude/skills/plan-groundwork/references/groundwork.md`, so this reads both `.canon/intake/` and `.canon/groundwork/` regardless of which kind was asked for.
9
+ `canon records` backs the record folders under `.canon/` to a private remote and reports on them. `canon hooks post-merge` runs `push` after every merge.
25
10
 
26
11
  ```bash
27
- canon records ordinal intake my-topic
28
- canon records ordinal groundwork my-topic --claim
29
- canon records ordinal groundwork my-topic --claim --json
30
- ```
31
-
32
- | Option | Behavior |
33
- | --------------- | ------------------------------------------------------ |
34
- | `--json` | Add a machine-readable record on stdout |
35
- | `--claim` | Create the folder atomically instead of only reporting |
36
- | `--root <path>` | Project root, defaulting to the main worktree |
37
-
38
- Without `--claim` this only reports, so two sessions reading at once can still report the same number, which is what let two sessions open two different record folders under one ordinal on the same day. `--claim` closes that: it reserves the number at a path both an `intake` claim and a `groundwork` claim resolve to identically, whichever kind is asking, and only creates the kind's own `<nn>-<slug>/` folder once that reservation is won. A losing reservation is retried against a freshly read ordinal rather than reported as a collision, bounded to five attempts before refusing as `ordinal-contended`.
39
-
40
- Exit codes: `0` reported the next ordinal, or `--claim` created the folder. `1` refused, `unknown-kind` when the argument names neither `intake` nor `groundwork`. `2` `--claim` lost every retry to a collision.
41
-
42
- ## Size
43
-
44
- `canon records size` reports what each record folder holds and how much of it is recent. It reads the same backed folders push carries, resolved the same way, plus the scratch folder, and it gates nothing.
45
-
46
- ```bash
47
- canon records size
12
+ canon records push
13
+ canon records pull
48
14
  canon records size --json
15
+ canon records prune-tmp --older-than 30
49
16
  ```
50
17
 
51
- | Option | Behavior |
52
- | --------------- | --------------------------------------------- |
53
- | `--json` | Add a machine-readable record on stdout |
54
- | `--root <path>` | Project root, defaulting to the main worktree |
55
-
56
- The table carries one row per folder that exists, heaviest first, with the file count, the bytes, a count for each growth window, and the dates of the least and most recently written file. Those dates render in the machine's local time, which is the calendar day whoever wrote the file was living in, and the reading is per-machine already. Folders that do not exist are named on one line below it rather than printed as rows of zeros. At the legacy `.claude` root, the record a `--json` call emits carries every folder in the fixed list either way, each with a `present` flag, so a caller reading the record gets a stable set of keys and can tell an absent folder from one the reading skipped. At a `.canon` root, the folder set is read off the directory itself rather than off a fixed list, so an absent folder is not listed at all: nothing enumerates a name nobody has created yet.
57
-
58
- Ordering by weight is what makes the reading worth taking. A folder listed alphabetically hides behind its neighbors, and the row a reader came for is the one that grew.
59
-
60
- The reading carries two windows rather than one, at 7 and 30 days. A single window cannot separate a folder growing steadily from one that took a single batch: a folder whose 7-day count is most of its 30-day count moved in one pass, and one where the two are proportional is growing at a rate.
18
+ Run `canon records <verb> --help` for every flag.
61
19
 
62
- Nothing fails on a number here. A record folder has no correct size, so the reading is a number to notice rather than a threshold to gate, and the point of the verb is that the next reading is taken by a command instead of by someone remembering to count the folder. A pen went from 44 entries to 236 between two readings taken by hand two weeks apart, which is the measurement this replaces.
20
+ ## Output
63
21
 
64
- The scratch folder is read here and skipped by a backup, because deletable without loss is not the same as empty. The routing handoffs and a retired entry archive both sit there and both accumulate. `.canon/.records.git` stays out because it is the backup history rather than a record, and `.claude/worktrees/` stays out because each entry is a checkout of the project with its own removal verb, and one of them outweighs every record folder combined.
65
-
66
- The window counts read `mtime`, so what they report is a file written inside the window rather than one created there. An entry edited long after it landed reads as recent, which overstates growth and never understates it, and these folders are append-mostly so the two readings agree on nearly every file. The one reading that is wrong rather than early is a machine restored by `canon records pull`, which resets the work tree hard and re-dates every file it writes, so a window taken there counts the restore. Nothing on the filesystem separates the two, since a restored file is new by every stamp it carries.
67
-
68
- Exit codes: `0` the reading completed, `1` refused. The one refusal is `no-folder`, raised when the project holds neither record root. A project holding a root and no records is empty rather than absent: at the legacy `.claude` root each folder's own `present` flag says which ones it carries, and at a `.canon` root the folder list itself is already the answer, since nothing absent is named.
69
-
70
- ## Prune
71
-
72
- `canon records prune-tmp` reports scratch nobody has touched inside an age window, and deletes it only with `--write`. Nothing removed an abandoned scratch folder before this, so a spike folder from months ago sat beside the ones a session still needs.
22
+ Set the history up once per machine, pointed at a private repository that is not a remote of the project.
73
23
 
74
24
  ```bash
75
- canon records prune-tmp
76
- canon records prune-tmp --write
77
- canon records prune-tmp --older-than 30 --json
25
+ git --git-dir=.canon/.records.git init
26
+ git --git-dir=.canon/.records.git remote add origin <private-repo-url>
27
+ printf '.canon/\n' >> .gitignore
78
28
  ```
79
29
 
80
- | Option | Behavior |
81
- | --------------------- | -------------------------------------------------------------- |
82
- | `--json` | Add a machine-readable record on stdout |
83
- | `--write` | Delete every candidate the report lists |
84
- | `--older-than <days>` | Age a unit's newest file must clear to be offered (default 14) |
85
- | `--root <path>` | Project root, defaulting to the main worktree |
86
-
87
- A candidate is a unit whose newest file is older than the threshold: a `tmp/<slug>/` folder, a folder one level inside `runs/`, `render/`, or `pr/`, or a single marker file inside `hooks/<hook>/`. `pr/review/` groups its body files by pull request number rather than reporting as one folder, since a review pass leaves one file per pass and a folder holding thousands of them would otherwise report as a single row nobody can prune apart. A unit holding no files, empty subfolders included, is offered whatever its age.
88
-
89
- `tmp/handoff/` and `tmp/pr/poll/` are never offered. A reader deletes a handoff themselves once it has been read, and a poll baseline is live state rather than scratch. The pre-split names the reserved split replaced, `memory-routing/`, `teach-promotion/`, `ui-checklist/`, and `pr-poll/` at the scratch root, are skipped the same way on a project the rename never reached, naming the folder they moved to, rather than being offered as ordinary slugs. An unread handoff is the one thing a wrong delete here loses for good.
30
+ - `push` commits every top-level folder under `.canon/` except `tmp`, `ordinal-locks`, and `.records.git`, then pushes. New folders list under `added`.
31
+ - `pull` restores the folders on a new machine after the setup above. It refuses rather than overwrite records the history lacks.
32
+ - `size` reports one row per folder, heaviest first, with file count, bytes, and growth over 7 and 30 days. After a `pull` every file reads as new.
33
+ - `prune-tmp` lists scratch untouched for 14 days and deletes it only with `--write`. It never offers `tmp/handoff/` or `tmp/pr/poll/`.
90
34
 
91
- A scratch-root name `canon migrate record-layout` or `canon migrate scratch-evidence` moves out of scratch for good, such as `memory-archive`, is skipped the same way on a project that has not yet run that migration, naming the verb to run. The retired-entry archive `memory-archive` holds is never deleted, per the memory standard, so it stays skipped rather than aging into an offer.
35
+ `push` blocks a file over 25 MB or one `canon secrets scan` would report, listing it under `blocked`. Move it out of the record folders, then push again.
92
36
 
93
- It writes nothing until `--write` is passed, since a session record has no history to undo a wrong delete from. It reads `mtime` the way `canon records size` does, so a machine restored by `canon records pull` reads its whole tree as new and fails safe by offering nothing.
37
+ Every verb exits `0` when done and `1` when refused. `prune-tmp` exits `2` when candidates exist and `--write` was not passed.
94
38
 
95
- Exit codes: `0` nothing to prune, or `--write` deleted every candidate. `1` refused, sharing `no-folder` with `size`, or a delete failed. `2` candidates exist and `--write` was not passed.
39
+ ## Refusals
96
40
 
97
- ## Stale
41
+ Branch on `reason` in the `--json` record, not the exit code.
98
42
 
99
- `canon records stale canonical` reports when the requirements record was last reviewed and how many releases have shipped since. How the release count is taken and the refusals are in `records-stale.md`.
43
+ | Reason | Meaning |
44
+ | ------------------- | ------------------------------------------------------------------------------ |
45
+ | `no-folder` | The project holds neither record root, from `size` or `prune-tmp` |
46
+ | `split-roots` | Record folders sit under both roots. Finish the move or put them back together |
47
+ | `no-repository` | No `.canon/.records.git`. Run the setup above |
48
+ | `no-remote` | The records history has no `origin` |
49
+ | `remote-unreadable` | The project's own remotes could not be read |
50
+ | `remote-shared` | The records origin is also a remote of the project. Use a private repository |
51
+ | `no-remote-records` | `pull` found no branch on the records origin |
52
+ | `local-changes` | `pull` found records on disk that the history lacks. Run `push` first |
53
+ | `local-ahead` | `pull` found local commits that never reached the origin. Run `push` first |
54
+ | `unsafe-payload` | `push` found a file over 25 MB or carrying a credential |
55
+ | `git-failed` | A git call failed, with its stderr in the message |
100
56
 
101
- ## Push and pull
57
+ ## See also
102
58
 
103
- `canon records push` backs the record folders to a private remote and `canon records pull` restores them. The payload, the setup, and the refusals are in `records-push.md`.
59
+ - `canon records ordinal <kind> <slug> --claim`: reserve the next intake or groundwork ordinal
60
+ - `canon records validate <kind> --help`: where a record folder disagrees with its standard
61
+ - `canon records stale --help`: when the requirements record was last reviewed