@erclx/canon 5.11.0 → 5.13.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 (72) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/api-design/REQUIREMENT.md +1 -1
  3. package/claude/skills/api-design/SKILL.md +1 -1
  4. package/claude/skills/api-design/references/adopted.md +1 -1
  5. package/claude/skills/auto-ship/REQUIREMENT.md +2 -0
  6. package/claude/skills/auto-ship/SKILL.md +7 -10
  7. package/claude/skills/auto-ship/references/failure-recovery.md +0 -1
  8. package/claude/skills/auto-ship/references/ui-checklist.md +21 -0
  9. package/claude/skills/build-in-slices/REQUIREMENT.md +45 -0
  10. package/claude/skills/build-in-slices/SKILL.md +94 -0
  11. package/claude/skills/build-in-slices/references/adopted.md +41 -0
  12. package/claude/skills/code-craft/REQUIREMENT.md +48 -0
  13. package/claude/skills/code-craft/SKILL.md +107 -0
  14. package/claude/skills/code-craft/references/adopted.md +52 -0
  15. package/claude/skills/codebase-layout/REQUIREMENT.md +1 -1
  16. package/claude/skills/git-pr/references/evidence.md +7 -7
  17. package/claude/skills/git-ship/REQUIREMENT.md +2 -1
  18. package/claude/skills/git-ship/SKILL.md +2 -2
  19. package/claude/skills/review-craft/REQUIREMENT.md +3 -0
  20. package/claude/skills/review-craft/SKILL.md +1 -1
  21. package/claude/skills/review-craft/references/adopted.md +8 -0
  22. package/claude/skills/review-craft/references/security.md +5 -0
  23. package/claude/skills/review-craft/references/ui.md +1 -2
  24. package/claude/skills/review-pr/SKILL.md +1 -1
  25. package/claude/skills/review-ui/REQUIREMENT.md +1 -0
  26. package/claude/skills/review-ui/SKILL.md +11 -0
  27. package/claude/skills/role-worker/REQUIREMENT.md +1 -0
  28. package/claude/skills/role-worker/SKILL.md +4 -0
  29. package/claude/skills/test-first/REQUIREMENT.md +3 -0
  30. package/claude/skills/test-first/SKILL.md +2 -1
  31. package/claude/skills/ui-checklist/SKILL.md +1 -18
  32. package/docs/agents/gate.md +1 -1
  33. package/docs/agents/hooks.md +2 -0
  34. package/docs/agents/install-and-sync.md +2 -2
  35. package/docs/agents/pr-evidence.md +26 -25
  36. package/docs/agents/pr-preview.md +40 -12
  37. package/docs/agents/tasks-validate.md +3 -3
  38. package/docs/target/migrations.md +1 -1
  39. package/docs/workflow/ai-workflow.md +2 -1
  40. package/docs/workflow/parallel-features.md +3 -3
  41. package/docs/workflow/skill-map.md +2 -0
  42. package/governance/rules/code/000-code.md +5 -1
  43. package/package.json +3 -4
  44. package/scripts/lib/sandbox-path.sh +1 -1
  45. package/src/claude/cases/misc.ts +10 -0
  46. package/src/claude/skills-audit.ts +2 -0
  47. package/src/commands/pr.ts +123 -21
  48. package/src/commands/tooling.ts +39 -16
  49. package/src/gate/stages.ts +10 -2
  50. package/src/pr/preview.ts +66 -0
  51. package/src/tasks/validate.ts +37 -27
  52. package/src/tooling/manifest.ts +16 -0
  53. package/src/tooling/verify-stack.ts +249 -0
  54. package/standards/tasks.md +1 -1
  55. package/tooling/base/configs/.husky/post-merge +30 -77
  56. package/tooling/base/configs/.husky/post-rewrite +3 -3
  57. package/tooling/base/reference.md +1 -1
  58. package/tooling/base/seeds/.cspell/tech-stack.txt +2 -0
  59. package/tooling/go/manifest.toml +3 -0
  60. package/tooling/go/reference.md +1 -0
  61. package/tooling/php/manifest.toml +3 -0
  62. package/tooling/php/reference.md +1 -0
  63. package/tooling/python/manifest.toml +3 -0
  64. package/tooling/python/reference.md +1 -1
  65. package/tooling/python/seeds/.cspell/tech-stack.txt +0 -1
  66. package/scripts/core/clean.sh +0 -36
  67. package/scripts/core/regen-agent-fixture.sh +0 -235
  68. package/scripts/core/regen-claude-copies.sh +0 -10
  69. package/scripts/core/regen-indexes.sh +0 -10
  70. package/scripts/core/snapshot.sh +0 -134
  71. package/scripts/core/update.sh +0 -35
  72. package/scripts/tooling/verify.sh +0 -184
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Rendered-surface evidence
3
- description: How git-pr finds the UI checklist and the local server, posts the evidence comparison or the checklist alone, and adds the preview address, for a change that touches a rendered surface
3
+ description: How git-pr finds the UI checklist and the local server, posts the evidence comparison or the checklist alone as the fallback for a git refusal, and adds the preview address, for a change that touches a rendered surface
4
4
  ---
5
5
 
6
6
  # Rendered-surface evidence
@@ -13,7 +13,7 @@ The rendered-surface step of `git-pr`, reached after the final command printed t
13
13
 
14
14
  When it exists, scan it against `${CLAUDE_SKILL_DIR}/../../standards/publish.md` before either step posts it, the same as the pull request body in the skill's pre-publish scan.
15
15
 
16
- Where it lands is decided by the evidence step below rather than here, since a checklist reads next to the screenshots it annotates and posting it on its own is the fallback for a branch that changed no screenshot.
16
+ Where it lands is decided by the evidence step below rather than here, since a checklist reads next to the screenshots it annotates and posting it on its own is the fallback for a refused render.
17
17
 
18
18
  ## Find the local server
19
19
 
@@ -31,7 +31,7 @@ The verb reads the listening sockets on this machine, keeps those whose process
31
31
 
32
32
  Pass `--local` to the evidence step only on an `ok` here. The binary answering `ok` is the one carrying the flag, so a binary lacking the verb never meets a flag it would reject.
33
33
 
34
- With both `--checklist` and `--local`, a diff carrying no evidence image still renders `ok`, with the link opening a comment the checklist closes, so a reviewer gets both in one place. A local link with neither an evidence image nor a checklist still reports `no-evidence`, which keeps a branch that changed no rendered surface from getting a comment holding a link and nothing else.
34
+ With a checklist, a diff carrying no evidence image still renders `ok`, and a local link rides in the same comment above the checklist, so a reviewer gets both in one place. A local link with neither an evidence image nor a checklist still reports `no-evidence`, which keeps a branch that changed no rendered surface from getting a comment holding a link and nothing else.
35
35
 
36
36
  A seeded workflow replaces the line with a note once the pull request closes, so the link does not outlive the branch it points at.
37
37
 
@@ -43,13 +43,13 @@ Run the verb once against the number the skill's final command printed. Pass `--
43
43
  canon pr evidence <number> --checklist <main-root>/.canon/tmp/handoff/ui-checklist/<slug>.md --local <url> --json
44
44
  ```
45
45
 
46
- One call answers both questions because a checklist does not decide `no-evidence`. The verb reports `no-evidence` on a diff carrying no evidence image whether or not a checklist came with it, so the branch below reads the same `reason` it would have read without the flag, and the checklist is folded in only on the path that has a comparison to fold it into. The local link is the one exception, stated in the step above.
46
+ One call answers both questions because a checklist decides `no-evidence`. The verb renders `ok` on a diff carrying a checklist and no evidence image, with a marked body holding the checklist, so the checklist always lands in the one comment a later call finds and edits. Without a checklist, a diff carrying no evidence image reports `no-evidence`.
47
47
 
48
48
  Pass `--checklist` only for a file that exists. The verb refuses as `unreadable-checklist` on a path it cannot read or one holding nothing, which is a caller bug rather than a transient failure, so stop and repair the path rather than posting a body with the checklist silently dropped.
49
49
 
50
50
  Read `reason` on the record rather than the exit code.
51
51
 
52
- - `no-evidence`: nothing changed under an `evidence/` segment, so there is no comparison to post and no body was rendered. Say nothing about the evidence and fall through to the checklist step below.
52
+ - `no-evidence`: nothing changed under an `evidence/` segment and no checklist was passed, so no body was rendered. Say nothing about the evidence and move on.
53
53
  - `ok`: write `body` to `.canon/tmp/pr/evidence/body-<number>.md` at the main worktree root (resolved the way `session-worktree` does), then post or update the comment:
54
54
 
55
55
  ```bash
@@ -68,7 +68,7 @@ Any other `reason` is one of the mirrored git refusals (`gh-missing`, `gh-failed
68
68
 
69
69
  ## Post the UI checklist alone
70
70
 
71
- Run this step only when the evidence step above did not carry the checklist, meaning it reported `no-evidence` or one of the git refusals, and a checklist file exists. A local link sends the checklist through the evidence step instead, so this step runs on a branch with no evidence image only when the local step found no server. Post it as its own comment on `<number>`:
71
+ Run this step only as the fallback, when the evidence step above reported one of the git refusals and a checklist file exists, since every other branch with a checklist rendered `ok` and carried it. The comment this posts carries no marker, so a later `canon pr evidence` call reports no `checklist` for it. Post it as its own comment on `<number>`:
72
72
 
73
73
  ```bash
74
74
  gh pr comment <number> --body-file <main-root>/.canon/tmp/handoff/ui-checklist/<slug>.md
@@ -92,7 +92,7 @@ Deleting the file is what makes the later re-render safe. `git-followup` re-runs
92
92
 
93
93
  ## Post the preview address
94
94
 
95
- Run this step only when the evidence step above returned `ok` or a checklist was posted, by either step. Either one means the pull request changes a rendered surface, and a reviewer holding a checklist with no screenshots needs the live page most. Otherwise skip it silently.
95
+ Run this step only when the evidence step above returned `ok` or a checklist was posted by the fallback. Either one means the pull request changes a rendered surface, and a reviewer holding a checklist with no screenshots needs the live page most. Otherwise skip it silently.
96
96
 
97
97
  The evidence comment is already posted, so the reviewer has the screenshots while the deploy runs. Mint the preview against the same `<number>`:
98
98
 
@@ -9,7 +9,7 @@ description: What the ship chain is for, the gaps it closes, and why it does not
9
9
 
10
10
  Without this skill, the post-feature sequence runs from memory. Doc sync gets skipped, so the pull request ships with planning docs describing the previous scope, or it runs after staging has already closed and its output never reaches a commit. Chaining by hand is also where a session narrates between steps, which turns one flow into a conversation and invites a decision at every boundary.
11
11
 
12
- The sequence also reaches the remote on work nothing re-checked. This skill is the resume point `auto-ship` names at four of its stop points, including the one a failed verify takes, so the fix the user makes by hand after that stop is pushed with no suite run against it. Every branch shipped so far passed that verify on its first attempt, which is why the path has produced no instance rather than being closed.
12
+ The sequence also reaches the remote on work nothing re-checked. This skill is the resume point `auto-ship` names at three of its stop points, including the one a failed verify takes, so the fix the user makes by hand after that stop is pushed with no suite run against it. Every branch shipped so far passed that verify on its first attempt, which is why the path has produced no instance rather than being closed.
13
13
 
14
14
  ## Must
15
15
 
@@ -17,6 +17,7 @@ The sequence also reaches the remote on work nothing re-checked. This skill is t
17
17
  - Say so and continue when the project names no verify command, since silence there reads as a suite that passed
18
18
  - Invoke each step through the Skill tool in the stated order and continue without waiting
19
19
  - Stage after the sync skills write, so what they produced reaches a commit
20
+ - Reach the pull request on a branch whose work is already committed and whose sync steps wrote nothing, since `git-stage` stops on an empty index and a session building in slices leaves one
20
21
  - Emit no text between steps. The sequence is the unit and prose inside it reopens settled decisions.
21
22
  - Name the one point a wrapping caller may act at, so a chain built on this one is not left to pick a gap of its own
22
23
  - Name the condition under which the closing block is not emitted, since a caller that closes on its own block leaves two instructions about the last line and nothing deciding between them
@@ -15,7 +15,7 @@ Run the verify commands `CLAUDE.md` names (lint, typecheck, tests) before the se
15
15
 
16
16
  When `CLAUDE.md` names no verify command, say so on one line and continue. A project with no suite is not a project with a failing one.
17
17
 
18
- Verify runs ahead of the sync skills so a stop leaves the tree exactly as the user left it. Re-running a suite the caller already ran costs one command, and the path it closes is the one that has no other guard: `auto-ship` verifies at its own Step 3 and then hands four of its stop points straight back here, so a fix made by hand after one of those stops otherwise reaches the remote with nothing re-run.
18
+ Verify runs ahead of the sync skills so a stop leaves the tree exactly as the user left it. Re-running a suite the caller already ran costs one command, and the path it closes is the one that has no other guard: `auto-ship` verifies at its own Step 3 and then hands three of its stop points straight back here, so a fix made by hand after one of those stops otherwise reaches the remote with nothing re-run.
19
19
 
20
20
  ## Pre-check
21
21
 
@@ -28,7 +28,7 @@ Run `git diff --cached --name-only 2>/dev/null` to check for staged files. If ou
28
28
  3. Invoke `canon:docs-sync` to sync public docs against changes since main
29
29
  4. Run `git add -A` to stage any files the sync skills wrote
30
30
  5. Run `canon tasks plan-reach <plan> --json` and report both lists it carries. Name the plan this branch built under, by path or by slug. Read `claimed` first and say who holds each path, since that is the half a reader acts on, then say how many of the changed paths `undeclared` names. Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero. This step reports and never stops the sequence.
31
- 6. Invoke `canon:git-stage` to group staged changes and commit by concern
31
+ 6. Invoke `canon:git-stage` to group staged changes and commit by concern, only when `git diff --cached --name-only` prints a path. When it prints nothing, the work already reached history in slices, so continue to step 7.
32
32
  7. Invoke `canon:git-branch` to rename branch to match conventional format
33
33
  8. Invoke `canon:git-pr` to push branch and open pull request
34
34
  9. After the PR opens, watch CI in the background, so the session stays reachable while CI runs. Read `### Watching CI` below for the commands and the wake.
@@ -13,6 +13,8 @@ The miss is measured. On a seeded branch that deleted a dirty-tree guard while t
13
13
 
14
14
  Rendered output is the larger hole. `git-pr` posts an `## Evidence` comment through `canon pr evidence`, carrying base and head screenshots and a checklist a reviewer ticks. The pull request review named that heading only in its heading set, and no step opened an image. On a seeded pull request whose narrow head screenshot shows the navigation running off the edge while a ticked box claims it collapses, the review filed the finding off the stylesheet diff and said it had not rendered the page. On a styling change with no screenshot at all, on a project whose trunk keeps them, it did not mention the absence.
15
15
 
16
+ A dependency upgrade is not a measured miss. On 2026-10-02 a seeded branch bumped one dependency across a major version beside a second, with a lockfile adding a transitive package that carried an install script, and the body without a dependency reference reported it. The run that measured it passed every assertion, including a graded finding naming the upgrade, so the case is a floor and no reference exists for it. An earlier run matched only a loose pattern and left no report to read, so it counts for nothing. The security reference still had no threat-model step and no class for a delete or clean on a derived path, both groundwork lifts with no miss to measure.
17
+
16
18
  The axes also said nothing about a change that passes by lowering the bar it has to clear, such as a test skipped, a threshold moved, or a path excluded from a checker, nothing about performance, and one sentence about design with no procedure behind it. A refactor that only moved complexity read as a design gain, and a removal could call code dead without reading why it was added. The body carried no excuses, red flags, or closing checklist, so a session about to stop at the hunk met no answer first.
17
19
 
18
20
  ## Must
@@ -27,6 +29,7 @@ The axes also said nothing about a change that passes by lowering the bar it has
27
29
  - State a design finding as a concrete cost, so it survives the procedure's filter against subjective suggestions
28
30
  - Carry the evidence bar: confirm against the file at the reviewed head, quote a cited rule, name the breaking input, and drop what a gate owns or what rests on unread state
29
31
  - Carry security as classes with an exclusion list, in a reference loaded when the diff reaches one
32
+ - Open the security reference with a threat model drawn from the diff, and carry destructive operations on a derived path as a class, so a cleanup deleting an unchecked variable's glob is read for the empty-value collapse
30
33
  - Open the evidence images on a rendered change through `git show` at the row's own sha, never through the embedded address, within a stated read budget
31
34
  - Test each ticked checklist box against an opened image, and name a ticked box no image covers as untested
32
35
  - Flag a rendered change that carries no screenshot, as a finding where the trunk already keeps them and as a question elsewhere
@@ -36,7 +36,7 @@ An axis whose condition the diff meets is not optional. When the security or the
36
36
 
37
37
  Read each of these when the diff reaches what it names.
38
38
 
39
- 7. **Security.** Read `${CLAUDE_SKILL_DIR}/references/security.md` when the diff runs a command, writes a file, handles input from outside the process, touches a secret, adds a dependency, or changes a default a consumer installs.
39
+ 7. **Security.** Read `${CLAUDE_SKILL_DIR}/references/security.md` when the diff runs a command, writes or deletes a file, handles input from outside the process, touches a secret, adds a dependency, or changes a default a consumer installs.
40
40
  8. **Rendered output.** Read `${CLAUDE_SKILL_DIR}/references/ui.md` when the diff touches a file that paints, such as markup, a stylesheet, a component, a template, or a design token, or when the pull request carries an evidence comment. A finding read off a stylesheet diff says what the author changed, and only the screenshot says what a user sees, so the reference opens the images and checks whether any exist.
41
41
  9. **Developer experience and operations.** Ports, hooks, scripts, config, and scaffold defaults a consumer installs. A new required step nobody documented, a default that fails on a clean machine, or a hook that slows every commit is a finding even when the code is correct.
42
42
  10. **Executable prose.** A skill body, a rule, or a prompt is instructions a model runs. Read it the way you read code: a step that races another, a precondition it never states, a branch with no exit, and two instructions that contradict each other are bugs.
@@ -34,6 +34,10 @@ From `addyosmani/agent-skills@2686b620`, `skills/constraint-driven-development/S
34
34
 
35
35
  From `addyosmani/agent-skills@2686b620`, `skills/code-review-and-quality/SKILL.md` for the axis itself, a call inside a loop and an unbounded fetch, and `skills/performance-optimization/SKILL.md` for keep or revert: measure before and after the same way, and a neutral result argues for removing the added complexity. The repeated-run requirement against noise is stated here so one run on each side reads as an anecdote.
36
36
 
37
+ ### Security additions
38
+
39
+ From `addyosmani/agent-skills@2686b620`, `skills/security-and-hardening/SKILL.md`: the threat model first and the derived-path rule. Adopted as the opening step of `references/security.md`, which names who supplies each input and what the code can reach before any class is read, and as the destructive-operations class, which asks for the empty-value collapse, the glob or symlink escape, the missing root check, and no read of what is there before the call. Both stay stack-neutral. No red run measures them, so they ship as lifts rather than as an answer to a miss.
40
+
37
41
  ### Closing sections
38
42
 
39
43
  The excuses, red flags, and closing checklist follow the practice skill shape every practice skill in this toolkit carries, taken from `addyosmani/agent-skills@2686b620`, [`docs/skill-anatomy.md`](https://github.com/addyosmani/agent-skills/blob/2686b620fc1fed2e8f60c704839c766b8594c6b6/docs/skill-anatomy.md). Each excuse row rebuts a reason a review stops at the hunk, such as passing tests against a test the diff skipped, rather than an objection nobody raised.
@@ -46,4 +50,8 @@ The excuses, red flags, and closing checklist follow the practice skill shape ev
46
50
 
47
51
  **Named structural remedies as a catalog.** A list of refactorings to propose reads as a menu a session fills in. A design finding states the cost, and the remedy is the author's.
48
52
 
53
+ **A dependency upgrade reference.** From `addyosmani/agent-skills@2686b620`, `skills/code-review-and-quality/SKILL.md`: read the changelog across the range, one dependency per change, and the lockfile diff. Declined because the seeded upgrade is not a miss. The body without such a reference reported the planted major bump and the install script in the lockfile on the second of two runs, which passed the assertion naming them. Revisit when a run on a changed lockfile misses what a reference would have named.
54
+
55
+ **STRIDE and abuse-case tests as a procedure.** They belong to a design session rather than a diff read, and the threat-model opening in the security reference takes the one step a diff supports, naming who supplies each input.
56
+
49
57
  **The web and database tables from `performance-optimization`.** They name a stack, and the axis stays stack-neutral until a target asks for a per-stack reference.
@@ -2,10 +2,15 @@
2
2
 
3
3
  Read each class against the diff. A class the diff does not reach is skipped, not reported as clean. Name the input that reaches the sink and the path it takes, since a class named with no path is a suspicion rather than a finding.
4
4
 
5
+ ## Open with the threat model
6
+
7
+ Before the classes, name from the diff who supplies each input and what the code can reach with it: a file, a process, a network, a credential. Skip a class that model rules out, and say which one and why, so the skip reads as a decision rather than an oversight.
8
+
5
9
  ## Classes to check
6
10
 
7
11
  - **Shell and command injection.** A value from an argument, an environment variable, a file, or a network response reaching a shell: through `eval`, an unquoted expansion, a string built into a command, or a subprocess call that takes a single command string. Quoting the variable at the call site is the fix to look for.
8
12
  - **Path traversal in a file write.** A path built from outside input and written without being resolved and checked against the directory it must stay inside. A `..` segment, an absolute path, and a symlink inside the target each escape a naive join.
13
+ - **Destructive operation on a derived path.** A delete, overwrite, move, or clean whose path is built from a variable, a config value, or a listing. Look for the empty or unset value that collapses it to a root, a glob or a symlink that carries it outside the folder it was meant for, no check that the resolved path sits at least one level below an expected root, and no read of what is there, owned by this tool or not, before the call. The fix to look for is resolve, require the root, then act. A write escaping its folder is path traversal. When a finding fits both, name the class whose fix applies.
9
14
  - **Secrets and personal data leaving the process.** A token, a key, a password, or personal data reaching a log line, an error message, a session transcript, a published page, a pull request comment, or a sync to a remote. Check what a debug line prints and what a verbose flag adds.
10
15
  - **Supply chain.** One-shot package execution of an unpinned version, which runs whatever the registry serves that day. A new dependency, and what its install scripts run. A lockfile that changes without a manifest change.
11
16
  - **Time of check to time of use on a file write.** An existence or permission check followed by a write to the same path in a later step, and a predictable name in a shared temporary directory. Another process can swap the path in between.
@@ -45,7 +45,7 @@ Take the sha from the row, so the base and head sides are exactly what the comme
45
45
 
46
46
  Open at most 12 images per pass, filled in this order:
47
47
 
48
- 1. Every image a ticked checklist box or the `Seen at:` line names
48
+ 1. Every image a ticked checklist box names
49
49
  2. The head side of each state at its narrowest and widest captured width, in one theme
50
50
  3. The base side, only for a state whose head image looks wrong or whose diff was meant to change appearance
51
51
 
@@ -65,7 +65,6 @@ The `## What to look at` block between the `pr-checklist` markers is the author'
65
65
 
66
66
  - Test each ticked box against an opened image showing the state and width it names. Flag a box whose image contradicts it, quoting the box.
67
67
  - A ticked box no image covers is untested rather than confirmed, and the finding says so.
68
- - Read the `**Widths:**` block's `Seen at:` line against the widths the images carry.
69
68
  - Test only the rows the current comment shows. A box ticked on an earlier head can describe images the comment no longer carries.
70
69
  - Leave unchecked boxes to the procedure's own read of the description.
71
70
 
@@ -73,7 +73,7 @@ Read each test file the branch added or changed against the final filter in `can
73
73
 
74
74
  Then read the description's `## Testing` section, which is the one part of a pull request body this session is positioned to falsify. The branch author cannot see what a sibling branch drove the same day, and the arm list is what settles the claim rather than the diff.
75
75
 
76
- Test every unchecked box against the testing discipline in `${CLAUDE_SKILL_DIR}/../../standards/pr.md`, which reserves an unchecked box for a capability the agent lacks. Raise the box when it names no human at all, when the human it names is a live agent session, or when it names a person for a step the repository ships a harness for, `scripts/sandbox/run.sh` and `scripts/eval/run.sh` being the two. Cost alone is not an answer, since authorizing a spend is the operator's and performing the run is not. A refusal the author actually met is an answer, and it names which one.
76
+ Test every unchecked box against the testing discipline in `${CLAUDE_SKILL_DIR}/../../standards/pr.md`, which reserves an unchecked box for a capability the agent lacks. Raise the box when it names no human at all, when the human it names is a live agent session, or when it names a person for a step the repository ships a harness for, such as `scripts/sandbox/run.sh`. Cost alone is not an answer, since authorizing a spend is the operator's and performing the run is not. A refusal the author actually met is an answer, and it names which one.
77
77
 
78
78
  Test every ticked box too, bounded to one naming a file or a command. Confirm what it names still exists at `<headRefOid>`, `git show <headRefOid>:<path>` for a file or `git grep <command> <headRefOid>` for a command, rather than re-running what it claims. A box naming neither stays untested, since nothing here can confirm a claim carrying no artifact. A box ticked before a later commit removed or replaced what it names is a `should-fix` finding on the body itself, filed under a `**PR body**` block ahead of the file blocks, since what it corrupts is the merge record rather than a file in the diff.
79
79
 
@@ -18,6 +18,7 @@ A verdict also goes stale without saying so. A push after the pass changes what
18
18
  - Drive every checklist box in the running app and give each exactly one verdict: pass or fail read off the page with the value quoted, pass or fail against a frame with what it shows, needs eyes for a box marked as taste, or not driven with the reason
19
19
  - Drive a box written before the drivable format all the same, naming the route or width it guessed, so no box is skipped silently
20
20
  - Read the checklist and the address from the evidence record rather than from the diff, the plan, or the author's description
21
+ - Drive only a build of the head the verdict names, and stop with nothing posted when the hosted preview was built from an older one
21
22
  - Take the hosted preview, then the local preview, then an address the launch names, and refuse when none exists
22
23
  - Report an address that does not answer as not driven rather than as a fail, and leave the review closed on a box no commit on the branch can settle, so an absent preview never holds the draft mark at every head
23
24
  - Sweep console errors, sideways overflow at each named width, and the focus and details probes over the states reached, and nothing beyond them
@@ -21,6 +21,7 @@ rather than inside it, so the two passes finish on their own clocks.
21
21
  - No address resolves under Step 3: stop and post nothing, `❌ No address to drive. PR #<number> carries no preview and no local preview, and the launch named none.`
22
22
  - The wrapper refuses to open the browser because its revision is absent: stop and post nothing, passing its install command to whoever launched the pass. Never install mid-pass.
23
23
  - The newest UI verdict is `## UI review closed` at the current head: stop, `The UI review already covers <short-sha>.` An open verdict at the head stops the same way only when nothing has arrived since it: drive again when a `## Review response` or `## Post-review findings` comment is newer than the verdict, or the launch names an address.
24
+ - The hosted preview was not built from the head: stop and post nothing, naming `built`, `tip`, and `canon pr preview <number> --json` for the dispatcher. A verdict posted against an older build carries a marker naming a head nobody drove.
24
25
  - Post and stop. Never merge, never tick a box on the evidence comment, and never lift the draft mark.
25
26
 
26
27
  ## Step 1: resolve the pull request
@@ -55,6 +56,16 @@ Take the first rung that answers, testing each with `curl -s -o /dev/null -w '%{
55
56
  2. The `local` address off the record.
56
57
  3. An address the launch names.
57
58
 
59
+ When the hosted rung answers, check that it was built from the head before driving it:
60
+
61
+ ```bash
62
+ canon pr preview <number> --check --json
63
+ ```
64
+
65
+ Branch on the record's `reason`. Only `fresh` continues. Any other stops with nothing posted, naming `built`, `tip`, and the next act: `stale` and `no-build` ask the dispatcher to run `canon pr preview <number> --json`, and `building` asks it to wait, since a mint is already running for the tip. This pass never mints. A `canon` that rejects `--check` predates the verb. Report that this pass needs a `canon` whose `pr preview` takes `--check`, naming the installed `canon --version`, and stop rather than reading the missing flag as `fresh`, the way Step 2 does for its own older binaries. A `reason` of `no-deploy`, `unfenced`, or `no-alias` means the project has no fenced deploy to read, so the hosted build cannot be verified. Stop on the hosted address then too, and say so.
66
+
67
+ The check covers the hosted address only. The local rung and an address the launch names have no deploy run to read, so say in the comment that they were driven unverified rather than implying the same coverage.
68
+
58
69
  An address that exists and does not answer is a different outcome from no address at all. When every rung that exists fails to answer, skip the drive, record each box as not driven with the addresses tried and what each returned, and go to Step 7. A local address on another machine is the usual cause, and it says nothing about the change.
59
70
 
60
71
  ## Step 4: open the browser
@@ -52,5 +52,6 @@ The draft mark fails from both ends. On 2026-08-31 four sessions re-drafted a pu
52
52
 
53
53
  - Entering the worktree, which `session-worktree` owns
54
54
  - The chain from implement through pull request, which `auto-ship` owns
55
+ - How the build moves through the plan, slice by slice with a commit each, which `build-in-slices` owns
55
56
  - Answering a posted review, which `review-address` owns
56
57
  - The controlling session's half of the channel, the review poll, and the watch beside it, which `role-orchestrator` and its runbooks hold
@@ -20,6 +20,10 @@ from the skill that owns it and invoke none of the three from here. The ordinary
20
20
  path arrives through `auto-ship` Step 0, which means that chain is already
21
21
  running and re-invoking it would restart the build.
22
22
 
23
+ This body holds what the session may write. How it builds, in committed slices
24
+ with an out-of-scope finding noted rather than fixed, is `build-in-slices`,
25
+ which `auto-ship` Step 2 loads.
26
+
23
27
  ## Where the session stands
24
28
 
25
29
  - Resolve `.canon/plans/`, `.canon/tasks/`, `.canon/review/`, `.canon/memory/`, and `.canon/ready/` at the main worktree root, never against the linked worktree this session builds in. Those folders are gitignored, so `git worktree add` never creates them and the copy beside the build is absent rather than empty.
@@ -14,6 +14,7 @@ Without this skill, a session implementing a planned change writes the test afte
14
14
  - Write or extend the test before the implementation it covers, for any behavior whose shape is already known
15
15
  - Run the test before implementing and confirm it fails for the missing behavior, not for an unrelated mistake in the test itself
16
16
  - Implement the minimum the current test demands, and start a new test before extending past it
17
+ - Load `canon:code-craft` before writing the implementation when its shape is still open and the session has not loaded the skill, so the shape of the code is settled before the body rather than during refactor
17
18
  - Re-run the test after implementing and confirm the pass is the one the test was written to prove
18
19
  - Refactor the implementation and the test once the suite is green, re-running it after each change, since the smallest change that passes leaves duplication and shortcuts the loop exists to remove before they reach history
19
20
 
@@ -33,4 +34,6 @@ Without this skill, a session implementing a planned change writes the test afte
33
34
  - Choosing the layer a test belongs at and judging whether it is a good test: `canon:test-craft`, loaded at step 1 rather than restated here
34
35
  - Finding the cause of an unexplained failure: `canon:systematic-debugging`
35
36
  - Confirming visual output after a change lands: `567-planning.md` states the order and `ui-checklist` writes the list to confirm against
37
+ - Shaping the function, class, or module the implementation writes: `canon:code-craft`, loaded at step 4 rather than restated here
38
+ - Committing the behavior as a slice and ordering the slices of a larger build: `canon:build-in-slices`
36
39
  - The mechanical audit of whether an implementation reached history ahead of its test: `canon gov test-order`, invoked from `auto-ship`
@@ -10,7 +10,7 @@ description: Write the failing test for a behavior before writing the code that
10
10
  1. Load `canon:test-craft` and pick the layer the test belongs at before writing it. Then find or write the test at that layer before touching the code it covers. Follow the pairing convention the project already uses, which is commonly a test sitting beside its subject under one name, so that `foo.ts` takes `foo.test.ts` beside it. Report it rather than proceeding silently when `canon:test-craft` does not resolve, and write the test at the smallest layer that can observe the behavior.
11
11
  2. Write the test against the behavior as it should exist once the change lands, naming the case for what it proves rather than for the function it calls.
12
12
  3. Run only that test and read the failure. Confirm it fails because the behavior is missing, not because of a typo, a missing import, or a signature the test itself got wrong. A test failing for the wrong reason still reads green the moment any code exists to satisfy that wrong reason, so treat any other failure as a defect in the test and fix the test before moving on.
13
- 4. Implement the smallest change that turns the failure into a pass. Do not add behavior the test does not ask for. A second behavior wants its own test written first, not a shortcut folded into the change already open.
13
+ 4. Load `canon:code-craft` and settle the shape of the code before writing it, unless the shape is already fixed or this session already loaded the skill. Report it rather than proceeding silently when `canon:code-craft` does not resolve, and write the plainest code the test asks for. Then implement the smallest change that turns the failure into a pass. Do not add behavior the test does not ask for. A second behavior wants its own test written first, not a shortcut folded into the change already open.
14
14
  5. Run the test again and confirm it passes for the reason it was written for, not by accident. Then run the surrounding suite so a change made to satisfy this test has not broken another.
15
15
  6. Refactor with the suite green. Remove the duplication and the shortcuts step 4 left, in the implementation and in the test, without changing what either does. Run the suite after each change, and undo a change that turns anything red rather than fixing forward.
16
16
 
@@ -23,4 +23,5 @@ description: Write the failing test for a behavior before writing the code that
23
23
 
24
24
  - Which layer a test belongs at and what makes it a good one. `canon:test-craft` owns that judgment, and step 1 loads it.
25
25
  - A failure with no known cause. Route through `canon:systematic-debugging` first, whose own fix phase already writes the reproducing test as part of finding the cause. This skill starts once the shape of the fix or the feature is already decided.
26
+ - Committing the green, refactored behavior as one slice of a larger build. `canon:build-in-slices` owns the commit and the order slices land in.
26
27
  - Confirming a change looks right once it has landed, which is a separate concern from whether the test passed and runs after implementation rather than before it.
@@ -34,30 +34,13 @@ Group visual items by feature area. Use `- [ ]` checkbox syntax, and write each
34
34
  Write each box so a session driving the running app can run it without guessing:
35
35
 
36
36
  - **Route.** Name where the box is driven, as a path or the control that reaches it. A box naming no route is driven from the address the driver opens, so a single-page app with no routing needs no invented path. Write "from the page the app opens on" when a state is reached by a sequence rather than a URL.
37
- - **Width.** Name the width a box is driven at only when the change can make it differ by width, the same test the widths block below uses for omission. A box about copy or color renders the same everywhere, so leave the width off rather than inventing one.
37
+ - **Width.** Name the width a box is driven at only when the change can make it differ by width, such as layout, spacing, or anything responsive. A box about copy, color, or a state that renders the same everywhere leaves the width off rather than inventing one.
38
38
  - **Verb.** Open on an action a driver performs, such as "scroll to", "click", "resize to", or "tab to". Never open on "read" or "look at", which name no action.
39
39
  - **Taste.** End the line with `(taste)` when the result is a judgment of look or feel rather than a fact a driver can observe. A driver reports such a box as needing a person's eyes instead of passing it.
40
40
  - **Setup.** A box needing a state the page does not open in states the setup inline. When a hosted preview cannot reach that state, such as a stopped service or a seeded record, end the line with `(local only)` instead.
41
41
 
42
42
  The checkbox stays `- [ ]` followed by the action and the result, since the evidence comment carries ticked boxes forward by that shape.
43
43
 
44
- ## The widths block
45
-
46
- A screenshot shows one width, so a reviewer cannot tell from the picture alone whether any other width was looked at. When a change touches layout, spacing, or anything responsive, put this block above the visual items, since it frames what the screenshots beside it can support:
47
-
48
- ```markdown
49
- **Widths:**
50
-
51
- Seen at: <the widths the reviewer looked at>
52
- Not seen at: <the widths nobody looked at, or "none">
53
- ```
54
-
55
- Leave both lines for the reviewer to complete. This session cannot verify a width, so the block asks what was seen and never asserts coverage. Fix no width list in the block, since which widths matter belongs to the project.
56
-
57
- Keep the `Not seen at:` line even when the answer is "none", so an omission reads as a claim rather than a gap.
58
-
59
- Omit the block when the change cannot differ by width, such as copy, color, or a state that renders the same everywhere, the way the missing-test heading is omitted below.
60
-
61
44
  ## The missing-test list
62
45
 
63
46
  Name every automatable change this branch ships with no test, one per line, with the layer `test-craft` would place it at. Do not write the test and do not read the project's test config to write one against.
@@ -5,7 +5,7 @@ description: Running the gate this repository verifies a branch with, what the s
5
5
 
6
6
  # Merge gate
7
7
 
8
- `canon gate run` runs every stage that guards a branch here, in order, stopping at the first stage that finds a fact. `bun run check` and `bun run check:ci` resolve to it, and `scripts/core/update.sh` calls it with `--nested` after a dependency update.
8
+ `canon gate run` runs every stage that guards a branch here, in order, stopping at the first stage that finds a fact. `bun run check` and `bun run check:ci` resolve to it, and the `update` package script runs `bun run check` after a dependency update.
9
9
 
10
10
  ```bash
11
11
  canon gate run
@@ -16,6 +16,8 @@ canon hooks post-merge --root "$root"
16
16
  | --------------- | ---------------------------------------------------------------------------------------------------- |
17
17
  | `--root <path>` | Main worktree root holding the board and the records, resolved from `git worktree list` when omitted |
18
18
 
19
+ The base stack ships the same call as its `.husky/post-merge`, behind the same board guard and upgrade bootstrap, so a target that syncs it runs these five steps.
20
+
19
21
  Exit codes: `0` on every path, since a hook failing aborts nothing useful. Lines go to stderr, and stdout stays empty. An ordinary merge prints nothing.
20
22
 
21
23
  ## The steps
@@ -139,8 +139,8 @@ since a target has no rule source of its own that ships nowhere.
139
139
 
140
140
  It takes `--root <path>` and defaults to the toolkit root, prints nothing on success, and
141
141
  reports the reason on stderr with exit 1 when the record names a stack or rule
142
- that does not resolve. `scripts/core/regen-claude-copies.sh` calls it, and the
143
- Consumed copies stage of `bun run check` asserts the result is committed.
142
+ that does not resolve. The Consumed copies stage of `bun run check` calls it and
143
+ asserts the result is committed.
144
144
 
145
145
  ## Whole-project sync
146
146
 
@@ -46,19 +46,19 @@ image or marks the case new.
46
46
 
47
47
  `reason` on the record is what a caller branches on, not the exit code:
48
48
 
49
- | Reason | What it means |
50
- | ---------------------- | ----------------------------------------------------------------------------- |
51
- | `ok` | A body was rendered. `commentId` is set when a marked comment already exists. |
52
- | `no-evidence` | Nothing in the diff carries an `evidence/` segment. An ordinary silent no-op. |
53
- | `unreadable-checklist` | `--checklist` named a path that could not be read, or one holding nothing. |
54
- | `gh-missing` | `gh` is not on the path, so no pull request could be resolved. |
55
- | `gh-failed` | `gh` could not answer for this repository or branch, or read its comments. |
56
- | `no-branch` | The pull request carries no head branch name. |
57
- | `no-object-head` | The pull request object reported no head commit. |
58
- | `no-base` | GitHub reported no merge base for the pull request. |
59
- | `unreadable-changes` | GitHub could not list what the pull request changed. |
60
- | `would-empty` | The render holds no cases and the marked comment holds some. No body. |
61
- | `check-writes` | `--check` was passed with `--preview`, `--local`, or `--checklist`. |
49
+ | Reason | What it means |
50
+ | ---------------------- | --------------------------------------------------------------------------------------- |
51
+ | `ok` | A body was rendered. `commentId` is set when a marked comment already exists. |
52
+ | `no-evidence` | No `evidence/` segment in the diff and no `--checklist` or `--preview`. A silent no-op. |
53
+ | `unreadable-checklist` | `--checklist` named a path that could not be read, or one holding nothing. |
54
+ | `gh-missing` | `gh` is not on the path, so no pull request could be resolved. |
55
+ | `gh-failed` | `gh` could not answer for this repository or branch, or read its comments. |
56
+ | `no-branch` | The pull request carries no head branch name. |
57
+ | `no-object-head` | The pull request object reported no head commit. |
58
+ | `no-base` | GitHub reported no merge base for the pull request. |
59
+ | `unreadable-changes` | GitHub could not list what the pull request changed. |
60
+ | `would-empty` | The render holds no cases and the marked comment holds some. No body. |
61
+ | `check-writes` | `--check` was passed with `--preview`, `--local`, or `--checklist`. |
62
62
 
63
63
  `no-evidence` is not a refusal a caller reports. A project on a stack that
64
64
  carries no evidence path, such as `base` or `python`, hits this reason on
@@ -76,8 +76,9 @@ shows a reviewer, each field present only when that comment holds it:
76
76
 
77
77
  These come from the comment already posted, never from the flags the call
78
78
  passed, so a skill driving a review reads which address and which checklist a
79
- reviewer has without parsing the comment itself. A checklist posted on its own
80
- carries no marker, so the record reports none of the three for it. Reading
79
+ reviewer has without parsing the comment itself. A checklist posted raw, which
80
+ only the fallback for a refused render does now, carries no marker, so the
81
+ record reports none of the three for it. Reading
81
82
  them means the thread is read before the `no-evidence` answer too, so an
82
83
  unreadable thread refuses as `gh-failed` on both reasons rather than
83
84
  reporting the fields absent.
@@ -156,11 +157,11 @@ already holds, the same way the preview address is carried. That is what keeps
156
157
  already ticked, since `git-pr` deletes the handoff once the first post reports
157
158
  success and no later call has a file to pass.
158
159
 
159
- The option does not decide `no-evidence` alone. A branch with a checklist and
160
- no changed evidence image still reports `no-evidence`, and the caller posts the
161
- checklist on its own, which is what it did before this option existed. A
162
- checklist passed together with `--local` is the exception, covered below. A path
163
- that cannot be read, or one holding nothing, refuses as
160
+ The option decides `no-evidence` on its own. A branch with a checklist and no
161
+ changed evidence image renders `ok` with a marked body holding the checklist,
162
+ so the checklist always lands in the one comment a later call finds and edits.
163
+ The raw post survives only as the caller's fallback when the render is refused.
164
+ A path that cannot be read, or one holding nothing, refuses as
164
165
  `unreadable-checklist` rather than rendering without it: the caller deletes the
165
166
  handoff once a post succeeds, so a silently dropped checklist is the only copy
166
167
  gone.
@@ -175,8 +176,8 @@ the local address the marked comment already holds, the same as the other two.
175
176
 
176
177
  The option does not turn `no-evidence` into `ok` on its own. A branch with a
177
178
  dev server running and no rendered change would otherwise get a comment holding
178
- a link and nothing else. Together with `--checklist` it does, so the link and
179
- the checklist land in one comment rather than the checklist going out alone.
179
+ a link and nothing else. Together with `--checklist` the link and the
180
+ checklist land in one comment.
180
181
  `canon docs pr-local` covers where the address comes from and how the line is
181
182
  removed at close.
182
183
 
@@ -214,9 +215,9 @@ those ask a read to write.
214
215
 
215
216
  The thread cannot separate a skipped preview step from a failed one. A pull
216
217
  request whose deploy run failed or timed out reads as owing a preview, and the
217
- session that ran it holds the cause. A checklist posted on its own carries no
218
- marker, so a branch with a checklist and no evidence image never reads as
219
- owing a preview, though `git-pr` runs the preview step for it. The deploy
218
+ session that ran it holds the cause. A checklist-only branch posts a marked
219
+ comment, so it reads as owing a preview once that comment exists and a deploy
220
+ workflow resolves. The deploy
220
221
  workflow is read from the checkout rather than the pull request's head, so a
221
222
  pull request that adds or removes its own deploy workflow reads against trunk's.
222
223
 
@@ -14,6 +14,7 @@ rather than screenshots and a checklist alone.
14
14
  ```bash
15
15
  canon pr preview --json
16
16
  canon pr preview 1341 --json --timeout 20
17
+ canon pr preview 1341 --check --json
17
18
  ```
18
19
 
19
20
  ## Which workflow it dispatches
@@ -61,22 +62,49 @@ keeps the match independent of the local clock.
61
62
 
62
63
  `reason` on the record is what a caller branches on, not the exit code:
63
64
 
64
- | Reason | What it means |
65
- | ------------- | -------------------------------------------------------------------------- |
66
- | `ok` | The preview was published. `url` carries the address and `runId` the run. |
67
- | `no-deploy` | No dispatchable workflow runs `pages deploy`. |
68
- | `unfenced` | The deploy passes no `--branch`, so nothing was dispatched. |
69
- | `no-alias` | The workflow prints no alias line, or the finished run's log carried none. |
70
- | `run-failed` | The deploy run finished without succeeding. `runId` names it. |
71
- | `timeout` | The run did not finish inside `--timeout` minutes, 15 by default. |
72
- | `bad-timeout` | `--timeout` was not a positive number. |
73
- | `gh-missing` | `gh` is not on the path. |
74
- | `gh-failed` | `gh` could not read the pull request, or list or dispatch the workflow. |
75
- | `no-branch` | The pull request carries no head branch name. |
65
+ | Reason | What it means |
66
+ | --------------- | -------------------------------------------------------------------------- |
67
+ | `ok` | The preview was published. `url` carries the address and `runId` the run. |
68
+ | `no-deploy` | No dispatchable workflow runs `pages deploy`. |
69
+ | `unfenced` | The deploy passes no `--branch`, so nothing was dispatched. |
70
+ | `no-alias` | The workflow prints no alias line, or the finished run's log carried none. |
71
+ | `run-failed` | The deploy run finished without succeeding. `runId` names it. |
72
+ | `timeout` | The run did not finish inside `--timeout` minutes, 15 by default. |
73
+ | `bad-timeout` | `--timeout` was not a positive number. |
74
+ | `gh-missing` | `gh` is not on the path. |
75
+ | `gh-failed` | `gh` could not read the pull request, or list or dispatch the workflow. |
76
+ | `no-branch` | The pull request carries no head branch name. |
77
+ | `check-timeout` | `--check` was combined with `--timeout`. |
76
78
 
77
79
  The exit is 0 on `ok` and 1 on every refusal. A `timeout` leaves the run
78
80
  going, so the preview may still land after the verb has given up on it.
79
81
 
82
+ ## Checking which head a preview was built from
83
+
84
+ `--check` dispatches nothing and waits on nothing. It lists the branch's
85
+ `workflow_dispatch` runs, takes the newest successful one by creation time, and
86
+ compares the head it ran at with the branch tip read from the remote. The tip
87
+ comes from the remote rather than the pull request object's `headRefOid`, which
88
+ lags a push. `--check` takes no `--timeout` and refuses one as `check-timeout`.
89
+
90
+ The alias serves the newest successful deployment for the branch and names no
91
+ sha, so the run listing is the only record of the built head. The
92
+ `workflow_dispatch` filter also drops the cleanup run a closed pull request
93
+ fires, which concludes `success` with its deploy skipped. A project that deploys
94
+ on `push` to a feature branch would need that filter widened.
95
+
96
+ | Reason | What it means |
97
+ | ---------- | --------------------------------------------------------------------- |
98
+ | `fresh` | The newest successful deploy built the tip. Exit 0. |
99
+ | `stale` | It built an earlier head. `built` and `tip` name both. Mint again. |
100
+ | `building` | A deploy of the tip is still running. Wait rather than minting again. |
101
+ | `no-build` | No deploy of the branch has succeeded. |
102
+
103
+ The record also carries `runId` for the deploy it read. The check covers the
104
+ hosted address only, so a local address has no run to read and stays unverified.
105
+ `review-ui` runs it before driving a hosted preview and posts nothing on any
106
+ reason but `fresh`.
107
+
80
108
  ## How the address reaches the pull request
81
109
 
82
110
  `canon pr evidence --preview <url>` puts `**Preview:** <url>` on the first line
@@ -17,7 +17,7 @@ Seven checks run. Collisions reaches one half of the `## Run now` test the board
17
17
  | Check | What it reports |
18
18
  | ---------- | ----------------------------------------------------------------------------------------------------------------------- |
19
19
  | Shape | A row whose cell count disagrees with its table's header, or one stranded behind a table a blank line already closed |
20
- | Plan | A row whose plan citation disagrees with its group or its task file, or a task whose `Plan:` line links several plans |
20
+ | Plan | A row whose plan citation disagrees with its group or its task file, or a task whose `Plan:` line lists one plan twice |
21
21
  | Mapping | A row or backlog line naming no task file |
22
22
  | Grouping | A task carrying a row in more than one readiness group, or on both surfaces |
23
23
  | Ordering | A `## Needs a plan` row whose stated position disagrees with where it actually sits, or which states no position at all |
@@ -26,11 +26,11 @@ Seven checks run. Collisions reaches one half of the `## Run now` test the board
26
26
 
27
27
  Shape runs before any other check reads a row, since a row failing it carries no dependable fields for the rest to check. A blank or prose line closes the table above it, so the walk treats the next pipe line as a fresh header candidate rather than as a continuation. That candidate counts as a header only when the line behind it is a separator carrying the same cell count, and one that fails is `row-untabled`, stranded behind a table that already closed. Cell count still has to match the header on every row that clears that test, and a row whose count disagrees is `row-misshapen`, the shape a dropped pipe or a merged column produces.
28
28
 
29
- The Plan check reads the row and the task file both, because the two are written by different hands and only the task's own `Plan:` line reaches the archive. A row carrying a plan whose task states none is `plan-uncited`, and a pair naming two different plans is `plan-mismatched`. Both sides resolve against the board and against the project root before they compare, so a row writing `../plans/x.md` and a task writing `.canon/plans/x.md` name one file rather than two.
29
+ The Plan check reads the row and the task file both, because the two are written by different hands and only the task's own `Plan:` line reaches the archive. A row carrying a plan whose task states none is `plan-uncited`, and a pair where the row's plan is none of the task's links is `plan-mismatched`. Both sides resolve against the board and against the project root before they compare, so a row writing `../plans/x.md` and a task writing `.canon/plans/x.md` name one file rather than two.
30
30
 
31
31
  The other two groups have no Plan column, so the same check reads the task file's own `Plan:` line against what the group name claims. A `## Needs a plan` row whose task cites a plan that sits live on disk is `plan-parked`, since the group says no such plan exists and the row is either misplaced or its plan is stale and belongs in the archive. An `## Up next` row whose task cites no live plan is `plan-absent`, since that group says one is written. A plan already archived, or a cited path with no file behind it, reads as not live on both sides. A row whose task file is gone is left to `task-unresolved`. An `## Up next` row with nothing left to build, waiting on an act only a person can take, reports `plan-absent` once its plan archives while the task stays live. That report is expected, since the check reads the task file and not the `Waiting on` prose, so a row in that state moves to `## Needs a plan` or closes.
32
32
 
33
- A task whose `Plan:` line links more than one plan is `plan-several` in every group, and it replaces the `plan-uncited` or `plan-absent` report the line would otherwise draw. The line is there, so saying it is missing would send the reader looking for the wrong fault. The line breaks the one-plan-per-task rule the tasks standard states instead, and `plan-mismatched` does not fit either, since that kind means the row and the task disagree while here the task alone is at fault. The message lists every plan the line links, and the fix is to split the task or drop the extra links.
33
+ A `Plan:` line linking several distinct plans is the shape a sliced task carries, so the checks read the whole line rather than reporting it. A `## Run now` row passes when its plan is any one of the links, and a parked row is judged on whether any link is live: `## Up next` needs one, and `## Needs a plan` reports `plan-parked` when one is. The check adds no sliced marker, so an unsliced task given a second link by hand reads as sliced. `plan-several` now means a line listing one plan twice, compared by the file each link resolves to, so `../plans/x.md` beside `.canon/plans/x.md` counts. It is reported in every group and replaces the `plan-uncited` or `plan-absent` report the line would otherwise draw. The message lists the links, and the fix is to drop the repeat.
34
34
 
35
35
  Mapping spans two surfaces, because a task sits on `priority.md` when it would plausibly be planned soon and on `backlog.md` otherwise. A row or a backlog line naming no task file is `task-unresolved`, and a file both surfaces name is `row-duplicated` for the reason a task in two groups is: it claims two things about itself and only one can hold. A task file neither surface names is `unplaced` rather than a finding, since that is the normal state between a session filing it and a live orchestrator placing it on the board. One check across both is what lets a task move between them without the move reading as a dropped file.
36
36
 
@@ -62,7 +62,7 @@ The first line reports the plan and the second applies it, relocating each rule
62
62
 
63
63
  Twenty-five plugin skills dropped their `claude-` prefix for two-word names, so `canon:claude-docs` answers as `canon:context-fold` and `canon:claude-tasks` as `canon:task-board`. A project that installed governance or tooling before that release holds files naming the old ones, and the plugin answers to none of them. A later release renamed `docs-fold` to `context-fold`, since the skill folds a session into the context entries and the task board rather than into `docs/`, and the same sweep carries a project across that rename too. <!-- canon-keep-retired -->
64
64
 
65
- Two of those files run rather than sit there. `.husky/post-merge` prints a command for a person to type, and `.claude/hooks/pr-create-log.sh` hands a session a message naming a skill, so a stale copy tells someone to invoke something that no longer exists. A rule under `.claude/rules/canon/core/` names skills too, though a rule is read rather than run.
65
+ Two of those files run rather than sit there. `.husky/post-merge` runs the `canon hooks post-merge` verb, so a stale copy of the hook calls a binary that may lack it, and `.claude/hooks/pr-create-log.sh` hands a session a message naming a skill, so a stale copy tells someone to invoke something that no longer exists. A rule under `.claude/rules/canon/core/` names skills too, though a rule is read rather than run.
66
66
 
67
67
  Resync what the toolkit owns, then sweep what the project wrote:
68
68
 
@@ -87,7 +87,8 @@ When features are independent, run them in parallel, one worktree and one sessio
87
87
  For features on a mature stack, chain the post-plan pipeline in one session. Approve the plan, invoke `canon:auto-ship`, and the skill runs implement → verify → review → ship sequentially.
88
88
 
89
89
  - Use when the plan is tight and the stack has real verify commands and test coverage
90
- - Autoship stops on: verify failure after one fix attempt, UI manual checklist non-empty, an inherited review finding above minor, no diff baseline resolving against `main`, an empty changed-file list, or hook failure
90
+ - Autoship stops on: verify failure after one fix attempt, an inherited review finding above minor, no diff baseline resolving against `main`, an empty changed-file list, or hook failure
91
+ - A produced UI checklist does not stop it. The checklist rides to the draft pull request's evidence comment, and the closing block names how many visual boxes are still unchecked and who owes them
91
92
  - Every stop leaves recoverable state. Fix and resume with `/git-ship`
92
93
  - Skip autoship for auth, migrations, security-sensitive changes, or work where the plan itself is uncertain
93
94