@erclx/canon 5.77.0 → 5.78.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 (77) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/auto-ship/references/ui-checklist.md +9 -1
  4. package/claude/skills/code-craft/REQUIREMENT.md +1 -1
  5. package/claude/skills/code-craft/SKILL.md +7 -0
  6. package/claude/skills/code-craft/references/adopted.md +8 -0
  7. package/claude/skills/pr-evidence/SKILL.md +6 -1
  8. package/claude/skills/pr-evidence/references/evidence.md +1 -0
  9. package/claude/skills/pr-evidence/references/reel.md +9 -1
  10. package/claude/skills/review-craft/references/ui.md +13 -1
  11. package/claude/skills/review-pr/references/body-variants.md +2 -0
  12. package/claude/skills/review-pr/references/close-out.md +2 -0
  13. package/claude/skills/review-pr/references/post.md +1 -0
  14. package/claude/skills/review-ui/REQUIREMENT.md +1 -0
  15. package/claude/skills/review-ui/SKILL.md +2 -2
  16. package/claude/skills/review-ui/references/driving.md +32 -4
  17. package/claude/skills/review-ui/references/post.md +6 -2
  18. package/claude/skills/role-orchestrator/SKILL.md +2 -2
  19. package/claude/skills/role-orchestrator/references/orchestrator-output.md +24 -1
  20. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +3 -2
  21. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +35 -4
  22. package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +1 -1
  23. package/claude/skills/role-worker/SKILL.md +1 -0
  24. package/claude/skills/test-craft/SKILL.md +1 -0
  25. package/claude/skills/ui-checklist/REQUIREMENT.md +1 -1
  26. package/claude/skills/ui-checklist/SKILL.md +2 -1
  27. package/claude/skills/ui-craft/references/capture.md +16 -0
  28. package/claude/skills/ui-film/REQUIREMENT.md +2 -2
  29. package/claude/skills/ui-film/SKILL.md +6 -1
  30. package/claude/skills/ui-film/references/compose-craft.md +68 -11
  31. package/claude/skills/ui-film/references/config.md +2 -2
  32. package/claude/skills/ui-film/references/handoff.md +1 -1
  33. package/claude/skills/ui-film/references/harness.md +71 -0
  34. package/claude/skills/ui-film/references/take-craft.md +42 -17
  35. package/docs/workflow/operating-model.md +6 -5
  36. package/docs/workflow/visual-design-workflow.md +2 -1
  37. package/governance/ledger/skills/code-craft/SKILL.md +9 -0
  38. package/governance/ledger/skills/test-craft/SKILL.md +1 -0
  39. package/governance/ledger/skills/ui-craft/capture.md +18 -5
  40. package/package.json +4 -3
  41. package/src/commands/pr.ts +117 -31
  42. package/src/commands/take.ts +12 -1
  43. package/src/design/components.ts +5 -107
  44. package/src/design/favicon.ts +21 -17
  45. package/src/design/render.ts +9 -27
  46. package/src/design/template.ts +42 -0
  47. package/src/design/templates/favicon.svg +17 -0
  48. package/src/design/templates/hand-drawn-figure.css +54 -0
  49. package/src/design/templates/preview.html +27 -0
  50. package/src/design/templates/scrollbar.css +29 -0
  51. package/src/design/templates/status.css +24 -0
  52. package/src/gate/embedded.ts +400 -0
  53. package/src/gate/stages.ts +8 -0
  54. package/src/pr/evidence.ts +43 -4
  55. package/src/pr/frames.ts +1 -6
  56. package/src/pr/lift.ts +77 -9
  57. package/src/pr/rest.ts +2 -0
  58. package/src/pr/review-scope.ts +28 -0
  59. package/src/pr/review-state.ts +5 -5
  60. package/src/pr/waiver.ts +45 -0
  61. package/src/take/camera.ts +337 -208
  62. package/src/take/film.ts +7 -1
  63. package/src/take/log.ts +43 -2
  64. package/src/take/perform.ts +85 -13
  65. package/src/take/plan.ts +26 -5
  66. package/src/take/reel.ts +30 -5
  67. package/src/take/screencast.ts +110 -3
  68. package/src/take/stepper.ts +24 -4
  69. package/src/commands/fixtures/gh.sh +0 -64
  70. package/src/take/fixtures/busy.html +0 -107
  71. package/src/take/fixtures/fade.html +0 -49
  72. package/src/take/fixtures/fetch.html +0 -38
  73. package/src/take/fixtures/input.html +0 -138
  74. package/src/take/zoom/NOTICE.md +0 -45
  75. package/src/take/zoom/math-utils.ts +0 -69
  76. package/src/take/zoom/region-utils.ts +0 -55
  77. package/src/take/zoom/spring.ts +0 -133
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.78.0](https://github.com/erclx/canon/compare/v5.77.0...v5.78.0) (2026-11-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * **capture:** owe a case for every added or altered rendered state ([#2876](https://github.com/erclx/canon/issues/2876)) ([e13daa2](https://github.com/erclx/canon/commit/e13daa25182da60b0654725326fd14d07f56c69d))
9
+ * **gate:** keep long pages, stylesheets, and scripts out of strings ([#2883](https://github.com/erclx/canon/issues/2883)) ([44ca538](https://github.com/erclx/canon/commit/44ca538cbcb982bc10b0964807e8ea319df5dc17))
10
+ * **orchestrate:** check painted pull requests for proof before review ([#2877](https://github.com/erclx/canon/issues/2877)) ([403a80d](https://github.com/erclx/canon/commit/403a80dd0ada3913f4bb7dac1336fc483b53c6e1))
11
+ * **pr:** bind the UI review only on a drivable checklist box ([#2880](https://github.com/erclx/canon/issues/2880)) ([3027274](https://github.com/erclx/canon/commit/3027274a1d97ec0c8016157f7278fc8ff30b4916))
12
+ * **pr:** refuse a lift on a painted path with no capture or waiver ([#2873](https://github.com/erclx/canon/issues/2873)) ([c05036e](https://github.com/erclx/canon/commit/c05036e2929572cd680a9ddc62e4d7e408b55b3a))
13
+ * **take:** align screencast moves to frames and calibrate the lag ([#2882](https://github.com/erclx/canon/issues/2882)) ([5f274b8](https://github.com/erclx/canon/commit/5f274b877a84496831f3db19917b0127aca3ad94))
14
+ * **take:** pace each press and time every zoom to a glide ([#2875](https://github.com/erclx/canon/issues/2875)) ([6319458](https://github.com/erclx/canon/commit/63194586f6e8e0a5566df872a5fbe4197ef7c09a))
15
+ * **ui-film:** add the film craft a launch film reshoot needed ([#2878](https://github.com/erclx/canon/issues/2878)) ([94ac7fc](https://github.com/erclx/canon/commit/94ac7fc2b8e6a3cdf2da67d13dc7d76fb850bc1d))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **take:** clear the cap timers so the command exits with filming ([#2879](https://github.com/erclx/canon/issues/2879)) ([8fefa21](https://github.com/erclx/canon/commit/8fefa21dbf357ab32c688a4dcc2e1c688c25cab2))
21
+ * **take:** encode the reel lossless so a fade leaves no ghost ([#2881](https://github.com/erclx/canon/issues/2881)) ([5cede29](https://github.com/erclx/canon/commit/5cede2913953c3dd041f2fe505565a036c0bb15b))
22
+
3
23
  ## [5.77.0](https://github.com/erclx/canon/compare/v5.76.0...v5.77.0) (2026-10-31)
4
24
 
5
25
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
4
- "version": "5.77.0",
4
+ "version": "5.78.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -14,10 +14,18 @@ The chain continues to Step 6 here in every run, dispatched or hand-run, since t
14
14
  Count two things in `.canon/tmp/handoff/ui-checklist/<slug>.md` at the main worktree root:
15
15
 
16
16
  - `<N>`, the `- [ ]` lines
17
- - `<M>`, how many of those end in `(taste)`
17
+ - `<M>`, how many of those carry `(taste)` among their trailing markers
18
18
 
19
19
  Hold both until the Output block. `pr-evidence` removes the file once it posts the evidence comment, so a count taken after Step 6 reads nothing.
20
20
 
21
+ ## Owed fixture states
22
+
23
+ Settle each state the checklist names before Step 6, since a box the driver cannot reach opens the UI review.
24
+
25
+ - Add the state to the drive fixture for each box ending in `(needs fixture)`, then drop the marker from the box
26
+ - Confirm every state the checklist names has a capture case, per the `canon:ui-craft` capture reference, and add the missing ones
27
+ - A project with no `canon/config/app-drive.toml` takes the setup inline on the box instead of a fixture file
28
+
21
29
  ## Reading the counts into the closing block
22
30
 
23
31
  The Output block's second line takes both counts. When every box is a taste box, `<N>` and `<M>` match and the boxes are owed to the operator alone, so say the operator and drop the UI reviewer clause.
@@ -16,7 +16,7 @@ Agents fail there in recorded ways. They copy where reuse belonged, spread one c
16
16
  - Open with the change test, naming the change most likely to come next and counting what it would touch before choosing a shape
17
17
  - State depth as rules a session can act on, covering a unit's interface being simpler than its work, inlined pass-throughs, and splitting by the decision hidden
18
18
  - State each tension as when one side wins, such as one place for knowledge against coupling two things that only look alike
19
- - State hidden inputs: pass in the clock, environment, and randomness, and keep business rules free of transport and storage code
19
+ - State what a unit keeps out of itself, being the clock, environment, and randomness, transport and storage code in a business rule, and a long page, stylesheet, or script held in a string
20
20
  - Close with the agent failure modes as checks on the session's own diff, with excuses, red flags, a checklist, and the external sources adopted and declined with the reason for each
21
21
 
22
22
  ## Must not
@@ -55,6 +55,13 @@ A pattern earns its indirection only when the problem it solves is present now.
55
55
  - Keep business rules free of transport and storage code, so a rule can be read and tested without a request or a database.
56
56
  - Skip ports and adapters for an application that only reads and writes records. The layers cost more than the change they protect against.
57
57
 
58
+ ## Give another language its own file
59
+
60
+ - Put a page, a stylesheet, or a script past five lines in its own file with its language's extension. A string reaches no formatter or linter, and the next session copies it from its neighbor.
61
+ - Put a script that branches in its own file at any length, since a branch is logic no test reads as its own input.
62
+ - Keep a template that interpolates data in code, since it is code generating output. Count only its static lines, so one interpolation cannot exempt a long page.
63
+ - Keep a short string inline when it is the input under test, so a reader sees it beside the assertion that reads it.
64
+
58
65
  ## Check your own diff
59
66
 
60
67
  Agents fail at writing time in recorded ways. Read the diff for each before calling it done.
@@ -67,6 +67,14 @@ Martin Fowler, [Beck Design Rules](https://martinfowler.com/bliki/BeckDesignRule
67
67
 
68
68
  Martin Fowler, Inversion of Control Containers and the Dependency Injection pattern, hands a component what it depends on rather than letting it find it. Adopted as the hidden-inputs rule for the clock, the environment, randomness, and globals, without any container.
69
69
 
70
+ ## Adopted for embedded languages
71
+
72
+ ### Inline resource
73
+
74
+ Gerard Meszaros, xUnit Test Patterns, names putting a file's contents into a string in the test as Inline Resource and recommends it over the Mystery Guest a separate file creates. The same page warns that a large resource signals an Eager Test.
75
+
76
+ Adopted as both sides of the embedded-file rule. A short input under test stays inline, and a long page, stylesheet, or script leaves for its own file. The five-line threshold mirrors the limit the `ci-workflow` skill sets on an inline `run:` step, so one number covers both.
77
+
70
78
  ## Adopted for agent failure modes
71
79
 
72
80
  Birgitta Böckeler, The role of developer skills in agentic coding, 2025, and How far can we push AI autonomy in code generation?, 2025.
@@ -52,7 +52,12 @@ The no-local-preview and no-reel lines also land on the evidence comment, throug
52
52
 
53
53
  ### The uncaptured line
54
54
 
55
- Collect `📸 <N> painted paths changed with no evidence capture. Recapture and push.` when the evidence record lists `uncaptured` paths, with `<N>` its count. The line never stops the run, since no step here can take a capture, and the review raises the same paths as a finding until a recapture lands.
55
+ Collect `📸 <N> painted paths changed with no evidence capture. Recapture and push.` when the evidence record lists `uncaptured` paths, with `<N>` its count. It never stops the run. No step here can take a capture, so the gap reaches the thread and the lift by other routes:
56
+
57
+ - The evidence call writes an `**Uncaptured:**` line naming each path on the marked comment, posting it even on a head with no other news
58
+ - The line drops on the first render after a capture lands or a reel filmed at the head renders beside it
59
+ - `canon pr lift` refuses `painted-uncaptured` until a capture, a reel at the head, or a waiver in the newest review pass covers every listed path
60
+ - A worker never writes the waiver, since a review is the one surface a worker never posts
56
61
 
57
62
  ### The no-local-preview line
58
63
 
@@ -150,6 +150,7 @@ rmdir <main-root>/.canon/tmp/handoff/ui-checklist 2>/dev/null || true
150
150
 
151
151
  - The `rmdir` is a no-op while another branch's pending checklist sits in the folder, so this step never deletes a handoff not its own
152
152
  - `git-followup` re-runs `canon pr evidence` with no `--checklist`, and the verb carries the checklist forward out of the comment it edits
153
+ - Drop a carried checklist with `canon pr evidence <number> --no-checklist --json` and post its `ok` body, never a hand edit, which the next render undoes
153
154
  - A tick survives a render at the head its stamp names and is cleared by one at a newer head, so the next UI pass earns it again
154
155
 
155
156
  ## Post the preview address
@@ -36,7 +36,7 @@ Film where motion is the claim. Filter the boxes first, then spend the three bea
36
36
 
37
37
  A box is filmable when it asks the reviewer to click, fill, or scroll something and its result is a visible change.
38
38
 
39
- - Skip a box ending in `(taste)`, since no driver judges it
39
+ - Skip a box whose trailing markers hold `(taste)`, in any order, since no driver judges it
40
40
  - Skip a box that asks only to look at a state the page opens on, since a screenshot already shows it
41
41
  - Skip a negative box, one asserting that something does not happen, such as "no drop line draws", since footage of nothing shows nothing
42
42
 
@@ -80,6 +80,14 @@ Branch on the record's `reason`, never on the exit code.
80
80
  | `over-cap` | Drop `beat` and each beat after it, then film once more. Skip as `over-cap` if `beat` is first or it refuses again |
81
81
  | Any other value | Skip under that `reason`, such as `target-missing` with its `beat` |
82
82
 
83
+ ### A skip for a state the fixture lacks
84
+
85
+ A skip whose cause is a state the drive fixture lacks, such as `target-missing` on a control only that state renders, is work the building session owes.
86
+
87
+ - Film a beat from a box carrying `(needs fixture)` only if the state answers, and otherwise skip it as owed
88
+ - Report the owed skip on the evidence comment's `--reel-skip` line and in the closing report, naming the state, the selector, or the setup step the beat needed
89
+ - Never report it as a plain skip, since the next pull request meets the same fixture and skips again
90
+
83
91
  ## Check each still
84
92
 
85
93
  Open each beat's `still.jpg`, the closing frame the record names in its `still` field, and confirm it shows the state the beat's box describes. This check replaces the operator's approval, so it runs on every reel.
@@ -36,12 +36,24 @@ canon pr evidence <number> --check --json
36
36
 
37
37
  Read the record's `uncaptured` field, never the exit code.
38
38
 
39
- - It lists paths: the project keeps captures and this change skipped them. File a finding against the pull request naming each path.
39
+ - It lists paths: the project keeps captures and this change skipped them. File a should-fix naming each path, since `canon pr lift` refuses `painted-uncaptured` until each one is covered.
40
40
  - An empty list beside a changed `evidence/` image means the capture exists and the comment fails to show it. File that as a finding too.
41
41
  - An empty list with no changed `evidence/` image means the painting file sits outside the painted set, such as a token file. Raise a question naming the path.
42
42
  - It is absent: the base keeps no capture, or its tree went unread. Raise a question with no severity, since a project with no capture harness would otherwise take the flag on every styling change.
43
43
  - The record refuses: it decides nothing, so take the tree test under the last section instead
44
44
 
45
+ ### Waiving a capture
46
+
47
+ A worker may state why a listed path needs no capture, such as a change to keyboard focus order that no reachable state shows. Accept the exemption only when the diff bears it out, and leave the should-fix standing otherwise.
48
+
49
+ - Write the waiver line in this pass's body, in the form `${CLAUDE_SKILL_DIR}/../review-pr/references/post.md` gives, one line per path
50
+ - Keep it outside any code fence, since the lift skips a fenced line as a quoted sample
51
+ - Copy the path from the record, since the lift matches it only as `uncaptured` spells it
52
+ - Write the reason after the colon, since a line with none waives nothing
53
+ - Restate it in every later pass, a close-out included, since the lift reads only the newest marked pass of each family and never a reply
54
+
55
+ ### A recapture of the design
56
+
45
57
  A recapture under `canon/design/` redraws the accepted design rather than the built app, so it never stands in for an evidence capture. The verb leaves those paths out of `uncaptured`, and a pass clears a painted change on the evidence comment alone, however many design references beside it were redrawn.
46
58
 
47
59
  Stop reading this reference once the flag is raised, since there is nothing to open. Otherwise continue with the comment in hand.
@@ -63,6 +63,8 @@ A pass carrying nothing at all takes `## Review closed` and a short body, with t
63
63
  - A prior finding this pass withdrew leaves the count, and its line names the fact that settled it rather than a fix, per the withdrawal rule below
64
64
  - A closing remark that fits no prior finding, such as a ticked Testing box, takes one line of its own after them
65
65
 
66
+ Each capture waiver this pass still accepts takes its `- **Waived capture:**` line last, unfenced. The lift reads waivers from the newest pass alone, so a close-out that drops one withdraws it.
67
+
66
68
  ### Only minors
67
69
 
68
70
  A pass carrying only minors is an ordinary finding-carrying pass, so it takes the open heading and the full shape, since neither short line reports the minors. A pass carrying only Testing questions or only an unanswered reviewer request takes the same route. Keep whichever scope sentence the pass owes on the summary line:
@@ -19,6 +19,8 @@ gh api --paginate 'repos/{owner}/{repo}/pulls/<number>/reviews?per_page=100' --j
19
19
 
20
20
  Write the replacement body to `<body-file>`, the name `post.md` already derived at the top of Step 4. Keep the heading and the footer, and name what this pass covered on the scope line in place of what the old one covered.
21
21
 
22
+ Carry every `- **Waived capture:**` line the standing close-out holds into the replacement when this pass still accepts it. The lift reads waivers from the newest pass alone, so a rewrite that drops one withdraws it and the lift refuses `painted-uncaptured` again.
23
+
22
24
  - Derive that name the same way whichever path reached here, since the guard reads `<prior-heading>` alone and a repeated head resolves the third segment as usual
23
25
  - The folder then gains a record of every covered head rather than losing the one the standing comment named
24
26
 
@@ -65,6 +65,7 @@ X critical, Y should-fix, Z minor. Reviewed against project docs and the board.
65
65
  - Fix: the change
66
66
  - **minor** ([lines 30-34](<repo-url>/blob/<headRefOid>/path/to/file.ext#L30-L34)): what breaks, in one sentence.
67
67
  - Fix: the change
68
+ - **Waived capture:** `src/client/board/overlay.tsx`: keyboard focus order only, no visible change in any reachable state
68
69
 
69
70
  <details>
70
71
  <summary>Review focus, N checked</summary>
@@ -16,6 +16,7 @@ A model asked to drive a checklist also drifts. It judges a taste box the operat
16
16
  - Every checklist box is driven in the running app and gets one verdict, pass or fail with the value quoted, needs eyes for a taste box, or not driven with the reason
17
17
  - The checklist and address come from the evidence record rather than the diff, and only a build of the head the verdict names is driven
18
18
  - An unreachable address reports not driven rather than fail, a box no commit can settle leaves the review closed, and a pass that drove no box never reads as closed
19
+ - A box not driven for a state the drive fixture lacks is the exception, since the building session can add that state, so it opens the review. A state no fixture file can hold stays closed.
19
20
  - The review posts as its own pull request review under `## UI review` while anything is owed and `## UI review closed` once nothing is, ending on a marker naming the head it drove
20
21
  - The pass drives a pinned browser under its own named session and sweeps only console errors, overflow, and the focus, details, and axe probes, opening on an axe finding the CLI marks as gating
21
22
 
@@ -84,7 +84,7 @@ Take the first rung that answers, testing each with `curl -s -o /dev/null -w '%{
84
84
  2. The `local` address off the record.
85
85
  3. An address the launch names.
86
86
 
87
- When the hosted rung answers, read `${CLAUDE_SKILL_DIR}/references/hosted-preview.md` to check it was built from the head before driving. A pass on the local rung or a launch-named address skips it.
87
+ When the hosted rung answers, read `${CLAUDE_SKILL_DIR}/references/hosted-preview.md` to check it was built from the head before driving. A pass on the local rung or a launch-named address skips that check, and every rung then takes the config's `access` per `## The access join` in `${CLAUDE_SKILL_DIR}/references/driving.md`.
88
88
 
89
89
  ### Unverified and unanswered addresses
90
90
 
@@ -112,7 +112,7 @@ Read `${CLAUDE_SKILL_DIR}/references/driving.md` on reaching this step for how a
112
112
  - Make each box its own item on the session's task list, and tick it as its verdict lands. Skip it on a host that offers no list tool.
113
113
  - Give every box exactly one verdict, in checklist order, since skipping a box silently reads the same as passing it
114
114
  - Drive a box the pass could not place on a route or a width from the opened address at 1440 wide, and name the guess in the verdict
115
- - Never judge a box ending in `(taste)`. Drive to the state, capture both themes, and mark it needs eyes.
115
+ - Never judge a box whose trailing markers hold `(taste)`, in any order. Drive to the state, capture both themes, and mark it needs eyes.
116
116
  - Drive a ticked box the same as an unchecked one, at any head, since a tick is a claim this pass exists to check
117
117
 
118
118
  ### Push a frame
@@ -1,25 +1,36 @@
1
1
  ---
2
2
  title: Drive each box
3
- description: Step 5 of review-ui, which maps a checklist line to browser calls, reads each value off the page, and gives the box one of four verdicts with the evidence that verdict carries
3
+ description: The access join Step 3 of review-ui makes, then Step 5, which maps a checklist line to browser calls, reads each value off the page, and gives the box one of four verdicts with the evidence that verdict carries
4
4
  ---
5
5
 
6
6
  # Drive each box
7
7
 
8
8
  Step 5 of `review-ui`. The browser is open at the address Step 3 picked, under the session Step 4 named. Every call below goes through `canon browser -s=<project>-<number>`, shortened here to `pw`.
9
9
 
10
+ ## The access join
11
+
12
+ Step 3 reads this section once a rung answers. Read `access` from `canon/config/app-drive.toml` at the main worktree root, since a page needing a write link, a signed-in route, or a demo token shows its start state only behind it. An absent file or key opens the rung as it stands.
13
+
14
+ - Join a value starting with `/` to the rung, and take a full address as it stands. Step 4 opens the joined address alone.
15
+ - Every later route joins to the bare rung, never the joined address, since an `access` holding a query would swallow the path
16
+ - Test the rung bare in Step 3, so the `curl` spends no single-use link before the drive
17
+ - Take no `access` value from the launch brief, since the config is the one source
18
+ - Never print the joined address in a post, a frame, or a message, since `access` may carry a token. Name the rung and say the config's `access` was joined.
19
+
10
20
  ## Reading a line
11
21
 
12
22
  A box written to the drivable format reads:
13
23
 
14
24
  ```plaintext
15
- - [ ] <route>[, <width>]: <verb> <target> → <expected result>[ (taste)][ (local only)]
25
+ - [ ] <route>[, <width>]: <verb> <target> → <expected result>[ (taste)][ (local only)][ (needs fixture)]
16
26
  ```
17
27
 
18
- - **Route.** A path goes through `pw goto <address><path>`. A control name, or "from the page the app opens on", is reached by clicking from the opened address.
28
+ - **Route.** A path goes through `pw goto <rung><path>`, the rung before the access join. A control name, or "from the page the app opens on", is reached by clicking from the opened address.
19
29
  - **Width.** `pw resize <width> 900` before acting. A box naming no width is driven at 1440.
20
30
  - **Verb.** The action the driver performs: `click`, `fill`, `press`, `hover`, a scroll through `eval` of `scrollIntoView`, or a resize.
21
31
  - **`(taste)`.** The result is a judgment of look or feel. Capture, never judge.
22
32
  - **`(local only)`.** The state needs something a hosted preview cannot reach. On a hosted address the box is not driven, naming the marker as the reason.
33
+ - **`(needs fixture)`.** The drive fixture lacks the state. Drive the box if the state now answers, and otherwise it is not driven for a cause on the branch, as the verdicts below state.
23
34
 
24
35
  A box written before that format may carry no route, no width, and a verb such as "read" or "look at". Drive it anyway: take the opened address as the route and 1440 as the width, turn the verb into the nearest action, and name each guess in the verdict's evidence.
25
36
 
@@ -51,13 +62,30 @@ Each box takes exactly one, keyed here by the state it answers.
51
62
  - The evidence is the value read, quoted, such as `scrollWidth 436 against clientWidth 320`.
52
63
  - **pass or fail, frame.** The result has a stated criterion only an image settles.
53
64
  - The evidence is the matching evidence stem or the pushed frame's link, inline under the failed box's heading, and what the frame shows, in words.
54
- - **needs eyes.** The box ends in `(taste)`.
65
+ - **needs eyes.** The box's trailing markers hold `(taste)`, in any order.
55
66
  - The evidence is the dark and the light shot as a pair, each the matching evidence stem or a pushed frame's link, and what the frames showed, with nothing about whether it looks right.
56
67
  - **not driven.** The state could not be reached.
57
68
  - The evidence is the reason, such as an unreachable address, a `(local only)` box on a hosted preview, or a target the snapshot does not hold.
58
69
 
59
70
  A fail names what was expected beside what was read. A not driven never reads as a fail, since it says the pass could not look rather than that the page is wrong. Its reason says whether the cause sits on the page or off it, since only a cause on the page opens the heading Step 7 picks.
60
71
 
72
+ ### A state the fixture lacks
73
+
74
+ A box the pass could not drive because the drive fixture holds no such state is a cause on the branch, never a skip. The building session owes the state, and no other session can add it.
75
+
76
+ - Name the cause as a missing fixture state, with the state to add and the selector or setup step the box needs
77
+ - Count it as not driven, and never as a fail, since the page may be right
78
+ - An address that did not answer stays a cause off the branch, and a `(local only)` box on a hosted address does too
79
+ - A state no fixture file can hold is the exit, such as one a project with no `canon/config/app-drive.toml` cannot reach inline. Name it a cause off the branch, so the review stays closed.
80
+
81
+ ### A refused address
82
+
83
+ A permission rule or a classifier that denies the call opening or driving an address leaves the box not driven, for a cause off the branch. Name the refusal as the host worded it and the call it denied.
84
+
85
+ - Never retry through another route, being a second address, a different browser call, or a direct fetch, since the refusal is a decision about this pass rather than a fault to work around
86
+ - A refused opening call drives no box, so the pass posts open with the refusal as its cause line, per `${CLAUDE_SKILL_DIR}/references/post.md`
87
+ - Name the address by its rung and never print the joined `access`, which may carry a token
88
+
61
89
  ### Which image a verdict carries
62
90
 
63
91
  A pass or fail resting on a frame carries one image at most, and never one the worker's evidence already shows. A needs-eyes box carries the pair, dark and light, since a taste call is made by comparing them.
@@ -11,7 +11,10 @@ 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 raised a finding Step 6 names, the worktree check read `changed`, 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 raised a finding Step 6 names, the worktree check read `changed`, a box was not driven for a cause the branch holds, or the pass drove no box at all.
15
+
16
+ A missing fixture state opens the heading because a commit settles it, being the state the building session adds. That pass reaches `review-address` like a failed box, with the state to add named in the box's Not driven entry. A state no fixture file can hold, which `driving.md` names, is a cause off the branch and leaves the review closed.
17
+
15
18
  - `## UI review closed` otherwise.
16
19
 
17
20
  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.
@@ -95,7 +98,7 @@ The post takes the length limits alone, with a box's reason under 350 characters
95
98
  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.
96
99
 
97
100
  - 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.
98
- - A box takes exactly one section by its verdict, so a box that fails and also ends in `(taste)` appears once, under Failed
101
+ - A box takes exactly one section by its verdict, so a box that fails and also carries `(taste)` appears once, under Failed
99
102
  - Quote page text in backticks as content, never as the pass's own words
100
103
 
101
104
  ### A loopback address
@@ -104,6 +107,7 @@ A loopback address has `localhost`, `127.0.0.1`, `0.0.0.0`, or `[::1]` as its ho
104
107
 
105
108
  - 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
109
  - A hosted address posts as written, since it outlives the session, and the close drops it from the evidence comment
110
+ - `<address>` is always the rung before the access join, followed by `behind the config's access` when one was joined, per `## The access join` in `${CLAUDE_SKILL_DIR}/references/driving.md`
107
111
  - The evidence comment carries the local link while the pull request is open, for an operator on the same machine
108
112
  - `canon pr local --remove` masks a loopback address left on a review's `Driven at` line when the pull request closes
109
113
 
@@ -101,9 +101,9 @@ Learning that a PR moved is the mechanical half. Read `${CLAUDE_SKILL_DIR}/refer
101
101
  A pass posting anything owed reaches the session holding the branch from the reviewer directly, and the re-review request goes straight back to that reviewer, per `role-reviewer` and `role-worker`. Read `${CLAUDE_SKILL_DIR}/references/orchestrator-handback.md` on reaching this step.
102
102
 
103
103
  - This session sends either leg only where a cloud reviewer posted the pass or one side reports the other unreachable
104
- - Each reviewer runs `canon pr lift` on its close. The verb lifts the draft once both reviews read closed at the head, the evidence settled, the head current, and CI passed.
104
+ - Each reviewer's close runs `canon pr lift`, lifting the draft once both reviews close at the head, the evidence settles, the head is fresh, and CI passes
105
105
  - Run it yourself when a close message reports a refusal other than the other review being open, or when a cloud reviewer's close posts, per Boundaries below
106
- - Then the human merges. After each merge, tell each trailing worker whose files the merged diff touches to run `review-address`, which rebases it where needed.
106
+ - Then the human merges from a line naming its proof, per `${CLAUDE_SKILL_DIR}/references/orchestrator-output.md`. Tell each trailing worker the merged diff touches to run `review-address`.
107
107
 
108
108
  The handback runbook holds how the message is addressed and worded, what to do when no live session holds the branch, and where a worker's reply goes.
109
109
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Orchestrator output formats
3
- description: The invocation block with its four sections, the three-slot shape every later report opens with, and the rule against a shape for a correction
3
+ description: The invocation block with its four sections, the three-slot shape every later report opens with, the proof a merge line names, and the rule against a shape for a correction
4
4
  ---
5
5
 
6
6
  Read this on invocation, before reporting the state of play, and again before any later report.
@@ -70,4 +70,27 @@ Keep the detail below the block and keep it skippable. A decision reached at the
70
70
  - A rename leaves the launch name matching nothing, so read the current name off `canon sessions list --json` when the line is written
71
71
  - `claude rm` takes the id alone, so that line carries the id
72
72
 
73
+ ## The merge line
74
+
75
+ A pull request recommended for merge names the proof its evidence comment carries, beside the merge command, since a lift's `ok` says the gates held and nothing about what anyone could look at.
76
+
77
+ ```plaintext
78
+ PR #<n> <title> proof: <screenshots|reel|screenshots and reel|waived|none>
79
+ → canon pr merge <n>
80
+ ```
81
+
82
+ Read the slot off `canon pr evidence <number> --json` at the moment the line is written, branching on the record rather than the exit code.
83
+
84
+ - `screenshots` when the record carries `states`, and `reel` when it carries `reel`
85
+ - `waived` when neither is carried and `canon pr lift <number> --check --json` names every listed path under `waived`, quoting no token from the reason
86
+ - `none` otherwise, and a pull request with no painted path takes no slot at all
87
+
88
+ ### An unproven painted pull request
89
+
90
+ A painted pull request whose slot reads `none` is unproven. Report it as unproven, never as ready, whatever the lift returned, and put the missing proof to the operator as a decision.
91
+
92
+ - Painted means the record carries a `checklist`, or the check record lists `uncaptured` paths
93
+ - A project whose base keeps no captures reads `uncaptured` absent, so a checklist with no stills and no reel is still unproven there
94
+ - Send the branch's session back to run `pr-evidence` under the proof check in `orchestrator-review-dispatch.md`, rather than recommending the merge
95
+
73
96
  Write no shape for a correction. A correction is a sentence, and a format for admitting error invites ceremony where plainness is the whole value.
@@ -47,8 +47,9 @@ bun <POLL_SCRIPT> --wait 1200
47
47
  Act on what the poll printed when it exited, then start it again.
48
48
 
49
49
  - A release pull request, whatever state follows it: report it and stop. Its sweep carries no findings, so no pass is owed. Test this before any rule below, since a release pull request is reported OPENED like any other and would otherwise match that rule first.
50
+ - MOVED, before the rule below: run the review dispatch runbook's proof check on the new head. A head first carrying an uncaptured path, or owing evidence, goes back to its worker and holds the re-review. One first carrying a `drivable` box with no UI reviewer launched takes one now.
50
51
  - MOVED or RESPONSE on a pull request already reviewed: when a live local reviewer holds it, report it and stop, since the worker asks that reviewer for the re-review directly. When none does, which a cloud reviewer always leaves, launch a fresh reviewer under the review dispatch runbook of the canon:role-orchestrator skill. Never run the canon:review-pr skill here. Re-reviews read prior..head and gain nothing from waiting.
51
- - OPENED, or a pull request with no prior review pass: launch its reviewer under the review dispatch runbook of the canon:role-orchestrator skill, and its UI reviewer beside it when the evidence record carries a checklist. Never run the first pass here. A draft counts, since every pull request here opens as one and skipping drafts skips everything.
52
+ - OPENED, or a pull request with no prior review pass: once that runbook's proof check reads proof, launch its reviewer under the review dispatch runbook of the canon:role-orchestrator skill, and its UI reviewer beside it when the evidence record counts a `drivable` box. Never run the first pass here. A draft counts, since every pull request here opens as one and skipping drafts skips everything.
52
53
  - SEEN: report it and stop. A pass already covers that head, whether it arrived out of band or before the poll first saw the pull request, so no review follows.
53
54
  - STALLED: read the last pass and report what it carried. The pass has sat open for hours with nothing following it, so a worker mid-task is already ruled out and the dispatch either never went out or the session holding it is gone. Confirm and re-send it under the dispatch rule below, whatever grades the pass carried. Do not re-run a review to correct the heading, since a pass on an unchanged head with no response behind it stops by design.
54
55
  - CONFLICT or STALE: send the session holding that branch the rebase handback in the canon:role-orchestrator skill's handback runbook, resolving it the way the handback rule below does. The branch owner rebases, not this session. The poll prints one of the two for a pull request, never both, so it earns one message. The canon pr lift verb refuses on either standing itself, since the green run tested a main that no longer exists, so nothing here lifts the mark.
@@ -80,7 +81,7 @@ The eight headings the script knows are written by `review-pr`, `review-address`
80
81
  - The reply family matches by a filter in `poll.ts`, so a project posting it under different headings edits that
81
82
  - The review and UI families match inside `canon pr review-state`, which the script and `review-pr` both read, so renaming one changes the verb
82
83
  - The `UI-` states report the newest UI pass on its transition, and `UI-STALE` fires beside the `MOVED` of a push that left its marker behind
83
- - A first sighting reports no `UI-` state at no cost to the mark, since `canon pr lift` reads the UI review itself and holds the mark on a `canon pr evidence` checklist until a UI close names the head
84
+ - A first sighting reports no `UI-` state at no cost to the mark, since `canon pr lift` reads the UI review itself and holds the mark on a drivable checklist until a UI close names the head
84
85
 
85
86
  ### UNMATCHED and RESPONSE
86
87
 
@@ -1,10 +1,39 @@
1
1
  ---
2
2
  title: Orchestrator review dispatch runbook
3
- description: Which pull requests take a reviewer dispatched under role-reviewer, the model it runs on, when a UI reviewer goes beside it, when small pull requests share one reviewer pair, and how the cross-branch pass per wave reaches each brief
3
+ description: The painted-proof check every pull request clears before a reviewer launches, which pull requests take a reviewer dispatched under role-reviewer, the model it runs on, when a UI reviewer goes beside it, when small pull requests share one reviewer pair, and how the cross-branch pass per wave reaches each brief
4
4
  ---
5
5
 
6
6
  Run this on every first pass, from `### The review dispatch` in the skill body. Review splits by what each side can see rather than by pass number. A dispatched reviewer holds one pull request and reads it deeper than this session can while holding a wave, and this session holds the wave and reads what no single pull request shows.
7
7
 
8
+ ## Proof before any reviewer
9
+
10
+ A painted pull request missing proof goes back to its worker before any reviewer launches. Read it on every first pass, ahead of every launch below, and on every `MOVED` the poll routes here:
11
+
12
+ ```bash
13
+ canon pr evidence <number> --check --json
14
+ ```
15
+
16
+ Branch on the record's fields rather than on the exit code, and never derive the painted set from `gh pr diff` against a test written here, since the verb already reads it.
17
+
18
+ | Record | Route |
19
+ | ------------------------------ | ------------------------------------------------------------------------------------------ |
20
+ | `uncaptured` lists paths | Send it back for a capture and `pr-evidence`, naming each path as the record spells it |
21
+ | `owed` holds `evidence` | Send it back to run `pr-evidence`, since an evidence image changed and no comment shows it |
22
+ | `settled`, `uncaptured` empty | Proof stands, so launch the reviewers below |
23
+ | `uncaptured` absent | The base keeps no captures, so route on `owed` alone and never send it back for a capture |
24
+ | A refusal, such as `gh-failed` | Report the `reason`, launch the reviewers, and name the check unread in each brief |
25
+
26
+ `owed` holding only `preview` is the ship chain's deploy wait rather than missing proof, so it holds no reviewer.
27
+
28
+ ### Sending it back
29
+
30
+ Message the session holding the branch, resolved the way `## Sending the handback` in `orchestrator-handback.md` resolves it, with the paths and the skill to run. Hold both reviewers until a re-read reads proof, since a code review on a head about to gain captures runs twice.
31
+
32
+ - A worker reporting that no capture can be taken, such as a state no fixture reaches, releases the hold, so launch the reviewers
33
+ - Carry the uncaptured paths as a fact in the code reviewer's brief then, since only a review pass may write the waiver `canon pr lift` reads
34
+ - Re-read the check on the worker's reply or the next `MOVED`, never on a timer
35
+ - A refusal never reads as proof. `canon pr lift` reads the same record and holds the mark on its own gate, so a launch past an unread check costs no merge.
36
+
8
37
  ## Every pull request takes a reviewer
9
38
 
10
39
  Dispatch every pull request's review to a reviewer under `role-reviewer`, first pass included, whatever its size or its paths. A release pull request is the one exception, since its sweep carries no findings and owes no pass.
@@ -16,9 +45,11 @@ Dispatch every pull request's review to a reviewer under `role-reviewer`, first
16
45
 
17
46
  ## The UI reviewer
18
47
 
19
- A pull request whose evidence record carries a checklist takes a second reviewer, a UI reviewer under `review-ui`, dispatched beside the code reviewer so the two passes run at once. Read the record with `canon pr evidence <number> --json` and branch on its `checklist` field rather than on the exit code.
48
+ A pull request whose evidence record counts a drivable box takes a second reviewer, a UI reviewer under `review-ui`, dispatched beside the code reviewer so the two passes run at once. Read the record with `canon pr evidence <number> --json` and branch on `drivable` above zero rather than on the exit code.
20
49
 
21
50
  - A record carrying no checklist dispatches no UI reviewer, since the pass would stop on arrival
51
+ - A record whose `drivable` reads zero, every box being `(taste)`, dispatches none either, while a `checklist` with no readable `drivable` dispatches one, as the lift binds on it
52
+ - A pull request that gains a drivable box in review takes its UI reviewer then, on the `MOVED` that brought it, since a first pass that read none launched nobody
22
53
  - The UI reviewer reads no diff and never hears the code verdict, so neither brief carries the other's result
23
54
  - Each posts its own family and runs `canon pr lift` on its close, and the verb lifts the draft mark only once both read closed at the head
24
55
 
@@ -38,7 +69,7 @@ A pull request failing any condition takes its own reviewer pair, as above. A ro
38
69
 
39
70
  ### One pair per batch
40
71
 
41
- Launch one code reviewer for the whole batch, and one UI reviewer beside it when any batched evidence record carries a checklist. `orchestrator-reviewer-launch.md` carries both batch shapes.
72
+ Launch one code reviewer for the whole batch, and one UI reviewer beside it when any batched evidence record carries a `drivable` count above zero. `orchestrator-reviewer-launch.md` carries both batch shapes.
42
73
 
43
74
  - Each pull request still takes its own pass, its own verdict comment, and its own `canon pr lift`, since the poll routes on each comment's heading
44
75
  - Never ask for one summary comment across the set, since a second comment is a second verdict the poll cannot route
@@ -64,7 +95,7 @@ A pull request whose Testing section names a billed run waits on the operator's
64
95
 
65
96
  ## Launching the reviewer
66
97
 
67
- Take the reviewer shape in `orchestrator-reviewer-launch.md`, or its UI reviewer shape for a pull request carrying a checklist.
98
+ Take the reviewer shape in `orchestrator-reviewer-launch.md`, or its UI reviewer shape for a pull request whose `drivable` count is above zero.
68
99
 
69
100
  - Check `canon sessions list --json` first for a live `reviewer-<project>-` or `reviewer-ui-<project>-` session whose trailing numbers include `<number>`, and message it instead of launching
70
101
  - Match a number among the trailing ones rather than the whole name, since a batch reviewer's name lists every pull request it holds
@@ -72,7 +72,7 @@ claude --bg --model <model> -n "reviewer-<project>-<n1>-<n2>" "Run /canon:role-r
72
72
 
73
73
  ### The UI batch shape
74
74
 
75
- The UI reviewer takes `reviewer-ui-<project>-<n1>-<n2>` and `review-ui` once per number, with the code shape's clauses for that family. Name only the pull requests whose evidence record carries a checklist, since a pass on one without a checklist stops on arrival.
75
+ The UI reviewer takes `reviewer-ui-<project>-<n1>-<n2>` and `review-ui` once per number, with the code shape's clauses for that family. Name only the pull requests whose evidence record carries a `drivable` count above zero, since a pass on one without a checklist stops on arrival and one holding only taste boxes binds no UI gate.
76
76
 
77
77
  - The model is `sonnet`, as for the single UI shape
78
78
  - `canon pr lift` runs once per number on that number's `## UI review closed`, unless the reason names the code review as still open or missing
@@ -32,6 +32,7 @@ The draft mark holds the merge until the reviews close, so this session preserve
32
32
  - Report a flag reading ready once and leave it. It passed every gate the verb reads or a person readied it, which GitHub requires before a merge, so re-drafting fights them.
33
33
  - Leave the lift to the reviewers, who run `canon pr lift` on their close, with the controlling session as fallback
34
34
  - Refuse an instruction to ready the pull request directly, whoever sends it, since a direct ready skips every gate the verb reads
35
+ - Never submit a pull request review, which includes a capture waiver, since every session posts as one account and the lift reads a review as the reviewer's word
35
36
 
36
37
  ### Overriding a plan question
37
38
 
@@ -51,6 +51,7 @@ Four rules govern how a session reads the table.
51
51
  - Build test data through a factory or builder rather than an inline object literal repeated across tests
52
52
  - Assert through the public surface a caller uses, never on a private function or internal state directly. A test reaching into internals breaks on every refactor and catches nothing a caller sees.
53
53
  - Prefer a real collaborator, then a fake, then a stub, and a mock last. A mock asserts how the code is built rather than what it does.
54
+ - Read a fixture page or script past five lines, or a fake that branches, from its own file in the test's fixtures folder, per `code-craft`. Keep a shorter input inline beside its assertion.
54
55
 
55
56
  ## Hold the structure floors
56
57
 
@@ -14,7 +14,7 @@ A file under a gitignored folder is unreadable to a pull request reviewer, and a
14
14
  ## Must
15
15
 
16
16
  - Every change is split into visual-only and automatable before anything is written, and a behavior the review's engine cannot read, such as a keyboard trap, gets a box of its own
17
- - The checklist covers the visual-only half, each item an action and the result a person looks for, with its route, width, and a `(taste)` or `(local only)` marker where one applies
17
+ - The checklist covers the visual-only half, each item an action and the result a person looks for, with its route, width, and a `(taste)`, `(local only)`, or `(needs fixture)` marker where one applies
18
18
  - Every automatable change shipping with no test is named, with the layer `test-craft` would place it at, in the same file
19
19
  - The file is written to the `.canon/tmp/handoff/ui-checklist/<slug>.md` handoff at the main worktree root, overwriting
20
20
  - A change with nothing visual and nothing untested reports the coverage and writes no file
@@ -40,7 +40,8 @@ Write each box so a session driving the running app can run it without guessing,
40
40
  - **Width.** Only when the change can differ by width, such as layout, spacing, or anything responsive. Copy, color, or a state rendering the same everywhere leaves it off.
41
41
  - **Verb.** An action a driver performs, such as "scroll to", "click", "resize to", or "tab to". Never "read" or "look at", which name no action.
42
42
  - **Taste.** End the line with `(taste)` when the result is a judgment of look or feel. A driver reports such a box as needing a person's eyes rather than passing it.
43
- - **Setup.** State inline any setup the page does not open in. End the line with `(local only)` when a hosted preview cannot reach that state, such as a stopped service or a seeded record.
43
+ - **Setup.** State inline the setup the page does not open in and the fixture state it needs. End with `(local only)` when a hosted preview cannot reach it, such as a stopped service.
44
+ - **Fixture.** End with `(needs fixture)` when the drive fixture lacks the state. The builder adds it, or with no `canon/config/app-drive.toml` writes the setup inline and drops the marker.
44
45
 
45
46
  When the plan cites `canon/design/<surface>/` under `**Design references:**`, name the reference as the comparison on each box for that surface, such as "→ matches `canon/design/<surface>/` in light and dark". The reviewer then judges the build against the accepted design instead of against taste.
46
47
 
@@ -9,8 +9,24 @@
9
9
 
10
10
  - Run the project's capture command against a preview server, never a dev server, after changing a surface. Report a project with none.
11
11
  - Capture every theme the surface ships, and again after fixing a defect found.
12
+ - Add a case for every rendered state the change adds or alters, on a new surface or an already captured one. Proof is the default.
13
+ - Count a focus ring, an outline, a toast, a tooltip, a size change from server-written markup, and a state reachable only by interaction as visible states.
12
14
  - Read your capture against the `canon:ui-checklist` boxes before opening the pull request, reporting a skill that does not resolve.
13
15
 
16
+ ### The exemption
17
+
18
+ Only a change with no visible result in any reachable state is exempt, such as a keyboard order that moves no ring. A capture costs one command and a missing one costs a review round, so the worker never judges the exemption alone.
19
+
20
+ - State it in the pull request body as a line naming the path and why nothing shows.
21
+ - A reviewer accepts it by writing a ``- **Waived capture:** `<path>`: <reason>`` line in a review.
22
+ - A painted path with neither a case nor that line keeps the pull request in draft.
23
+
24
+ ## When the fixture cannot reach a state
25
+
26
+ - Extend the drive fixture in the same branch when a box or a case needs a state it lacks, and commit the added state under a path the fixture declares, since the throwaway copy wipes anything else.
27
+ - Never record the missing state as a skip, since the next pull request meets the same fixture and skips again.
28
+ - State the setup inline when the project has no `canon/config/app-drive.toml`, since the drive there starts from the served root and a fixture file is not owed.
29
+
14
30
  ## What a capture covers
15
31
 
16
32
  - Capture the full page, never a component in isolation.
@@ -14,14 +14,14 @@ A session that does reach `canon take` writes the plan from its own reading of t
14
14
  ## Must
15
15
 
16
16
  - The operator approves the beats in plain words before anything is filmed
17
- - Each beat films one action from a fresh start state, with a hold before and after it
17
+ - Each beat films one action from a fresh start state, with a hold before and after it, and a harness beat has its known faults named before it films
18
18
  - Every take carries its clip, its click log, and its camera track, and no clip carries a drawn cursor
19
19
  - The session looks at a still of each take before handing it off, and names a beat that fell back to the real-time route
20
20
  - HyperFrames receives the takes with what it needs to apply the camera and draw the cursor
21
21
 
22
22
  One video keeps its plan, takes, and composition in one workspace the operator can find, backed up without its media. A README there records how to re-film the takes, and the composing session adds how to re-render. It keeps what a re-film and a re-render need and clears the rest.
23
23
 
24
- A session composing the takes finds the footage guidance HyperFrames lacks, being captions over app UI, other shapes, a README loop, and sped-up clips. A machine without HyperFrames keeps its takes and stops at the handoff naming the install, which the session never runs itself.
24
+ A session composing the takes finds the footage guidance HyperFrames lacks, being captions over app UI, other shapes, a README loop, sped-up clips, pacing, transitions, and the review of a cut's motion. A machine without HyperFrames keeps its takes and stops at the handoff naming the install, which the session never runs itself.
25
25
 
26
26
  ## Must not
27
27