@erclx/canon 4.118.0 → 4.120.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.
- package/README.md +2 -2
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/canon-rollout/SKILL.md +2 -2
- package/claude/skills/create-skill/SKILL.md +2 -1
- package/claude/skills/design-extract/SKILL.md +1 -1
- package/claude/skills/docs-fold/SKILL.md +2 -2
- package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
- package/claude/skills/draft-ready/SKILL.md +1 -1
- package/claude/skills/draft-screencast/SKILL.md +1 -1
- package/claude/skills/git-pr/REQUIREMENT.md +1 -0
- package/claude/skills/git-pr/SKILL.md +39 -7
- package/claude/skills/memory-capture/SKILL.md +3 -3
- package/claude/skills/memory-review/SKILL.md +3 -3
- package/claude/skills/plan-feature/SKILL.md +1 -1
- package/claude/skills/review-branch/SKILL.md +1 -1
- package/claude/skills/review-pr/SKILL.md +13 -242
- package/claude/skills/review-pr/references/close-out.md +30 -0
- package/claude/skills/review-pr/references/post.md +145 -0
- package/claude/skills/review-pr/references/scope.md +82 -0
- package/claude/skills/role-orchestrator/REQUIREMENT.md +9 -16
- package/claude/skills/role-orchestrator/SKILL.md +9 -153
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +4 -193
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +22 -0
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +85 -0
- package/claude/skills/role-orchestrator/references/orchestrator-output.md +61 -0
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +56 -0
- package/claude/skills/role-orchestrator/references/orchestrator-review-fallback.md +18 -0
- package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +1 -1
- package/claude/skills/role-planner/REQUIREMENT.md +1 -1
- package/claude/skills/role-planner/SKILL.md +1 -1
- package/claude/skills/role-worker/REQUIREMENT.md +1 -1
- package/claude/skills/role-worker/SKILL.md +10 -41
- package/claude/skills/seed-sync/SKILL.md +1 -1
- package/claude/skills/session-compact/SKILL.md +1 -1
- package/claude/skills/session-map/SKILL.md +1 -1
- package/claude/skills/session-relay/REQUIREMENT.md +7 -1
- package/claude/skills/session-relay/SKILL.md +4 -7
- package/claude/skills/session-worktree/REQUIREMENT.md +5 -1
- package/claude/skills/session-worktree/SKILL.md +7 -1
- package/claude/skills/session-worktree/references/main-root-writes.md +24 -0
- package/claude/skills/task-board/SKILL.md +4 -3
- package/claude/skills/teach-workspace/SKILL.md +4 -4
- package/claude/skills/ui-checklist/SKILL.md +1 -1
- package/claude/skills/ux-audit/SKILL.md +1 -1
- package/claude/skills/ux-measure/SKILL.md +1 -1
- package/claude/skills/ux-walkthrough/references/relay.md +1 -1
- package/docs/agents/commands.md +4 -3
- package/docs/agents/counts.md +1 -1
- package/docs/agents/index.md +8 -3
- package/docs/agents/install-and-sync.md +11 -6
- package/docs/agents/pr-evidence.md +19 -3
- package/docs/agents/pr-local.md +102 -0
- package/docs/agents/pr-preview.md +2 -2
- package/docs/agents/records-push.md +61 -0
- package/docs/agents/records-validate.md +73 -0
- package/docs/agents/records.md +3 -119
- package/docs/agents/sessions-transfer.md +91 -0
- package/docs/agents/sessions.md +3 -82
- package/docs/agents/skills-audit.md +2 -2
- package/docs/agents/tasks.md +4 -4
- package/docs/agents/teach-authoring.md +112 -0
- package/docs/agents/teach.md +3 -103
- package/docs/target-projects.md +2 -2
- package/docs/workflow/ai-workflow.md +10 -10
- package/docs/workflow/visual-design-workflow.md +2 -2
- package/governance/renames.toml +13 -0
- package/governance/rules/claude/570-skill.md +2 -1
- package/governance/rules/core/085-worktrees.md +2 -8
- package/package.json +1 -1
- package/scripts/core/check-ignore-parity.sh +1 -1
- package/scripts/lib/sandbox-git.sh +1 -1
- package/src/claude/cases/workflow.ts +10 -0
- package/src/commands/pr.ts +324 -2
- package/src/commands/sync.ts +11 -3
- package/src/commands/tasks.ts +9 -8
- package/src/context/classify/extract.ts +2 -2
- package/src/counts/scan.ts +1 -1
- package/src/design/base.css +19 -19
- package/src/design/board.ts +18 -7
- package/src/design/neutral.ts +127 -0
- package/src/design/regen.ts +6 -4
- package/src/design/tokens.ts +6 -3
- package/src/gate/stages.ts +6 -6
- package/src/gov/adapter.ts +51 -1
- package/src/gov/install.ts +13 -3
- package/src/gov/renames.ts +76 -0
- package/src/gov/stacks.ts +5 -1
- package/src/pr/evidence.ts +42 -2
- package/src/pr/local.ts +125 -0
- package/src/sync/check.ts +19 -4
- package/src/sync/engine.ts +67 -3
- package/src/tasks/archive.ts +28 -7
- package/src/tasks/record.ts +16 -9
- package/src/tasks/validate.ts +3 -1
- package/standards/index.md +1 -0
- package/standards/skill-requirement.md +72 -0
- package/standards/skill.md +34 -152
- package/standards/tasks.md +5 -3
- package/tooling/astro/reference.md +1 -1
- package/tooling/base/configs/.github/workflows/pr-local-preview.yml +32 -0
- package/governance/rules/core/091-channel.md +0 -13
|
@@ -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,
|
|
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.
|
|
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,
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
-
|
|
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
|
|
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
|
-
|
|
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-
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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.
|
|
259
|
-
|
|
260
|
-
|
|
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Orchestrator dispatch runbook
|
|
3
|
-
description: The plan-answer gate, the collision check before a self-dispatch, the file-set disjointness gate, the branch and model the launch names, the
|
|
3
|
+
description: The plan-answer gate, the collision check before a self-dispatch, the file-set disjointness gate, the branch and model the launch names, the fallback to a human launch, and the loop's stopping condition
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Run this at loop step 4, for a `## Run now` row whose plan is verified, in place of handing the worktree to a human. The disjointness gate below is where that row's file set is tested against every track in flight.
|
|
@@ -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
|
|
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.
|
|
78
78
|
|
|
79
79
|
## Pick the model
|
|
80
80
|
|
|
@@ -84,196 +84,7 @@ Name `<model>` on the launch, and pick it against the task rather than copying w
|
|
|
84
84
|
|
|
85
85
|
## Dispatch
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
claude --bg --model <model> -n "worker-<project>-<slug>" "/canon:auto-ship <plan>
|
|
89
|
-
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 pull request opens, carrying the number, the branch, the head sha, the CI state, and every point you departed from the plan on, and message it again if you stop on a question."
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
`--bg, --background` starts the session as a background agent and returns immediately, `-n, --name` sets the display name that tells a self-dispatched worker from an operator's own launch in `canon sessions list`, and `--model` overrides the inheritance the section above states. Pass `-n` on every dispatch rather than letting the client derive one. A launch that omits it leaves the session named for a fragment of its own identifier, which is both its address on the send channel and the whole of what the operator sees for it in agent view.
|
|
93
|
-
|
|
94
|
-
The prefix reads `worker-` because that is the role it marks. A prefix naming a role the session does not hold sends a worker filtering the roster for that string to a sibling or itself on every row. Nothing matches the prefix programmatically, which keeps a rename down to three strings.
|
|
95
|
-
|
|
96
|
-
`<project>` is the basename of the main worktree root, not of wherever the dispatcher happens to be running. Resolve the main root first, the way `session-worktree` Step 1 does, since a bare `git rev-parse --show-toplevel` inside a linked worktree returns the worktree path rather than the project's.
|
|
97
|
-
|
|
98
|
-
`claude agents` lists every session on the machine with no path column and no per-project filter, so `<project>` in the name is the only thing left telling two fleets apart, and a session named off the worktree path instead would carry the branch folder rather than the project. Two projects each dispatching a bare `worker-page-driver` used to read as one row in that view.
|
|
99
|
-
|
|
100
|
-
Read `<dispatcher-id>` with `canon sessions list --self --json` and interpolate the `sessionId` that row carries. Carry the id rather than the name. A name is derived from whatever the session turned out to be doing, and across the 181 records stamping both fields, nine were renamed after launch at a median of 5.4 minutes and a maximum of 509. Three landed more than ten minutes in, which is inside the window a worker announces its pull request in, so a name written into the prompt is aimed at a send that happens after it goes stale.
|
|
101
|
-
|
|
102
|
-
Where the installed CLI answers `--self` with an unknown option, that flag is newer than the release the target holds. Read the `sessionId` from the record the client writes for this session under its configuration directory, and say which route answered so the reader knows whether the id was read or inferred.
|
|
103
|
-
|
|
104
|
-
The worker resolves that id back to a name through `canon sessions list --json`, which carries `sessionId` per row, rather than through the agent listing, which prints a name and a short ref and no id at all. A worker reaching for the listing first therefore finds no lookup and can conclude there is none. That failure is silent in both directions: the session has nothing useful to do with the message it owes and goes idle holding it, and nothing on this side reports the quiet, so the loss surfaces as a missing worktree or a pull request that never opens rather than as anything watching for it.
|
|
105
|
-
|
|
106
|
-
The template carries no worktree call. `auto-ship` Step 0 invokes `canon:role-worker` and then `canon:session-worktree` itself, and neither carries the flag, so both are reachable through the `Skill` tool regardless of where a call to them would sit in a prompt. The autoship call carries `<plan>`, the same file this runbook already read to derive the branch, so its Step 1 takes it as the caller-supplied plan rather than re-deriving one from the slug the worker's branch happens to carry.
|
|
107
|
-
|
|
108
|
-
The template names no branch, and it does not need to. `auto-ship` Step 0 runs `canon tasks plan-branch <plan>` on the same file this runbook derived the candidate from, and hands the `<type>/<slug>` it reports to `session-worktree` as its tier 0 argument, so the two sides agree by calling one derivation rather than by a string copied between them.
|
|
109
|
-
|
|
110
|
-
That retires the inference four workers took before the verb existed, which was to derive the name from `<plan>` by their own reading of it. Nothing has to reach `session-worktree`'s ladder now, which mattered because a worker launched onto `main` cannot match tier 1, a board carrying more than one plan puts tier 2 out of reach, and tier 3 tells it to ask a person who is not there. What still travels on judgment is the fallback: a worker whose installed binary carries no `plan-branch` derives by prose, which is where both live disagreements came from.
|
|
111
|
-
|
|
112
|
-
### Expansion needs position zero and a clean delimiter, not leading order alone
|
|
113
|
-
|
|
114
|
-
The client expands a slash command at position zero of a launch prompt as a
|
|
115
|
-
user invocation, which is the route `disable-model-invocation: true` permits
|
|
116
|
-
and gates. Everything that reaches the session as prose instead falls to the
|
|
117
|
-
model, which invokes it through the `Skill` tool, and that route answers a
|
|
118
|
-
flagged skill inconsistently. `auto-ship` has carried the flag since early in its life, and seven other
|
|
119
|
-
shipped skills carry it too.
|
|
120
|
-
|
|
121
|
-
Three launches bound what makes a command take
|
|
122
|
-
that route. Observation A is the first refused worker, launched as `Run
|
|
123
|
-
/canon:session-worktree ..., then /canon:auto-ship ...`, which expanded
|
|
124
|
-
nothing. Observation B is a re-dispatch launched as `/canon:auto-ship
|
|
125
|
-
<plan>` with a space before the path, which expanded and shipped.
|
|
126
|
-
|
|
127
|
-
Observation C is a planning dispatch launched as `/canon:role-planner, then
|
|
128
|
-
/canon:plan-feature <task>` with a comma glued to the command name at
|
|
129
|
-
position zero, which expanded nothing and reached both bodies through the
|
|
130
|
-
`Skill` tool instead. A rules out leading order alone, C rules out position
|
|
131
|
-
zero on its own, and the only visible difference between B and C is the
|
|
132
|
-
delimiter after the command token: a space in B, a comma in C.
|
|
133
|
-
|
|
134
|
-
Read that delimiter reading as a candidate with a falsifier rather than as
|
|
135
|
-
settled. The cheapest test is one dispatch leading with a bare command whose
|
|
136
|
-
name is followed directly by a period, and the next real dispatch can carry it
|
|
137
|
-
at no extra cost. Until it fails, the operational rule is the conjunction the
|
|
138
|
-
three observations support: put the flagged command at position zero, followed
|
|
139
|
-
by a space and its argument, with nothing before it.
|
|
140
|
-
|
|
141
|
-
Four sessions made the same tool call against the same plugin cache on one
|
|
142
|
-
day. Two were answered with the body and shipped, and two were refused
|
|
143
|
-
with `Skill canon:auto-ship cannot be used with Skill tool due to
|
|
144
|
-
disable-model-invocation`. Prefixing separated nothing, since three of the
|
|
145
|
-
four carried the namespace and those three landed on both answers, so nothing
|
|
146
|
-
a dispatcher writes predicts which answer a launch through the tool gets.
|
|
147
|
-
|
|
148
|
-
Read that as a route a dispatch may not depend on rather than one that usually
|
|
149
|
-
works. The refusal closes the fallback in the same message, telling the
|
|
150
|
-
session not to replicate the workflow by other means, so a refused worker has
|
|
151
|
-
no route left and stops with a clean worktree. Both failed dispatches produced
|
|
152
|
-
nothing rather than a degraded run, which is the correct outcome and not a
|
|
153
|
-
thing to soften.
|
|
154
|
-
|
|
155
|
-
Recovery belongs to whoever writes the next prompt, since a blocked session
|
|
156
|
-
cannot replay its own launch. The refusal is sticky inside a session rather
|
|
157
|
-
than something a retry clears, measured when one refused worker repeated the
|
|
158
|
-
identical prefixed call and got the byte-identical error back. Re-dispatch
|
|
159
|
-
onto the same branch with the build template above, which already leads with
|
|
160
|
-
the one command that needs the expansion route.
|
|
161
|
-
|
|
162
|
-
The review shape and the planning shape below depend on no expansion at all.
|
|
163
|
-
None of `role-worker`, `review-address`, `role-planner`, or
|
|
164
|
-
`plan-feature` carries the flag, so both correctly keep their leading word
|
|
165
|
-
regardless of the delimiter or the position it sits at.
|
|
166
|
-
|
|
167
|
-
The same collapse reaches a human relay rather than a `claude --bg` string. A
|
|
168
|
-
controller that hands an operator two chained blocks to paste as separate
|
|
169
|
-
messages risks both landing as one, where everything after the first
|
|
170
|
-
command's name is read as that command's own argument and the second command
|
|
171
|
-
never fires, measured four times out of four.
|
|
172
|
-
A slash command expands as a user invocation only at position zero of a
|
|
173
|
-
prompt, and a later one in the same text reaches the session as prose instead.
|
|
174
|
-
The fix is what the template above already takes: one message, one command,
|
|
175
|
-
at position zero.
|
|
176
|
-
|
|
177
|
-
### What the brief may carry
|
|
178
|
-
|
|
179
|
-
The prompt carries pointers and standing context, and stops there. The branch and the plan stay arguments, because a skill resolves an argument through a documented ladder and reads no prose at all. What the prose reaches is the worker's judgment, so it holds only what a session has to weigh:
|
|
180
|
-
|
|
181
|
-
- Name the addressee and what it is owed, which the two message clauses above already do.
|
|
182
|
-
- Carry standing context this session holds that a cold one cannot derive, such as a constraint settled in conversation that never reached the plan.
|
|
183
|
-
- 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.
|
|
184
|
-
|
|
185
|
-
The last bullet is the one under pressure, since the dispatch that first proved unattended work possible sent a prompt naming the task file, the likely files, the conventions, and the check to run. It shipped in 874 seconds and touched four files its task never named. Scope that arrives as prose is scope nothing verifies afterward.
|
|
186
|
-
|
|
187
|
-
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.
|
|
188
|
-
|
|
189
|
-
## Dispatch to address a review
|
|
190
|
-
|
|
191
|
-
`review-address` is a single pass, not a chain, so a launch naming it
|
|
192
|
-
alone reaches no `role-worker` and takes no role, which owes no message
|
|
193
|
-
either. A replacement session was launched that way once, onto the branch a
|
|
194
|
-
review had already posted findings against, and it answered by posting a
|
|
195
|
-
thread reply and telling its controller nothing. Reach the role directly on
|
|
196
|
-
this launch instead of wrapping a second chain around one skill that has none
|
|
197
|
-
of its own.
|
|
198
|
-
|
|
199
|
-
The branch already exists here, opened by whatever built it, so this shape
|
|
200
|
-
skips the plan-derived name the build shape resolves above. Take `<branch>`
|
|
201
|
-
off the pull request's own head ref. Enter the worktree the original build
|
|
202
|
-
left on disk, `.claude/worktrees/<slug>/`, with `EnterWorktree`'s `path` form
|
|
203
|
-
when it is still there, or `git worktree add .claude/worktrees/<slug>/
|
|
204
|
-
<branch>` when it was cleaned up, so `<slug>` is that directory name either
|
|
205
|
-
way.
|
|
206
|
-
|
|
207
|
-
`EnterWorktree` refuses that path in the ordinary case, because the original
|
|
208
|
-
build session stays registered against its own worktree after going idle and
|
|
209
|
-
holds a harness-level lock the roster does not report. Work in the folder
|
|
210
|
-
directly with `Bash`, `Read`, and `Edit` instead of retrying the tool, which
|
|
211
|
-
is the route two workers already took today on two different branches.
|
|
212
|
-
|
|
213
|
-
```bash
|
|
214
|
-
claude --bg --model <model> -n "worker-<project>-<slug>" "Enter the worktree for <branch> at .claude/worktrees/<slug>/, creating it from that branch if the folder is gone. Run /canon:role-worker, then /canon:review-address. 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 address pass finishes, carrying what was addressed and the PR's CI state, and message it again if you stop on a question."
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
`<dispatcher-id>`, `<model>`, and `<project>` resolve the same way the build
|
|
218
|
-
shape resolves them above.
|
|
219
|
-
|
|
220
|
-
Take this shape wherever a review needs answering and no live session already
|
|
221
|
-
holds the branch. Where one does, message it to run `review-address`
|
|
222
|
-
instead, per the loop's own step 6, since a session already there needs no
|
|
223
|
-
second one dispatched onto the same branch.
|
|
224
|
-
|
|
225
|
-
That check is blind to a session working through the direct-path fallback
|
|
226
|
-
above, since a session that never runs `EnterWorktree` never moves its
|
|
227
|
-
registered branch off `main`, so `canon sessions list --branch` reports nothing
|
|
228
|
-
holding it. A dispatch landing on a branch worked that way collides with
|
|
229
|
-
nothing the check can see.
|
|
230
|
-
|
|
231
|
-
## Dispatch to plan a row
|
|
232
|
-
|
|
233
|
-
`plan-feature` is a procedure rather than a role, so a launch naming it alone
|
|
234
|
-
reaches no `role-planner` and takes no role, which owes no message either.
|
|
235
|
-
Both trials of this shape ran on prose the controller retyped into each launch,
|
|
236
|
-
which held every obligation those sessions took and is where the first one's
|
|
237
|
-
in-flight read went wrong. Reach the role directly on this launch, the way the
|
|
238
|
-
build shape above reaches `role-worker`.
|
|
239
|
-
|
|
240
|
-
No branch and no worktree exist here and none is created. A planner writes one
|
|
241
|
-
gitignored file at the main worktree root, so this shape names the row's task
|
|
242
|
-
file rather than a branch and opens with the role instead of a worktree call.
|
|
243
|
-
That write meets the isolation guard the same way a linked worktree's
|
|
244
|
-
main-root write does, with no worktree here to redirect it to, so
|
|
245
|
-
`role-planner` sends it as a `Bash` heredoc rather than through `Write`.
|
|
246
|
-
|
|
247
|
-
```bash
|
|
248
|
-
claude --bg --model <model> -n "planner-<project>-<slug>" "Run /canon:role-planner, then /canon:plan-feature <task>. 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 plan lands, carrying the path and what the task file got wrong, and message it again if you stop on a question."
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
`<task>` is the row's task file path and `<slug>` the slug its plan will take,
|
|
252
|
-
resolved off the row the way the build shape resolves one off a plan.
|
|
253
|
-
`<dispatcher-id>`, `<model>`, and `<project>` resolve the same way they do
|
|
254
|
-
above. The prefix reads `planner-` for the reason the worker's reads `worker-`,
|
|
255
|
-
which is that it marks the role of the session it names rather than the one
|
|
256
|
-
that launched it.
|
|
257
|
-
|
|
258
|
-
None of the three checks above binds this shape. The branch check has no
|
|
259
|
-
candidate to read, and the disjointness gate has nothing to compare, since a
|
|
260
|
-
planner writes one file no track in flight can hold. The plan-answer gate
|
|
261
|
-
reaches no plan at all, because the planner is dispatched to write the file a
|
|
262
|
-
build would later read, so running it here would refuse every planning dispatch
|
|
263
|
-
over a plan nobody has written yet.
|
|
264
|
-
|
|
265
|
-
What a planning dispatch owes instead is the reverse reading, because the plan
|
|
266
|
-
it produces carries a constraint per track in flight and a row planned during a
|
|
267
|
-
wave is planned against a tree that wave is changing. `role-planner` composes
|
|
268
|
-
the session roster with the pull request list for that read rather than reading
|
|
269
|
-
pull requests alone, which is why the brief carries no branch list for it: the
|
|
270
|
-
planner takes this reading itself either way.
|
|
271
|
-
|
|
272
|
-
One row per dispatch. A session reused across a batch pays the context load once
|
|
273
|
-
and ages its picture of the tree while it works, which is what puts the in-flight
|
|
274
|
-
read on the task rather than on the batch, and one that compacts mid-batch loses
|
|
275
|
-
the reasoning behind its earlier plans with nothing reporting it. Cap a reused
|
|
276
|
-
session where the saving is worth it and say what the cap was.
|
|
87
|
+
Read `${CLAUDE_SKILL_DIR}/references/orchestrator-launch.md` once every check above clears, and launch from its build template. It also holds the review-address and planning shapes, which skip the checks above.
|
|
277
88
|
|
|
278
89
|
## Fall back to the human
|
|
279
90
|
|
|
@@ -281,7 +92,7 @@ Hand the row to the human-launch line in step 4 instead of dispatching when any
|
|
|
281
92
|
|
|
282
93
|
The first of those five is the one that reaches a person rather than the board. A row held for a collision or for a serialize reason waits on the wave clearing, where a row held on its plan waits on an answer only the operator can give, so hand that one over with the question label and its stated reason attached rather than as a name and a refusal.
|
|
283
94
|
|
|
284
|
-
Hand the person one command: `/canon:auto-ship <plan>`, naming the row, the plan path, and the branch together, with no worktree call ahead of it. A leading worktree call adds nothing beyond what `auto-ship` Step 0 already reaches for itself
|
|
95
|
+
Hand the person one command: `/canon:auto-ship <plan>`, naming the row, the plan path, and the branch together, with no worktree call ahead of it. A leading worktree call adds nothing beyond what `auto-ship` Step 0 already reaches for itself. A second command also risks a client folding two commands into one message, which reads everything after the first command's name as its own argument and drops the second, per `### Position zero` in `orchestrator-launch.md`.
|
|
285
96
|
|
|
286
97
|
Suggest, as one line to the operator, that they rename their own session to the row's id, so a process listing shows what the session is for without a cross-reference to the board.
|
|
287
98
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Orchestrator handback runbook
|
|
3
|
+
description: Addressing the session that answers a posted review, what to report when none is live, and where a worker's reply is placed by what it changes
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Read this at loop step 6, once a review pass has posted anything owed and the session holding that branch is to run `review-address`.
|
|
7
|
+
|
|
8
|
+
## Sending the handback
|
|
9
|
+
|
|
10
|
+
- 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
|
|
11
|
+
- Resolve the target at the moment of sending with `canon sessions list --branch`, never from a mapping written down earlier, since names rotate as sessions end and one recorded earlier in a session has failed inside the hour. `orchestrator-poll.md` routes on the count and the confidence it answers with
|
|
12
|
+
- Open the message with the worktree and branch the sender believes the reader holds, asking to be corrected, whenever that mapping is inferred rather than confirmed
|
|
13
|
+
- Name the skill for the reader to run rather than writing an invocation, which arrives as text
|
|
14
|
+
- Read the pull request's own draft flag rather than the state a worker reports, and report what the read returned and when rather than the state alone. `canon docs pr-reads` states why a reported field can lag and say nothing about it. The flag settles the question only once the worker's chain has run its undo, and nothing marks that moment, so a read taken between the pull request opening and that call sees a genuinely ready pull request about to become a draft and has told an operator the opposite of what the worker had said.
|
|
15
|
+
|
|
16
|
+
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
|
+
|
|
18
|
+
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.
|
|
19
|
+
|
|
20
|
+
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.
|
|
21
|
+
|
|
22
|
+
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` in the skill body 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.
|