@erclx/canon 4.119.0 → 4.121.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 (112) hide show
  1. package/README.md +2 -2
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/auto-ship/SKILL.md +29 -115
  4. package/claude/skills/auto-ship/references/draft-mark.md +30 -0
  5. package/claude/skills/auto-ship/references/failure-recovery.md +27 -0
  6. package/claude/skills/auto-ship/references/tier-failures.md +26 -0
  7. package/claude/skills/auto-ship/references/verb-absent.md +37 -0
  8. package/claude/skills/auto-ship/references/worktree-name.md +22 -0
  9. package/claude/skills/bash-script/SKILL.md +1 -1
  10. package/claude/skills/bash-script/references/patterns.md +0 -99
  11. package/claude/skills/bash-script/references/prompts.md +100 -0
  12. package/claude/skills/create-skill/SKILL.md +3 -2
  13. package/claude/skills/create-snippet/SKILL.md +1 -1
  14. package/claude/skills/create-standard/SKILL.md +1 -1
  15. package/claude/skills/design-extract/SKILL.md +1 -1
  16. package/claude/skills/design-taste/REQUIREMENT.md +2 -2
  17. package/claude/skills/docs-sync/SKILL.md +1 -1
  18. package/claude/skills/draft-context/SKILL.md +1 -1
  19. package/claude/skills/draft-docs/SKILL.md +1 -1
  20. package/claude/skills/draft-figure/SKILL.md +1 -1
  21. package/claude/skills/draft-readme/SKILL.md +1 -1
  22. package/claude/skills/draft-wiki/SKILL.md +1 -1
  23. package/claude/skills/draft-wireframes/SKILL.md +1 -1
  24. package/claude/skills/git-issue/SKILL.md +1 -1
  25. package/claude/skills/git-issue/references/issue.md +1 -1
  26. package/claude/skills/git-pr/REQUIREMENT.md +1 -0
  27. package/claude/skills/git-pr/SKILL.md +35 -5
  28. package/claude/skills/git-stage/SKILL.md +1 -1
  29. package/claude/skills/memory-capture/SKILL.md +1 -1
  30. package/claude/skills/restate-plainly/REQUIREMENT.md +1 -1
  31. package/claude/skills/review-address/SKILL.md +4 -3
  32. package/claude/skills/review-branch/REQUIREMENT.md +1 -0
  33. package/claude/skills/review-branch/SKILL.md +5 -6
  34. package/claude/skills/review-craft/REQUIREMENT.md +43 -0
  35. package/claude/skills/review-craft/SKILL.md +50 -0
  36. package/claude/skills/review-craft/references/security.md +21 -0
  37. package/claude/skills/review-craft/references/ui.md +72 -0
  38. package/claude/skills/review-pr/REQUIREMENT.md +1 -0
  39. package/claude/skills/review-pr/SKILL.md +14 -243
  40. package/claude/skills/review-pr/references/close-out.md +30 -0
  41. package/claude/skills/review-pr/references/post.md +145 -0
  42. package/claude/skills/review-pr/references/scope.md +82 -0
  43. package/claude/skills/role-orchestrator/REQUIREMENT.md +9 -16
  44. package/claude/skills/role-orchestrator/SKILL.md +9 -153
  45. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +4 -193
  46. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +22 -0
  47. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +85 -0
  48. package/claude/skills/role-orchestrator/references/orchestrator-output.md +61 -0
  49. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
  50. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +1 -1
  51. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +56 -0
  52. package/claude/skills/role-orchestrator/references/orchestrator-review-fallback.md +18 -0
  53. package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +1 -1
  54. package/claude/skills/role-planner/REQUIREMENT.md +2 -2
  55. package/claude/skills/standards-audit/SKILL.md +1 -1
  56. package/claude/skills/write-human/REQUIREMENT.md +7 -4
  57. package/claude/skills/write-human/SKILL.md +4 -2
  58. package/claude/skills/write-human/references/machine-tells.md +1 -1
  59. package/claude/skills/write-human/references/source-material.md +5 -2
  60. package/docs/agents/audits.md +1 -1
  61. package/docs/agents/commands.md +3 -2
  62. package/docs/agents/counts.md +1 -1
  63. package/docs/agents/index.md +8 -3
  64. package/docs/agents/markdown-audit.md +13 -19
  65. package/docs/agents/pr-evidence.md +19 -3
  66. package/docs/agents/pr-local.md +105 -0
  67. package/docs/agents/pr-preview.md +2 -2
  68. package/docs/agents/records-push.md +61 -0
  69. package/docs/agents/records-validate.md +73 -0
  70. package/docs/agents/records.md +3 -119
  71. package/docs/agents/sessions-transfer.md +91 -0
  72. package/docs/agents/sessions.md +3 -82
  73. package/docs/agents/skills-audit.md +2 -2
  74. package/docs/agents/teach-authoring.md +112 -0
  75. package/docs/agents/teach.md +3 -103
  76. package/docs/target-projects.md +2 -2
  77. package/docs/workflow/ai-workflow.md +2 -1
  78. package/docs/workflow/visual-design-workflow.md +1 -1
  79. package/governance/rules/claude/500-prose.md +1 -1
  80. package/governance/rules/claude/501-markdown.md +3 -3
  81. package/governance/rules/claude/570-skill.md +2 -1
  82. package/package.json +1 -1
  83. package/scripts/core/check-ignore-parity.sh +1 -1
  84. package/scripts/lib/sandbox-git.sh +1 -1
  85. package/src/autoship/paths.ts +3 -2
  86. package/src/claude/cases/workflow.ts +5 -0
  87. package/src/commands/audits.ts +2 -2
  88. package/src/commands/markdown.ts +7 -9
  89. package/src/commands/pr.ts +331 -2
  90. package/src/context/classify/extract.ts +2 -2
  91. package/src/counts/scan.ts +1 -1
  92. package/src/design/tokens.ts +1 -1
  93. package/src/gate/measures.ts +4 -4
  94. package/src/gate/stages.ts +2 -3
  95. package/src/markdown/bans.ts +11 -68
  96. package/src/markdown/gate.ts +1 -1
  97. package/src/markdown/scan.ts +7 -88
  98. package/src/pr/evidence.ts +42 -2
  99. package/src/pr/local.ts +125 -0
  100. package/standards/docs.md +1 -1
  101. package/standards/index.md +2 -1
  102. package/standards/markdown.md +5 -7
  103. package/standards/pr.md +1 -1
  104. package/standards/publish.md +1 -1
  105. package/standards/readme.md +2 -2
  106. package/standards/skill-requirement.md +72 -0
  107. package/standards/skill.md +34 -152
  108. package/tooling/astro/reference.md +1 -1
  109. package/tooling/base/configs/.github/workflows/pr-local-preview.yml +32 -0
  110. package/tooling/base/configs/scripts/verify.sh +2 -2
  111. package/tooling/base/seeds/canon/context/development.md +1 -1
  112. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +5 -6
@@ -0,0 +1,82 @@
1
+ ---
2
+ title: Scope the review read
3
+ description: Step 2 of review-pr, which finds the commit and verdict the prior pass left, stops on an unchanged head with no new reply, and scopes the read to the whole change or to the commits added since
4
+ ---
5
+
6
+ # Scope the read
7
+
8
+ Step 2 of `review-pr`. The session reads this file on reaching that step, ahead of Step 3, so the unchanged-head stop below still fires before any review runs.
9
+
10
+ Find the commit the last pass covered and the verdict it posted:
11
+
12
+ ```bash
13
+ canon pr review-state <number> --json
14
+ ```
15
+
16
+ The fields are `<prior-oid>` off `commit`, `<prior-heading>` off `heading`, and `<prior-at>` off `readAt // submittedAt`. The commit scopes the read below, the heading feeds the repeat guard at the end of this step, and the instant scopes the reply query further down, so one call answers all three rather than three reads of the same review. Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
17
+
18
+ `source` says which stamp answered. `marker` is the pass's own read-time record and is the authority. `fallback` is a pass posted before this mechanism shipped, so its commit is whatever the head was when GitHub recorded the review rather than what that session read, and a push inside its compose window is invisible. `none` is a thread carrying no pass at all.
19
+
20
+ Do not read `commit.oid` or `submittedAt` off `gh pr view --json reviews` here. Both are stamped at submission, so a push landing between a pass's read and its post moves them onto a commit that pass never saw, and this step then scopes the delta past it and reports it covered.
21
+
22
+ A target whose CLI predates the verb meets a missing subcommand rather than a record. Fall back there to the jq below, which reads the stamps and carries the defect above, and say the fallback answered so a reader can tell a marker read from a stamped one:
23
+
24
+ ```bash
25
+ gh pr view <number> --json reviews --jq '[.reviews[] | select(.body // "" | split("\n")[0] | rtrimstr("\r") | . == "## Review" or . == "## Review closed")] | last | select(. != null) | ((.commit.oid // "") + "\t" + (.body | split("\n")[0] | rtrimstr("\r")) + "\t" + (.submittedAt // ""))'
26
+ ```
27
+
28
+ The three fields are `<prior-oid>`, `<prior-heading>`, and `<prior-at>`. Keep the `select(. != null)` guard, since the string concatenation aborts jq on the null an empty selection returns, and an aborted command reaches the session as an error rather than as the empty result the first-pass branch reads.
29
+
30
+ Match the first line for equality against the two headings this skill posts. A prefix test also matches `## Review response` and any heading merely starting with those words, which would scope the pass to whatever commit that comment carried. The `\r` trim covers a body composed in the GitHub web editor, which stores CRLF.
31
+
32
+ A `source` of `none`, or an empty result from the fallback, is a first pass. Read the whole change:
33
+
34
+ ```bash
35
+ gh pr diff <number>
36
+ ```
37
+
38
+ ```bash
39
+ gh pr diff <number> --name-only
40
+ ```
41
+
42
+ A commit is a later pass. Fetch the pull request head so both commits are local:
43
+
44
+ ```bash
45
+ git fetch -q origin pull/<number>/head
46
+ ```
47
+
48
+ A failed fetch stops the skill: `❌ Could not fetch the PR head. Retry once the remote is reachable.` Do not fall through to the full pass. A fetch failure and a rebase both leave the prior commit unreachable, and the fallback below states a rebase as fact on the pull request, so conflating the two publishes a claim the skill never checked.
49
+
50
+ Then test that the prior commit still reaches the head:
51
+
52
+ ```bash
53
+ git merge-base --is-ancestor <prior-oid> <headRefOid>
54
+ ```
55
+
56
+ On exit zero, review `<prior-oid>..<headRefOid>` and nothing else. `git diff` and `git log --oneline` over that range are the whole read, because the first pass already covered everything behind it. A non-zero exit means the branch was rebased or force-pushed, so the delta is undefined rather than empty. Fall back to the full pass above and say so in the body.
57
+
58
+ A commit is its own ancestor, so an unchanged head passes that test too, with an empty range. When `<prior-oid>` equals `<headRefOid>`, decide whether this pass has anything to add before reading anything else, since the empty range itself cannot answer that:
59
+
60
+ ```bash
61
+ gh pr view <number> --json comments --jq '[.comments[] | select(.body // "" | split("\n")[0] | rtrimstr("\r") | . == "## Review response" or . == "## Rebase" or . == "## Post-review findings") | select(.createdAt > "<prior-at>")] | last | .url // empty | split("-") | last'
62
+ ```
63
+
64
+ `<prior-at>` is the instant Step 2 resolved above, which is the prior pass's `readAt` where it wrote one. Reading `submittedAt` off the thread here instead is the same submission-time defect on the time axis: a reply posted inside that pass's compose window sorts before the stamp and reads as already answered, when in fact the pass had stopped reading before it landed.
65
+
66
+ Scope the replies to those newer than the prior pass, never to every reply the thread carries. A pass answering the newest reply and a pass answering an older one derive the same third segment (Step 4), so an unscoped read hands a re-run after a close-out the name its own prior pass already wrote. That is the collision this case exists to prevent, reached without a rebase or an error.
67
+
68
+ Read the number off `.url`. The `id` field carries a GraphQL node id, which the thread never displays. Keep the `// empty` guard, since `split` aborts jq on the null an empty selection returns, and an aborted command reaches the session as an error rather than as the empty result the stop below reads.
69
+
70
+ An empty result means no reply arrived since the prior pass, so the head is unchanged and this pass has nothing new to add. Stop here, before Step 3 or Step 4 run: `❌ The head is unchanged since the prior pass on <short-sha>. Nothing new to review.` This is the earliest point every path crosses, which is why the check sits here rather than inside Step 4's filename derivation. A path that decides there is nothing to add never reaches a step reached only when composing a body, so a stop written there is a stop a shortcut path can route around.
71
+
72
+ A non-empty result carries the comment id Step 4 needs for the third filename segment. A `## Review response` or a `## Rebase` reply answers a finding already argued or reports a stale branch resolved without one, so read it for what the worker changed or accepted, treat an accepted finding as closed rather than restating it, and skip the diff and file reads below. This is the entire read on a repeated head for either heading, since the empty range above has nothing in it to say whether a prior finding landed and neither reply needs anything more to answer that.
73
+
74
+ A `## Post-review findings` reply carries no argued finding behind it, since it asserts a new defect rather than answering one, and this pass is its first independent reader. Restating it as a finding without opening anything is repeating the worker's claim rather than checking it. Read the file the comment names at `<headRefOid>`, the same `git show <headRefOid>:<path>` read Step 3 already runs to confirm a ticked box, and confirm the defect before it becomes a finding of this pass's own.
75
+
76
+ A moved head has its own way to add nothing, which the ancestor test cannot see. When `<prior-heading>` reads `## Review closed`, the standing verdict already reports the branch clear, so a pass over the new commits that raises nothing posts a comment saying what the one above it said.
77
+
78
+ The producing shape is narrow. A prior pass tells the author a change is their own call, the author makes it, and the delta reaching the next pass has nothing left to say by construction.
79
+
80
+ Carry `<prior-heading>` into Step 4 and run Step 3 unchanged. This guard cannot stop the pass the way the unchanged-head stop above does, because that stop reads an empty range and this one turns on what the pass carries, which is Step 3's output. Deciding ahead of the review would swallow the pass that does find something, and that is the costlier error, so the rule is stated beside its sibling and executed where the heading is picked.
81
+
82
+ Read each changed file in scope. Skip deleted files. Run reads in parallel.
@@ -15,17 +15,17 @@ Refilling on a merge and on a thinning ready list leaves the window between them
15
15
 
16
16
  Every one of those triggers looks forward, so a row already parked is measured once and re-tested never. A blocker cell is a claim about the tree that was true the day it was written, and a merge changes the tree under every parked row at once rather than under the rows naming it. A row therefore sits on a condition that stopped holding weeks earlier with nothing reporting the gap.
17
17
 
18
- Re-measuring one goes wrong twice over, and both ways return a confident number rather than an error. A count read looser than its consumer defines the shape answers a question nobody asked, and a condition measured against the tree at hand reads as unreachable while it stays live in the corpus a shipped command runs against. Waiting is not evidence either, so a row untouched for weeks invites promotion on its age, and a task colliding with everything by construction gets re-measured every pass rather than split.
18
+ Re-measuring one goes wrong twice over, and both ways return a confident number rather than an error. A count read looser than its consumer defines the shape answers a question nobody asked, and a condition measured against the tree at hand reads as unreachable while it stays live in the corpus a shipped command runs against. Waiting is not evidence either, and a task colliding with everything by construction gets re-measured every pass rather than split.
19
19
 
20
20
  Writing those plans against the tree alone is the second half, since several branches are already changing the shape a plan describes. A plan carrying a bare path list rather than a constraint per in-flight track leaves the worker guessing which act each path forbids, which is the dangling citation the rule against bare paths already exists to prevent.
21
21
 
22
- The session also writes surfaces it is told only how to read. Refilling the queue promotes and demotes rows in the priority file with no stated method, so a session invents one, and an inline rewrite exits clean when it matches nothing and leaves the board wrong with nothing reporting it. A row carrying prose where a plan pointer belongs costs a worker dispatch, because the ship chain refuses at its guard after the worktree is already open. A plan archived from a worker's own branch strands the pointer the board still carries, and the row reads as correct until someone follows it.
22
+ The session also writes surfaces it is told only how to read. With no stated method for rewriting the priority file, a session invents one that can exit clean on a non-match, carry prose where a plan pointer belongs, or keep a pointer to a plan a worker's branch archived, and each reads as correct until a dispatch spends on it.
23
23
 
24
24
  The ban on writing at all fails on a different axis, which is that nothing enforces it and a session weighs proportionality against it. A one-line prose correction found while orchestrating satisfies the root instruction to handle a small edit immediately and violates this rule, and a session given no statement of which one wins takes the smaller apparent cost and authors the change.
25
25
 
26
26
  That removes the independent pass the repository built to catch what a self-review misses, and the vantage does not come back, since no later session can review the change without re-deriving the context that produced it. A correction no open task owns has nowhere to go either, so the route matters as much as the ban.
27
27
 
28
- The contract also stops one state short of what the session holds. Its sections cover what the human launches and what they review, which was the whole state space while every build was hand-launched, and a self-dispatching session adds a third that sits between them for ten to thirty minutes. A session holding four running workers reports them under no section and invents a shape per report, which the same block forbids while offering no term to use, and the launch section keeps recommending a plan a worker is already building because the plan file sits in place for the whole build.
28
+ The contract also stops one state short of what the session holds. Its sections cover what the human launches and what they review, and a self-dispatching session adds a third that sits between them for ten to thirty minutes, which a session reports under no section while the launch section keeps recommending a plan a worker is already building.
29
29
 
30
30
  Output drifts everywhere the contract stops. The specified shape covers invocation alone, so a sweep report, a board report, and an analysis each end in a decision the human owns and each buries it under the evidence they would have skipped. A compaction is the same failure one step earlier, since the file that survives it has a stated reader and no stated writer, and the session improvises what to save and loses the reasoning the board never held.
31
31
 
@@ -33,17 +33,17 @@ Naming that reader without naming the way in fails on the next axis, and it fail
33
33
 
34
34
  The runbooks the session cannot run from memory fail on a third axis, which is where they are kept. A runbook reached by an installed path is a dependency on a channel this skill does not travel on, so a project holding the plugin and running no install follows the citation to nothing, and nothing reports the break because a missing file produces no error until someone opens the path.
35
35
 
36
- Every one of them therefore has to ship inside the skill, the four covering the moments the loop cannot detect and the fifth holding the review trigger. That last one fails on an axis of its own, since it names one client's command as the way to start the loop, so a session holding a different recurring-prompt scheduler reaches no path and a client without that command reaches none either, and the file ships to every target holding the plugin.
36
+ Every one of them therefore has to ship inside the skill. The review trigger fails on an axis of its own when it names one client's command as the way to start the loop, since the file ships to every target holding the plugin and a client without that command reaches no path.
37
37
 
38
38
  The dispatch's return leg is unplaced for the same reason. A reply reaches this session as a message and settles a finding, corrects which session holds which branch, or names a gate a worker's edits pass through, and reading it is the whole of what happens to it. Both sessions then end, so the half that belonged on the pull request never reaches it and the half that belonged on the board is rediscovered by whoever needs it next.
39
39
 
40
- Both halves of that channel are written here and one of them is performed elsewhere, so a worker owes what nothing it loads states. The review trigger inherits the same asymmetry: it fires on a dispatch as well as on a pull request, spending an interval against a building worker that has none, which five consecutive runs measured as reporting nothing across roughly fifteen minutes. The one reading that would close the gap is the roster read the poll script cannot make, since a worker that finishes goes idle and one that crashes vanishes, so a trigger matching pull requests alone stays silent through the second. The loop that did make it ran for an afternoon in a session's temporary directory against a hardcoded repository path, and it died with that session with no runbook naming it.
40
+ Both halves of that channel are written here and one of them is performed elsewhere, so a worker owes what nothing it loads states. The review trigger inherits the same asymmetry: firing on a dispatch spends an interval against a building worker that has no pull request, and a trigger matching pull requests alone stays silent through a worker that crashes, which only a roster read the poll script cannot make would catch.
41
41
 
42
42
  Cost never enters the decision to widen a wave. The rule weighing how many tracks to open measures collision and review attention, and both describe what a worker writes rather than what the loop spends. Claude Code delivers a message from another session as a fresh turn carrying the whole accumulated context, and a recurring poll bills that same window on its own interval, so a wave's spend tracks this session's context rather than the work coming back. The one setting that bounds it reads as available to whoever finds it, and the two values that would bound it break the handback the loop runs on.
43
43
 
44
44
  The session also records nothing of what it learns. Both other callers of memory capture are ship-chain skills and this one never ships, so the session taking every operator correction is the session with no moment that writes one down. Hanging that moment on the merge sweep answers it and bills the operator a capture pass per batch of merges while nothing is being built, which is a cost paid on the days shipping is fastest.
45
45
 
46
- Dispatch parts from what it checked in three measured ways. The gate and the worker each derived the branch from their own reading of one plan until 2026-09-06: one run checked `docs/remaining-skill-verdicts` against a worker that took `docs/skill-verdicts-decide`, another checked `fix/path-form-hook` against `feat/path-form-hook`, and four dispatches on 2026-09-05 produced three strings for one plan. A launched worker inherits its launcher's model rather than the configured default, measured on 2026-08-27 with `~/.claude/settings.json` set to `sonnet` while both dispatched workers ran `claude-opus-5`. A cleared file set does not hold either, since three tracks in the wave of 2026-09-08 crossed a set they had cleared against and every one merged clean.
46
+ Dispatch parts from what it checked in three ways. The gate and the worker each derive the branch from their own reading of one plan and reach different strings, a launched worker inherits its launcher's model rather than the configured default, and a cleared file set does not hold, since a track can cross the set it cleared against and still merge clean.
47
47
 
48
48
  ## Must
49
49
 
@@ -52,24 +52,18 @@ Dispatch parts from what it checked in three measured ways. The gate and the wor
52
52
  - Verify a plan against the tree before handing it over, counting the sites it claims and opening the files it describes
53
53
  - List a candidate's file set against every track in flight, and serialize when the sets are not disjoint
54
54
  - State what a wave costs in inbound turns beside the collision rule, and name the control that bounds it together with the reason it stays unset, since a lever recorded without its conclusion reads as available to set
55
- - Place every finding a merged pull request produced against the surface that owns it
55
+ - Place every finding a merged pull request produced against the surface that owns it, and write the board so a row's readiness and disjointness claims stay checkable
56
56
  - Place what the return leg carries by what it changes, sending an answer that settles a finding onto the pull request and one that corrects this session's model of the world to the task owning that surface, since neither becomes a record by being read
57
57
  - Keep one planned, non-conflicting task in reserve beyond what is running
58
58
  - Refill the queue while a wave is still building, rather than on a merge and a thinning list alone, since neither of those fires across the window planning costs nothing
59
59
  - Carry a constraint naming each in-flight track's file set in every plan written from here, stating per set which of the two acts it forbids, since a bare path list leaves a broken citation in place
60
- - Write the priority file with an editing tool that errors on a non-match, since the board is where a silent failure costs a dispatch
61
- - Carry a plan pointer in the Plan column and a file set in the Touches column, so a row's readiness and disjointness claims stay checkable
62
- - Re-resolve the board's plan pointers after any archive, since the archiving skill rewrites the task file and knows nothing about the board
63
- - Carry a slot for this session's own workers mid-build, one row apiece naming the session, the branch, the model, and a progress reading taken from the worktree, since the two other slots cover what a human launches and what they review and nothing between
64
- - Withhold a plan from the launch slot once a row in flight names it, since the plan file sits in place for the whole build and listing it recommends handing off work already underway
60
+ - Carry a slot for this session's own workers mid-build, and withhold their plans from the launch slot, since the two other slots cover what a human launches and what they review and nothing between
65
61
  - Start the review poll on an open pull request rather than on a dispatch, and keep the dispatch as a fallback for an announcement that never arrives, since the script reads pull requests and a building worker has none
66
62
  - Point at the worker's own skill for the announcement, the block message, and the board ban, rather than holding the only written copy of obligations this session does not perform
67
63
  - Ship the roster-and-pull-request watch as a script beside the poll, since the loop that covered the gap lived in one session's temporary directory against a hardcoded path and died with that session
68
64
  - Lead a sweep report, a board report, and an analysis with the state, the open decisions, and the next action, keeping the evidence below them
69
65
  - Write the pre-compact handoff with what no other file carries, and name the runbook that reads it back
70
66
  - Carry the resume invocation and the owed poll restart in the handoff file itself, not in the runbook that writes it alone, since the file is what survives a compaction and the runbook is what a session has to already be holding to read
71
- - State the invocation as a resume request to this skill with one client's command as the example, since the handoff ships to every target holding the plugin
72
- - Resolve every runbook path the handoff carries to an absolute path as it is written, since the reader holds no skill and the path variable does not expand in the turn that reads the file back
73
67
  - Distinguish this resume from the general-purpose `session-resume`, since the name an operator reaches for first reaches a skill that knows nothing about the board
74
68
  - Capture what the session learned at the handoff rather than in the merge sweep, and have the sweep report the debt dated from the last handoff, since a capture per batch of merges bills the operator a wait while nothing ships and an undated row reads the same however long the debt has run
75
69
  - Retire the sessions this wave delivered before the map is written and ahead of any reclaim run, reporting a hand-launched one rather than acting on it, since no verb decides a planner or an unmerged worker and a live session holding a worktree is what the reclaim reading refuses on
@@ -85,8 +79,7 @@ Dispatch parts from what it checked in three measured ways. The gate and the wor
85
79
  - Check a candidate branch is unclaimed by an existing worktree or a live session before dispatching a background worker for it, since the measured failure this closes is a worker colliding with someone else's work already sitting in the row
86
80
  - Dispatch only a candidate whose file set is disjoint from every track in flight, compared at the file path rather than a folder above it, since a count knows nothing about what two workers write
87
81
  - Hold a candidate whose sets are disjoint when a stated reason serializes it, and write that reason on the hold, since disjointness is necessary and not sufficient
88
- - Name each self-dispatched worker with the `worker-` prefix, since that is the role the name marks and no controlling session has ever carried it
89
- - Pass `-n` explicitly on every self-dispatch, since a launch that omits it leaves the client to derive a name and the fallback is a fragment of the session's own identifier
82
+ - Name each self-dispatched worker explicitly with the `worker-` prefix, since that is the role the name marks and a client-derived name is a fragment of the session's identifier
90
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
91
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
92
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
@@ -53,83 +53,18 @@ That routing lives in this body and this skill is user-invoked, so a session tha
53
53
 
54
54
  ## Output
55
55
 
56
- ```plaintext
57
- Orchestrator ready.
58
-
59
- Ready to build (hand each to its own worker):
60
-
61
- <feature>
62
- plan: .canon/plans/feature-<slug>.md
63
- → /auto-ship
64
-
65
- <feature>
66
- no plan yet. A handoff without a plan has no scope
67
- → /plan-feature here first
68
-
69
- In flight (this session's workers, building now):
70
-
71
- <session name> <branch> <model>
72
- task: <row>, <n> commits, <n> files changed
73
-
74
- In review (your turn):
75
-
76
- PR #<n> <title>
77
- → /review-pr
78
-
79
- Merge order: #<a> before #<b> (shared seam: <files>).
80
-
81
- Next: <the single most useful action>
82
- ```
83
-
84
- Omit any section with nothing in it. Recommend a handoff only for a plan whose file set is disjoint from every track already in flight, per Parallelism below.
85
-
86
- `In flight` covers the state between the other two, which lasts ten to thirty minutes and is most of what an operator sees once this session dispatches its own workers. Before it existed, a session holding running workers reported them under no section and invented a shape per report, which the block below forbids two paragraphs down while giving it no term to use. Read the progress figures off each worker's worktree rather than from the worker, since a busy status says a session is alive and nothing about whether it is moving, and name the model because a dispatcher now picks one per row.
87
-
88
- Leave a plan out of `Ready to build` once a row in flight names it. The plan file stays in `.canon/plans/` for the whole build, so listing it there recommends handing off work already underway, and the disjointness rule below withdraws the recommendation only for a reader who already knows what is running.
89
-
90
- The block opens on the board rather than on a version, because no committed file states one. Adding a version line here would restate what a reader can already see on the rows, dated by nothing, which is how the retired sequencing surface produced an unsourced claim on every run.
91
-
92
- ### Every later turn
93
-
94
- The block above covers invocation alone. A sweep report, a board report, and an analysis each end in something the human decides, so each opens with the same three slots and puts its evidence underneath:
95
-
96
- ```plaintext
97
- State: <what changed since they last looked>
98
-
99
- Decisions:
100
- 1. <one line each, or "none open">
101
-
102
- Next: <the single most useful action>
103
- ```
104
-
105
- Keep the detail below the block and keep it skippable. A decision reached at the bottom of three paragraphs has been buried, which is the failure this shape exists to prevent. Reuse the vocabulary above rather than inventing a second one, and keep a file set on the row claiming it so the reader can check a disjointness claim instead of taking it.
106
-
107
- Write no shape for a correction. A correction is a sentence, and a format for admitting error invites ceremony where plainness is the whole value.
56
+ Report the state of play in the invocation block, and open every later sweep report, board report, and analysis with its state, the open decisions, and the next action, keeping the evidence below them. Read `${CLAUDE_SKILL_DIR}/references/orchestrator-output.md` for both shapes before writing either.
108
57
 
109
58
  ## The loop
110
59
 
111
- 1. Plan the next feature. The cross-feature call stays in this warm session, being which rows collide, what merges before what, and whether a row should run at all. Per-row planning runs either way: `plan-feature` here with that context, or a cold planner dispatched under `role-planner` through the planning shape in `${CLAUDE_SKILL_DIR}/references/orchestrator-dispatch.md`. Every plan written from here also carries a constraint per track in flight, which the paragraph below this list states.
60
+ 1. Plan the next feature. The cross-feature call stays in this warm session, being which rows collide, what merges before what, and whether a row should run at all. Per-row planning runs either way: `plan-feature` here with that context, or a cold planner dispatched under `role-planner` through the planning shape in `${CLAUDE_SKILL_DIR}/references/orchestrator-launch.md`. Every plan written from here also carries a constraint per track in flight, which the paragraph below this list states.
112
61
  2. Decide parallelism and merge order. Note which plans touch a shared wiring seam so their PRs merge in sequence, not at once.
113
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.
114
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.
115
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.
116
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.
117
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.
118
- - Read the threshold off `review-pr`, which states it once and governs the heading with it, so an open heading and an owed dispatch answer the same question and either one is enough to send
119
- - 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. The runbook read at step 5 routes on the count and the confidence it answers with
120
- - 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
121
- - Name the skill for the reader to run rather than writing an invocation, which arrives as text
122
- - 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.
123
-
124
- 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. Every dispatch in the trial behind this step found a live session, so this branch stands on reasoning rather than on observation.
125
-
126
- 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.
127
-
128
- What the worker owes on its own side is stated in the `role-worker` skill that session loads, being the pull-request announcement, the message a block goes out as before it becomes a prompt, and the ban on writing the board. Do not restate any of it here. This half held the only written copy of a channel that runs both ways, which put every obligation on the side that does not perform it.
129
-
130
- What arrives there does not become a record by being read, so place it by what it changes. An answer that settles a finding goes onto the pull request through the next pass, which withdraws or regrades that finding and names the fact behind it, per `review-pr`. An answer that changes what this session believes about the world instead, which is a mapping correction or a constraint on what a worker can do, settles no finding and reaches no thread, so route it the way Boundaries below routes a change found while orchestrating, which lands it on the task owning the surface it describes. Writing a tracked file to hold either is forbidden here, which leaves the pull request and the board as the two surfaces this session writes.
131
-
132
- Step 1 splits a rule that used to hold every plan in this session, and the evidence narrows it rather than retiring it. Two trials put a cold planner on four rows, and it reported ten things the task files got wrong, corrected this session's own premise twice, and overturned one row's closing conclusion. What that measures is finding quality. The rule's own claim is that a warm plan front-loads reasoning a cold worker would otherwise re-derive, which is a statement about a plan's downstream value, and no plan from either trial has been built. So the per-row measurement goes cold on the evidence and the cross-feature call stays here on the boundary the same trials confirmed, which is that a planner reading the board sees blockers and file sets and can write a confident merge order off a partial picture.
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.
133
68
 
134
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.
135
70
 
@@ -141,7 +76,7 @@ Stamp the block with the commit this session read the tree at, which the same se
141
76
  - Do not implement features in this session. Hand the plan to a worker.
142
77
  - Do not merge. Recommend merge or changes. The human merges.
143
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.
144
- - 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 dispatch in `orchestrator-dispatch.md` is a separate `claude --bg` process with its own worktree and its own PR, so it preserves that property instead.
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.
145
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.
146
81
  - Do not edit tracked files from this session, at any size. The boundary offers no proportionality exception and nothing enforces it.
147
82
  - Do not hand a worker anything but a plan, since scope lives there. A plan carries exact diffs only when they are already known, otherwise it states the scope and the open questions and lets the worker write the diff.
@@ -160,57 +95,9 @@ Both kinds read the same tree, and only a sizing count commits a plan to a scope
160
95
 
161
96
  ## Refilling the ready queue
162
97
 
163
- Keep enough planned, non-conflicting tasks available that a free worker never waits, and place the findings the last merge produced before promoting anything new. Run this after every merge, whenever the ready list thins, and whenever a wave is in flight with fewer unclaimed plans than there are workers building. `${CLAUDE_SKILL_DIR}/references/orchestrator-sweep.md` wraps this procedure for a batch of merges and adds the plan re-verification that a merge invalidates.
164
-
165
- The third trigger reaches a window the first two cannot. A session with three workers mid-build has merged nothing and has watched its ready list sit still, so both reactive triggers stay silent across the one stretch where planning costs the session nothing, and the wave finishes into an empty `## Run now`. Nothing starts this pass, in the way nothing starts the review poll, so it holds only while the session applies it.
166
-
167
- It counts unclaimed plans against workers rather than reading the reserve in step 4, which is sized for one worker finishing and falls short of a wave landing together. A plan a worker has already taken serves nobody who finishes next, so counting it is what lets the queue read full while it is about to empty. How many to write forward past that floor is the parallelism call rather than a count of free slots, and `## Parallelism` below states what binds it.
168
-
169
- ### Running the refill
170
-
171
- 1. Run `gh pr list --state open` and `git log --oneline -8`. Report any pull request whose review has not been posted and stop for that one first.
172
- 2. For each pull request merged since the last sweep, place every finding it produced. Route a finding that changes a rule to the standard or rule that states it, one that changes another task to that task's Findings, and one that overturns a groundwork lean to that folder marked answered. Never leave a finding in a pull request thread alone.
173
- 3. Archive what closed.
174
- - A task whose outcomes are all `[x]` runs `docs-fold` for the plan sweep, then `task-board` to archive
175
- - A task whose outcomes describe standing policy rather than a deliverable never closes on its own, so hand it to a worker to encode the policy where it is enforced, then cut the outcomes with the reason recorded and archive once that branch merges. Encoding it from this session would write a tracked file, which Boundaries forbids.
176
- 4. Read `.canon/tasks/priority.md` and count entries under its `## Run now` heading that carry a written plan. Keep one in reserve beyond what is running.
177
- 5. Promote from the top of `## Needs a plan`, which is where the last sweep recorded what to plan next. Depart from that order when something has changed under it and say what changed, since a position nobody honors is the ordering going stale on the surface built to hold it. What sets the order in the first place is whether a task establishes functionality rather than how old it is, so prefer a task that adds or proves a mechanism over one that trims, tidies, or audits an existing surface.
178
- - Re-take the demotion half of the board-or-backlog call while the file is open. A row that has stopped being near-term moves to `backlog.md`, one line removed from `priority.md` and written there, per the standard's test. The promotion half is `references/orchestrator-parked.md`'s to run as a full walk over every backlog row, so this step does not repeat it. A row that pass clears already sits at the bottom of `## Needs a plan` before this sweep reads the heading.
179
- 6. Before promoting a candidate, list the files it touches against every task already running, per Parallelism below. Name the overlap and serialize when the sets are not disjoint.
180
- - A candidate held by something outside the tree stays where it is whatever those sets show. A collision is one of the reasons a task cannot start, so disjointness clears that reason alone and leaves an external condition standing.
181
- 7. Write a plan for each newly promoted task with `plan-feature`, carrying the in-flight constraint that The loop above states, then report:
182
-
183
- ```plaintext
184
- Capture: owed since <the last handoff, or session start when none has run>
185
- Findings placed: <finding> → <destination>
186
- Archived: <task>
187
- Promoted: <task>, touches <surfaces>, parallel with <task> because <disjoint sets>
188
- Serialized: <task> behind <task>, both write <file>
189
- Backlogged: <task>, because <what stopped being near-term>
190
- Ready now: <tasks with plans, and what each waits on>
191
- ```
192
-
193
- The capture row states a standing debt rather than a per-run result. Running capture from here costs the operator a pass between merges while nothing ships, so the row leaves the timing to them and dates the debt, since a capture owed for twenty minutes and one owed all day want different answers and undated text reads the same either way. `${CLAUDE_SKILL_DIR}/references/orchestrator-handoff.md` holds the step that pays it.
194
-
195
- That block is the detail. Lead the reply with the three slots under Every later turn above, so the human reads what they own before the evidence for it.
196
-
197
- Treat a task that edits `canon/context/` entries wholesale as conflicting with every other task, because the root instruction file requires each task to update its own domain entry as it lands.
198
-
199
- Do not promote a task to fill the queue when nothing qualifies. A thin queue is a real answer and it beats a plan nobody needed.
200
-
201
- ### Writing the board
202
-
203
- Promoting, demoting, and archiving a row all write `.canon/tasks/priority.md`, and this session is the only writer apart from `canon tasks archive`. Moving a task between the board and `.canon/tasks/backlog.md` writes both files, and this session is that file's only writer.
204
-
205
- - Edit the file with the file-editing tool. A shell stream editor and an inline string replace both exit clean on a non-match, so a promotion that matched nothing leaves the board wrong with nothing reporting it, and the file-editing tool errors instead.
206
- - Write both halves of a move before reporting it. A row removed from one surface and not written to the other leaves a task file nothing names, and the folder is gitignored with no history to recover the row from. `canon tasks validate` reports that state, so run it after any move.
207
- - Put the reason a row sits where it does in its Waiting on cell. Position is the ordering and the cell is where the ordering's rationale lives, so a row promoted with the cell left alone carries an order the next sweep cannot check.
208
- - Put a pointer in the Plan column, never prose. `## Run now` claims a written plan covers every open outcome, and `auto-ship` refuses at its guard when it follows the column and finds no plan, which spends a worker dispatch to learn what the row should have said.
209
- - Name the file set in the Touches column. The disjointness call in step 6 is only checkable later when the sets are written down rather than reasoned once and discarded.
210
- - Re-resolve every Plan pointer after anything archives a plan
211
- - Read the file back after writing it, since the row that lands is the row a worker acts on
98
+ Keep enough planned, non-conflicting tasks available that a free worker never waits, and place the findings the last merge produced before promoting anything new. Run this after every merge, whenever the ready list thins, and whenever a wave is in flight with fewer unclaimed plans than there are workers building.
212
99
 
213
- A Plan pointer goes stale from a branch this board never sees. `docs-fold` moves a plan to `.canon/plans/archive/` and rewrites the citation in the task file alone, so a row for a task still on the board keeps pointing into `.canon/plans/` at a file that has moved. Workers running the ship chain on their own branches archive plans this board still cites, and the board reads as correct until a pointer is followed.
100
+ Read `${CLAUDE_SKILL_DIR}/references/orchestrator-refill.md` on reaching any of the three. It holds the procedure, its report, and the rules for writing the board.
214
101
 
215
102
  ## Parallelism
216
103
 
@@ -255,37 +142,6 @@ hand only when two worktrees derive the same offset.
255
142
 
256
143
  Two conditions move the review itself out of this session rather than binding the
257
144
  track count. One is a diff too large for this session to hold. The other is three
258
- or more open pull requests awaiting a first pass. Either one makes review the
259
- bottleneck every track is waiting on, and both answer the same way: dispatch the
260
- narrow re-review and keep the first pass here.
261
-
262
- Three is the operator's number, set by hand and marked as such so a
263
- measurement replaces it rather than argues with it. It is calibrated against a
264
- session that ran five workers across two waves and reviewed five pull requests in
265
- one stretch, several of them twice, where a sixth track was declined on judgment
266
- and on nothing else. What counts toward it is a pull request awaiting a first
267
- pass rather than every open one, since a branch already closed out and waiting on
268
- a merge costs this session nothing and counting it would fire the switch on a
269
- queue that is clear.
270
-
271
- Only the narrow re-review dispatches. A first pass reads across branches, and the
272
- findings that pay for its cost are the ones no single pull request shows: two
273
- branches regenerating one binary asset for the same count, three writing one
274
- context entry, a merge order making one branch's figure true only after another
275
- lands. A re-review asks whether the prior findings landed and whether the fix
276
- regressed anything, which is bounded to a delta and carries none of that reading,
277
- so it is the half that leaves cleanly.
278
-
279
- What it dispatches has no role skill yet. `role-worker` and `role-planner`
280
- each state what their session may not do and no third body states a reviewer's,
281
- so a dispatched re-review would hold its obligations in whatever launch string
282
- this session types. That is the defect those two skills were written to close, so
283
- write the third after the first trial rather than before it, since nothing has
284
- ever dispatched a reviewer and a body written now encodes a shape nobody has
285
- driven. Until it exists, say in the launch itself that the pass is bounded to the
286
- delta and that the cross-branch reading stays here.
287
-
288
- Nothing counts the pull requests. This is prose this session applies to itself,
289
- on the same standing as the poll's own start condition, so a wave past three
290
- reviewed one at a time is a rule that went unread rather than a check that
291
- failed.
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.