@erclx/canon 4.123.0 → 4.125.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 (104) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/auto-ship/SKILL.md +2 -4
  3. package/claude/skills/codebase-layout/REQUIREMENT.md +1 -1
  4. package/claude/skills/codebase-layout/SKILL.md +1 -1
  5. package/claude/skills/create-rule/SKILL.md +5 -2
  6. package/claude/skills/design-taste/SKILL.md +1 -1
  7. package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
  8. package/claude/skills/memory-review/SKILL.md +1 -1
  9. package/claude/skills/memory-review/references/later-phases.md +1 -1
  10. package/claude/skills/memory-review/references/receipt-format.md +1 -1
  11. package/claude/skills/role-orchestrator/REQUIREMENT.md +2 -1
  12. package/claude/skills/role-orchestrator/SKILL.md +11 -9
  13. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -1
  14. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +2 -0
  15. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +18 -3
  16. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +4 -4
  17. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +29 -0
  18. package/claude/skills/role-reviewer/REQUIREMENT.md +75 -0
  19. package/claude/skills/role-reviewer/SKILL.md +73 -0
  20. package/claude/skills/role-worker/SKILL.md +1 -1
  21. package/claude/skills/test-first/REQUIREMENT.md +1 -1
  22. package/docs/agents/counts.md +3 -3
  23. package/docs/agents/drift-surfaces.md +2 -2
  24. package/docs/agents/rule-citations.md +4 -4
  25. package/docs/agents/test-order.md +1 -1
  26. package/docs/index.md +3 -0
  27. package/docs/target-migrations.md +93 -0
  28. package/docs/target-projects.md +4 -216
  29. package/docs/target-scenarios.md +56 -0
  30. package/docs/target-sync.md +93 -0
  31. package/docs/workflow/ai-workflow.md +2 -201
  32. package/docs/workflow/index.md +2 -0
  33. package/docs/workflow/operating-model.md +28 -58
  34. package/docs/workflow/parallel-features.md +77 -0
  35. package/docs/workflow/skill-map.md +149 -0
  36. package/governance/renames.toml +34 -0
  37. package/governance/rules/{claude/560-diagrams.md → canon/660-diagrams.md} +1 -1
  38. package/governance/rules/{claude/562-session.md → canon/662-session.md} +1 -1
  39. package/governance/rules/code/000-code.md +21 -0
  40. package/governance/rules/lib/300-testing-ts.md +0 -4
  41. package/governance/rules/lib/330-testing-py.md +0 -4
  42. package/governance/rules/{claude/500-prose.md → writing/800-prose.md} +1 -1
  43. package/governance/rules/{claude/501-markdown.md → writing/801-markdown.md} +1 -1
  44. package/governance/rules/{claude/502-mermaid.md → writing/802-mermaid.md} +1 -1
  45. package/governance/stacks/base.toml +7 -6
  46. package/governance/stacks/node.toml +1 -1
  47. package/governance/stacks/python.toml +1 -1
  48. package/package.json +1 -1
  49. package/scripts/core/install-check.sh +3 -5
  50. package/src/claude/cases/workflow.ts +5 -0
  51. package/src/claude/routing.ts +1 -1
  52. package/src/commands/gov.ts +1 -1
  53. package/src/gate/measures.ts +1 -6
  54. package/src/gov/citations.ts +1 -1
  55. package/src/gov/list.ts +1 -1
  56. package/src/gov/restated.ts +2 -2
  57. package/src/init/steps.ts +1 -1
  58. package/src/labels/spelling.ts +1 -1
  59. package/src/records/prune.ts +1 -1
  60. package/src/shipped/references.ts +1 -1
  61. package/standards/rule.md +4 -6
  62. package/tooling/base/reference.md +1 -1
  63. package/tooling/claude/seeds/.claude/hooks/path-form.sh +1 -1
  64. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +1 -1
  65. package/tooling/web/reference.md +1 -1
  66. package/claude/skills/role-orchestrator/references/orchestrator-review-fallback.md +0 -18
  67. package/governance/rules/core/000-constitution.md +0 -30
  68. package/governance/rules/core/020-concurrency.md +0 -22
  69. package/governance/rules/core/030-error-handling.md +0 -31
  70. package/governance/rules/core/040-performance.md +0 -20
  71. package/governance/rules/core/050-logging.md +0 -20
  72. package/governance/rules/core/060-naming.md +0 -19
  73. package/governance/rules/snippets/600-at-references.md +0 -9
  74. /package/governance/rules/{core/025-indexes.md → canon/601-indexes.md} +0 -0
  75. /package/governance/rules/{core/035-tasks.md → canon/602-tasks.md} +0 -0
  76. /package/governance/rules/{core/045-memory.md → canon/603-memory.md} +0 -0
  77. /package/governance/rules/{core/055-scratch.md → canon/604-scratch.md} +0 -0
  78. /package/governance/rules/{core/085-worktrees.md → canon/605-worktrees.md} +0 -0
  79. /package/governance/rules/{core/087-git.md → canon/606-git.md} +0 -0
  80. /package/governance/rules/{claude/510-context.md → canon/610-context.md} +0 -0
  81. /package/governance/rules/{claude/511-indexes.md → canon/611-indexes.md} +0 -0
  82. /package/governance/rules/{claude/520-wireframes.md → canon/620-wireframes.md} +0 -0
  83. /package/governance/rules/{claude/530-requirements.md → canon/630-requirements.md} +0 -0
  84. /package/governance/rules/{claude/540-architecture.md → canon/640-architecture.md} +0 -0
  85. /package/governance/rules/{claude/545-decisions.md → canon/645-decisions.md} +0 -0
  86. /package/governance/rules/{claude/550-design.md → canon/650-design.md} +0 -0
  87. /package/governance/rules/{claude/555-tasks.md → canon/655-tasks.md} +0 -0
  88. /package/governance/rules/{claude/556-groundwork.md → canon/656-groundwork.md} +0 -0
  89. /package/governance/rules/{claude/557-intake.md → canon/657-intake.md} +0 -0
  90. /package/governance/rules/{claude/558-plan.md → canon/658-plan.md} +0 -0
  91. /package/governance/rules/{claude/559-memory.md → canon/659-memory.md} +0 -0
  92. /package/governance/rules/{claude/561-teach.md → canon/661-teach.md} +0 -0
  93. /package/governance/rules/{claude/563-ready.md → canon/663-ready.md} +0 -0
  94. /package/governance/rules/{core/005-behavior.md → claude/565-behavior.md} +0 -0
  95. /package/governance/rules/{core/015-output.md → claude/566-output.md} +0 -0
  96. /package/governance/rules/{core/070-planning.md → claude/567-planning.md} +0 -0
  97. /package/governance/rules/{core → code}/010-testing.md +0 -0
  98. /package/governance/rules/{ci → tooling}/700-ci-workflow.md +0 -0
  99. /package/governance/rules/{core/065-spelling.md → tooling/765-spelling.md} +0 -0
  100. /package/governance/rules/{core/075-dependencies.md → tooling/775-dependencies.md} +0 -0
  101. /package/governance/rules/{core/080-config-comments.md → tooling/780-config-comments.md} +0 -0
  102. /package/governance/rules/{core/090-code-comments.md → tooling/790-code-comments.md} +0 -0
  103. /package/governance/rules/{claude/580-readme.md → writing/880-readme.md} +0 -0
  104. /package/governance/rules/{claude/591-standard-authoring.md → writing/891-standard-authoring.md} +0 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "4.123.0",
4
+ "version": "4.125.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -99,8 +99,6 @@ If the diff touches UI files (JSX, TSX, Vue, Svelte, HTML, or CSS under `src/`),
99
99
 
100
100
  If `ui-checklist` produces a checklist, stop: `❌ UI requires visual verification. Checklist at .canon/tmp/handoff/ui-checklist/<slug>.md, which reaches the pull request once /git-ship runs. Verify manually, then run /git-ship.`
101
101
 
102
- The stop is the only thing that makes the operator look before `git-ship`.
103
-
104
102
  If there is nothing to verify visually and nothing shipping untested, continue.
105
103
 
106
104
  ## Step 6: review
@@ -137,7 +135,7 @@ Read origin as causation rather than authorship, so staleness this run induced i
137
135
 
138
136
  Bound the repair at one pass, the way Step 3 bounds verify. When that re-read shows the finding still standing, stop: `❌ A self-introduced finding survived one fix pass. See .canon/review/branch-<slug>.md. Fix and run /git-ship.`
139
137
 
140
- This chain owns the receipt's lifetime, which is what makes the Output block's citation resolve on a run that reaches it. The `context-fold` sweep under Step 8 reaches only reports whose branch is gone, which collects this one a branch later rather than during the run that wrote it.
138
+ This chain owns the receipt's lifetime. The `context-fold` sweep under Step 8 reaches only reports whose branch is gone, so it collects this one a branch later.
141
139
 
142
140
  ## Step 8: ship
143
141
 
@@ -145,7 +143,7 @@ Invoke `canon:git-ship`. That body owns the sequence, being the verify gate, mem
145
143
 
146
144
  One thing this chain adds. Mark the pull request as a draft as soon as `git-ship`'s pull request step returns, ahead of its CI watch, naming the number that step returned rather than one resolved by branch. Read `${CLAUDE_SKILL_DIR}/references/draft-mark.md` on reaching that point for the one command that proves the target, marks it, and reads the flag back. A `false` read stops the chain, and never re-issue the mark on it.
147
145
 
148
- `git-ship` verifies again at its own gate, which repeats this chain's Step 3 on the run where nothing stopped. That cost is deliberate, since most stop points hand the run straight back to that body, and a gate the chain skips for being redundant is a gate no resumed run ever meets.
146
+ `git-ship` verifies again at its own gate, repeating Step 3 on a clean run. Keep that repeat, since a resumed run enters at that gate.
149
147
 
150
148
  ## Output
151
149
 
@@ -30,7 +30,7 @@ Sharpening the rules does not reach the decision. Both were loaded and neither w
30
30
  - Carry a hard file-count threshold. A command folder holding sixty files of one role reads fine flat, and a number would fail it.
31
31
  - Restate the layer rule, the layer table, or the final filter `test-craft` owns
32
32
  - Mandate one layout across projects, such as a `features/` tree, over the project's own strategy
33
- - Restate the constitution's directory line. The rule points here and this body carries the depth.
33
+ - Restate the pointer in `000-code`, which loads in code stacks only. The rule points here and this body carries the depth.
34
34
 
35
35
  ## Guards
36
36
 
@@ -53,5 +53,5 @@ Read `${CLAUDE_SKILL_DIR}/references/adopted.md` only when extending this guidan
53
53
 
54
54
  - Which layer a test belongs at: `test-craft`. This skill decides where the test file sits once its layer is chosen.
55
55
  - Where a new path gets written into a plan, and the plan's shape: `plan-feature` and the plan standard
56
- - The directory principle every session loads: `000-constitution`
56
+ - The pointer here that every session of a code stack loads: `000-code`
57
57
  - Runner, suffix, and top-level test folder conventions per language: `300-testing-ts`, `330-testing-py`, `335-testing-go`, and `336-testing-php`
@@ -23,12 +23,15 @@ Resolve both from the request, and ask only for what is missing. Attach a propos
23
23
 
24
24
  Pick the subdir from the topic. It sits under `.claude/rules/project/` and names the rule's domain:
25
25
 
26
- - `core/`: global persona, testing, error handling, planning. Always-on, no `paths:`.
26
+ - `code/`: code placement, errors, naming, testing.
27
27
  - `lang/`: one programming language.
28
28
  - `framework/`: one framework.
29
29
  - `lib/`: one library or tool.
30
30
  - `ui/`: UI copy, accessibility, forms.
31
- - `claude/`: `.claude/` authoring surfaces.
31
+ - `claude/`: session conduct and `.claude/` authoring surfaces.
32
+ - `canon/`: the toolkit workflow and its record folders.
33
+ - `tooling/`: dev setup, such as CI workflows, dependencies, and config.
34
+ - `writing/`: prose and document shape.
32
35
 
33
36
  ## Step 3: pick a free number
34
37
 
@@ -44,7 +44,7 @@ Two worked examples:
44
44
 
45
45
  The read fixes the kind, which decides which rules apply, and names the layer, which decides what may vary. A round that cannot state its read has not decided what it is doing.
46
46
 
47
- Put the read to the operator through the structured question surface where it genuinely forks, per `005-behavior`. Where context settles it, declare and proceed rather than asking.
47
+ Put the read to the operator through the structured question surface where it genuinely forks, per `565-behavior`. Where context settles it, declare and proceed rather than asking.
48
48
 
49
49
  ## The layers, and the order they settle
50
50
 
@@ -50,4 +50,4 @@ Without this skill, a session holding finished files that hands them to a worker
50
50
  - `role-worker` copies the folder into a worktree and ships it. This stops once the three artifacts exist.
51
51
  - `task-board` creates a task file for a row with no ready folder. This writes the row it needs beside the folder and calls the same verbs.
52
52
  - `git-ship` ships a branch, and a ready folder never reaches one.
53
- - `standards/ready.md` owns the folder's shape and lifecycle, and this skill owns the procedure that produces it, the way `write-human` and `500-prose` split.
53
+ - `standards/ready.md` owns the folder's shape and lifecycle, and this skill owns the procedure that produces it, the way `write-human` and `800-prose` split.
@@ -72,7 +72,7 @@ An entry's `unresolved` list in the stale record names paths it cites that the t
72
72
  For each in-scope entry (see Scope), pick one action:
73
73
 
74
74
  - **Promote to an always-loaded rule**: the rule is cross-domain behavior or a design principle applied across the whole project, passing `592-claude-md.md`'s test (applies every session regardless of what is being edited). Do not author a toolkit rule file inline.
75
- - In the toolkit repo, hand off to `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md`, which own `internal/rules/core/` (this repo only, never ships) and `governance/rules/core/` (ships to every target).
75
+ - In the toolkit repo, hand off to `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md`, which own `internal/rules/` (this repo only, never ships) and the audience group under `governance/rules/` the rule serves (ships to every target that group reaches).
76
76
  - In a target project, append the rule to an existing file under `.claude/rules/project/` with `Edit`, since a project rule is project-owned and sync never overwrites it, or hand off to the `create-rule` skill when no existing file fits.
77
77
  - **Promote to a skill body**: the rule fires only when editing a specific path-scoped domain. Name the target skill.
78
78
  - **Promote to a standards file**: the rule is an authoring reference that belongs in the project's own standards folder as `<domain>.md`.
@@ -44,7 +44,7 @@ Before applying any item, check the worktree state:
44
44
 
45
45
  If the result is `MAIN`, stop and tell the user: `❌ Apply phase mutates tracked files. Run /session-worktree first.` Discuss and Challenge phases only touch `.canon/review/` scratch and run from anywhere.
46
46
 
47
- Before applying a promote to an always-loaded rule in the toolkit repo, load `internal-governance`, which owns `internal/rules/core/` and `governance/rules/core/` and is the only route that may author into either. In a target project, hand off to `create-rule` with no load, since `internal-governance` is a toolkit-internal skill under `.claude/skills/` that a target project never has. An `Edit` appending to an existing file under `.claude/rules/project/` needs no load either.
47
+ Before applying a promote to an always-loaded rule in the toolkit repo, load `internal-governance`, which owns `internal/rules/` and `governance/rules/` and is the only route that may author into either. In a target project, hand off to `create-rule` with no load, since `internal-governance` is a toolkit-internal skill under `.claude/skills/` that a target project never has. An `Edit` appending to an existing file under `.claude/rules/project/` needs no load either.
48
48
 
49
49
  Promotions are a separate concern from any feature in flight. Keep the promoted edits on their own commit. Do not fold an always-loaded-rule or skill-body change into a feature's commits, because a feature reviewer should not have to vet a change to how the agent operates.
50
50
 
@@ -61,4 +61,4 @@ Decision:
61
61
 
62
62
  ## Variation by action
63
63
 
64
- For Hand off items, the body is a pointer to the governance target instead of a rewritten rule: `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md` in the toolkit repo, or the `create-rule` skill in a target project. For a Promote to an always-loaded rule item, the H2 target names the rule file path the rule lands in, `<target>` in the template above, being an existing path under `internal/rules/core/` or `governance/rules/core/` in the toolkit repo, or under `.claude/rules/project/` in a target project, or the `create-rule` pointer when no existing file fits. For Retire and Keep items, skip the rewrite block. A batch holding fewer than 25 entries says so in the `**Batch:**` line, and a scope the user named replaces that line with `**Batch:** named by the user, <n> entries`. Every item gets a `Decision:` slot regardless of action. `Take:` is added only when a question response is needed.
64
+ For Hand off items, the body is a pointer to the governance target instead of a rewritten rule: `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md` in the toolkit repo, or the `create-rule` skill in a target project. For a Promote to an always-loaded rule item, the H2 target names the rule file path the rule lands in, `<target>` in the template above, being an existing path under `internal/rules/` or `governance/rules/` in the toolkit repo, or under `.claude/rules/project/` in a target project, or the `create-rule` pointer when no existing file fits. For Retire and Keep items, skip the rewrite block. A batch holding fewer than 25 entries says so in the `**Batch:**` line, and a scope the user named replaces that line with `**Batch:** named by the user, <n> entries`. Every item gets a `Decision:` slot regardless of action. `Take:` is added only when a question response is needed.
@@ -83,6 +83,7 @@ Dispatch parts from what it checked in three ways. The gate and the worker each
83
83
  - Carry the dispatcher's `sessionId` in the launch prompt, since a worker owed two messages has no property on the roster that resolves a controller and a name goes stale inside the window it builds in
84
84
  - Report each self-dispatch and the row it fired against loudly enough to follow, since a person no longer watches the launch step happen
85
85
  - Stop dispatching once `## Run now` is empty or every row in it reads claimed, rather than waking again to fire on a board nobody is clearing
86
+ - Dispatch a pull request's review, first pass included, to `role-reviewer` on load or on executable content, a skill body included, and keep one cross-branch pass per wave here, since a warm pass holding a wave skims what a cold one reads
86
87
 
87
88
  ## Must not
88
89
 
@@ -112,7 +113,7 @@ Dispatch parts from what it checked in three ways. The gate and the worker each
112
113
  ## Out of scope
113
114
 
114
115
  - Writing the plan itself, which `plan-feature` owns and this session runs rather than reimplements
115
- - Reviewing a worker's pull request, which `review-pr` owns
116
+ - Reviewing a worker's pull request, which `review-pr` owns, and the bounds of a reviewer dispatched to run it, which `role-reviewer` owns
116
117
  - Entering the worktree a build runs in, which the worker opens for itself whether a human launched it or this session dispatched it
117
118
  - The worker's half of the channel and the boundaries a building session holds, which `role-worker` states where that session reads them
118
119
  - The operating model this enacts, which the toolkit's own docs hold
@@ -61,9 +61,9 @@ Report the state of play in the invocation block, and open every later sweep rep
61
61
  2. Decide parallelism and merge order. Note which plans touch a shared wiring seam so their PRs merge in sequence, not at once.
62
62
  3. Verify the plan against the tree. Reading it is not enough, since a plan goes stale from whatever merged after it was written. Grep for each construct it names and count the sites against the count it claims. Check that every phase label it cites is still open. Open each file it describes rather than trusting its account of the contents. Correct the plan before handing it over.
63
63
  4. Hand off. Read `${CLAUDE_SKILL_DIR}/references/orchestrator-dispatch.md` and follow it: check the branch is unclaimed, check the row's file set against every track in flight, then dispatch a background worker with `claude --bg`. Fall back to the human-launch line it replaces when the check refuses, the sets overlap, or a stated reason serializes the row behind something already out.
64
- 5. Review the PR. When a worker opens a PR, run `review-pr` to post findings to it. This is the deep, independent pass. The worker's autoship self-review was only the green gate.
64
+ 5. Review the PR. When a worker opens a PR, its first pass runs through `review-pr` in a reviewer dispatched under `role-reviewer` when `### The review dispatch` below fires, and in this session otherwise. Either is the deep, independent pass. The worker's autoship self-review was only the green gate.
65
65
  - Learning that a PR moved is the mechanical half, so read `${CLAUDE_SKILL_DIR}/references/orchestrator-poll.md` and run the poll under the condition it states rather than checking the board by hand. That runbook holds the routing and the trigger, and a summary of it here is a second source that drifts from it.
66
- 6. Dispatch the handback. A pass posting anything owed, a finding at any severity or a testing question, tells the session holding that branch to run `review-address`, rather than waiting for a person to relay it. Re-review when the worker's own message says the address pass finished, per the channel `role-worker` states, rather than polling for an answer nothing else marks as landed. Once a pass posts `## Review closed`, lift the pull request's draft mark yourself, per Boundaries below. Then the human merges. Tell the trailing worker to rebase when its branch shares a seam with the merged one.
66
+ 6. Dispatch the handback. A pass posting anything owed, a finding at any severity or a testing question, tells the session holding that branch to run `review-address`, rather than waiting for a person to relay it. Re-review when the worker's own message says the address pass finished, per the channel `role-worker` states, rather than polling for an answer nothing else marks as landed, and send the re-review back to the dispatched reviewer when one took the first pass. Once a pass posts `## Review closed`, lift the pull request's draft mark yourself, per Boundaries below. Then the human merges. Tell the trailing worker to rebase when its branch shares a seam with the merged one.
67
67
  - Read `${CLAUDE_SKILL_DIR}/references/orchestrator-handback.md` on reaching this step. It holds how the message is addressed and worded, what to do when no live session holds the branch, and where a worker's reply goes.
68
68
 
69
69
  A plan written here is written against a tree several branches are already changing, so it names the file set of every track in flight as a constraint, one set per track, read from the Touches column of that track's row. State for each set which of the two acts it forbids, per Constraints in `${CLAUDE_SKILL_DIR}/../../standards/plan.md`. A bare path list leaves the worker guessing, which is how a plan ends up forbidding the repair of a citation the change broke.
@@ -75,7 +75,7 @@ Stamp the block with the commit this session read the tree at, which the same se
75
75
  - Run one orchestrator at a time. The board is gitignored, so a second session sees none of this one's writes: two task files land minutes apart under different labels for the same work, one session archives a task mid-sweep in the other, and each archives a plan the other had retargeted. An Owner column does not fix this, since neither session can read the other's rows.
76
76
  - Do not implement features in this session. Hand the plan to a worker.
77
77
  - Do not merge. Recommend merge or changes. The human merges.
78
- - Lift a pull request's draft mark once this session's own review of it closes, acting directly on the pull request rather than dispatching a worker to do it. `role-worker` states the mirroring refusal: a worker cannot verify who is asking or whether review actually closed, so the act stays with whoever closed the review.
78
+ - Lift a pull request's draft mark once its review closes, whether this session or a reviewer it dispatched closed it, acting directly on the pull request rather than dispatching a worker to do it. `role-worker` and `role-reviewer` state the mirroring refusal: neither can verify who is asking or whether review actually closed, so the act stays with this session.
79
79
  - Do not spawn a worker with the Agent tool. An in-process subagent shares this session's context and cannot be steered or reached independently, which breaks the property this boundary protects rather than the mechanism it names. The launch in `orchestrator-launch.md` is a separate `claude --bg` process with its own worktree and its own PR, so it preserves that property instead.
80
80
  - Dispatch a background worker only once the collision check in `orchestrator-dispatch.md` clears and the row's file set is disjoint from every track in flight. Colliding with an existing worktree or session is what the check exists to catch rather than a judgment call this session makes case by case. No fixed count binds how many tracks run at once, and Parallelism below states what does.
81
81
  - Do not edit tracked files from this session, at any size. The boundary offers no proportionality exception and nothing enforces it.
@@ -138,10 +138,12 @@ stack derives it from the worktree it runs in through `scripts/worktree-port.sh`
138
138
  Read that value rather than assigning one, and set `WORKTREE_PORT_OFFSET` by
139
139
  hand only when two worktrees derive the same offset.
140
140
 
141
- ### The review fallback
141
+ ### The review dispatch
142
142
 
143
- Two conditions move the review itself out of this session rather than binding the
144
- track count. One is a diff too large for this session to hold. The other is three
145
- or more open pull requests awaiting a first pass. Read
146
- `${CLAUDE_SKILL_DIR}/references/orchestrator-review-fallback.md` when either one
147
- trips.
143
+ Review splits by what each side can see. A reviewer dispatched under
144
+ `role-reviewer` takes the per-pull-request read, first pass included, when the
145
+ load is past what this session can hold or the change touches executable or
146
+ security surface. Below both, this session reviews in place. It keeps one
147
+ cross-branch pass per wave either way, over file sets, the board, and merge
148
+ order. Read `${CLAUDE_SKILL_DIR}/references/orchestrator-review-dispatch.md` on
149
+ every first pass for the trigger and how that pass reaches each brief.
@@ -74,7 +74,7 @@ A declared set is what a branch sets out to write rather than a bound on it, so
74
74
 
75
75
  Disjointness is necessary and not sufficient, so hold a candidate whose sets do not touch when a stated reason serializes it, and write the reason on the hold. One row creating a skill and another auditing that catalog and counting it write nothing in common, and dispatching both still leaves the audit counting a denominator that moves underneath it. Nothing verifies that a reason was written, so the rule holds only while the dispatcher applies it.
76
76
 
77
- What binds past that is review attention rather than a count, and `## Parallelism` in the skill body states it along with the cap an operator can set for a session. The one number this skill carries is the review fallback's count of three in `orchestrator-review-fallback.md`, which moves a review rather than binding a track, and this runbook carries none.
77
+ What binds past that is review attention rather than a count, and `## Parallelism` in the skill body states it along with the cap an operator can set for a session. The one number this skill carries is the review dispatch's count of three in `orchestrator-review-dispatch.md`, which moves a review rather than binding a track, and this runbook carries none.
78
78
 
79
79
  ## Pick the model
80
80
 
@@ -13,6 +13,8 @@ Read this at loop step 6, once a review pass has posted anything owed and the se
13
13
  - Name the skill for the reader to run rather than writing an invocation, which arrives as text
14
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.
15
15
 
16
+ A re-review goes back to the reviewer that took the first pass. Find a live `reviewer-<project>-<number>` for that pull request through `canon sessions list --json` by name and message it to re-run `review-pr`. When none is live, launch a fresh reviewer through the reviewer shape in `orchestrator-launch.md` or review in place, under the same trigger `orchestrator-review-dispatch.md` states, rather than messaging a name that no longer answers.
17
+
16
18
  A session is reachable when it appears in a live listing, which reads what each session registered on disk rather than probing it, and a message carries plain text and no authority. When no live session holds the branch, report the invocation for the human, naming the branch, the pull request, and the skill to run, then stop. Retrying or waiting leaves the loop believing it is open while nothing acts on it.
17
19
 
18
20
  The channel runs both ways and the return leg carries what the pull request cannot. A worker answering a posted finding by naming the plan question that had already declined it changes the outcome in the moment, where a thread comment waits on whoever reads it next. Read what a worker volunteers as part of the review rather than as an aside, and read a refusal the same way, since the corrections that landed on this session's model of the world arrived as a worker arguing back rather than complying.
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  title: Orchestrator launch runbook
3
- description: The launch every dispatch kind shares, being the build, review-address, and planning templates, the session name and controller id each carries, why the command sits at position zero, and what the brief may carry
3
+ description: The launch every dispatch kind shares, being the build, review-address, planning, and reviewer templates, the session name and controller id each carries, why the command sits at position zero, and what the brief may carry
4
4
  ---
5
5
 
6
- Run this once `orchestrator-dispatch.md` has cleared a row, or from its planning or review shape below when that runbook's checks do not bind. The model each template names comes from `## Pick the model` in that runbook.
6
+ Run this once `orchestrator-dispatch.md` has cleared a row, or from its planning, review-address, or reviewer shape below when that runbook's checks do not bind. The model each template names comes from `## Pick the model` in that runbook.
7
7
 
8
8
  ## Dispatch
9
9
 
@@ -34,7 +34,7 @@ Put a command whose skill carries `disable-model-invocation: true` at position z
34
34
 
35
35
  Never depend on the `Skill` tool route for a flagged skill. A refusal there tells the session not to replicate the workflow by other means, so a refused worker stops with a clean worktree, which is the correct outcome. The refusal is sticky inside a session, so recovery belongs to whoever writes the next prompt: re-dispatch onto the same branch with the build template above.
36
36
 
37
- The review shape and the planning shape below depend on no expansion at all. None of `role-worker`, `review-address`, `role-planner`, or `plan-feature` carries the flag, so both correctly keep their leading word regardless of the delimiter or the position it sits at.
37
+ The review-address, planning, and reviewer shapes below depend on no expansion at all. None of `role-worker`, `review-address`, `role-planner`, `plan-feature`, `role-reviewer`, or `review-pr` carries the flag, so all three correctly keep their leading word regardless of the delimiter or the position it sits at.
38
38
 
39
39
  The same rule reaches a human relay. Hand an operator one message carrying one command at position zero, since two chained blocks pasted as separate messages can land as one, where everything after the first command's name reads as that command's argument and the second never fires.
40
40
 
@@ -45,6 +45,7 @@ The prompt carries pointers and standing context, and stops there. The branch an
45
45
  - Name the addressee and what it is owed, which the two message clauses above already do.
46
46
  - Carry standing context this session holds that a cold one cannot derive, such as a constraint settled in conversation that never reached the plan.
47
47
  - Leave out anything scope-shaped. A file list, a naming convention, or a check to run belongs in the plan, where the review reads it back against the diff. Scope that arrives as prose is scope nothing verifies afterward.
48
+ - On a reviewer launch, carry the sibling pull requests in flight, the files each shares with this one, the merge order, and a constraint settled in conversation, all as facts. Leave out this session's own read of the change, the author's argument for it, and any list of what to look for, since the first two cost the independence the dispatch pays for and the third is `review-craft`'s.
48
49
 
49
50
  Report the dispatch as loudly as the human-launch line it replaces: name the branch, the model, the task, and the session name, so a person reading the transcript can follow what fired without watching it happen.
50
51
 
@@ -83,3 +84,17 @@ None of the three checks in `orchestrator-dispatch.md` binds this shape. The bra
83
84
  What a planning dispatch owes instead is the reverse reading, because the plan it produces carries a constraint per track in flight and a row planned during a wave is planned against a tree that wave is changing. `role-planner` composes the session roster with the pull request list for that read rather than reading pull requests alone, which is why the brief carries no branch list for it: the planner takes this reading itself either way.
84
85
 
85
86
  One row per dispatch. A session reused across a batch pays the context load once and ages its picture of the tree while it works, which is what puts the in-flight read on the task rather than on the batch, and one that compacts mid-batch loses the reasoning behind its earlier plans with nothing reporting it. Cap a reused session where the saving is worth it and say what the cap was.
87
+
88
+ ## Dispatch to review a pull request
89
+
90
+ `review-pr` is a single pass rather than a role, so a launch naming it alone reaches no `role-reviewer` and owes no message. Reach the role first on this launch, the way the planning shape reaches `role-planner`. `orchestrator-review-dispatch.md` decides whether a pull request takes this shape at all.
91
+
92
+ No branch and no worktree are entered here. The reviewer reads the pull request at the head `review-pr` resolves and writes the comment and its body file alone.
93
+
94
+ ```bash
95
+ claude --bg --model <model> -n "reviewer-<project>-<number>" "Run /canon:role-reviewer, then /canon:review-pr <number>. Your controller is the session whose sessionId is <dispatcher-id>. Resolve its current name from that id through canon sessions list --json, which carries sessionId per row, at the moment you send, and never resolve an addressee by name prefix. Message it when the pass posts, carrying the heading and the count line, and message it again if you stop on a question."
96
+ ```
97
+
98
+ `<number>` is the pull request's number, and the brief pins no head, since `review-pr` resolves the head itself and stamps the range it covered in its marker. `<dispatcher-id>`, `<model>`, and `<project>` resolve the same way they do above. Append the cross-branch facts after the controller clause, per `### What the brief may carry`.
99
+
100
+ Check `canon sessions list --json` for a live `reviewer-<project>-<number>` before launching, and message that session instead when one holds the pull request.
@@ -35,8 +35,8 @@ Resolve `${CLAUDE_SKILL_DIR}/scripts/poll.sh` to an absolute path and paste that
35
35
  Poll GitHub for pull request movement by running <POLL_SCRIPT>, then act on what it reports.
36
36
 
37
37
  - A release pull request, whatever state follows it: report it and stop. Its sweep carries no findings, so no pass is owed. Test this before any rule below, since a release pull request is reported OPENED like any other and would otherwise match that rule first.
38
- - MOVED or RESPONSE on a pull request I have already reviewed: run the canon:review-pr skill on it immediately, narrow pass. Re-reviews read prior..head and gain nothing from waiting.
39
- - OPENED, or a pull request with no prior review pass: run the canon:review-pr skill on it. A draft counts, since every pull request here opens as one and skipping drafts skips everything.
38
+ - MOVED or RESPONSE on a pull request I have already reviewed: run the canon:review-pr skill on it immediately, narrow pass, or message the live reviewer that took its first pass to run it. Re-reviews read prior..head and gain nothing from waiting.
39
+ - OPENED, or a pull request with no prior review pass: take its first pass where the review dispatch runbook of the canon:role-orchestrator skill places it, running the canon:review-pr skill here or launching a reviewer. A draft counts, since every pull request here opens as one and skipping drafts skips everything.
40
40
  - SEEN: report it and stop. A pass already covers that head, whether it arrived out of band or before the poll first saw the pull request, so no review follows.
41
41
  - STALLED: read the last pass and report what it carried. The pass has sat open for hours with nothing following it, so a worker mid-task is already ruled out and the dispatch either never went out or the session holding it is gone. Confirm and re-send it under the dispatch rule below, whatever grades the pass carried. Do not re-run a review to correct the heading, since a pass on an unchanged head with no response behind it stops by design.
42
42
  - CONFLICT: report it and stop. The branch owner rebases, not this session.
@@ -66,9 +66,9 @@ The five review headings the script matches are written by `review-pr` and `revi
66
66
 
67
67
  The state reaches every stalled dispatch, since one threshold governs the heading and the dispatch alike and a pass carrying anything posts the open heading. A minors-only pass therefore reports here on the same terms as a blocking one, which widens the state from what it caught while the two were split. It stays a heading test rather than a count test, so nothing here pins the summary line, which is a second string this script does not own.
68
68
 
69
- ### The count behind the review fallback
69
+ ### The count behind the review dispatch
70
70
 
71
- The report is also where the count in `orchestrator-review-fallback.md` is legible. That threshold trips on open pull requests awaiting a first pass, which is what `OPENED` and a pull request with no prior pass name here and what `SEEN` excludes, so read the count off these lines rather than off `gh pr list`, which counts a branch closed out and waiting on a merge the same as one nobody has read. It is a separate condition from the poll-start fallback above, which decides when this loop runs rather than where a review runs.
71
+ The report is also where the load count in `orchestrator-review-dispatch.md` is legible, and that count decides where a first pass runs, here or in a dispatched reviewer. That threshold trips on open pull requests awaiting a first pass, which is what `OPENED` and a pull request with no prior pass name here and what `SEEN` excludes, so read the count off these lines rather than off `gh pr list`, which counts a branch closed out and waiting on a merge the same as one nobody has read. It is a separate condition from the poll-start fallback above, which decides when this loop runs rather than where a review runs.
72
72
 
73
73
  The count used to read low, and it erred in the direction that breaks the trigger. A review's `commit.oid` is stamped with the head at submission rather than with the commit the reviewer read, so an author pushing between the diff read and the post left the pass recorded against a commit it never saw, and `SEEN` then fired on a head still awaiting its first look at that delta.
74
74
 
@@ -0,0 +1,29 @@
1
+ ---
2
+ title: Orchestrator review dispatch runbook
3
+ description: When a pull request's review leaves this session for a reviewer dispatched under role-reviewer, what stays in place below both triggers, and how the cross-branch pass per wave reaches each brief
4
+ ---
5
+
6
+ Run this on every first pass, from `### The review dispatch` in the skill body. Review splits by what each side can see rather than by pass number. A dispatched reviewer holds one pull request and reads it deeper than this session can while holding a wave, and this session holds the wave and reads what no single pull request shows.
7
+
8
+ ## The two triggers
9
+
10
+ Either one dispatches the pull request's review to a reviewer under `role-reviewer`, first pass included.
11
+
12
+ - Load. Three or more open pull requests awaiting a first pass, or one diff too large for this session to hold. Three is the operator's number, set by hand and marked as such so a measurement replaces it rather than argues with it. What counts toward it is a pull request awaiting a first pass rather than every open one, since a branch already closed out and waiting on a merge costs this session nothing. `orchestrator-poll.md` states where the count is legible.
13
+ - Content. The change touches `src/`, `scripts/`, a workflow, a hook, a skill or rule body, or a security surface, meaning anything that writes files, runs commands, pushes records, or handles transcripts. A skill or rule body counts as executable whatever its extension, since a model runs it. A pull request carrying an `## Evidence` comment with screenshots counts as well, since a reviewer holding one pull request opens every capture and a long-lived context rarely pays for that.
14
+
15
+ Nothing counts either trigger. This is prose this session applies to itself, on the same standing as the poll's own start condition, so a wave reviewed in place past both is a rule that went unread rather than a check that failed.
16
+
17
+ ## What stays in place
18
+
19
+ Below both triggers this session runs `review-pr` itself: a change to `docs/` or a context entry, a wording sweep, a release or capture pull request, and anything this session can review in one read. A cold reviewer pays a full session context per pull request, which on a small prose change costs more than the warm pass it replaces.
20
+
21
+ ## The cross-branch pass
22
+
23
+ This session keeps one pass per wave, whichever way each review went. It reads the file sets of every open pull request against each other, the board, and the merge order, and never the diffs themselves. What it looks for is what no single pull request shows: two branches regenerating one asset, several writing one context entry, a figure true only after another branch lands.
24
+
25
+ Hand what it finds to each reviewer as facts in the brief, per `### What the brief may carry` in `orchestrator-launch.md`, rather than posting a comment of its own, so each pull request keeps one live verdict. A fact surfacing after a reviewer posted goes to that reviewer as a re-review request.
26
+
27
+ ## Launching the reviewer
28
+
29
+ Take the reviewer shape in `orchestrator-launch.md`. Check `canon sessions list --json` for a live `reviewer-<project>-<number>` first and message it instead of launching a second, since two reviewers on one pull request post two verdicts. The re-review after a worker's address pass goes back the same way, per `orchestrator-handback.md`.
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: role-reviewer
3
+ description: What a dispatched reviewing session is, what it writes and reads, the acts it refuses, and the one message it owes the session that dispatched it
4
+ ---
5
+
6
+ # Role reviewer requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, review is the one stage of the loop that does not fan out.
11
+ Planning goes to `role-planner` sessions and building to `role-worker` sessions,
12
+ while every first pass still runs inside the controlling session with several
13
+ pull requests held in one context. A loop is as parallel as its narrowest stage,
14
+ so a wave of planners and workers ends in a queue at review however wide the
15
+ first two stages run.
16
+
17
+ Holding many pull requests at once is also what makes the warm pass shallow. In
18
+ a blind trial on 2026-09-25, a cold session reviewing one pull request found two
19
+ real stale-count findings on a branch the controlling session had closed clean
20
+ while holding eight, because that session read the source diff closely and
21
+ skimmed the docs the branch touched. A live trial the same day dispatched three
22
+ first passes to cold sessions, and on the one pull request both sides read, the
23
+ cold pass found a finding the warm pass missed and showed one of the warm pass's
24
+ own findings wrong.
25
+
26
+ Every one of those launches restated the reviewer's bounds by hand: write the
27
+ comment alone, no worktree, no branch, no board write, no fix, no draft lift.
28
+ Each is a property of the role rather than of the pull request, so each dispatch
29
+ reproduced them from memory, the shape `role-planner` was written to retire on
30
+ the planning side.
31
+
32
+ Three bounds matter more than the rest. A reviewer asked to fix what it found
33
+ has reviewed its own change afterwards, and nothing recovers the independent
34
+ read. A reviewer posting `## Review closed` is one step from lifting the draft
35
+ mark, and `review-pr` Step 5 reports a merge recommendation that reads as
36
+ permission when the body is silent. A reviewer with no checkout reading a file
37
+ off the main worktree reads the trunk, so every finding it files there is about
38
+ a tree the pull request never changed.
39
+
40
+ ## Must
41
+
42
+ - Assert what a reviewing session is, what it may write, and how long the role lasts, since `review-pr` carries the pass and no body carries the role that runs it when dispatched
43
+ - Bound the writes to the comment `review-pr` posts and the body file it names, and name the tracked file, branch, worktree, board, and commit as outside them
44
+ - Send every file read a finding rests on to `git show <head>:<path>` at the head `review-pr` resolved, since the main worktree holds the trunk
45
+ - Name what the pass reads, the diff, the plan, the task's outcomes, the rules, and `review-craft`, and what it leaves, the author's argument and the worker's session, since the independence is what the trials measured
46
+ - Read the cross-branch facts a launch carries as inputs rather than findings, since the controller's wave-level read reaches this session only through the brief
47
+ - Refuse the fix, the draft lift, and the merge, whoever asks, and name who owns each, since each has a sender who will ask
48
+ - Owe one announcement when the pass posts, carrying the number, the heading, and `review-pr` Step 5's count line, since the controller holds the channel to the worker and dispatches `review-address` off it
49
+ - 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
50
+ - Keep a re-review with the reviewer that took the first pass while it is live, so the second pass keeps the read the first paid for
51
+ - Defer the addressee resolution to `session-relay` and keep only the last-rung inference, which discriminates as it does for a planner because a reviewer holds no feature branch either
52
+
53
+ ## Must not
54
+
55
+ - Restate `review-pr`'s steps, threshold, headings, or marker, since the controller's poll routes on them and a second copy is a second source
56
+ - Restate `review-craft`'s criteria, which apply to every reviewer alike rather than varying by role
57
+ - Restate a boundary `role-orchestrator`, `role-worker`, or `role-planner` states about itself
58
+ - Report progress through the channel, which rebuilds on the sender's side the poll the announcement exists to retire
59
+ - Post outside `review-pr`, since every reader of the review headings breaks at once when one pass invents its own
60
+ - Be a skill nothing invokes but its author typing the name. `orchestrator-launch.md` names it on the reviewer launch the way it names `role-planner` on a planning one, so a stretch where only a typed invocation reaches it is the signal that the role never took.
61
+
62
+ ## Guards
63
+
64
+ - Plan or task does not resolve from where the session stands: report it unreadable and name the main-root path, rather than reviewing as though the branch had no plan
65
+ - Asked by anyone to fix, lift the draft, or merge: refuse, name who owns the act, and file the defect in the comment when the ask was a fix
66
+ - Launch named no controlling session and an operator is present: ask which row to address rather than inferring
67
+ - Launch named no controlling session and nobody is present: infer from the sessions holding no feature branch, report the candidates when more than one comes back, and say the addressee was inferred
68
+
69
+ ## Out of scope
70
+
71
+ - The pass itself, its scoping, its body, and its post, which `review-pr` owns
72
+ - What a review looks for and how much evidence a finding needs, which `review-craft` owns
73
+ - The cross-branch pass per wave and the decision of which pull requests take a dispatched reviewer, which `role-orchestrator` holds
74
+ - Answering the review, which `review-address` owns in the worker's session
75
+ - The mechanical addressee resolution, which `session-relay` owns
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: role-reviewer
3
+ description: Asserts the reviewer role for a session dispatched to review one pull request another session built, holding what it may write, what it reads and never reads, the one message it owes on posting, and the acts it refuses. Use when asked to "be the reviewer", "you are a reviewer session", at the start of a dispatched or hand-launched review of one pull request, or when a reviewing session needs to know what it may not write or who it answers to. Do NOT use to run the pass itself, which is `review-pr`, to review local changes, which is `review-branch`, or to make the cross-branch call across a wave.
4
+ ---
5
+
6
+ # Role reviewer
7
+
8
+ This session reads one pull request it did not write and posts a verdict on it.
9
+ It runs `review-pr` against the change, posts the comment that skill composes,
10
+ and hands back the count.
11
+
12
+ It does not fix what it finds, it does not decide what merges before what, and
13
+ it does not merge. Those belong to the worker holding the branch, to the
14
+ controlling session, and to the human.
15
+
16
+ This body states the role, the reads, the boundaries, and the channel, and it
17
+ starts no step of its own. `review-pr` owns the pass, its threshold, its
18
+ headings, and its marker, and `review-craft` owns what a pass looks for, so read
19
+ both from the skills that own them and reach them from the launch rather than
20
+ from here.
21
+
22
+ ## Where the session stands
23
+
24
+ - Write the review comment through `review-pr` and the body file it names under `.canon/tmp/pr/review/`, and nothing else. A tracked file, a branch, a worktree, a board file, and a commit all sit outside what this session may touch.
25
+ - Never enter a worktree and never check the branch out. The pass reads a change, and a checkout is the first step toward editing it.
26
+ - Read a file a finding rests on at the head `review-pr` resolved, through `git show <head>:<path>`. The main worktree holds the trunk, so a finding read off its copy is about a tree the pull request never changed, and it reads as confidently as one that is right.
27
+ - Resolve `.canon/plans/`, `.canon/tasks/`, and `.canon/tmp/` at the main worktree root, and send the body file as a heredoc, the main-root route `session-worktree` states. Report a plan or task that fails to resolve there as unreadable, naming the path, rather than reviewing as though the branch had none.
28
+ - Review the pull request the launch named. Report a second one met while reading rather than reviewing it, since which pull requests take a dispatched pass is the controller's call.
29
+
30
+ ## What to read and what to leave
31
+
32
+ - Read the diff, the plan the branch built under, the task's `## Outcomes`, the rules the diff's paths load, and `review-craft`. That is the artifact and the contract the change answers to.
33
+ - Read the pull request's `## For the reviewer` bullets and its `## Testing` section, which `review-pr` already bounds its read to, and stop there.
34
+ - Leave the author's Summary and Technical Context unread while judging, and never open the worker's session or its transcript. Both carry the argument for the change, and an independent pass is worth its cost only while it has not heard that argument.
35
+ - Treat the cross-branch facts the launch carries as facts rather than findings. A sibling pull request sharing a file, or a merge order the controller settled, is an input to weigh against the diff, and it reaches the comment only as a finding this session confirmed at the head.
36
+
37
+ ## The board is read-only
38
+
39
+ - Never write `.canon/tasks/priority.md`, `.canon/tasks/backlog.md`, a task file, or a plan. Each is gitignored, so an overwrite drops content no history holds.
40
+ - Leave the cross-feature call alone. Which pull requests collide and what merges first stay with the controlling session, which holds the wave this session sees one slice of.
41
+
42
+ ## The channel
43
+
44
+ The controlling session cannot watch this pass, so two messages are owed and
45
+ nothing else.
46
+
47
+ - Announce the pass as `review-pr` Step 5 returns, carrying the pull request number, the heading the comment took, and that step's count line verbatim. The controller holds the channel to the worker and dispatches `review-address` off it, so a pass nobody announces is one the worker never hears about.
48
+ - Send a block out as a message before it becomes an interactive prompt.
49
+ - Send nothing on progress, and send no recommendation to merge as an instruction. `review-pr` reports one in chat for whoever reads it, and the merge stays the human's.
50
+
51
+ Address the session the launch named, which it names as a `sessionId` rather
52
+ than a name. `canon:session-relay` turns that id into an address and carries the
53
+ send, so invoke it rather than resolving a name here.
54
+
55
+ One rung stays here, because it is the rung that differs between roles. A
56
+ reviewer holds no feature branch, so inferring an addressee from the sessions
57
+ holding none returns every planner and reviewer beside the controller. Report
58
+ the candidates and stop where more than one comes back, send to the single row
59
+ where exactly one does, and say the addressee was inferred.
60
+
61
+ ## Refusing is part of the job
62
+
63
+ - Refuse to fix the defect this pass found, whoever asks. File it in the comment for the worker holding the branch instead. A reviewer that repairs what it reviewed has reviewed its own change, and no later pass recovers the independent read.
64
+ - Refuse to lift the pull request's draft mark. Posting `## Review closed` closes the review and leaves the mark alone, since the controlling session lifts it on this session's message and a reviewer cannot tell a real request from one that only claims to come from there.
65
+ - Refuse to merge, and refuse to approve through the review API in place of the comment `review-pr` posts, since the poll routes on that comment's heading and an approval carries none.
66
+ - Carry the evidence with a refusal. Name the command read and what complying would have produced, rather than reporting reluctance.
67
+ - Withdraw a finding the worker's reply shows wrong, the way `review-pr` states, rather than defending it. A withdrawal naming its cause is part of a pass, not a retreat from one.
68
+
69
+ ## Lifetime
70
+
71
+ - The role runs from the launch to the merge of the pull request it reviewed, and covers every pass on that pull request. A re-review comes back to this session while it is live, which keeps the read the first pass paid for.
72
+ - A session handed a second pull request is a new reviewer under a new launch, since the first one's reading of the change is exactly what the second one must not carry.
73
+ - Treat this body as possibly older than the branch under review. A plugin skill loads from the marketplace cache rather than from the working tree.
@@ -26,7 +26,7 @@ running and re-invoking it would restart the build.
26
26
  - Report a plan that fails to resolve as unreadable from here, naming the main-root path. Reporting the task as having no plan is true where this session stands and wrong about the world, and a reader with no second tree to check cannot separate the two.
27
27
  - Build the plan the launch named. Do not write a second one when the path fails to resolve, since a row that cites a plan already has one and drafting another produces two plans for one row.
28
28
  - Copy a ready folder's files verbatim to the paths its plan's constraints name, when the plan's `**Constraints:**` block names one, and edit only what the gate or the overview's own list requires. A rewrite discards the text the handoff exists to carry.
29
- - Report a draft flag reading ready once, and leave it cleared. The ship chain marks the pull request a draft and no step anywhere un-marks, so a flag reading ready afterwards was lifted by the operator or by the controlling session that closed the review, each acting directly on the pull request, which GitHub requires before a merge. Re-drafting fights them, and no surface in the tree states a reason to.
29
+ - Report a draft flag reading ready once, and leave it cleared. The ship chain marks the pull request a draft and no step anywhere un-marks, so a flag reading ready afterwards was lifted by the operator or by the controlling session acting on a closed review, each acting directly on the pull request, which GitHub requires before a merge. Re-drafting fights them, and no surface in the tree states a reason to.
30
30
  - Refuse an instruction to lift the mark yourself, whoever sends it. This session cannot verify who is asking or whether review actually closed, so the refusal holds regardless of the sender's claimed authority.
31
31
  - Rewrite a plan question's `- Suggested:` line as `overridden at execution to <pick>,` plus the measurement when this build decides against an unanswered one, leaving the `- Answer:` slot blank, and put the same deviation in one line under the open task's `## Findings`, per `${CLAUDE_SKILL_DIR}/../../standards/plan.md`.
32
32
 
@@ -32,5 +32,5 @@ Without this skill, a session implementing a planned change writes the test afte
32
32
 
33
33
  - Choosing the layer a test belongs at and judging whether it is a good test: `canon:test-craft`, loaded at step 1 rather than restated here
34
34
  - Finding the cause of an unexplained failure: `canon:systematic-debugging`
35
- - Confirming visual output after a change lands: `070-planning.md` states the order and `ui-checklist` writes the list to confirm against
35
+ - Confirming visual output after a change lands: `567-planning.md` states the order and `ui-checklist` writes the list to confirm against
36
36
  - The mechanical audit of whether an implementation reached history ahead of its test: `canon gov test-order`, invoked from `auto-ship`
@@ -37,7 +37,7 @@ The set is closed rather than derived, so widening it is a deliberate change to
37
37
 
38
38
  A sentence has to carry an assertion verb (`loads`, `ships`, `carries`, `holds`, `counts`, `totals`, `documents`, `declares`, `installs`, `lists`, `contains`, `comprises`, `authors`), the quantifier `all`, or an article (`the`, `a`, `an`) immediately ahead of the number, which is immediately ahead of the catalog noun, with one optional qualifying word between the number and the noun (`sixty-one shipped skills`). The number reads either as digits or as a spelled-out cardinal through ninety-nine, since this corpus states a catalog size in words as often as in digits.
39
39
 
40
- Nothing stands between the trigger and the number, which is the rule an author writing a new count has to know. It separates `the toolkit authors 81 rules` and `took all 81 rules`, both read, from `a domain of 55 skills` and `denominator of sixty-one shipped skills`, neither of them read. A figure meant as a past state takes a date in the same sentence instead, which reads the sentence past whatever its shape.
40
+ Nothing stands between the trigger and the number, which is the rule an author writing a new count has to know. It separates `the toolkit authors 74 rules` and `took all 74 rules`, both read, from `a domain of 55 skills` and `denominator of sixty-one shipped skills`, neither of them read. A figure meant as a past state takes a date in the same sentence instead, which reads the sentence past whatever its shape.
41
41
 
42
42
  ## How the trigger set grew
43
43
 
@@ -45,7 +45,7 @@ The verb gate is not the first design tried. A bare number next to a catalog nou
45
45
 
46
46
  The article half was added after a first review of the shipped design found a live miss: the development domain's gating-stages entry stated a stale audit total while the tree held 20, and the verb gate never reached it, since that sentence puts its verb after the noun rather than ahead of the number. Widening the trigger set to admit an article ahead of the number closed that gap.
47
47
 
48
- The quantifier `all` and the verb `authors` joined on the same evidence, two more live misses failing only the trigger test. Allowing words to stand between the trigger and the number was the other candidate, measured and declined: at widths of one, two, and three it reached 77, 104, and 132 sentences against a baseline of 65, missed `took all 81 rules` at every width, and reached `the toolkit authors 81 rules` only at a width admitting the indirect-noun shape below. The two words reach both at 75 sentences and no false positive.
48
+ The quantifier `all` and the verb `authors` joined on the same evidence, two more live misses failing only the trigger test. Allowing words to stand between the trigger and the number was the other candidate, measured and declined: at widths of one, two, and three it reached 77, 104, and 132 sentences against a baseline of 65, missed `took all 74 rules` at every width, and reached `the toolkit authors 74 rules` only at a width admitting the indirect-noun shape below. The two words reach both at 75 sentences and no false positive.
49
49
 
50
50
  ## The plausibility filter
51
51
 
@@ -59,7 +59,7 @@ The article gate is looser than the verb list and carries a real cost. Re-runnin
59
59
 
60
60
  A delta phrased as a transition (`from fourteen to fifteen`), a fraction (`thirteen of sixteen`), and a total reached through an indirect noun (`denominator of sixty-one shipped skills`) are all catalog-size claims this corpus carries, and none matches the trigger shape this reads. Each stays a known gap. The false-positive rate that gated closing them has a measurement behind it now, and what it showed is that the widening these three need is the one that costs a false positive rather than the one that does not.
61
61
 
62
- A second figure in a sentence whose first figure already matched is a fourth gap and a structural one, since one match is taken per catalog per sentence. `authors 81 rules under governance/rules/ and consumes 62 into .claude/rules/` is read for its 81 alone, which is correct here because the two figures name different populations, and a sentence stating one catalog twice would go unread the same way.
62
+ A second figure in a sentence whose first figure already matched is a fourth gap and a structural one, since one match is taken per catalog per sentence. `authors 74 rules under governance/rules/ and consumes 62 into .claude/rules/` is read for its 74 alone, which is correct here because the two figures name different populations, and a sentence stating one catalog twice would go unread the same way.
63
63
 
64
64
  A calendar date (`2026-08-21`) or a backticked commit reference in the same sentence reads the whole sentence past, since that is how this corpus already marks a figure as a historical record rather than a live claim. `canon/ARCHITECTURE.md` and the context entries carry a figure this way deliberately, and every one of them stays correct forever.
65
65
 
@@ -114,8 +114,8 @@ that is not a full clone as unmeasured rather than clean. This gap does not
114
114
  reach the chain-based read above, since it consults no anchor at all.
115
115
 
116
116
  In the fallback, entitlement is filtered, because a stack does not receive
117
- every rule. The base stack takes the `core` and `claude` folders whole and
118
- every other stack extends it with individually named rules, so an unfiltered
117
+ every rule. The base stack takes four folders whole, the code stacks add `code`, and
118
+ every other stack extends them with individually named rules, so an unfiltered
119
119
  list would tell a base consumer about rules it can never receive and train the
120
120
  reader to skip the section.
121
121
 
@@ -24,7 +24,7 @@ Under `--json` the record holds stdout alone and the frame still renders on stde
24
24
 
25
25
  ## What the drift check cannot see
26
26
 
27
- `bun run check` asserts that an authored rule and its consumed copy agree, and a dead citation passes that assertion because both copies carry it. `governance/rules/claude/561-teach.md` told its reader to open a `references/glossary.md` inside the teach skill that had never existed at that path. The rule shipped, survived a release, and fired during a real teach run before anyone noticed.
27
+ `bun run check` asserts that an authored rule and its consumed copy agree, and a dead citation passes that assertion because both copies carry it. `governance/rules/canon/661-teach.md` told its reader to open a `references/glossary.md` inside the teach skill that had never existed at that path. The rule shipped, survived a release, and fired during a real teach run before anyone noticed.
28
28
 
29
29
  Two files agreeing is what a drift check measures. Whether either one is right about the tree is a different question, and this is the stage that asks it.
30
30
 
@@ -49,7 +49,7 @@ A `path` is anchored on the whole backticked span rather than on a trailing patt
49
49
  A rule body is full of backticked spans carrying a slash, and almost none of them names a file in this tree. The stage declines four shapes outright.
50
50
 
51
51
  - A placeholder or glob segment describes a shape rather than naming a file: `canon/context/<domain>.md`, `standards/<name>.md`, `app/**/route.ts`, `${CLAUDE_SKILL_DIR}/../../standards/<name>.md`.
52
- - A bare filename names a convention: `route.ts`, `manifest.toml`, `components.json`, `playwright.config.ts`. A bare name is read as a citation only when it matches a rule filename, which is how `562-session.md` points at `555-tasks.md`.
52
+ - A bare filename names a convention: `route.ts`, `manifest.toml`, `components.json`, `playwright.config.ts`. A bare name is read as a citation only when it matches a rule filename, which is how `662-session.md` points at `655-tasks.md`.
53
53
  - A span carrying no file extension is a folder or a module specifier: `src/pages/`, `next/font`, `try/except`, `react-hooks/set-state-in-effect`, `oven-sh/setup-bun@v2`, `@/lib/utils`.
54
54
  - A fenced block displays a path rather than pointing at one.
55
55
 
@@ -59,11 +59,11 @@ The extension test is what carries most of the separation, and it costs one real
59
59
 
60
60
  Two classes resolve to nothing and are right to. Both are reported by name rather than dropped, so a reader can see what the verdict declined to judge.
61
61
 
62
- **Governed.** A rule spelling a path in its own frontmatter `paths:` is naming an artifact a target holds rather than a file here. `governance/rules/claude/560-diagrams.md` declares `.claude/DIAGRAMS.md` and then tells its reader to convert one an older install left behind, so the file is correctly absent from this tree and correctly named in the rule.
62
+ **Governed.** A rule spelling a path in its own frontmatter `paths:` is naming an artifact a target holds rather than a file here. `governance/rules/canon/660-diagrams.md` declares `.claude/DIAGRAMS.md` and then tells its reader to convert one an older install left behind, so the file is correctly absent from this tree and correctly named in the rule.
63
63
 
64
64
  Only an exact declaration exempts, never a glob match against one. A glob declares a shape, so a body path sitting inside it is still a citation and a stale one is still a defect. A rule scoped at `docs/**` citing a `docs/agents/renamed.md` that moved is exactly the class this stage exists to catch, and matching the glob would excuse it.
65
65
 
66
- **Ignored.** A path git ignores is session scratch no clone is expected to hold. `governance/rules/claude/555-tasks.md` cites `.canon/tasks/index.md`, which is real in a live project and absent from a fresh clone and from every linked worktree. Resolving against the filesystem alone would make the verdict depend on which tree the stage ran in, so the unresolved paths go to one batched `git check-ignore` and an ignored one is excused. A read git cannot answer refuses rather than reporting those paths dead.
66
+ **Ignored.** A path git ignores is session scratch no clone is expected to hold. `governance/rules/canon/655-tasks.md` cites `.canon/tasks/index.md`, which is real in a live project and absent from a fresh clone and from every linked worktree. Resolving against the filesystem alone would make the verdict depend on which tree the stage ran in, so the unresolved paths go to one batched `git check-ignore` and an ignored one is excused. A read git cannot answer refuses rather than reporting those paths dead.
67
67
 
68
68
  ## Why the glob half reads one corpus
69
69