@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
@@ -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
 
@@ -9,7 +9,7 @@ Applies to a feature plan that sequences its work into more than one batch. A de
9
9
 
10
10
  ## One batch, one file
11
11
 
12
- One batch is one pull request, and one pull request is one plan file, named for its own concern rather than a numeric suffix. `canon tasks plan-branch` derives one branch from one plan filename.
12
+ One batch is one pull request, and one pull request is one plan file, named for its own concern rather than a numeric suffix. `canon tasks plan-branch` derives one branch from one plan filename. A fan-in member is the exception, keeping its own file and branch and opening no pull request, per `## Fan-in`.
13
13
 
14
14
  - A `**Batch N**` sub-heading inside one file's `**Files to touch:**` shares that branch across every batch, so the first merge strands the rest
15
15
  - Write a plan opening five batches as five files before the first line of any of them
@@ -44,3 +44,50 @@ Slice a batch by one user-visible path end to end rather than by layer, so each
44
44
  - Sequence the riskiest batch first, so the batch most likely to change the plan is built while every later batch can still move
45
45
  - Sequence a sweep last, even when it is the riskiest. A batch rewriting files earlier batches touched forces every batch behind it into one review anywhere else.
46
46
  - Keep each batch's commits contiguous once the work starts, since a batch interrupted by another cannot be lifted onto its own branch afterward
47
+
48
+ ## Fan-in
49
+
50
+ A fan-in builds batches in parallel and ships them as one pull request. Each member builds on its own branch and opens none, and one integrator merges every member branch, resolves the shared lines once, and opens the pull request. The trade is one large review, read one merge commit at a time, in place of a review and a rebase per batch.
51
+
52
+ ### When to fan in
53
+
54
+ Fan in when the split was made for speed rather than meaning. Every one of these holds:
55
+
56
+ - One task with one outcome, so no batch is a feature on its own
57
+ - The batches share edge files, such as a gated-paths list or an index, so separate pull requests rebase each other on every merge
58
+ - No batch needs to ship, be reviewed, or be reverted alone
59
+
60
+ Keep separate pull requests when the pieces are independent features, when one needs its own review or revert, when one may stall and hold the rest, or when the combined diff is too large to review even one merge commit at a time. Batches that build on each other stack or fold, per `## Dependent batches`, since a member stacks on nothing.
61
+
62
+ ### Declaring the set
63
+
64
+ Each side names the other in its `**Constraints:**`, and `canon records validate plans` reports `fan-in-unmatched` wherever the two disagree.
65
+
66
+ - A member writes ``Fan-in member of `feature-<integrator>` ``, naming one integrator
67
+ - The integrator writes ``Fans in `feature-<a>`, `feature-<b>`, and `feature-<c>` ``, in the order it merges them
68
+ - The task's `Plan:` line cites every member and the integrator, since the merge archives only the plans that line cites and a live integrator listing archived members reports `fan-in-unmatched`
69
+ - The integrator is its own plan, since its writes, being the shared lists, the citer repoints, and the index regeneration, need a `**Files to touch:**` of their own
70
+
71
+ `canon tasks plan-answers` holds a member whose integrator is missing or does not list it with a `Fan-in set` entry. It holds the integrator with a `Fan-in pending` entry until every member branch is on origin.
72
+
73
+ ### Dispatching the set
74
+
75
+ The members of one set dispatch together by design, since they share edge files and the integrator resolves those lines once. A shared file between members is never a hold.
76
+
77
+ - Hold the integrator on a dependency until `plan-answers` reads it launchable
78
+ - Re-read it after a fetch on each member's done message, which carries the branch and head commit in place of a pull request
79
+ - Never read a member with no pull request as stalled, since a member opens none
80
+
81
+ ### What a member writes
82
+
83
+ A member builds, verifies, pushes its branch, and announces it done. A pushed branch is the done signal, so a member pushes once, at the end.
84
+
85
+ A member may edit the shared lists and regenerate the indexes on its own branch, since the pre-push check fails a member that deletes a path a gated list still names. The integrator resolves those lines once at merge.
86
+
87
+ ### What the integrator writes
88
+
89
+ The integrator merges each member branch with `git merge --no-ff` in the order its bullet lists, resolving conflicts by the rules a rebase follows. It then writes its own files, regenerates the indexes once, and opens the pull request.
90
+
91
+ - Say in the pull request body that each member is one merge commit, so a reviewer reads `git log --first-parent` one member at a time
92
+ - Fix a semantic conflict a merge surfaces on the integrator's branch, since a member has no pull request to fix it on
93
+ - Delete each member branch on origin with `git push origin --delete <branch>` once the pull request merges
@@ -105,6 +105,8 @@ A constraint measured against work in flight expires when that work merges, as a
105
105
  - Re-test a stamped constraint before honoring it with `git fetch origin main --quiet && git log <commit>..origin/main --oneline -- <the paths the constraint names>`. Any commit it reports means the track landed and the constraint is dead. Fetch and scope the log to the paths in the same command, since a stale ref reads a dead constraint as live and a squashed subject names no track.
106
106
  - Read an unstamped constraint as unverified rather than as live, and confirm it against the open pull request list for the named paths.
107
107
 
108
+ A batch in a fan-in set declares its side in a constraint bullet, ``Fan-in member of `feature-<integrator>` `` on a member and ``Fans in `feature-<a>`, `feature-<b>` `` on the integrator. `batches.md` holds when to fan in, and `canon records validate plans` reports `fan-in-unmatched` for a set whose two sides disagree.
109
+
108
110
  A constraint naming a `.canon/ready/` folder is a third shape beside the hold and the shared path. It makes that folder's files the verbatim source for the paths this plan's `**Files to touch:**` lists, per `ready.md`, so the executing session copies those paths rather than authoring them.
109
111
 
110
112
  ### Verification
@@ -45,4 +45,4 @@ The batching fails a fifth way. A folder holding thirty unread items put as thir
45
45
  - Promoting an answered item onto the board, which is `task-board` and runs after the answers land
46
46
  - The item format, the answer contract, and retrieval, which the `plan-intake` skill's `references/intake.md` owns and this skill cites
47
47
  - The comparable answer slots in groundwork and feature plans, which carry their own contracts and are a separate measurement
48
- - Deciding when to fire. The skill is user-invoked through `disable-model-invocation`, so answering is the operator's call rather than a description match.
48
+ - Deciding when to fire. The description states `Do NOT auto-trigger`, so answering is the operator's call rather than a description match.
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: plan-intake-answer
3
- description: Walks an intake folder's unread items and puts them as batched questions in chat, writing each selection back onto the item it answers. Use when asked to "answer the intake", "work through the open items", "answer my intake questions", "go through the dump", or "let me answer these from here". Do NOT use to file a dump or write new items, which is `plan-intake`.
4
- disable-model-invocation: true
3
+ description: Walks an intake folder's unread items and puts them as batched questions in chat, writing each selection back onto the item it answers. Use when asked to "answer the intake", "work through the open items", "answer my intake questions", "go through the dump", or "let me answer these from here". Do NOT use to file a dump or write new items, which is `plan-intake`. Do NOT auto-trigger.
5
4
  metadata:
6
5
  family: decide
7
6
  ---
@@ -21,6 +21,13 @@ Pop only when the stash ran. On a clean tree, which is what a run whose findings
21
21
 
22
22
  Never merge `main` into the branch. The repository squash-merges, so a merge commit here reads as noise on the pull request.
23
23
 
24
+ ### A branch carrying merges
25
+
26
+ A branch carrying merge commits of its own, such as a fan-in integrator's, rebases with `git rebase --rebase-merges origin/main` instead. A plain rebase drops the merges and replays every member commit in one line, which loses the one merge commit per member a reviewer reads.
27
+
28
+ - `--rebase-merges` re-runs each merge without the resolution it recorded, so resolve each again by the rules below
29
+ - `git config rerere.enabled true`, set before the first merge, replays a recorded resolution in place of resolving it again
30
+
24
31
  ## Resolving a conflict
25
32
 
26
33
  These rules apply to a hunk from the rebase and a hunk from the stash pop alike:
@@ -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
 
@@ -147,6 +148,8 @@ A console error or an overflow is a finding. A probe result is a reading to conf
147
148
 
148
149
  Read `${CLAUDE_SKILL_DIR}/references/post.md` for the heading rule, the body shape, the marker every body ends on, and the post command. Each box lands in the section its verdict names, and the `link` its Step 5 push returned goes as given into that box's own entry.
149
150
 
151
+ That file sends a pass closing under a prior `## UI review closed` at an older head on to `${CLAUDE_SKILL_DIR}/references/close-out.md`, which rewrites the standing close in place.
152
+
150
153
  ## Step 8: tick the passed boxes
151
154
 
152
155
  Read `${CLAUDE_SKILL_DIR}/references/tick.md` once the verdict has posted, for how every box that passed is ticked through `canon pr tick` at the head this pass drove, and what a refusal reports.
@@ -0,0 +1,47 @@
1
+ ---
2
+ title: Rewrite a repeated UI close in place
3
+ description: The branch of review-ui Step 7 taken when the prior verdict closed the review and this pass owes nothing, which replaces the standing close with a PUT rather than posting a second one
4
+ ---
5
+
6
+ # A UI close that repeats the standing one
7
+
8
+ Part of Step 7 of `review-ui`, reached only on the branch `post.md` names. The body shape, the scan, and the marker still come from `post.md`.
9
+
10
+ When the newest UI verdict from Step 1 reads `## UI review closed` at an older head and this pass closes too, the thread already holds this verdict. Replace the standing close rather than posting a second one beside it.
11
+
12
+ Only a closed prior is replaced. A prior `## UI review` stays in the thread when this pass closes, so the close posts normally. A pass that owes anything posts under `## UI review` normally too, whatever the prior read, since a rewrite there would hide a failed box behind a heading that reads closed.
13
+
14
+ Resolve the standing close's numeric id off the REST listing, keeping the newest match across every page:
15
+
16
+ ```bash
17
+ gh api --paginate 'repos/{owner}/{repo}/pulls/<number>/reviews?per_page=100' --jq '.[] | select((.body // "") | split("\n")[0] | rtrimstr("\r") == "## UI review closed") | .id' | tail -n 1
18
+ ```
19
+
20
+ ## Write the replacement
21
+
22
+ Write the full body for this pass to the file `post.md` names, with the heading, the count line, and the Passed table regenerated from what this pass drove. Patching only the marker would leave a count line and an address naming the old head.
23
+
24
+ Add one line under the count line, reading the prior head off the standing verdict's marker and the commit count off the compare:
25
+
26
+ ```bash
27
+ gh api repos/{owner}/{repo}/compare/<prior-head>...<head> --jq .total_commits
28
+ ```
29
+
30
+ ```plaintext
31
+ Re-checked at `<short-sha>`, <N> commits since the prior pass.
32
+ ```
33
+
34
+ Omit the line when the standing verdict carried no marker, since there is no prior head to count from. Run the scan `post.md` states against the file. Then record the instant just before the call, in UTC, as `<posted-at>`, since Step 9 keeps only the review-event runs created after it and the `PUT` fires one on `edited`. Replace the comment:
35
+
36
+ ```bash
37
+ date -u +%Y-%m-%dT%H:%M:%SZ
38
+ gh api -X PUT repos/{owner}/{repo}/pulls/<number>/reviews/<review-id> -F body=@<body-file>
39
+ ```
40
+
41
+ ## The marker
42
+
43
+ The body ends on `<!-- review-ui: head=<head> -->` at the full head this pass drove, with no line below it. `PUT` leaves the review's `commit.oid` pinned at the head the close was first posted against, and the marker is what `resolveUiScope`, the lift gate, and the orchestrator poll read in its place. A body missing it reads as the pinned stamp, so the lift refuses `ui-behind` and the poll reports the close as behind.
44
+
45
+ `PUT` keeps the comment's timestamp and its place in the thread, so the poll's age test reads a rewritten close as old, the same reading the code review accepts. A submitted review cannot be deleted, which is why this rewrites the standing close rather than replacing it with a fresh one.
46
+
47
+ The frames the standing verdict linked sit under their own pass stamp on the frames branch. The new body links this pass's frames, and the old images stay reachable by path.
@@ -104,12 +104,14 @@ The count line and the sections beneath it hold the same totals, and Step 10's p
104
104
 
105
105
  ## The marker
106
106
 
107
- End every body on `<!-- review-ui: head=<head> -->`, carrying the full sha Step 1 resolved. The orchestrator's poll reads it off the last non-empty line to decide whether the verdict covers the current head, and the submission stamp cannot answer that, since GitHub stamps it with whatever the head is at the moment of posting.
107
+ End every body on `<!-- review-ui: head=<head> -->`, carrying the full sha Step 1 resolved. A body that replaces a standing close by `PUT` carries it too, at the head this pass drove. The orchestrator's poll reads it off the last non-empty line to decide whether the verdict covers the current head, and the submission stamp cannot answer that, since GitHub stamps it with whatever the head is at the moment of posting.
108
108
 
109
109
  ## Posting
110
110
 
111
111
  Run `canon labels scan --body-file .canon/tmp/pr/review-ui/body-<number>-<short-sha>.md` against the body first, which applies the rule in `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md`, since nothing gates a file under `.canon/tmp/` before the review-event run does. Fix the body and scan again on a finding. Then post it as a review rather than an issue comment, since the poll reads the UI family off reviews:
112
112
 
113
+ Do not run the post below when the newest verdict from Step 1 reads `## UI review closed` at an older head and this pass closes too. That pass replaces the standing close, so read `${CLAUDE_SKILL_DIR}/references/close-out.md` instead of posting. Posting first leaves two closes, both naming a head.
114
+
113
115
  Record the instant just before the post, in UTC, as `<posted-at>`. Step 9 keeps only review-event runs created after it, since the code review posts its own review on the same head and fires a run of the same gate.
114
116
 
115
117
  ```bash
@@ -45,6 +45,7 @@ Run `canon tasks plan-answers <plan> --json` and read `launchable` off the recor
45
45
 
46
46
  - The command refuses for any other reason, or the record carries no `launchable` key: treat the row as unverified rather than clear, name what could not be read, and fall back to the human. A gate that reads nothing and proceeds is the gate not running.
47
47
  - The same read also refuses a plan still staging its batches in one file with `**Batch N**` sub-headings. That reports as an entry in `open` labeled `Batch staging`, so a staged plan reads as `launchable: false` the same way an unanswered operator call does, and the row waits on a split rather than on the operator.
48
+ - A `Fan-in` entry holds a fan-in set, per `batches.md` in `plan-feature`
48
49
  - An entry labeled `Stacked sibling` means the plan stacks on a live sibling it mostly shares files with, so the row waits on a fold into that sibling or on a `Judged apart from` constraint line, and not on the operator.
49
50
 
50
51
  Branch on `launchable` rather than on the exit code, which a shell function wrapping `canon` can flatten to zero and so read a held row as a clear one.
@@ -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
 
@@ -28,6 +28,7 @@ The draft mark fails from both ends. On 2026-08-31 four sessions re-drafted a pu
28
28
  - State the shared board as read-only for a building session and name what it does instead when a row is owed, since a task file with no row is a dropped task and placing the row is the controlling session's call
29
29
  - Owe an announcement the moment the pull request opens, carrying the number, the branch, and the task, since that transition is the one interval only the worker can observe
30
30
  - Carry a row the build turned up in the pull request announcement, and send it alone only when the session ends before a pull request opens, since the worker files no task file of its own and the channel is the row's only route
31
+ - Carry a fan-in member's branch and head commit in its done message in place of a pull request number, and end its role at the push, since a member opens no pull request and its integrator fixes what the merge surfaces
31
32
  - Owe a message before a block becomes an interactive prompt, since a session already waiting on input never reaches the tool round an inbound message drains at
32
33
  - Defer the addressee resolution to `session-relay` and keep only the operator ask and the last-rung inference, which discriminates here because a worker holds a feature branch and a controller does not, and which falls back to the roster and says so, since an operator's own launch names nobody
33
34
  - Keep refusing a dispatcher a first-class move carrying its evidence, since the halts measured so far were correct and the cost of each fell where it belonged
@@ -61,6 +61,14 @@ Announce the pull request as the ship chain's pull request step returns, carryin
61
61
  - Say the reach was unread rather than clear when the installed binary carries no such subcommand, and leave the undeclared list to the pull request
62
62
  - Carry a row this build turned up with its title, origin, and surface. Send it alone only when the session ends before a pull request opens.
63
63
 
64
+ ### A fan-in member
65
+
66
+ A member of a fan-in set opens no pull request, so its done message stands in for the announcement. `auto-ship` sends it after the push.
67
+
68
+ - Carry the branch, the head commit, and the integrator the plan names in place of a pull request number
69
+ - Carry no reach, since the integrator's announcement carries it once for the whole set
70
+ - An integrator is an ordinary worker and owes the announcement above
71
+
64
72
  ### Re-review and blocks
65
73
 
66
74
  Ask for the re-review as `review-address` Step 8 returns, carrying what was addressed and the PR's new CI state. That transition is the other moment only this session knows, and the message starts the re-review.
@@ -117,5 +125,6 @@ Never concede on the name alone or proceed as if the sets never collided. A work
117
125
  ## Lifetime
118
126
 
119
127
  - The role runs from the worktree entry to the merge of the branch it built, and nothing carries to a second row, which is a new build under a new plan
128
+ - A fan-in member's role ends at its push, since the integrator merges the branch and fixes on its own branch whatever the merge surfaces
120
129
  - A talk session outlives the merge and ends when the operator says the conversation is over
121
130
  - Treat this body as possibly older than the branch under it. A plugin skill loads from the marketplace cache, so a session changing its own skills may hold the copy from before the last update.
@@ -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
 
@@ -62,4 +62,4 @@ Two failures land specifically on where the folder sits. A workspace resolved ag
62
62
  - Creating a wiki folder a project never chose
63
63
  - What the verbs do internally, which is the CLI domain's own contract rather than this skill's, and the records backup that carries the folder off one disk
64
64
  - What a lesson renders as and how a quiz behaves once rendered, which the lesson-craft reference shapes and a rendering layer executes
65
- - Deciding when to fire. The skill is user-invoked through `disable-model-invocation`, so opening a workspace is the learner's call rather than a description match.
65
+ - Deciding when to fire. The description states `Do NOT auto-trigger`, so opening a workspace is the learner's call rather than a description match.
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: teach-workspace
3
- description: Opens and runs a learning workspace on one subject, holding a mission, resources, numbered lessons, reference pages, a glossary, and learning records that survive across sessions, proposes where a durable page from one belongs once it outgrows the workspace, and lands the confirmed pages from a branch. Use when asked to "teach me X", "open a learning workspace", "I want to learn X", "quiz me on this", "continue the lesson", "resume my workspace on X", "promote this reference page", or "fold the promoted pages in". Do NOT use to write project documentation, which belongs to the surface owning that document, and do NOT use to answer one question, which is an ordinary reply.
4
- disable-model-invocation: true
3
+ description: Opens and runs a learning workspace on one subject, holding a mission, resources, numbered lessons, reference pages, a glossary, and learning records that survive across sessions, proposes where a durable page from one belongs once it outgrows the workspace, and lands the confirmed pages from a branch. Use when asked to "teach me X", "open a learning workspace", "I want to learn X", "quiz me on this", "continue the lesson", "resume my workspace on X", "promote this reference page", or "fold the promoted pages in". Do NOT use to write project documentation, which belongs to the surface owning that document, and do NOT use to answer one question, which is an ordinary reply. Do NOT auto-trigger.
5
4
  argument-hint: <subject to learn, the topic of a workspace to resume or promote, or fold>
6
5
  metadata:
7
6
  family: answer
@@ -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