@erclx/canon 5.39.0 → 5.40.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 (131) hide show
  1. package/README.md +5 -5
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/canvas-board/SKILL.md +1 -1
  4. package/claude/skills/draft-slides/SKILL.md +2 -3
  5. package/claude/skills/draft-slides/references/deck.md +183 -0
  6. package/claude/skills/git-pr/references/evidence.md +2 -2
  7. package/claude/skills/git-pr/references/final-command.md +1 -1
  8. package/claude/skills/git-pr/references/labels.md +1 -1
  9. package/claude/skills/git-pr/references/publish.md +1 -1
  10. package/claude/skills/review-ui/SKILL.md +1 -0
  11. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  12. package/claude/skills/task-board/references/board.md +1 -1
  13. package/claude/skills/task-board/references/tasks.md +1 -1
  14. package/claude/skills/ux-measure/SKILL.md +1 -1
  15. package/docs/agents/audits.md +61 -60
  16. package/docs/agents/context-audit.md +40 -58
  17. package/docs/agents/index.md +5 -51
  18. package/docs/agents/install-and-sync.md +1 -0
  19. package/docs/agents/markdown-audit.md +53 -99
  20. package/docs/agents/records.md +37 -79
  21. package/docs/agents/sessions-transfer.md +26 -70
  22. package/docs/target/index.md +0 -1
  23. package/docs/target/migrations.md +57 -66
  24. package/docs/target/projects.md +36 -78
  25. package/docs/target/scenarios.md +9 -19
  26. package/docs/target/sync.md +27 -68
  27. package/docs/workflow/ai-workflow.md +22 -68
  28. package/docs/workflow/operating-model.md +32 -186
  29. package/docs/workflow/parallel-features.md +29 -66
  30. package/docs/workflow/skill-map-on-request.md +22 -24
  31. package/docs/workflow/skill-map.md +72 -76
  32. package/docs/workflow/visual-design-workflow.md +25 -118
  33. package/docs/workflow/zshrc-aliases.md +18 -37
  34. package/package.json +1 -1
  35. package/src/canvas/client/inspector/fields.css +4 -0
  36. package/src/canvas/index.html +6 -0
  37. package/src/claude/skills-reach.ts +2 -2
  38. package/src/cli/ui.ts +3 -1
  39. package/src/commands/canvas.ts +23 -0
  40. package/src/commands/capture.ts +14 -0
  41. package/src/commands/claude.ts +13 -0
  42. package/src/commands/demo.ts +22 -0
  43. package/src/commands/deps.ts +6 -0
  44. package/src/commands/driver.ts +28 -2
  45. package/src/commands/gov.ts +52 -18
  46. package/src/commands/intake.ts +17 -0
  47. package/src/commands/labels.ts +38 -5
  48. package/src/commands/pr.ts +160 -43
  49. package/src/commands/records.ts +8 -0
  50. package/src/commands/secrets.ts +6 -0
  51. package/src/commands/sessions.ts +8 -0
  52. package/src/commands/tasks.ts +52 -0
  53. package/src/commands/teach.ts +13 -0
  54. package/src/commands/upstream.ts +25 -1
  55. package/src/commands/worktrees.ts +5 -0
  56. package/src/context/audit.ts +22 -4
  57. package/src/context/folders.ts +1 -1
  58. package/src/context/shape-gated.ts +4 -2
  59. package/src/counts/scan.ts +1 -1
  60. package/src/design/components.ts +1 -1
  61. package/src/design/document.ts +1 -1
  62. package/src/design/tokens.ts +1 -1
  63. package/src/gate/boundaries.ts +4 -1
  64. package/src/gate/measures.ts +1 -1
  65. package/src/gate/stages.ts +7 -7
  66. package/src/git/env.ts +6 -0
  67. package/src/markdown/scope.ts +5 -17
  68. package/src/pr/lift.ts +153 -19
  69. package/src/pr/painted.ts +87 -0
  70. package/src/roots/record.ts +18 -1
  71. package/src/sandbox/census.ts +1 -1
  72. package/src/sandbox/headless/run.ts +3 -2
  73. package/src/sandbox/headless/watch.ts +3 -4
  74. package/src/slides/convert/master.ts +3 -0
  75. package/src/sync/format.ts +46 -0
  76. package/src/sync/stamp.ts +6 -0
  77. package/tooling/astro/reference.md +2 -2
  78. package/tooling/base/reference.md +1 -1
  79. package/tooling/go/reference.md +1 -1
  80. package/tooling/nextjs/reference.md +1 -1
  81. package/tooling/php/reference.md +1 -1
  82. package/tooling/python/reference.md +1 -1
  83. package/tooling/vite-react/reference.md +1 -1
  84. package/tooling/web/reference.md +1 -1
  85. package/docs/agents/canvas.md +0 -86
  86. package/docs/agents/capture.md +0 -65
  87. package/docs/agents/census.md +0 -23
  88. package/docs/agents/comments.md +0 -34
  89. package/docs/agents/context-audit-checks.md +0 -108
  90. package/docs/agents/context-classify.md +0 -101
  91. package/docs/agents/counts.md +0 -74
  92. package/docs/agents/demo.md +0 -140
  93. package/docs/agents/driver.md +0 -103
  94. package/docs/agents/gate.md +0 -90
  95. package/docs/agents/hooks.md +0 -78
  96. package/docs/agents/intake.md +0 -81
  97. package/docs/agents/key-changes.md +0 -116
  98. package/docs/agents/label-coverage.md +0 -86
  99. package/docs/agents/markdown-audit-density.md +0 -39
  100. package/docs/agents/markdown-audit-weight.md +0 -83
  101. package/docs/agents/pr-evidence-layout.md +0 -70
  102. package/docs/agents/pr-evidence.md +0 -273
  103. package/docs/agents/pr-frames.md +0 -131
  104. package/docs/agents/pr-lift.md +0 -132
  105. package/docs/agents/pr-local.md +0 -111
  106. package/docs/agents/pr-preview.md +0 -155
  107. package/docs/agents/pr-reads.md +0 -111
  108. package/docs/agents/pr-tick.md +0 -72
  109. package/docs/agents/records-push.md +0 -61
  110. package/docs/agents/records-stale.md +0 -40
  111. package/docs/agents/records-validate.md +0 -57
  112. package/docs/agents/restated.md +0 -83
  113. package/docs/agents/routing.md +0 -61
  114. package/docs/agents/rule-citations.md +0 -90
  115. package/docs/agents/sandbox.md +0 -126
  116. package/docs/agents/sessions.md +0 -128
  117. package/docs/agents/skills-audit.md +0 -120
  118. package/docs/agents/skills-reach.md +0 -64
  119. package/docs/agents/slides.md +0 -176
  120. package/docs/agents/state-scoped-risk.md +0 -105
  121. package/docs/agents/superseded.md +0 -85
  122. package/docs/agents/tasks-archive.md +0 -83
  123. package/docs/agents/tasks-plans.md +0 -152
  124. package/docs/agents/tasks-validate.md +0 -133
  125. package/docs/agents/tasks.md +0 -123
  126. package/docs/agents/teach-authoring.md +0 -118
  127. package/docs/agents/teach.md +0 -157
  128. package/docs/agents/test-order.md +0 -58
  129. package/docs/agents/upstream.md +0 -94
  130. package/docs/agents/worktrees.md +0 -118
  131. package/docs/target/standards-moved.md +0 -34
package/README.md CHANGED
@@ -161,13 +161,13 @@ For the full journey from scaffold through adding a domain later to syncing upst
161
161
 
162
162
  Each domain carries an entry written for someone maintaining the toolkit rather than installing it. These paths resolve in a clone only. The published package ships `docs` and not `.claude`, so an installed copy does not carry them.
163
163
 
164
- - [Claude Code plugin](canon/context/claude-plugin/index.md)
165
- - [Governance rules](canon/context/governance/index.md)
166
- - [Tooling stacks](canon/context/tooling/index.md)
167
- - [Design system](canon/context/design.md)
164
+ - [Claude Code plugin](canon/context/surfaces/plugin.md)
165
+ - [Governance rules](canon/context/surfaces/governance.md)
166
+ - [Tooling stacks](canon/context/surfaces/tooling.md)
167
+ - [Design system](canon/context/features/design.md)
168
168
  - [Slides](canon/context/features/slides.md)
169
169
  - [Transcripts](canon/context/features/transcripts.md)
170
- - [Sandbox](canon/context/sandbox/index.md)
170
+ - [Sandbox](canon/context/development/sandbox.md)
171
171
 
172
172
  ## Contributing
173
173
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
4
- "version": "5.39.0",
4
+ "version": "5.40.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -9,7 +9,7 @@ metadata:
9
9
 
10
10
  Drive the canvas the operator is looking at. A page is a folder of HTML frames and a frame is one file, so a session draws by writing files and the operator sees each write land in the browser. The content format is fixed by `${CLAUDE_SKILL_DIR}/references/canvas.md`. Read it before writing a frame.
11
11
 
12
- Every verb below is `canon canvas`. Run `canon docs canvas` for the full verb reference, its records, and its refusals.
12
+ Every verb below is `canon canvas`. Run `canon canvas --help` for the verbs and their refusals, and read `${CLAUDE_SKILL_DIR}/references/canvas.md` for the content format.
13
13
 
14
14
  ## Guards
15
15
 
@@ -11,16 +11,15 @@ Draw each slide as an HTML file in a deck folder, then shell out to `canon slide
11
11
 
12
12
  ## Read the decks and the format
13
13
 
14
- Run both first. Never hardcode a deck name, an attribute, or a chart type. The CLI is the source of truth for each.
14
+ Run the list first, then read the format. Never hardcode a deck name, an attribute, or a chart type.
15
15
 
16
16
  ```bash
17
17
  canon slides list --json 2>/dev/null
18
- canon docs slides
19
18
  ```
20
19
 
21
20
  The list returns one row per deck, carrying its `name`, `title`, slide count, and `path`. When the request names a deck the list holds, edit that folder. Otherwise pick a new kebab-case name no row carries.
22
21
 
23
- The docs page states what `deck.json` holds, what each slide declares on its `<body>`, the motion attributes, speaker notes, charts, and what each refusal reports.
22
+ Read `${CLAUDE_SKILL_DIR}/references/deck.md`. It states what `deck.json` holds, what each slide declares on its `<body>`, the motion attributes, speaker notes, and charts. Each refusal prints as a `✗` line from the render.
24
23
 
25
24
  ## Draw the deck
26
25
 
@@ -0,0 +1,183 @@
1
+ ---
2
+ title: Deck reference
3
+ description: The deck folder, the deck.json fields, the attributes a slide sets on its body, motion, the master's token roles, speaker notes, and charts
4
+ ---
5
+
6
+ # Deck reference
7
+
8
+ Applies to the slides under `.canon/slides/<deck>/`, which `canon slides render <deck>` turns into an editable PowerPoint file. Each slide describes its own shapes. What belongs to the whole deck sits in `deck.json`.
9
+
10
+ ## Contents
11
+
12
+ - [The deck folder](#the-deck-folder)
13
+ - [The deck file](#the-deck-file)
14
+ - [What each slide declares](#what-each-slide-declares)
15
+ - [Motion](#motion)
16
+ - [The master](#the-master)
17
+ - [Speaker notes](#speaker-notes)
18
+ - [Charts](#charts)
19
+
20
+ ## The deck folder
21
+
22
+ | Path | Holds |
23
+ | -------------------------------- | ---------------------------------------------------- |
24
+ | `.canon/slides/<deck>/*.html` | the slides, rendered in filename order |
25
+ | `.canon/slides/<deck>/deck.json` | what belongs to the whole deck |
26
+ | `.canon/slides/<deck>/assets/` | images the slides reference by a relative path |
27
+ | `.canon/slides/layouts/` | the project's shared layouts, never listed as a deck |
28
+
29
+ A folder holding no `.html` file is not a deck. `render` with no name takes the only deck, and refuses with the list when there are several. It also takes a folder path outside `.canon/slides/`.
30
+
31
+ ## The deck file
32
+
33
+ `deck.json` is optional. Without it the deck takes the defaults below.
34
+
35
+ | Field | Default | What it sets |
36
+ | -------------- | ------------------------------ | ------------------------------------------------- |
37
+ | `title` | the folder name | the file's title and the default footer text |
38
+ | `header` | off | a band at the top, with `left`, `center`, `right` |
39
+ | `footer` | on, with the title on the left | a band at the bottom, with the same three slots |
40
+ | `slideNumbers` | `true` | the number at the right of the footer |
41
+ | `mark` | none | an image path, relative to the folder, top right |
42
+ | `fonts` | none | faces to embed in the file |
43
+
44
+ ```json
45
+ {
46
+ "title": "Q3 review",
47
+ "header": { "left": "Acme" },
48
+ "footer": { "center": "Internal" },
49
+ "mark": "assets/mark.png",
50
+ "fonts": [
51
+ { "family": "Inter", "path": "fonts/Inter-Regular.ttf" },
52
+ { "family": "Inter", "weight": 700, "path": "fonts/Inter-Bold.ttf" }
53
+ ]
54
+ }
55
+ ```
56
+
57
+ ### Bands and the mark
58
+
59
+ A band takes `show` and the three slots. A declared band merges over its default, so `"footer": { "center": "Internal" }` keeps the title on the left. Declaring any slot turns a band on unless it also says `"show": false`. Set a slot to `""` to empty it.
60
+
61
+ - Slide numbers hold the footer's right slot and show only where the footer does. Set `slideNumbers` to `false` to use `footer.right`.
62
+ - Prefer a raster mark, since an SVG mark lands as a vector with a broken fallback.
63
+
64
+ ### Embedded faces
65
+
66
+ Each `fonts` entry names one TrueType or OpenType file inside the deck folder:
67
+
68
+ | Key | Default | Rule |
69
+ | -------- | -------- | ------------------------------------------------------------------- |
70
+ | `family` | required | match the family the slides set |
71
+ | `weight` | `400` | `600` and above fills the bold slot, anything lighter fills regular |
72
+ | `style` | `normal` | `normal` or `italic` |
73
+ | `path` | required | relative to the folder, and has to stay inside it |
74
+
75
+ A face is left out, and the deck still written, when its license forbids embedding, when it is a variable font or a `woff` or `woff2` file, or when a second face lands in a filled slot. Supply static instances, one per weight, and the TrueType or OpenType file a `woff2` was compressed from.
76
+
77
+ ## What each slide declares
78
+
79
+ A slide sets its place in the deck through attributes on `<body>`.
80
+
81
+ | Attribute | Effect |
82
+ | --------------------------------------- | --------------------------------------------- |
83
+ | `data-section="Results"` | opens a section the slides after it stay in |
84
+ | `data-hidden` | hides the slide in the show |
85
+ | `data-header="off"` | drops the header on this slide |
86
+ | `data-footer="off"` | drops the footer and its number on this slide |
87
+ | `data-footer-left`, `-center`, `-right` | replaces that footer slot on this slide alone |
88
+ | `data-header-left`, `-center`, `-right` | replaces that header slot on this slide alone |
89
+
90
+ Once any slide opens a section, slides ahead of the first sit in a section named after the deck. A title the deck returns to after another section opens a new section numbered `Intro (2)`.
91
+
92
+ A slide dropping one band keeps the other from the master. A slide replacing a slot draws its band as its own text, so a later edit in the master view skips it.
93
+
94
+ ## Motion
95
+
96
+ | Attribute | Takes | Effect |
97
+ | -------------------------- | ------------------------------- | -------------------------------------- |
98
+ | `data-transition` | `fade`, `push`, `wipe`, `cover` | the transition into this slide |
99
+ | `data-transition-duration` | `500`, `500ms`, or `0.5s` | rounded to 0.5, 0.75, or 1 second |
100
+ | `data-enter` | `fade`, `fly`, `wipe`, `zoom` | an entrance played on a click |
101
+ | `data-enter-order` | a number | the click it plays on, lowest first |
102
+ | `data-enter-duration` | `500`, `500ms`, or `0.5s` | the entrance length, 500 ms by default |
103
+
104
+ `data-transition` and `data-transition-duration` go on `<body>`. The `data-enter` family goes on the element.
105
+
106
+ ```html
107
+ <body data-transition="fade">
108
+ <h1>Results</h1>
109
+ <div class="card" data-enter="fly" data-enter-order="1">…</div>
110
+ <div class="card" data-enter="fly" data-enter-order="2">…</div>
111
+ </body>
112
+ ```
113
+
114
+ - Each entrance is its own click, and entrances without an order play after the ordered ones, in page order.
115
+ - An entrance brings in every shape drawn from its element and the elements inside it, so a card's box and text arrive together.
116
+ - An inner element with its own `data-enter` waits for its own click.
117
+ - Fly comes in from the bottom and wipe from the left.
118
+
119
+ ## The master
120
+
121
+ The master takes its colors from the `<html>` element of the first slide, laid out with the project's token stylesheet injected:
122
+
123
+ | Source | Used for |
124
+ | -------------------------------------------- | ---------------- |
125
+ | `--color-background` | the slide ground |
126
+ | `--color-text` | the header text |
127
+ | `--color-muted` | the footer text |
128
+ | `--color-accent` | the slide number |
129
+ | `<body>` font family, ` Variable` suffix cut | the theme face |
130
+
131
+ A role the project declares no token for falls back to the slide body's own color and prints one `✗` line naming it. It never falls back to the toolkit's palette.
132
+
133
+ A slide whose `<body>` background differs from the master's keeps its own. A slide whose `<body>` computes a `--color-text` apart from the master's prints a `✗` line, since its bands keep the master's colors and may not read against its ground.
134
+
135
+ ## Speaker notes
136
+
137
+ `<aside class="notes">` becomes the slide's notes and is never drawn. Each paragraph or list item becomes its own line, and markup is dropped.
138
+
139
+ ## Charts
140
+
141
+ A `<figure data-chart="...">` holding a `<table>` becomes a native, editable chart where the figure was laid out. The table is not drawn as well.
142
+
143
+ ```html
144
+ <figure data-chart="bar" data-labels>
145
+ <figcaption>Revenue by region</figcaption>
146
+ <table>
147
+ <tr>
148
+ <th>Region</th>
149
+ <th>Q1</th>
150
+ <th>Q2</th>
151
+ </tr>
152
+ <tr>
153
+ <td>North</td>
154
+ <td>12</td>
155
+ <td>16</td>
156
+ </tr>
157
+ <tr>
158
+ <td>South</td>
159
+ <td></td>
160
+ <td>11</td>
161
+ </tr>
162
+ </table>
163
+ </figure>
164
+ ```
165
+
166
+ - `data-chart` takes `bar`, `line`, `area`, `pie`, or `doughnut`.
167
+ - The header row names the series and the first column names the categories.
168
+ - An empty cell is a gap, not a zero. A comma is a thousands separator only where it groups in threes, as in `12,345.5`, so a comma-decimal `3,5` refuses.
169
+ - `data-labels` shows each value, and a `<figcaption>` becomes the chart title.
170
+ - Series take the accent first, then every other declared `--color-*` role in sheet order except the background and surface, repeating past the last.
171
+
172
+ A refused chart, effect, or face is left out and the rest of the deck is written. Each prints a `✗` line, and the exit depends on the case:
173
+
174
+ | Case | Exit |
175
+ | ----------------------------------------------------------------------- | ---- |
176
+ | malformed or unknown `deck.json` field | 1 |
177
+ | `mark` path missing | 1 |
178
+ | `fonts` path missing or outside the folder | 1 |
179
+ | face that cannot embed | 0 |
180
+ | unknown transition or entrance name | 0 |
181
+ | entrance on an element that draws nothing | 0 |
182
+ | chart cell that is not a number, or `data-chart` off-list | 0 |
183
+ | token role not declared, or a slide body setting its own `--color-text` | 0 |
@@ -31,7 +31,7 @@ A reviewer on the operator's machine can open the branch's running dev server, w
31
31
  canon pr local --json
32
32
  ```
33
33
 
34
- The verb reads the listening sockets on this machine, keeps those whose process runs inside this worktree, and reports the lowest port serving an HTML page. A server in another worktree or in the main checkout is never reported, since it shows a different branch. `canon docs pr-local` states the contract. Read `reason` on the record rather than the exit code.
34
+ The verb reads the listening sockets on this machine, keeps those whose process runs inside this worktree, and reports the lowest port serving an HTML page. A server in another worktree or in the main checkout is never reported, since it shows a different branch. `canon pr local --help` states the contract. Read `reason` on the record rather than the exit code.
35
35
 
36
36
  - `ok`: hold the record's `url` for the evidence step below.
37
37
  - `no-server` or `no-listener-reader`: hold the reason for the closing report, pass no link, and move on. Do not start a server here, since one started inside the ship chain has no owner to stop it.
@@ -110,7 +110,7 @@ The evidence comment is already posted, so the reviewer has the screenshots whil
110
110
  canon pr preview <number> --json
111
111
  ```
112
112
 
113
- The verb dispatches the project's Pages deploy on the pull request's branch and waits on the run for up to 15 minutes. It refuses before dispatching anything when the deploy command passes no `--branch`, since that deploy would publish the branch to production, and when the workflow's own push path filter matches no path the pull request changed, since a dispatch ignores that filter and the site it published would show none of the change. `canon docs pr-preview` states the contract. Read `reason` on the record rather than the exit code.
113
+ The verb dispatches the project's Pages deploy on the pull request's branch and waits on the run for up to 15 minutes. It refuses before dispatching anything when the deploy command passes no `--branch`, since that deploy would publish the branch to production, and when the workflow's own push path filter matches no path the pull request changed, since a dispatch ignores that filter and the site it published would show none of the change. `canon pr preview --help` states the contract. Read `reason` on the record rather than the exit code.
114
114
 
115
115
  - `ok`: re-render the evidence body with the address on the line under `## Evidence`, then post or update it exactly as the evidence step does, through the same tmp file and the `commentId` the record carries:
116
116
 
@@ -54,7 +54,7 @@ case "$links" in
54
54
  printf 'Removed %s session link(s) from the live body of #%s.\n' "$links" "$pr_number" >&2
55
55
  ;;
56
56
  esac
57
- record=$(canon labels apply "$pr_number" --base "$base_branch" --json 2>/dev/null | tail -n 1)
57
+ record=$(canon labels apply "$pr_number" --json 2>/dev/null | tail -n 1)
58
58
  if printf '%s' "$record" | jq -e 'has("earned")' >/dev/null 2>&1; then
59
59
  owed=$(printf '%s' "$record" | jq -r '.owed | join(",")')
60
60
  missing=$(printf '%s' "$record" | jq -r '.missingOnRemote | join(",")')
@@ -82,7 +82,7 @@ What it leaves unmeasured is a prefix reaching no path, so a row left behind by
82
82
 
83
83
  Apply labels after the pull request resolves, never as a field on the create. A create carrying a label the remote lacks either fails whole or creates the label, depending on the surface, and neither is the map's call. One step against a pull request that already exists covers the create and the edit path alike. It runs over REST, so it works where GraphQL is blocked, and the REST endpoint creates any label it is handed that the remote lacks, so the step reads each name off the remote first and warns on a missing one rather than adding it.
84
84
 
85
- `canon labels apply <number>` is that step. It resolves the earned set the way `canon labels audit` does, binds to the pull request only when its head is the current branch and it is open, and adds each earned label the pull request lacks. `--check` reports what is owed and writes nothing. The record carries `number`, `earned`, `present`, `added`, `owed`, `missingOnRemote`, and `uncovered`.
85
+ `canon labels apply <number>` is that step. It resolves the earned set from the pull request's own files listing, or from the git range when `--base` is passed, binds to the pull request only when its head is the current branch and it is open, and adds each earned label the pull request lacks. `--check` reports what is owed and writes nothing. The record carries `number`, `earned`, `present`, `added`, `owed`, `missingOnRemote`, and `uncovered`.
86
86
 
87
87
  A pull request opened outside this skill, such as by `gh pr create`, takes the same command, since nothing else labels it.
88
88
 
@@ -44,7 +44,7 @@ A phase label is one way text names the board, and a path under a record root is
44
44
 
45
45
  Two shapes get past a reader scanning for a bare label. A code span quoting a label is still the label, so read a span whose whole content is one as a hit and leave a longer token inside a span alone, which is a fixture name rather than a reference. The second shape is a path under a record root, gitignored and therefore absent from every clone, so `.canon/feedback/` names a folder the remote's reader cannot open.
46
46
 
47
- Under the tracked `canon/` and `.claude/` folders there is no hit, since `canon/context/governance/rules.md` resolves everywhere. `.canon/` carries no such carve-out: one ignore line covers the root whole, so every path beneath it is a hit regardless of which folder names it.
47
+ Under the tracked `canon/` and `.claude/` folders there is no hit, since `canon/context/surfaces/governance.md` resolves everywhere. `.canon/` carries no such carve-out: one ignore line covers the root whole, so every path beneath it is a hit regardless of which folder names it.
48
48
 
49
49
  Rewrite a hit to name what the reader can reach rather than deleting it. A row's subject stated plainly replaces its label, and what a record folder holds, said in a sentence, replaces its path.
50
50
 
@@ -26,6 +26,7 @@ A pass stops rather than repeat a verdict that already covers the head.
26
26
  - An open verdict at the head stops the same way, unless a `## Review response` or `## Post-review findings` comment is newer than it or the launch names an address
27
27
  - The hosted preview was not built from the head: stop and post nothing, naming `built`, `tip`, and `canon pr preview <number> --json` for the dispatcher
28
28
  - Post, then run `canon pr lift <number>` on a close. Never merge or ready the PR by hand, and edit the evidence comment only through `canon pr tick`.
29
+ - A push after a close owes a re-drive only when it changed a painted path, since the lift carries the close across the rest.
29
30
 
30
31
  ## Step 1: resolve the pull request
31
32
 
@@ -11,7 +11,7 @@ Read this at loop step 6, once the poll reports a branch stale or conflicted, or
11
11
  - Resolve the target at the moment of sending with `canon sessions list --branch`, never from a mapping written down earlier, since names rotate as sessions end and one recorded earlier in a session has failed inside the hour. `orchestrator-poll.md` routes on the count and the confidence it answers with
12
12
  - Open the message with the worktree and branch the sender believes the reader holds, asking to be corrected, whenever that mapping is inferred rather than confirmed
13
13
  - Name the skill for the reader to run rather than writing an invocation, which arrives as text
14
- - Read the pull request's own draft flag rather than the state a worker reports, and report what the read returned and when rather than the state alone. `canon docs pr-reads` states why a reported field can lag and say nothing about it. The flag settles the question only once the worker's chain has run its undo, and nothing marks that moment, so a read taken between the pull request opening and that call sees a genuinely ready pull request about to become a draft and has told an operator the opposite of what the worker had said.
14
+ - Read the pull request's own draft flag rather than the state a worker reports, and report what the read returned and when rather than the state alone. `canon pr head --help` states why a reported field can lag and say nothing about it. The flag settles the question only once the worker's chain has run its undo, and nothing marks that moment, so a read taken between the pull request opening and that call sees a genuinely ready pull request about to become a draft and has told an operator the opposite of what the worker had said.
15
15
 
16
16
  ## The rebase handback
17
17
 
@@ -144,4 +144,4 @@ Add no fourth readiness group in place of this file. The three group names are t
144
144
 
145
145
  ## Validation
146
146
 
147
- `canon tasks validate` reads the board against the tree, and what it checks, what it refuses on, and what it reports are at `canon docs tasks-validate`. Run it when the readiness claim is made rather than on a schedule, since the board is gitignored per-machine scratch and no shared moment exists to hang it on.
147
+ `canon tasks validate` reads the board against the tree, and what it checks, what it refuses on, and what it reports are at `canon tasks validate --help`. Run it when the readiness claim is made rather than on a schedule, since the board is gitignored per-machine scratch and no shared moment exists to hang it on.
@@ -171,7 +171,7 @@ The line is what lets a merge close its own task. `canon tasks archive --pull-re
171
171
 
172
172
  ## Archiving
173
173
 
174
- Never delete a task file. A shipped task moves to `.canon/tasks/archive/` under its own name, and the live index regenerates without it. `canon tasks archive` owns that move, and what it does and what it refuses on are at `canon docs tasks-archive`.
174
+ Never delete a task file. A shipped task moves to `.canon/tasks/archive/` under its own name, and the live index regenerates without it. `canon tasks archive` owns that move, and what it does and what it refuses on are at `canon tasks archive --help`.
175
175
 
176
176
  The archive nests inside `.canon/tasks/` rather than sitting beside it as a flat `.claude/task-archive/`, which is what a binary predating this convention still writes. Meeting the flat sibling names an older checkout rather than a second archive to reconcile.
177
177
 
@@ -20,7 +20,7 @@ Report numbers. A sentence about what the source looks like is what this exists
20
20
  Read these in parallel from the project root, skipping any that do not exist:
21
21
 
22
22
  - `package.json`: the `scripts` block naming a dev, preview, or start command
23
- - `canon/context/development/`: the documented run commands and the port each serves
23
+ - `canon/context/development/local-loop.md`: the documented run commands and the port each serves
24
24
  - `CLAUDE.md`: project type and conventions
25
25
 
26
26
  Prefer a production-shaped build (`preview`, `start`) over the dev server, since a dev server ships unminified modules and reports a cost no user pays.
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  title: Audits
3
- description: 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
3
+ description: 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
4
+ kind: reference
4
5
  ---
5
6
 
6
7
  # Audits
7
8
 
8
- `canon audits run` runs every audit this repository owns, reports each one under a single verdict, and compares each count to the floor the last recorded run left behind. Nothing here is a new measure. Every number it prints comes from a verb that already published it, which is what makes the command cheap and what separates it from writing another check.
9
+ `canon audits run` runs every audit this repository owns, reports each under one verdict, and compares each count to the baseline the last recorded run left. Every number comes from a verb in the catalog below.
9
10
 
10
11
  ```bash
11
12
  canon audits run
@@ -15,36 +16,11 @@ canon audits run --corpus tracked --corpus per-machine
15
16
  canon audits list --json
16
17
  ```
17
18
 
18
- | Option | Behavior |
19
- | ----------------- | ---------------------------------------------------------------------------------------------------------- |
20
- | `--json` | Add a machine-readable record on stdout, keeping the frame |
21
- | `--root <path>` | Measure this tree instead of the current worktree |
22
- | `--record` | Write this run's tracked counts to `canon/config/baseline.json` |
23
- | `--corpus <name>` | Limit the run to one corpus (`tracked`, `per-machine`, `upstream`), repeatable, defaulting to every corpus |
19
+ `--record` writes the tracked counts to `canon/config/baseline.json`. `--corpus` limits the run to `tracked`, `per-machine`, or `upstream` and repeats. `--root <path>` measures another tree. `canon audits list` prints the catalog the run reads.
24
20
 
25
- ## What it runs
21
+ ## Output
26
22
 
27
- Twenty-one verbs, listed by `canon audits list`. Each runs once in its fullest form, and the aggregate reads that verb's own record rather than imposing a shared envelope on it. Every one of those records already has consumers naming its keys, so a common shape would be a breaking change bought for tidiness.
28
-
29
- The verbs walk separate trees and share no state, so they run together. Measured on the authoring machine at twelve verbs, a run finished in 0.8 seconds of wall clock against 4.4 seconds of processor, which is under every other stage in `bun run check`. `canon deps audit` is the one that changes that reading, since it reaches a network rather than a tree and its latency is the index's rather than this machine's: three clean runs measured 44.7, 77.6, and 62.9 seconds, and a stalled lookup runs to an explicit 120-second timeout rather than to bun's own 299-second ceiling. `--corpus` is what keeps a caller from paying that cost when it wants only the corpora on this disk.
30
-
31
- Eighteen of the twenty-one read a tree on this disk, the one added by `restated.md` and the one added by `census.md` among them, since the first reads four such trees against each other and the second counts every file in one. The two added by `state-scoped-risk.md` read committed state rather than an arriving change, which is the gap every review surface here leaves by construction, and the one added by `label-coverage.md` reads a branch range against a map the project declares.
32
-
33
- The placement entry runs `canon context classify sweep` pinned to `--backend off`, so it reads the regex layer alone whatever classifier setting the project resolves. A count that moves with whichever model a machine runs is not comparable across clones, and the model layer turned a sub-second read into one measured at about 17 minutes over this repository's context sections on the authoring machine. It retains one tally per verdict, `keep`, `rewrite`, and `move`, and gates on nothing, since each verdict is a judgment. `document-health` is the surface that reads the same sections with the model.
34
-
35
- Each is invoked as the CLI the caller is running rather than as a global `canon`. A globally installed binary resolves to the main checkout no matter which worktree is executing, so the aggregate would measure a tree the branch never touched and report a pass over it.
36
-
37
- ## What gates and what reports
38
-
39
- Five findings fail the run: an unresolved context citation, a banned character, a relative link resolving to nothing on disk, a skill folder carrying no `REQUIREMENT.md`, and a credential-shaped value in the tree the package ships. Each is a fact with no false-positive class behind it.
40
-
41
- Four of the five are the ones `canon gate run` already fails a push on. The secret scan is the one entry gating without a stage behind it, added on that same fact-or-judgment test rather than as a side effect of registering a measure, and the toolkit already ranks content leaving the repository above content that stays.
42
-
43
- Everything else reports. A heavy bullet, a long entry, a board row nothing resolves, a degradation term in a comment, and an implementation reaching history ahead of its test are judgments a reader settles. A push failing on one of those teaches contributors to route around the stage, which is the split recorded across every audit here and the one this command inherits rather than moves.
44
-
45
- Growth against the baseline reports too. The standards behind the largest measures set no hard cap, so a count that rose is a fact about the corpus and a judgment about whether it matters.
46
-
47
- ## Exit codes
23
+ `summary.verdict` is `clean`, `reported`, `findings`, or `incomplete`. Every run also states how many corpora it measured against how many it skipped.
48
24
 
49
25
  | Code | Meaning |
50
26
  | ---- | -------------------------------------------------------------------- |
@@ -53,52 +29,77 @@ Growth against the baseline reports too. The standards behind the largest measur
53
29
  | `2` | An audit carries a finding that is a fact |
54
30
  | `3` | An audit did not report, so the run measured less than the whole set |
55
31
 
56
- `3` is a defect in the run rather than in the tree, which is why it takes a code of its own. An aggregate reporting a pass over a set it never finished measuring is the failure the command exists against, so an unparseable record, a verb that could not be started, and an exit no verb documents all land there instead of quietly counting as clean. The `markdown audit` empty-ban-set exit is the precedent: a corpus walked with nothing looked for is a broken check, not a clean tree.
57
-
58
- The verdict is published as a field as well as an exit code. A reader branching on `summary.verdict` gets `clean`, `reported`, `findings`, or `incomplete` without inferring the difference between a quiet corpus and one nobody finished measuring.
59
-
60
- ### An absent corpus is not an unmeasured one
61
-
62
- A per-machine folder that is not on this disk reports as `absent` and moves neither the verdict nor the exit code. Every one of those folders is gitignored, so a fresh clone and every CI run carry none of them, and counting six expected absences as failures pins the verdict at `incomplete` forever. A signal that never changes is one nobody reads after the second time they see it.
63
-
64
- Which refusals count as an absence is declared per audit rather than inferred from the corpus alone. A per-machine corpus counts a missing folder. An upstream corpus counts every reason the advisory verb publishes, since each of the three means there is nothing resolved to measure. Several tracked audits override that default too, wherever the corpus not being there is the ordinary state of a target rather than a broken checkout: the secret scan for a project that publishes nothing, the skill corpora, the citation reach check, and the routing measure for a project holding no skills tree, and the context audit for a project that adopted none of the folders it measures.
65
-
66
- Everything else stays `unmeasured`. That covers a tree git cannot list, which is a broken checkout, and a package declaring no `files` field, where a publish would pack the whole tree and the scan read none of it. Both are a corpus that exists and went unread, so softening either would report a pass over a shipped tree nobody measured.
32
+ Code `3` marks a defect in the run rather than in the tree. An unparseable record, a verb that could not start, and an undocumented exit all land there.
67
33
 
68
- Each override exists because the corpus answers the wrong question for that audit. The secret scan earns its from `private: true`, the one declaration that a package is never published, so a project carrying it reports an absent corpus rather than a broken one. Without that the aggregate would report `incomplete` in every such project on every run, which is the permanent signal this section opens by rejecting.
34
+ Four findings gate, being an unresolved context citation, a banned character, a skill folder with no `REQUIREMENT.md`, and a credential-shaped value in the shipped tree. Everything else reports, and growth against the baseline reports too.
69
35
 
70
- `upstream` is the third corpus value, carried by the advisory check alone. Its count moves when someone publishes rather than when someone edits here, so it is kept out of the retained baseline for the mirror image of the reason gitignored scratch is, and an offline run reports it absent rather than pinning the verdict at `incomplete` on every machine without a network.
36
+ **Corpus states.**
71
37
 
72
- Every run states how many corpora it measured against how many it skipped, including a run with no findings at all. A count of what passed reads as a verdict on the whole set unless the run also says what it never reached.
38
+ An audit that finds no corpus reports `absent`, which moves neither the verdict nor the exit code. Anything unread that should exist is `unmeasured`, which sets `incomplete` and exit `3`.
73
39
 
74
- ### What a shallow checkout changes
40
+ - A per-machine folder missing from this disk is `absent`
41
+ - `upstream` is `absent` offline, and stays out of the baseline
42
+ - The secret scan reports `no-manifest`, `no-publish`, and `no-shipped-files` as `absent`, and `no-files-field` and `no-git` as `unmeasured`
43
+ - The skill audits, the citation reach check, and the routing measure report `absent` for a project with no skills tree
44
+ - The context audit reports `absent` for a project that adopted none of its folders, and a tree git cannot list is `unmeasured`
75
45
 
76
- `canon gov test-order` scopes its range against the trunk and falls back to the root commit when no trunk ref resolves. A depth-1 checkout has neither, so the range is empty and the verb reports zero of everything rather than refusing. The numbers are real for the history present, which means a shallow run under-reports against a baseline taken from a full clone and shows as shrinkage. That is the safe direction, and it is the reason a `test-order` delta is worth reading only from a clone carrying the history.
46
+ **Baseline.**
77
47
 
78
- ## The retained baseline
48
+ `canon/config/baseline.json` holds the counts from the last `--record`, with the day and the commit. It falls back to `.claude/canon/baseline.json` for a project that has not moved. Each run reports which counts moved and by how much.
79
49
 
80
- `canon/config/baseline.json` holds the counts from the last run recorded with `--record`, alongside the day it was taken and the commit it was read at, falling back to `.claude/canon/baseline.json` for a project that has not moved. Each later run reports which counts moved and by how much.
50
+ - A run with no baseline says so rather than showing a delta of zero
51
+ - A key on one side only is named as new or gone
52
+ - A per-machine record and the task board are reported every run and never recorded
53
+ - An audit that did not report is left out, never written as zero
54
+ - A baseline that does not parse refuses the run
81
55
 
82
- The file is committed. A per-machine record cannot answer the question this half exists for, which is whether a number grew since anyone last looked, and a fresh checkout has to inherit that answer rather than start over. The cost is that a branch moving a count either fixes it or re-records it and says why, which is the shape the consumed-copy assertion already carries.
56
+ `gov test-order` reads zero in a shallow clone, so read its delta only from a clone carrying the history.
83
57
 
84
- It sits under the project root rather than beside the aggregator in `src/`. The numbers describe one repository's corpus, and `src/` ships to every project that installs the CLI, so a baseline in the package would hand a target these counts to measure its own tree against.
58
+ **In `bun run check`.**
85
59
 
86
- ### What is kept out of it
60
+ `bun run check` runs `--corpus tracked --corpus per-machine` as a reporting stage after the gating stages, and never fails on it. `summary` is a flat object of unique scalar keys that a shell stage greps without a JSON parser. `deps` runs weekly instead.
87
61
 
88
- A gitignored record folder holds one machine's session scratch. Its counts describe somebody else's disk, so committing them writes a floor no other clone can reproduce and every contributor reads a regression against a number that was never about their tree. The board and every record kind are therefore reported on every run and recorded in none.
62
+ The placement entry runs pinned to `--backend off`, so its tally stays comparable across machines. Each audit runs as the CLI the caller is running, never a global `canon`.
89
63
 
90
- An audit that did not report is left out rather than written as zero. Zero there records a clean corpus nobody measured, and the next run reads its real numbers as a regression against a floor that was never taken.
64
+ **Catalog.**
91
65
 
92
- ### The first run
66
+ `canon audits list` reports the first 19 rows. The rows marked `outside` run by hand or from a skill and sit beside the aggregate.
93
67
 
94
- A run with no baseline behind it says so rather than showing a delta of zero. Those two states say the same thing to a reader and mean opposite things, which is a defect this repository has already had to fix twice elsewhere. The same reasoning covers a check the baseline never recorded, a key the run produced that the record does not carry, and a key the record carries that this run did not produce. Each is named for what it is.
68
+ | Id | Verb | Measures | Finding |
69
+ | -------------------- | ----------------------------------- | ---------------------------------------------------------------------------------- | -------------- |
70
+ | `context` | `canon context audit` | Context folder structure, citations, and the requirements record's word cap | Gates |
71
+ | `placement` | `canon context classify sweep` | Keep, rewrite, and move tally per context section, regex layer only | Reports |
72
+ | `markdown` | `canon markdown audit` | Bans, dead links, density, and reference shape | Gates |
73
+ | `skills` | `canon claude skills audit` | Both skill corpora against the skill standards | Gates |
74
+ | `skills-reach` | `canon claude skills reach` | Skill bodies citing a path no target receives | Reports |
75
+ | `skills-rank` | `canon claude skills rank` | Whether skill descriptions separate against a routing case corpus | Reports |
76
+ | `tasks` | `canon tasks validate` | Board rows nothing resolves | Reports |
77
+ | `records-plans` | `canon records validate plans` | Plan records against their shape | Reports |
78
+ | `records-groundwork` | `canon records validate groundwork` | Groundwork folders against their shape | Reports |
79
+ | `records-intake` | `canon records validate intake` | Intake folders against their shape | Reports |
80
+ | `records-teach` | `canon records validate teach` | Teach records against their shape | Reports |
81
+ | `comments` | `canon comments scan` | Comment density by language and kind, and `TODO`, `FIXME`, `HACK`, `XXX` | Reports |
82
+ | `test-order` | `canon gov test-order` | Implementations that reached branch history before their test | Reports |
83
+ | `secrets` | `canon secrets scan` | Credential-shaped values in the shipped tree | Gates |
84
+ | `labels` | `canon labels audit` | Changed paths no row of the label map reaches | Reports |
85
+ | `restated` | `canon gov restated` | Instructions `CLAUDE.md` states that a second surface states too | Reports |
86
+ | `counts` | `canon gov counts` | Sentences stating a closed catalog's size that disagree with the tree | Reports |
87
+ | `census` | `canon census` | File count, extension breakdown, and line total | Reports |
88
+ | `deps` | `canon deps audit` | Advisories against the resolved dependencies | Reports |
89
+ | `outside` | `canon gov citations` | Rule citations and internal frontmatter globs reaching nothing | Gates in check |
90
+ | `outside` | `canon gov superseded <old> <new>` | Where the tree still asserts a value a changed convention no longer produces | Reports |
91
+ | `outside` | `canon claude skills drift <ref>` | Shipped skill bodies rewritten since a ref | Reports |
92
+ | `outside` | `canon claude routing` | Per `CLAUDE.md` section, how many bullets name a path and whether a rule covers it | Reports |
93
+ | `outside` | `canon context classify diff` | Classification of the chunks a git range changed | Reports |
95
94
 
96
- A hand-edited baseline that does not parse refuses the whole run. Reading a broken record as an absent one would reset the floor the file exists to hold, silently.
95
+ A deliberate secret takes a `canon-allow-secret: <reason>` marker. A deliberate citation takes `canon-allow-citation: <reason>` and a deliberate superseded value takes `canon-allow-superseded: <reason>`, each on its own line or the one above. Run any verb with `--help` for its flags and refusal reasons.
97
96
 
98
- ## In the verify pipeline
97
+ ## Refusals
99
98
 
100
- `bun run check` runs a reporting stage after the three gating stages, and never fails on it. Those three stages keep their own specific remedies, so the aggregate reports the rest and the growth, and a fact still fails the push at the stage that names what to do about it.
99
+ - A baseline that does not parse
100
+ - A `--root` that is not a tree git can list
101
101
 
102
- The stage runs `--corpus tracked --corpus per-machine` rather than the full set, so `deps` and its network latency sit outside the gate entirely. A weekly job reads the advisory index instead, on a schedule rather than on every push. `summary.audited` in the gate's own record therefore reads 19 against the 20 `canon audits run` reaches with no filter.
102
+ ## See also
103
103
 
104
- The stage reads `summary`, a flat object of scalars published beside the nested arrays. Every key in it is unique across the whole record, so a shell stage greps one out without a JSON parser. The three verbs the gating stages already ran walk their trees a second time here, which is the 0.8 seconds measured above and the reason the scoped set runs rather than only the part those stages skip: one verdict over the corpora describing this tree is the value, and a stage measuring a narrower subset would report a health nobody took.
104
+ - `markdown-audit.md`: the markdown audit
105
+ - `context-audit.md`: the context audit