@erclx/canon 5.48.0 → 5.49.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 (47) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/auto-ship/SKILL.md +1 -1
  3. package/claude/skills/plan-feature/SKILL.md +18 -15
  4. package/claude/skills/plan-feature/references/batches.md +5 -5
  5. package/claude/skills/plan-feature/references/plan.md +82 -33
  6. package/claude/skills/plan-groundwork/SKILL.md +8 -0
  7. package/claude/skills/plan-groundwork/references/groundwork.md +2 -1
  8. package/claude/skills/plan-intake/SKILL.md +8 -0
  9. package/claude/skills/plan-intake/references/intake.md +9 -1
  10. package/claude/skills/plan-ready/REQUIREMENT.md +3 -3
  11. package/claude/skills/plan-ready/SKILL.md +3 -3
  12. package/claude/skills/plan-ready/references/assembly.md +7 -7
  13. package/claude/skills/plan-ready/references/ready.md +8 -6
  14. package/claude/skills/review-ui/REQUIREMENT.md +1 -1
  15. package/claude/skills/review-ui/SKILL.md +4 -11
  16. package/claude/skills/review-ui/references/close-out.md +1 -1
  17. package/claude/skills/review-ui/references/drive-copy.md +38 -0
  18. package/claude/skills/review-ui/references/post.md +24 -3
  19. package/claude/skills/review-ui/references/verb-absent.md +18 -0
  20. package/claude/skills/role-orchestrator/SKILL.md +3 -2
  21. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -1
  22. package/claude/skills/role-orchestrator/references/orchestrator-hand-launch.md +60 -0
  23. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +5 -0
  24. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +1 -1
  25. package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +1 -1
  26. package/claude/skills/role-planner/SKILL.md +1 -1
  27. package/claude/skills/role-worker/SKILL.md +4 -2
  28. package/claude/skills/session-compact/references/handoff-note.md +3 -1
  29. package/claude/skills/session-map/references/session.md +1 -1
  30. package/claude/skills/task-board/references/board.md +2 -2
  31. package/claude/skills/task-board/references/tasks.md +1 -1
  32. package/docs/agents/records.md +1 -1
  33. package/docs/workflow/parallel-features.md +2 -2
  34. package/package.json +1 -1
  35. package/src/commands/pr.ts +411 -23
  36. package/src/commands/records.ts +45 -8
  37. package/src/commands/tasks.ts +12 -0
  38. package/src/hooks/post-merge.ts +11 -0
  39. package/src/pr/drive-copy.ts +218 -0
  40. package/src/pr/local.ts +26 -0
  41. package/src/pr/rest.ts +2 -0
  42. package/src/records/structure.ts +147 -0
  43. package/src/records/validate.ts +113 -12
  44. package/src/tasks/archive.ts +137 -4
  45. package/src/tasks/board.ts +68 -13
  46. package/src/tasks/validate.ts +39 -2
  47. package/tooling/base/configs/.github/workflows/pr-local-preview.yml +1 -1
@@ -20,7 +20,7 @@ gh api --paginate 'repos/{owner}/{repo}/pulls/<number>/reviews?per_page=100' --j
20
20
 
21
21
  ## Write the replacement
22
22
 
23
- Write the full body for this pass to the file `post.md` names, with the heading, the count line, and the Passed table regenerated from what this pass drove. Patching only the marker would leave a count line and an address naming the old head.
23
+ Write the full body for this pass to the file `post.md` names, with the heading, the count line, and the Passed table regenerated from what this pass drove. Patching only the marker would leave a count line and an address naming the old head. A loopback address reads as a local preview here too, as on a first post.
24
24
 
25
25
  Add one line under the count line, reading the prior head off the standing verdict's marker and the commit count off the compare:
26
26
 
@@ -0,0 +1,38 @@
1
+ ---
2
+ title: Drive a throwaway copy
3
+ description: Steps 4 and 7 of review-ui, which rebuild the copy a preview saves into before the browser opens and check the worker's worktree for a tracked change before the post
4
+ ---
5
+
6
+ # Drive a throwaway copy
7
+
8
+ Steps 4 and 7 of `review-ui`. A preview that saves what the pass drives would otherwise write the worker's tracked files, and a drag would land in a committed fixture.
9
+
10
+ A project names the folders its preview writes in `canon/config/preview.json`. The worker starts its server reading a copy of them, and the pass rebuilds that copy at every head so the last pass's drags never reach this one.
11
+
12
+ ## Before the browser opens
13
+
14
+ Rebuild the copy and record the worktree's state:
15
+
16
+ ```bash
17
+ canon pr drive-copy <number> --baseline --json
18
+ ```
19
+
20
+ Branch on `reason` rather than on the exit code.
21
+
22
+ - `copied` means the address serves a throwaway copy rebuilt from the worker's worktree. Keep `copied` for the comment.
23
+ - `no-config` means the project declares no preview writes, so drive as usual. The check below still runs.
24
+ - `no-worktree` means no local worktree holds the branch, as for a cloud worker's. Drive, and say in the comment that the worktree check did not run.
25
+ - Any other refusal stops the pass with nothing posted, naming the `reason` and `message` for the dispatcher, since a broken `canon/config/preview.json` would send the drives into the real folders
26
+
27
+ ## Before the post
28
+
29
+ At the start of Step 7, with the browser closed since Step 6, read whether the drives changed anything git tracks in the worker's worktree:
30
+
31
+ ```bash
32
+ canon pr drive-copy <number> --check --json
33
+ ```
34
+
35
+ - `clean` continues with the post
36
+ - `changed` stops the pass. Step 7 posts it open with every path in `paths` named, and Step 8 ticks nothing, since the boxes ran against a tree the drives rewrote.
37
+ - Leave each changed path as it stands. The worktree is the worker's, so the pass reports a change and never reverts it.
38
+ - A refusal, `no-worktree` and `no-baseline` among them, means the check did not run. Say so in the comment rather than reading it as clean.
@@ -11,7 +11,7 @@ Step 7 of `review-ui`.
11
11
 
12
12
  The heading reports whether the branch owes anything, the way the code review's does.
13
13
 
14
- - `## UI review` when a box failed, the sweep found a console error or an overflow, a box was not driven for a cause on the page, or the pass drove no box at all.
14
+ - `## UI review` when a box failed, the sweep found a console error or an overflow, the worktree check read `changed`, a box was not driven for a cause on the page, or the pass drove no box at all.
15
15
  - `## UI review closed` otherwise.
16
16
 
17
17
  A needs-eyes box leaves the review closed, and so does a box not driven for a cause off the page, such as an address that did not answer or a `(local only)` box on a hosted preview, as long as something else was driven.
@@ -24,7 +24,7 @@ A needs-eyes box leaves the review closed, and so does a box not driven for a ca
24
24
 
25
25
  A pass with 0 failed, 0 need your eyes, 0 passed, and any box not driven looked at nothing, so it has not covered the head. It takes `## UI review` whatever the cause, including a checklist of only `(local only)` boxes on a hosted preview.
26
26
 
27
- - Put a line naming the cause directly under the count line, such as the addresses tried and what each returned, ahead of the address line
27
+ - Put a line naming the cause directly under the count line, such as the addresses tried and what each returned, ahead of the address line, naming a loopback address as A loopback address says
28
28
  - The cure is a re-drive on an address that answers, which Step 1 of `review-ui` allows past this verdict
29
29
  - Message the controller with the cause and send the worker no `review-address`, since the verdict holds no box a fix worker could change
30
30
  - `canon pr lift` refuses a close of this shape as `ui-undriven`, so a violation holds the draft mark
@@ -47,6 +47,10 @@ Driven at `<short-sha>` on <address>, rendered by <renderer>.
47
47
 
48
48
  ![box <n>](link)
49
49
 
50
+ ### Changed in the worktree
51
+
52
+ - `<path>`
53
+
50
54
  ### Needs your eyes
51
55
 
52
56
  **<n>. <box> (taste)**
@@ -90,10 +94,19 @@ The post takes the length limits alone, with a box's reason under 350 characters
90
94
 
91
95
  The body opens on its count line, in that order, so the operator reads what they owe before anything else. The second line carries the address, the head, and the renderer, and says the address was driven unverified when it came from the local rung or the launch.
92
96
 
93
- - Drop each section holding no box, along with its heading. Order is fixed: Failed, Needs your eyes, Not driven, then the folded blocks.
97
+ - Drop each section holding no box, along with its heading. The order is fixed as Failed, Changed in the worktree, Needs your eyes, Not driven, then the folded blocks.
94
98
  - A box takes exactly one section by its verdict, so a box that fails and also ends in `(taste)` appears once, under Failed
95
99
  - Quote page text in backticks as content, never as the pass's own words
96
100
 
101
+ ### A loopback address
102
+
103
+ A loopback address has `localhost`, `127.0.0.1`, `0.0.0.0`, or `[::1]` as its host, with any port. It never appears in the post, so the second line reads ``Driven at `<short-sha>` on a local preview, unverified, rendered by <renderer>.`` instead.
104
+
105
+ - The cause line of a pass that drove no box, and every Not driven entry, name a loopback address it tried as a local preview with its port, such as `a local preview on port 4211`
106
+ - A hosted address posts as written, since it outlives the session, and the close drops it from the evidence comment
107
+ - The evidence comment carries the local link while the pull request is open, for an operator on the same machine
108
+ - `canon pr local --remove` masks a loopback address left on a review's `Driven at` line when the pull request closes
109
+
97
110
  ### A failed box
98
111
 
99
112
  A box in a section opens on its number and the box as written, in bold, with the reading beneath it in words. A failed box that rests on a read has no image.
@@ -102,6 +115,14 @@ A box in a section opens on its number and the box as written, in bold, with the
102
115
  - That link names the frame's path on the frames branch, which stays put while other pull requests' frames are dropped and carries this pass's stamp
103
116
  - A later pass therefore never changes the image
104
117
 
118
+ ### The drive copy and the worktree check
119
+
120
+ The two `canon pr drive-copy` reads at the start of Steps 4 and 7 each leave a mark on the body, so the operator knows what the drives touched.
121
+
122
+ - When Step 4 read `copied`, add `Drove a throwaway copy of <copied paths>.` as its own line directly under the address line, naming each path in backticks
123
+ - When Step 7's check read `changed`, list every path from `paths` under Changed in the worktree, in backticks, with no reading beside it. The heading opens, and Step 8 ticks nothing.
124
+ - When either read refused, add `Worktree check did not run, <reason>.` on a line of its own under the address line, so a skipped check never reads as a clean one
125
+
105
126
  ### A needs-eyes box
106
127
 
107
128
  A needs-eyes box shows its dark and light shots in a two-cell table ahead of anything else it carries, since the operator decides from them without opening the app. A box whose evidence holds one theme still renders both cells.
@@ -0,0 +1,18 @@
1
+ ---
2
+ title: Resolve the pull request without the verb
3
+ description: Step 1 of review-ui on a target whose CLI predates canon pr head, which reads the number and the head straight off the REST pulls endpoint
4
+ ---
5
+
6
+ # Resolve the pull request without the verb
7
+
8
+ Step 1 of `review-ui`, read when `canon pr head` returns no record.
9
+
10
+ Every read here runs on REST, since `gh pr` runs on GraphQL and a cloud session's proxy refuses it. A target whose CLI predates the verb gets no record back, so read the pull request directly:
11
+
12
+ ```bash
13
+ gh api "repos/{owner}/{repo}/pulls?head={owner}:$(git branch --show-current | jq -Rr @uri)&state=open" --jq '.[].number'
14
+ gh api repos/{owner}/{repo}/pulls/<number> --jq .head.sha
15
+ ```
16
+
17
+ - With no number named, take the open pull requests on the current branch, and stop when that prints anything but one number
18
+ - A named number skips that lookup, since this checkout's branch need not be the pull request's
@@ -38,10 +38,11 @@ No surface carries cross-version sequencing, so report none, since reasoning spa
38
38
 
39
39
  ## Runbooks on request
40
40
 
41
- A compaction is a moment this skill cannot detect, so the human asks for each side of it and this skill reads the matching runbook.
41
+ The human asks for each side of a compaction, and this skill reads the runbook.
42
42
 
43
43
  - On a request to write the handoff, follow `${CLAUDE_SKILL_DIR}/references/orchestrator-handoff.md`. It sends the generic half to `canon:session-map` and adds decisions taken under delegated authority.
44
44
  - That map follows `${CLAUDE_SKILL_DIR}/../session-map/references/session.md`. Write nothing to the handoff that the board, a task file, or a groundwork folder already carries.
45
+ - Hand launch: `${CLAUDE_SKILL_DIR}/references/orchestrator-hand-launch.md`
45
46
  - A lesson about how to work goes to the owning task's Findings, as a bold-labeled line per `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md`
46
47
  - On a request to resume after a compaction, read `${CLAUDE_SKILL_DIR}/references/orchestrator-resume.md`, which reads that file back with the board and the groundwork behind the live work
47
48
 
@@ -108,7 +109,7 @@ The handback runbook holds how the message is addressed and worded, what to do w
108
109
 
109
110
  ### Constraints from the tracks in flight
110
111
 
111
- A plan written here targets a tree several branches are changing, so it names each in-flight track's file set as a constraint, read from that track's Touches column. State for each set which of the two acts it forbids, per Constraints in `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`.
112
+ A plan written here targets a tree several branches are changing, so it names each in-flight track's file set as a constraint, read from the plan its row links. State for each set which of the two acts it forbids, per Constraints in `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`.
112
113
 
113
114
  - A bare path list leaves the worker guessing, and can end up forbidding the repair of a citation the change broke
114
115
  - A shared path forbids an act only where the conflict check in `orchestrator-dispatch.md` holds the pair. Otherwise it names what the second branch to merge rebases over.
@@ -125,7 +125,7 @@ No practitioner source serializes on a shared file. Each builds in parallel and
125
125
  Read `canon sessions list --json` for the branches in flight as well as the board, since a hand-launched track carries no row, and read the plan of any branch no row names.
126
126
 
127
127
  - `canon tasks validate` and `canon tasks plan-reach` report the paths two rows share, a candidate list for the five holds and the paths a dispatch names
128
- - A shared folder means a Touches cell claimed it bare, so narrow that cell rather than reading the folder as a sweep
128
+ - A shared folder means a plan declared it bare, so narrow that plan's list rather than reading the folder as a sweep
129
129
  - A declared set predicts rather than bounds, since `canon:context-fold`, `canon:docs-sync`, and the drift stages write past every plan
130
130
  - Such a crossing changes what the second branch rebases over, not whether a row holds, and `canon tasks plan-reach` at `canon:git-ship` step 5 names it
131
131
 
@@ -0,0 +1,60 @@
1
+ ---
2
+ title: Orchestrator hand launch
3
+ description: The one lean prompts file the orchestrator writes when the operator launches sessions by hand, with its path, overwrite rule, shape, and a filled example
4
+ ---
5
+
6
+ Run this when the operator launches sessions by hand rather than through `claude --bg`. The rules every launch shares stay in `orchestrator-launch.md`.
7
+
8
+ # Hand launch
9
+
10
+ When the operator launches sessions by hand, write every prompt to one file, `.canon/tmp/dispatch/prompts.md`, and overwrite it at each wave. It lists every row with a session not yet launched. A row launched from an older copy and again from a newer one would build twice, and the branch check in `orchestrator-dispatch.md` catches that.
11
+
12
+ The operator opens the file, reads one lead line, and pastes one fence per session. The shape is fixed:
13
+
14
+ - An H1, `# Dispatch prompts`, then one lead line carrying the measured head, the merge order, and the folder to open each session in
15
+ - An H2 per task as `## <label>: <title>`, an H3 per role as `### Worker` or `### Reviewer`, a `Model: <model>.` line, and a `text` fence
16
+ - In the fence, the command at position zero, then `Controller: <sessionId>`, then only context the plan does not carry, one line each
17
+
18
+ Keep the command at position zero per `### Position zero` in `orchestrator-launch.md`, and carry only what `### What the brief may carry` there allows. Leave out the answer-slot default and anything else the plan states.
19
+
20
+ The `Controller:` line replaces the background template's message clauses, which `role-worker` and `session-relay` already own. `auto-ship` Step 0 loads `role-worker`, so the fence needs nothing more.
21
+
22
+ - A reviewer launched by hand takes the `### Reviewer` H3 under its row's H2, with the sibling facts `orchestrator-reviewer-launch.md` lists
23
+ - Its `<number>` exists once the worker opened its pull request, so a later wave carries `### Reviewer` alone under the row, and the launched worker's H3 is gone
24
+
25
+ The first wave carries the worker. The wave after its pull request opens carries the reviewer alone.
26
+
27
+ ````markdown
28
+ # Dispatch prompts
29
+
30
+ Head `<head>`. Merge `<label-a>` first, then `<label-b>`. Open each session in the repository root.
31
+
32
+ ## <label-b>: <title>
33
+
34
+ ### Worker
35
+
36
+ Model: opus.
37
+
38
+ ```text
39
+ /canon:auto-ship .canon/plans/feature-<slug>.md
40
+ Controller: <sessionId>
41
+ <branch-a> edits the section of <file> this plan leaves alone. Rebase over it when told.
42
+ ```
43
+ ````
44
+
45
+ ````markdown
46
+ # Dispatch prompts
47
+
48
+ Head `<head>`. Merge `<label-a>` first, then `<label-b>`. Open each session in the repository root.
49
+
50
+ ## <label-b>: <title>
51
+
52
+ ### Reviewer
53
+
54
+ Model: opus.
55
+
56
+ ```text
57
+ Run /canon:role-reviewer, then /canon:review-pr <number>
58
+ Controller: <sessionId>
59
+ ```
60
+ ````
@@ -12,6 +12,7 @@ Run this once `orchestrator-dispatch.md` has cleared a row, or from its planning
12
12
  - [Dispatch to address a review](#dispatch-to-address-a-review)
13
13
  - [Dispatch to plan a row](#dispatch-to-plan-a-row)
14
14
  - [Dispatch a talk session](#dispatch-a-talk-session)
15
+ - [Hand launch](#hand-launch)
15
16
  - [Dispatch a reviewer](#dispatch-a-reviewer)
16
17
 
17
18
  ## Dispatch
@@ -156,6 +157,10 @@ Read the session's `sessionId` off the launch output, carry it in this session's
156
157
 
157
158
  The map exists only once the handoff writes it, so this session's own launches, read with the prior map, are what the first wind-down reads talk sessions from.
158
159
 
160
+ ## Hand launch
161
+
162
+ Read `${CLAUDE_SKILL_DIR}/references/orchestrator-hand-launch.md` when the operator launches sessions by hand. It states the single prompts file at `.canon/tmp/dispatch/prompts.md`, the lean shape of each fence, and a filled example, while the background templates above keep their full controller paragraph.
163
+
159
164
  ## Dispatch a reviewer
160
165
 
161
166
  Read `${CLAUDE_SKILL_DIR}/references/orchestrator-reviewer-launch.md` when `orchestrator-review-dispatch.md` sends a pull request to a dispatched reviewer, for the code, cloud, and UI reviewer shapes.
@@ -32,7 +32,7 @@ The blocker cell states what the row waits on, and each kind is tested different
32
32
 
33
33
  ### A hold behind a track in flight
34
34
 
35
- The validator tests the file the cell cites against the Touches column of every `## Run now` row. A track that merged since the row was parked is no longer in flight, whatever the sets still share.
35
+ The validator tests the file the cell cites against the files every `## Run now` row's plans declare. A track that merged since the row was parked is no longer in flight, whatever the sets still share.
36
36
 
37
37
  - Re-test the cell under the conflict check in `orchestrator-dispatch.md` too, since a row parked on a shared file alone holds on nothing and moves up
38
38
  - A cell naming the file in prose rather than backticks cites nothing, so rewrite the hold in the board's spelling and the next run picks it up
@@ -9,7 +9,7 @@ Run this once `orchestrator-review-dispatch.md` has sent a pull request to a dis
9
9
 
10
10
  `review-pr` is a single pass rather than a role, so a launch naming it alone reaches no `role-reviewer` and owes no message. Reach the role first on this launch, the way the planning shape in `orchestrator-launch.md` reaches `role-planner`. `orchestrator-review-dispatch.md` decides whether a pull request takes this shape at all.
11
11
 
12
- No branch and no worktree are entered here. The reviewer reads the pull request at the head `review-pr` resolves and writes the comment and its body file alone.
12
+ No branch and no worktree are entered here. The reviewer reads the pull request at the head `review-pr` resolves and writes the comment and its body file alone. A reviewer the operator launches by hand takes the `### Reviewer` H3 that `orchestrator-hand-launch.md` states.
13
13
 
14
14
  ```bash
15
15
  claude --bg --model <model> -n "reviewer-<project>-<number>" "Run /canon:role-reviewer, then /canon:review-pr <number>. Your controller is the session whose sessionId is <dispatcher-id>. Resolve its current name from that id through canon sessions list --json, which carries sessionId per row, at the moment you send, and never resolve an addressee by name prefix. When the pass posts under ## Review, message the worker holding the branch to run review-address, carrying your own sessionId, and send it to the controller instead when no worker resolves. When it posts ## Review closed, run canon pr lift <number> --json and message the controller with the heading, the count line, and the reason the verb returned, unless that reason names the UI review as still open or missing. Message the controller again if you stop on a question."
@@ -39,7 +39,7 @@ Each item below is something a record needed and a launch string did not carry.
39
39
  - The task file, and its `## Findings` before deciding anything, since a row can carry its own disproof under a title still stating the original claim
40
40
  - The source files themselves, opened rather than summarized. Never a count quoted from the task file, which goes wrong or stale.
41
41
  - `CLAUDE.md` and the context entries the row touches, for the decision and the alternative it was taken against rather than for the decision alone
42
- - `.canon/tasks/priority.md` for the row's Touches column and its blocker. Read it for the shared-path lines and holds of the constraint block, never as a gate a plan must clear.
42
+ - `.canon/tasks/priority.md` for the row's blocker and the plan each running row links. Read those plans for the constraint block's shared paths and holds, never as a gate a plan must clear.
43
43
 
44
44
  ### Reads for a feature plan alone
45
45
 
@@ -50,6 +50,8 @@ A painted pull request needs a server in this worktree for `canon pr local` to f
50
50
  - Wait up to 60 seconds for the verb to read `ok`, then ship either way. A stack with no `dev` script gets no start, and `pr-evidence` reports `no-server`.
51
51
  - Never detach it with `nohup`, `setsid`, or `disown`, since the owning session is the stop. `canon worktrees reclaim` ends any listener left in a worktree it removes.
52
52
 
53
+ Run `canon pr drive-copy --json` before starting `dev`. On `copied`, start it with `env` set to `copyRoot`, so the operator's address serves the copy. On `no-config`, start it as it is. On a refusal, start nothing and send the controller the `reason`, since a broken config would send drives into tracked folders.
54
+
53
55
  A hand-run `git-ship` or `git-pr` starts no server, since only a worker has a lifetime to own one.
54
56
 
55
57
  ### Building against a design reference
@@ -86,7 +88,7 @@ The controlling session cannot watch this one build, so the messages below are o
86
88
 
87
89
  ### The pull request announcement
88
90
 
89
- Announce the pull request as the ship chain's pull request step returns, carrying the number, the branch, and the task it closes. That transition is the one moment only this session knows, and the controller's review poll no longer starts on a dispatch.
91
+ Announce the pull request as the ship chain's pull request step returns, carrying the number, the branch, the head sha, the CI state, the task it closes, and each departure from the plan. That transition is the one moment only this session knows, and the controller's review poll no longer starts on a dispatch.
90
92
 
91
93
  - Carry the reach in that message, being the paths this branch wrote that another live plan or `## Run now` row holds, and who holds each, which `canon tasks plan-reach` reports as `claimed`
92
94
  - Report each as a rebase the controller should order, never as a breach, since sharing a path is not a hold
@@ -123,7 +125,7 @@ A worker that improvised this read polled issue comments, never saw a UI close p
123
125
 
124
126
  ### Addressing
125
127
 
126
- Address the session the launch named, and the reviewer by its dispatch's sessionId, each as a `sessionId` rather than a name. `canon:session-relay` turns that id into an address and carries the send, falling back to a copyable block where no send tool exists.
128
+ Address the session the launch named, and the reviewer by its dispatch's sessionId, each as a `sessionId` rather than a name. `canon:session-relay` turns that id into an address and carries the send, falling back to a copyable block where no send tool exists. A `Controller: <sessionId>` line names the controller.
127
129
 
128
130
  Ask the operator when the launch named nobody and a person is there, putting candidate rows through the structured question surface. The halt is the cheaper error, since a message sent to the wrong session reports success and is lost.
129
131
 
@@ -39,7 +39,9 @@ Name what was not touched too. A session that changed no source says so, since a
39
39
 
40
40
  One bullet per decision, each naming what it beat. A decision with no rejected alternative reads as arbitrary and gets reopened by the next session that dislikes it.
41
41
 
42
- Where a decision rests on a measurement, give the number rather than the conclusion. `27 spacing values became 7` survives a reader disagreeing with it, where `the spacing was tidied` does not.
42
+ - Keep each bullet under 200 characters, and give a decision needing more an H3 of its own and a short paragraph
43
+ - The note holds to the six criteria under `### Structure` in `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`
44
+ - Where a decision rests on a measurement, give the number rather than the conclusion. `27 spacing values became 7` survives a reader disagreeing with it, where `the spacing was tidied` does not.
43
45
 
44
46
  ### `## What is open`
45
47
 
@@ -66,7 +66,7 @@ Three sections form the core, in this order, and every session fills all three.
66
66
  - `## Mistakes worth not repeating`: what went wrong and the rule it yields
67
67
  - `## Standing cautions`: commands that lie, tools that measure the wrong tree, and anything unbacked
68
68
 
69
- Add a section only for content that fits none of the three and would otherwise be lost.
69
+ Add a section only for content that fits none of the three and would otherwise be lost. Every section holds to the six criteria under `### Structure` in `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`.
70
70
 
71
71
  ## Writing one
72
72
 
@@ -91,9 +91,9 @@ A collision with running work blocks a task only where no merge settles it, bein
91
91
 
92
92
  ### The `Touches` cell
93
93
 
94
- A `Touches` cell is the first three paths of its plan's `**Files to touch:**` and a count of the rest, the union in line order for a sliced task. It states what the branch sets out to write, and a running branch outgrows it.
94
+ A `Touches` cell counts its plans' declared files and names the two fullest folders, as in ``12 files under `src/tasks/`, `claude/skills/` ``. The linked plan holds the file list, which the overlap checks read.
95
95
 
96
- - `canon tasks plan-reach <plan>` reads the current reach off the branch's diff
96
+ - `canon tasks plan-reach <plan>` reads the reach a running branch grew to
97
97
  - Change the plan's list to change the cell. A worker never edits this board.
98
98
 
99
99
  ### Cell width
@@ -83,7 +83,7 @@ Two headings, `## Outcomes` and `## Findings`. Outcomes are future and checkable
83
83
 
84
84
  - Add no third heading. Status stays inline on an outcome rather than becoming an "In progress" section.
85
85
  - Outcomes stay a checklist, since the board tools read them by their `- [ ]` line
86
- - Open each finding with a bold label naming its source and date, as in `- **planner, 2026-10-11:** <finding>`. Keep it to a few sentences, moving anything longer into the plan or groundwork folder.
86
+ - Open each finding with a bold label naming its source and date, as in `- **planner, 2026-10-11:** <finding>`. Past 200 characters it becomes a paragraph, and the plan holds anything longer.
87
87
 
88
88
  ### Sizing outcomes
89
89
 
@@ -57,5 +57,5 @@ Branch on `reason` in the `--json` record, not the exit code.
57
57
  ## See also
58
58
 
59
59
  - `canon records ordinal <kind> <slug> --claim`: reserve the next intake or groundwork ordinal
60
- - `canon records validate <kind> --help`: where a record folder disagrees with its standard
60
+ - `canon records validate <kind> [record] --help`: where a record disagrees with its standard, plus a `structure` report that never sets the exit
61
61
  - `canon records stale --help`: when the requirements record was last reviewed
@@ -32,7 +32,7 @@ Assert the orchestrator role in one warm session with `canon:role-orchestrator`.
32
32
 
33
33
  - To pick otherwise, rewrite that line as `overridden at execution to <pick>,` plus the measurement, and add one line to the open task's `## Findings`
34
34
 
35
- - `canon records validate plans` reports a plan breaking its standard, and takes `groundwork`, `intake`, and `teach`
35
+ - `canon records validate plans [plan]` reports a plan breaking its standard, plus `structure` misses that never set the exit, and takes `groundwork`, `intake`, and `teach`
36
36
  - `canon records push` and `canon records pull` carry `.canon/` to and from a private repository. See [records](../agents/records.md).
37
37
  - `canon records size`, `canon records prune-tmp`, and `canon records stale canonical` report folder size, idle scratch, and releases since the requirements review
38
38
  - `canon tasks archive` moves a shipped plan to `.canon/plans/archive/` and retargets the task's `Plan:` line
@@ -58,6 +58,6 @@ Merge order matters only when two branches touch the same file. Expect to rebase
58
58
  - A parked row is reported when its cited task reached the trunk, whether the cell links it or names its bare label
59
59
  - A group header must be the columns the group owns, a `Touches` cell must match its plan, and a `Waiting on` cell renders at most 120 characters
60
60
 
61
- `canon:task-board` creates a task file, and `canon:task-complete` marks outcomes. The `post-merge` hook archives the task a merged pull request closed, with its plan, and a `post-rewrite` hook does the same for a rebase pull. Both archive only when the merge carried a PR number and the task is closed.
61
+ `canon:task-board` creates a task file, and `canon:task-complete` marks outcomes. The `post-merge` hook archives the task a merged pull request closed, with its plan, and a `post-rewrite` hook does the same for a rebase pull. Both archive a closed task whose merge carried a PR number, and a slice merged before then moves only its own plan.
62
62
 
63
63
  `CANON_SKIP_RECLAIM`, `CANON_SKIP_UPGRADE`, and `CANON_SKIP_PLUGIN_UPDATE` turn off the three hook steps that act on the machine.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "5.48.0",
4
+ "version": "5.49.0",
5
5
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
6
6
  "license": "MIT",
7
7
  "bin": {