@erclx/canon 5.41.0 → 5.42.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 (154) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/api-design/references/adopted.md +34 -14
  3. package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
  4. package/claude/skills/auto-ship/SKILL.md +22 -2
  5. package/claude/skills/auto-ship/references/draft-mark.md +32 -6
  6. package/claude/skills/auto-ship/references/failure-recovery.md +15 -13
  7. package/claude/skills/auto-ship/references/tier-failures.md +31 -10
  8. package/claude/skills/auto-ship/references/ui-checklist.md +9 -4
  9. package/claude/skills/auto-ship/references/worktree-name.md +9 -3
  10. package/claude/skills/build-in-slices/references/adopted.md +35 -21
  11. package/claude/skills/canvas-board/references/canvas.md +17 -6
  12. package/claude/skills/canvas-board/references/design.md +26 -7
  13. package/claude/skills/code-craft/references/adopted.md +58 -20
  14. package/claude/skills/codebase-layout/references/adopted.md +49 -16
  15. package/claude/skills/codebase-layout/references/frontend.md +11 -4
  16. package/claude/skills/codebase-layout/references/tests.md +4 -2
  17. package/claude/skills/context-fold/references/classify.md +10 -15
  18. package/claude/skills/context-fold/references/completion-report.md +11 -9
  19. package/claude/skills/context-fold/references/context-refresh.md +26 -9
  20. package/claude/skills/context-fold/references/unusable-baseline.md +5 -3
  21. package/claude/skills/create-rule/REQUIREMENT.md +5 -0
  22. package/claude/skills/create-rule/SKILL.md +9 -1
  23. package/claude/skills/create-rule/references/checklist.md +26 -0
  24. package/claude/skills/create-rule/references/rule.md +98 -20
  25. package/claude/skills/create-skill/SKILL.md +1 -0
  26. package/claude/skills/create-skill/references/skill-disclosure.md +10 -2
  27. package/claude/skills/create-skill/references/skill-frontmatter.md +43 -0
  28. package/claude/skills/create-skill/references/skill-paths.md +7 -4
  29. package/claude/skills/create-skill/references/skill-practice.md +6 -3
  30. package/claude/skills/create-skill/references/skill-requirement.md +4 -2
  31. package/claude/skills/create-skill/references/skill.md +67 -44
  32. package/claude/skills/deploy-app/references/vercel.md +2 -1
  33. package/claude/skills/deprecation-migration/references/adopted.md +44 -25
  34. package/claude/skills/design-taste/references/adopted.md +9 -1
  35. package/claude/skills/design-taste/references/craft.md +3 -1
  36. package/claude/skills/design-taste/references/kinds.md +8 -7
  37. package/claude/skills/design-taste/references/preflight.md +12 -0
  38. package/claude/skills/design-taste/references/systems.md +7 -6
  39. package/claude/skills/design-taste/references/tells.md +24 -25
  40. package/claude/skills/design-taste/references/vocabulary.md +7 -5
  41. package/claude/skills/document-health/references/axes.md +54 -30
  42. package/claude/skills/draft-and-pick/references/live-arms.md +2 -2
  43. package/claude/skills/draft-doc/references/context.md +4 -3
  44. package/claude/skills/draft-doc/references/docs.md +6 -3
  45. package/claude/skills/draft-doc/references/readme/agent-facing.md +6 -0
  46. package/claude/skills/draft-doc/references/readme/application.md +8 -2
  47. package/claude/skills/draft-doc/references/readme/cli.md +5 -1
  48. package/claude/skills/draft-doc/references/readme/library.md +4 -0
  49. package/claude/skills/draft-doc/references/readme/plugin.md +9 -1
  50. package/claude/skills/draft-doc/references/readme.md +30 -13
  51. package/claude/skills/draft-doc/references/requirements.md +17 -4
  52. package/claude/skills/draft-doc/references/wiki-kind.md +20 -10
  53. package/claude/skills/draft-figure/references/architecture-set.md +7 -3
  54. package/claude/skills/draft-figure/references/figures.md +8 -6
  55. package/claude/skills/draft-figure/references/hand-drawn.md +3 -1
  56. package/claude/skills/draft-figure/references/mermaid-path.md +19 -9
  57. package/claude/skills/draft-figure/references/mermaid.md +10 -5
  58. package/claude/skills/draft-ready/references/ready.md +22 -15
  59. package/claude/skills/git-branch/references/branch.md +11 -1
  60. package/claude/skills/git-branch/references/slug.md +17 -5
  61. package/claude/skills/git-commit/references/commit.md +8 -0
  62. package/claude/skills/git-commit/references/versioning.md +23 -17
  63. package/claude/skills/git-followup/REQUIREMENT.md +1 -1
  64. package/claude/skills/git-followup/SKILL.md +5 -6
  65. package/claude/skills/git-issue/references/issue.md +29 -16
  66. package/claude/skills/git-pr/REQUIREMENT.md +1 -5
  67. package/claude/skills/git-pr/SKILL.md +11 -22
  68. package/claude/skills/git-pr/references/final-command.md +21 -8
  69. package/claude/skills/git-pr/references/labels.md +45 -28
  70. package/claude/skills/git-pr/references/pr.md +28 -9
  71. package/claude/skills/git-pr/references/publish.md +42 -28
  72. package/claude/skills/git-pr/references/task-number.md +20 -8
  73. package/claude/skills/git-ship/SKILL.md +1 -1
  74. package/claude/skills/plan-feature/references/plan-lifecycle.md +10 -3
  75. package/claude/skills/plan-feature/references/plan.md +57 -20
  76. package/claude/skills/plan-groundwork/references/groundwork-spikes.md +4 -1
  77. package/claude/skills/plan-groundwork/references/groundwork.md +14 -16
  78. package/claude/skills/plan-intake/references/intake.md +29 -24
  79. package/claude/skills/pr-evidence/REQUIREMENT.md +40 -0
  80. package/claude/skills/pr-evidence/SKILL.md +53 -0
  81. package/claude/skills/pr-evidence/references/evidence.md +150 -0
  82. package/claude/skills/review-address/references/post-review-findings.md +10 -3
  83. package/claude/skills/review-address/references/rebase-conflicts.md +15 -7
  84. package/claude/skills/review-address/references/rest-reads.md +3 -1
  85. package/claude/skills/review-craft/references/adopted.md +35 -18
  86. package/claude/skills/review-craft/references/security.md +42 -7
  87. package/claude/skills/review-craft/references/ui.md +23 -8
  88. package/claude/skills/review-pr/SKILL.md +6 -6
  89. package/claude/skills/review-pr/references/body-variants.md +29 -9
  90. package/claude/skills/review-pr/references/close-out.md +20 -5
  91. package/claude/skills/review-pr/references/key-changes.md +27 -3
  92. package/claude/skills/review-pr/references/post.md +83 -31
  93. package/claude/skills/review-pr/references/scope.md +54 -19
  94. package/claude/skills/review-ui/references/close-out.md +7 -5
  95. package/claude/skills/review-ui/references/driving.md +24 -4
  96. package/claude/skills/review-ui/references/post.md +43 -15
  97. package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +67 -17
  98. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +80 -40
  99. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +52 -15
  100. package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +48 -26
  101. package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +12 -13
  102. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +72 -36
  103. package/claude/skills/role-orchestrator/references/orchestrator-output.md +12 -4
  104. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +64 -24
  105. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +80 -26
  106. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +41 -18
  107. package/claude/skills/role-orchestrator/references/orchestrator-resume.md +19 -7
  108. package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +22 -12
  109. package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +15 -6
  110. package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +25 -7
  111. package/claude/skills/role-orchestrator/references/orchestrator-watch.md +34 -8
  112. package/claude/skills/role-orchestrator/scripts/poll.ts +16 -24
  113. package/claude/skills/role-worker/REQUIREMENT.md +1 -0
  114. package/claude/skills/role-worker/SKILL.md +8 -0
  115. package/claude/skills/search-craft/references/adopted.md +18 -6
  116. package/claude/skills/session-map/references/session.md +26 -7
  117. package/claude/skills/session-worktree/references/main-root-writes.md +25 -8
  118. package/claude/skills/systematic-debugging/references/adopted.md +30 -16
  119. package/claude/skills/target-check/references/domains.md +39 -11
  120. package/claude/skills/target-setup/references/detect.md +80 -65
  121. package/claude/skills/target-setup/references/indexes.md +8 -6
  122. package/claude/skills/task-board/references/board.md +86 -30
  123. package/claude/skills/task-board/references/decline.md +29 -13
  124. package/claude/skills/task-board/references/tasks-decline.md +12 -5
  125. package/claude/skills/task-board/references/tasks.md +65 -33
  126. package/claude/skills/task-complete/references/archive.md +41 -17
  127. package/claude/skills/task-complete/references/receipt-sweep.md +23 -9
  128. package/claude/skills/task-complete/references/unusable-baseline.md +10 -3
  129. package/claude/skills/teach-workspace/references/glossary.md +6 -2
  130. package/claude/skills/teach-workspace/references/lesson-build.md +44 -16
  131. package/claude/skills/teach-workspace/references/lesson-craft.md +12 -7
  132. package/claude/skills/teach-workspace/references/pedagogy.md +6 -3
  133. package/claude/skills/teach-workspace/references/promotion-fold.md +17 -11
  134. package/claude/skills/teach-workspace/references/promotion.md +31 -8
  135. package/claude/skills/teach-workspace/references/revisit.md +8 -2
  136. package/claude/skills/teach-workspace/references/teach.md +17 -9
  137. package/claude/skills/test-craft/references/adopted.md +48 -14
  138. package/claude/skills/test-craft/references/component.md +7 -4
  139. package/claude/skills/test-craft/references/e2e.md +25 -7
  140. package/claude/skills/test-craft/references/unit.md +3 -1
  141. package/claude/skills/test-first/references/adopted.md +12 -4
  142. package/claude/skills/ui-checklist/REQUIREMENT.md +1 -1
  143. package/claude/skills/ui-checklist/SKILL.md +1 -1
  144. package/claude/skills/video-craft/references/adopted.md +62 -18
  145. package/claude/skills/write-human/references/adopted.md +21 -8
  146. package/docs/workflow/parallel-features.md +1 -1
  147. package/docs/workflow/skill-map.md +1 -0
  148. package/package.json +1 -1
  149. package/src/claude/cases/workflow.ts +5 -0
  150. package/src/commands/pr.ts +142 -28
  151. package/src/markdown/scope.ts +144 -0
  152. package/src/pr/lift.ts +6 -3
  153. package/src/pr/review-state.ts +182 -0
  154. package/claude/skills/git-pr/references/evidence.md +0 -124
@@ -5,11 +5,18 @@ description: How review-address posts something worth reporting after Step 7 clo
5
5
 
6
6
  # Post a finding after the review closed
7
7
 
8
- Outside the numbered flow of `review-address`. The session reads this file when it has something worth reaching the reviewing session after Step 7 already closed the review.
8
+ Outside the numbered flow of `review-address`. The session reads this file when it has something worth reaching the reviewing session after Step 7 already closed the review, such as a settled risk, a filed follow-up, or another finding. It posts directly rather than waiting on a review pass that has nothing left to trigger it.
9
9
 
10
- Not everything worth reaching the reviewing session surfaces inside the numbered flow in `SKILL.md`. A worker that settled a risk, filed a follow-up, or found something else worth reporting after Step 7 already closed the review posts it directly rather than waiting on a review pass that has nothing left to trigger it. Write the body the way Step 6 writes a reply: load `write-human` for voice and word choice, follow `${CLAUDE_SKILL_DIR}/../markdown-craft/references/markdown.md` for punctuation, and run the `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md` scan before posting with `canon labels scan --body-file .canon/tmp/pr/reply/reply-<number>.md`.
10
+ ## The body
11
11
 
12
- Open with `## Post-review findings` rather than `## Review response`, since nothing on the thread is being answered. `review-pr` states the full heading set this belongs to and routes it the same as a response: `role-orchestrator`'s poll picks it up and sends the reviewing session back for a pass. Close the body with `🤖 Addressed by Claude Code` on its own line, matching the reply's footer.
12
+ Write the body the way Step 6 writes a reply.
13
+
14
+ - Load `write-human` for voice and word choice, and follow `${CLAUDE_SKILL_DIR}/../markdown-craft/references/markdown.md` for punctuation
15
+ - Run the `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md` scan before posting, with `canon labels scan --body-file .canon/tmp/pr/reply/reply-<number>.md`
16
+ - Open with `## Post-review findings` rather than `## Review response`, since nothing on the thread is being answered
17
+ - Close the body with `🤖 Addressed by Claude Code` on its own line, matching the reply's footer
18
+
19
+ `review-pr` states the full heading set this belongs to and routes it the same as a response, so `role-orchestrator`'s poll picks it up and sends the reviewing session back for a pass.
13
20
 
14
21
  ```bash
15
22
  gh api -X POST 'repos/{owner}/{repo}/issues/<number>/comments' -F body=@.canon/tmp/pr/reply/reply-<number>.md --silent
@@ -33,15 +33,23 @@ A branch carrying merge commits of its own, such as a fan-in integrator's, rebas
33
33
  These rules apply to a hunk from the rebase and a hunk from the stash pop alike:
34
34
 
35
35
  - Never take one side wholesale with `--ours` or `--theirs`. Both sides are valid content, so the drop is silent and passes every check.
36
- - Never resolve a generated file by hand. A file the project check rebuilds, such as an `index.md` carrying no `auto: false`, takes either side to clear the conflict and gets its real content from the regen below.
37
- - Where two branches wrote prose into one file, keep both sides and rewrite what the merge broke. A section that opens by counting what follows needs the count updated rather than the two versions concatenated.
38
- - When a hunk needs a decision the tree does not carry, stop rather than guessing, and name where the branch was left. The two sources recover differently. A guess that compiles is the failure this stage would otherwise introduce, and either case reaches the operator as an ordinary finding on the next review pass.
39
- - A hunk raised by the rebase takes `git rebase --abort`, which restores the old base: `❌ Conflict needs a decision at <file>. Branch left on its old base.`
40
- - A hunk raised by the stash pop arrives after the rebase already landed, so no abort applies and the conflict stays in the tree: `❌ Conflict needs a decision at <file>. Branch rebased, fixes left conflicted in the tree.`
36
+ - Never resolve a generated file by hand, such as an `index.md` carrying no `auto: false`. Take either side and let the regen below rebuild it.
37
+ - Where two branches wrote prose into one file, keep both sides and rewrite what the merge broke, such as a count opening a section
38
+ - Both sides of every hunk sit in the conflict and `git log origin/main` names what landed, so never wait on the orchestrator for context
41
39
 
42
- Both sides of every hunk sit in the conflict and `git log origin/main` names what landed, so the tree carries the context. Do not wait on the orchestrator for it.
40
+ ### A hunk needing a decision
43
41
 
44
- The stop still owes a message. When the launch named a controlling session, send it the stop line through `canon:session-relay` before halting, adding the commit on `main` that wrote the other side of the hunk, per the worker channel the `role-worker` skill states. Nothing else surfaces the halt, since the next review pass that would read it as a finding never starts on a branch nobody pushed. With nobody named, print the two lines above and stop.
42
+ When a hunk needs a decision the tree does not carry, stop rather than guessing and name where the branch was left, since a guess that compiles is the failure this stage would introduce. Either case reaches the operator as an ordinary finding on the next review pass. The two sources recover differently.
43
+
44
+ - A hunk raised by the rebase takes `git rebase --abort`, which restores the old base: `❌ Conflict needs a decision at <file>. Branch left on its old base.`
45
+ - A hunk raised by the stash pop arrives after the rebase landed, so the conflict stays in the tree: `❌ Conflict needs a decision at <file>. Branch rebased, fixes left conflicted in the tree.`
46
+
47
+ ### The stop message
48
+
49
+ The stop still owes a message, since the next review pass never starts on a branch nobody pushed. When the launch named a controlling session, send it the stop line through `canon:session-relay` before halting, per the worker channel the `role-worker` skill states.
50
+
51
+ - Add the commit on `main` that wrote the other side of the hunk
52
+ - With nobody named, print the two lines above and stop
45
53
 
46
54
  ## After the rebase
47
55
 
@@ -5,7 +5,9 @@ description: The reads review-address runs over REST, being the findings on the
5
5
 
6
6
  # REST reads
7
7
 
8
- The guard and Step 1 of `review-address`. The session reads this file on reaching Step 1, and from the guard when `canon pr head --json` hands back no `number`. Every read runs on REST, since the `gh pr` subcommands run on GraphQL and a cloud session's GitHub proxy refuses it. A cloud worker answering a review runs this skill, so one GraphQL call breaks the whole leg.
8
+ The guard and Step 1 of `review-address`. The session reads this file on reaching Step 1, and from the guard when `canon pr head --json` hands back no `number`.
9
+
10
+ Every read runs on REST, since the `gh pr` subcommands run on GraphQL and a cloud session's GitHub proxy refuses it. A cloud worker answering a review runs this skill, so one GraphQL call breaks the whole leg.
9
11
 
10
12
  ## The findings
11
13
 
@@ -7,51 +7,68 @@ description: Which external review patterns this skill adopted, which it decline
7
7
 
8
8
  External sources were read and filtered rather than imported. This file is the record. A later session extending the skill adds to it rather than re-arguing an item already settled here.
9
9
 
10
+ The `addyosmani/agent-skills` citations below all pin commit `addyosmani/agent-skills@2686b620`.
11
+
10
12
  ## Adopted
11
13
 
12
14
  ### Design and scope
13
15
 
14
16
  **The design procedure.** The nine ordered steps under the design axis draw on four sources, each restated without a language, a framework, or a transport so the walk holds on any stack.
15
17
 
16
- - Ousterhout's deep modules, information hiding, and pass-through methods ([A Philosophy of Software Design](https://web.stanford.edu/~ouster/cgi-bin/book.php)): the step asking what each unit hides and flagging a unit that passes its arguments straight through.
17
- - Parnas's criterion that a module boundary hides a decision likely to change ([On the Criteria To Be Used in Decomposing Systems into Modules](https://dl.acm.org/doi/10.1145/361598.361623)): the opening step naming the next likely change and counting what it touches.
18
- - Beck's design rules as Fowler records them, especially no duplication and fewest elements ([BeckDesignRules](https://martinfowler.com/bliki/BeckDesignRules.html)): the one-place step and the step matching each pattern to a problem the code has today.
19
- - Google's reviewer guide on design, complexity, and over-engineering ([What to look for in a code review](https://google.github.io/eng-practices/review/reviewer/looking-for.html)): the frame for the whole walk, and the reason a speculative abstraction is a finding.
18
+ | Source | Step it backs |
19
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
20
+ | Ousterhout's deep modules, information hiding, and pass-through methods, [A Philosophy of Software Design](https://web.stanford.edu/~ouster/cgi-bin/book.php) | Asking what each unit hides, and flagging a unit that passes its arguments straight through |
21
+ | Parnas's criterion that a boundary hides a decision likely to change, [On the Criteria To Be Used in Decomposing Systems into Modules](https://dl.acm.org/doi/10.1145/361598.361623) | The opening step, naming the next likely change and counting what it touches |
22
+ | Beck's design rules as Fowler records them, especially no duplication and fewest elements, [BeckDesignRules](https://martinfowler.com/bliki/BeckDesignRules.html) | The one-place step, and matching each pattern to a problem the code has today |
23
+ | Google's reviewer guide on design, complexity, and over-engineering, [What to look for in a code review](https://google.github.io/eng-practices/review/reviewer/looking-for.html) | The frame for the whole walk, and the reason a speculative abstraction is a finding |
20
24
 
21
25
  The procedure stays in the body rather than a reference, since every review reads the design axis and a reference would cost a read on every run.
22
26
 
23
- **Change sizing as a split.** Google's [small CLs](https://google.github.io/eng-practices/review/developer/small-cls.html) asks for one self-contained change. Adopted as the scope check that names a split when two concerns could land as two changes. From `addyosmani/agent-skills@2686b620`, `skills/code-review-and-quality/SKILL.md`, the same check.
27
+ ### Scope checks
24
28
 
25
- **Relocate versus reduce.** From the same `code-review-and-quality` skill: a refactor counts the concepts a reader must hold before and after. Adopted as the scope check that reports moved complexity as moved.
29
+ Three scope checks come from outside sources, each adopted as one check.
26
30
 
27
- **Chesterton's Fence.** From `addyosmani/agent-skills@2686b620`, `skills/code-simplification/SKILL.md`: read why code exists before removing it. Adopted as the scope check that reads `git blame` and the commit that added a removed path before calling it dead.
31
+ - **Change sizing as a split.** Google's [small CLs](https://google.github.io/eng-practices/review/developer/small-cls.html) and `skills/code-review-and-quality/SKILL.md` ask for one self-contained change, so the check names a split when two concerns could land apart.
32
+ - **Relocate versus reduce.** The same `code-review-and-quality` skill counts the concepts a reader holds before and after a refactor, so moved complexity reports as moved.
33
+ - **Chesterton's Fence.** `skills/code-simplification/SKILL.md` reads why code exists before removing it, so the check reads `git blame` and the adding commit before calling a path dead.
28
34
 
29
35
  ### Guard the bar
30
36
 
31
- From `addyosmani/agent-skills@2686b620`, `skills/constraint-driven-development/SKILL.md`: a change must not pass by weakening the constraint it has to meet. Adopted as its own axis after tests, listing the moved threshold, the skipped or hollowed test, the inline suppression, the excluded path, the throwing stub, the empty catch, and the new config exception. The clause accepting a suppression whose stated reason holds is this skill's own, since a legitimate suppression with its reason beside it would otherwise report noise the procedures' filter then drops.
37
+ From `skills/constraint-driven-development/SKILL.md`, a change must not pass by weakening the constraint it has to meet. Adopted as its own axis after tests.
38
+
39
+ - It lists the moved threshold, the skipped or hollowed test, the inline suppression, the excluded path, the throwing stub, the empty catch, and the new config exception
40
+ - The clause accepting a suppression whose stated reason holds is this skill's own, since a legitimate one would otherwise report noise the procedures then drop
32
41
 
33
42
  ### Performance
34
43
 
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.
44
+ The axis itself, a call inside a loop and an unbounded fetch, comes from `skills/code-review-and-quality/SKILL.md`. Keep or revert comes from `skills/performance-optimization/SKILL.md`.
45
+
46
+ - Measure before and after the same way, and a neutral result argues for removing the added complexity
47
+ - The repeated-run requirement against noise is stated here, so one run on each side reads as an anecdote
36
48
 
37
49
  ### Security additions
38
50
 
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.
51
+ From `skills/security-and-hardening/SKILL.md`, the threat model first and the derived-path rule. Both stay stack-neutral, and no red run measures them, so they ship as lifts rather than as an answer to a miss.
52
+
53
+ - The threat model is the opening step of `references/security.md`, naming who supplies each input and what the code can reach before any class is read
54
+ - The derived-path rule is the destructive-operations class, asking for the empty-value collapse, the glob or symlink escape, the missing root check, and no read before the call
40
55
 
41
56
  ### Closing sections
42
57
 
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.
58
+ The excuses, red flags, and closing checklist follow the shape every practice skill in this toolkit carries, from [`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.
44
59
 
45
60
  ## Declined
46
61
 
47
- **Google's approval standard.** "Approve a change once it definitely improves the overall code health" is a grading rule. Grading and posting belong to the procedures that load this skill, which each own a severity ladder, so the standard stays out of the body.
48
-
49
- **A fixed line count for sizing.** Google's small-CL page sets no hard limit, and one concern regularly runs past a hundred lines here. A number would fire on nearly every branch, so the split is stated by concern instead.
62
+ Each item names what was declined and the reason it stays out.
50
63
 
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.
64
+ - **Google's approval standard.** "Approve a change once it definitely improves the overall code health" is a grading rule, and grading belongs to the procedures that load this skill.
65
+ - **A fixed line count for sizing.** Google's page sets no hard limit, and one concern regularly runs past a hundred lines here, so the split is stated by concern.
66
+ - **Named structural remedies as a catalog.** A list of refactorings reads as a menu a session fills in. A design finding states the cost, and the remedy is the author's.
67
+ - **STRIDE and abuse-case tests as a procedure.** They belong to a design session, and the threat-model opening takes the one step a diff supports.
68
+ - **The web and database tables from `performance-optimization`.** They name a stack, and the axis stays stack-neutral until a target asks for one.
52
69
 
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.
70
+ ### A dependency upgrade reference
54
71
 
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.
72
+ `skills/code-review-and-quality/SKILL.md` reads 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 lockfile install script on the second of two runs, passing the assertion.
56
73
 
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.
74
+ Revisit when a run on a changed lockfile misses what a reference would have named.
@@ -8,13 +8,48 @@ Before the classes, name from the diff who supplies each input and what the code
8
8
 
9
9
  ## Classes to check
10
10
 
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.
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.
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.
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.
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.
17
- - **Insecure defaults a consumer installs.** A config, a scaffold, or a seed that turns a check off, opens a port on every interface, grants a broad permission, or ships a placeholder secret. The consumer inherits the default without reading it.
11
+ Each class names the sink, the path in, and the fix to look for.
12
+
13
+ ### Shell and command injection
14
+
15
+ A value from an argument, an environment variable, a file, or a network response reaches a shell.
16
+
17
+ - The paths in are `eval`, an unquoted expansion, a string built into a command, and a subprocess call taking a single command string
18
+ - Quoting the variable at the call site is the fix to look for
19
+
20
+ ### Path traversal in a file write
21
+
22
+ A path built from outside input is 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.
23
+
24
+ ### Destructive operation on a derived path
25
+
26
+ A delete, overwrite, move, or clean takes a path built from a variable, a config value, or a listing. The fix to look for is resolve, require the root, then act.
27
+
28
+ - An empty or unset value that collapses the path to a root
29
+ - A glob or a symlink that carries it outside the folder it was meant for
30
+ - No check that the resolved path sits at least one level below an expected root
31
+ - No read of what is there, owned by this tool or not, before the call
32
+ - A write escaping its folder is path traversal. When a finding fits both, name the class whose fix applies.
33
+
34
+ ### Secrets and personal data leaving the process
35
+
36
+ A token, a key, a password, or personal data reaches 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.
37
+
38
+ ### Supply chain
39
+
40
+ Three shapes bring in code nobody reviewed.
41
+
42
+ - One-shot package execution of an unpinned version, which runs whatever the registry serves that day
43
+ - A new dependency, and what its install scripts run
44
+ - A lockfile that changes without a manifest change
45
+
46
+ ### Time of check to time of use on a file write
47
+
48
+ An existence or permission check is followed by a write to the same path in a later step, or a predictable name sits in a shared temporary directory. Another process can swap the path in between.
49
+
50
+ ### Insecure defaults a consumer installs
51
+
52
+ A config, a scaffold, or a seed turns a check off, opens a port on every interface, grants a broad permission, or ships a placeholder secret. The consumer inherits the default without reading it.
18
53
 
19
54
  ## Excluded
20
55
 
@@ -20,7 +20,13 @@ gh api --paginate 'repos/{owner}/{repo}/issues/<number>/comments?per_page=100' -
20
20
 
21
21
  Take the comment whose body carries a `<!-- pr-evidence: head=<sha> -->` marker. Match the marker, never the first line, since the body opens with `## Evidence` and a comment posted before that layout opens with a `**Preview:**` line instead.
22
22
 
23
- Flag the change as shipping no screenshot when it paints and the pull request carries no marked comment, carries one with no comparison, meaning no `**Base:**` line and no `<details>` table, or carries only a checklist posted alone. The `## Evidence` heading opens every marked comment, a checklist-only one included, so the heading alone never means screenshots are present. Then decide how to raise it by whether the project keeps captures, which is the same test `canon pr evidence` applies:
23
+ Flag the change as shipping no screenshot when it paints and the pull request carries any of three comment shapes.
24
+
25
+ - No marked comment at all
26
+ - A marked comment with no comparison, meaning no `**Base:**` line and no `<details>` table
27
+ - Only a checklist posted alone, since `## Evidence` opens every marked comment and the heading alone never means screenshots are present
28
+
29
+ Then decide how to raise it by whether the project keeps captures, the same test `canon pr evidence` applies:
24
30
 
25
31
  ```bash
26
32
  git ls-tree -r --name-only <base> | grep -E '(^|/)evidence/.*\.(png|jpe?g|gif|webp|avif|svg)$' | head -1
@@ -45,7 +51,11 @@ mkdir -p .canon/tmp/pr/review/evidence/<number>
45
51
  git show <sha>:<path> > .canon/tmp/pr/review/evidence/<number>/<side>-<name>.png
46
52
  ```
47
53
 
48
- Take the sha from the row, so the base and head sides are exactly what the comment shows. Then open each file with the file-reading tool. A `*(new)*` base cell has no before image, and a view or flat block whose cases are all added has no base column at all. A view row holds one case per theme cell, so a width row names two images where a flat row names one. Read the URL out of the `src=` of each `<img>`, or out of `![](` in a comment posted before the embed changed.
54
+ Take the sha from the row, so the base and head sides are exactly what the comment shows. Then open each file with the file-reading tool.
55
+
56
+ - Read the URL out of the `src=` of each `<img>`, or out of `![](` in a comment posted before the embed changed
57
+ - A `*(new)*` base cell has no before image, and a view or flat block whose cases are all added has no base column at all
58
+ - A view row holds one case per theme cell, so a width row names two images where a flat row names one
49
59
 
50
60
  ## How many to open
51
61
 
@@ -59,21 +69,26 @@ A second theme earns a read only when the diff touches a color token. Name the s
59
69
 
60
70
  ## Comparing
61
71
 
62
- Check each opened image against the layout its checklist box claims, and check the tokens the image makes visible, being color, type scale, and spacing, against the stylesheet `canon/DESIGN.md`'s lead names, and its component rules against the record itself.
72
+ Check each opened image against the layout its checklist box claims. A finding names the checklist box or design-file line the image breaks, never a taste.
63
73
 
64
- Read the width and theme from whatever the image path carries, and fix no width list, since which widths matter belongs to the project. Skip the design file comparison silently when the project has none.
65
-
66
- A finding names the checklist box or design-file line the image breaks, never a taste.
74
+ - Check the visible tokens, being color, type scale, and spacing, against the stylesheet `canon/DESIGN.md`'s lead names
75
+ - Check its component rules against the record itself, and skip the design file comparison silently when the project has none
76
+ - Read the width and theme from whatever the image path carries, and fix no width list, since which widths matter belongs to the project
67
77
 
68
78
  ## The checklist
69
79
 
70
80
  The `## What to look at` block between the `pr-checklist` markers holds what was claimed as verified. A tick stamped `passed at` the current head is the UI reviewer's pass, and an unstamped tick is an author's or operator's claim.
71
81
 
72
- - Test each ticked box against an opened image showing the state and width it names, a stamped one included, since the UI reviewer drove the running app and not the committed stills. Flag a box whose image contradicts it, quoting the box.
82
+ - Test each ticked box against an opened image showing the state and width it names, a stamped one included, since the UI reviewer drove the app and not the stills
83
+ - Flag a box whose image contradicts it, quoting the box
73
84
  - A ticked box no image covers is untested rather than confirmed, and the finding says so.
74
85
  - Test only the rows the current comment shows. The next render clears a tick whose stamp names another head, so a tick not stamped at the current head may describe images the comment no longer carries.
75
86
  - Leave unchecked boxes to the procedure's own read of the description.
76
87
 
77
88
  ## With no pull request
78
89
 
79
- A local review has no comment to read. Read each changed image under an `evidence/` path segment from the working tree, and its base side with `git show <base>:<path>`. Read the checklist handoff at `.canon/tmp/handoff/ui-checklist/<slug>.md` at the main worktree root when it exists. A painting change with no changed evidence image takes the flag from the first section, decided by the same `git ls-tree` test.
90
+ A local review has no comment to read, so it reads the tree.
91
+
92
+ - Read each changed image under an `evidence/` path segment from the working tree, and its base side with `git show <base>:<path>`
93
+ - Read the checklist handoff at `.canon/tmp/handoff/ui-checklist/<slug>.md` at the main worktree root when it exists
94
+ - A painting change with no changed evidence image takes the flag from the first section, decided by the same `git ls-tree` test
@@ -26,12 +26,12 @@ A thread then reads as `## Review`, the worker's `## Review response` from `revi
26
26
 
27
27
  Both headings anchor as a section distinct from human threads. The full set is eight headings across four families, stated here once so `role-orchestrator`'s poll and every reply-posting skill cite it rather than carry a copy.
28
28
 
29
- | Family | Headings | Owner |
30
- | -------- | ------------------------------------------------------------ | ------------------------------------------------------------------------ |
31
- | Review | `## Review`, `## Review closed` | This skill alone |
32
- | Reply | `## Review response`, `## Rebase`, `## Post-review findings` | `review-address` |
33
- | Evidence | `## Evidence` | `canon pr evidence`, through `git-pr` and `git-followup` |
34
- | UI | `## UI review`, `## UI review closed` | `review-ui`, posted as a review beside this one and read by nothing here |
29
+ | Family | Headings | Owner |
30
+ | -------- | ------------------------------------------------------------ | --------------------------------------------------------------------------- |
31
+ | Review | `## Review`, `## Review closed` | This skill alone |
32
+ | Reply | `## Review response`, `## Rebase`, `## Post-review findings` | `review-address` |
33
+ | Evidence | `## Evidence` | `canon pr evidence`, through `pr-evidence` from `git-pr` and `git-followup` |
34
+ | UI | `## UI review`, `## UI review closed` | `review-ui`, posted as a review beside this one and read by nothing here |
35
35
 
36
36
  - The reply headings in order answer a finding, report a stale branch resolved without one, and carry a finding a worker produced after a close-out
37
37
  - `## Evidence` answers no comment on the thread, so the poll leaves it out of the unclassified count rather than reading it as a reply owing a re-review
@@ -7,7 +7,13 @@ description: The Testing and reviewer blocks a pass may carry, and the close-out
7
7
 
8
8
  Step 4 of `review-pr`, beside `post.md`. That file names the body file and gives the shape a finding-carrying pass takes, and this one holds the blocks and close-outs that vary it.
9
9
 
10
- A Testing box the Step 3 check raised goes in a `**Testing**` block placed after the file blocks, one bullet per box, each quoting the box and naming what would drive it. It carries no severity and enters no count, and it is still something owed, so a pass carrying one takes `## Review` and the full body rather than either ✅ line. Say so on the summary line as `plus N testing question(s)`, since the three counts read as zero and would otherwise report the pass as silent.
10
+ ## The testing block
11
+
12
+ A Testing box the Step 3 check raised goes in a `**Testing**` block placed after the file blocks, one bullet per box, each quoting the box and naming what would drive it.
13
+
14
+ - It carries no severity and enters no count, and it is still owed, so the pass takes `## Review` and the full body rather than either ✅ line
15
+ - Say so on the summary line as `plus N testing question(s)`, since the three counts read as zero and would otherwise report a silent pass
16
+ - Keep it to the boxes the check raised, since restating a box whose stated requirement holds teaches the author to skip the block
11
17
 
12
18
  ```markdown
13
19
  **Testing**
@@ -15,11 +21,14 @@ A Testing box the Step 3 check raised goes in a `**Testing**` block placed after
15
21
  - `- [ ] <the box as written>` names no capability the agent lacks. `canon sandbox run <arm>` drives it. Was there a reason to leave it?
16
22
  ```
17
23
 
18
- Keep it to the boxes the check raised. Restating a box whose stated requirement holds teaches the branch author to skip the block.
24
+ ## The reviewer block
19
25
 
20
- Every `## For the reviewer` bullet Step 3 read goes in a `**For the reviewer**` block placed after the Testing block, one bullet per request, each followed by its answer or, where the pass could not answer it, by what would settle it. It carries no severity and enters no count.
26
+ Every `## For the reviewer` bullet Step 3 read goes in a `**For the reviewer**` block placed after the Testing block, one bullet per request. Each is followed by its answer, or by what would settle it where the pass could not answer. It carries no severity and enters no count.
21
27
 
22
- An unanswered bullet is owed the same way an unanswered Testing box is, so a pass carrying one takes `## Review` and the full body rather than either ✅ line. A bullet the pass answered is not owed, since the answer is discharged in the same comment that carries it. A pass still posting a numeric summary line, because a finding, a Testing question, or an unanswered bullet already forces one, says so there as `plus N reviewer request(s)`. The all-answered close-out below carries the block in place of that line and needs no addition to it.
28
+ - An unanswered bullet is owed like an unanswered Testing box, so the pass takes `## Review` and the full body
29
+ - An answered bullet is not owed, since the answer is discharged in the same comment that carries it
30
+ - A pass already posting a numeric summary line adds `plus N reviewer request(s)` to it
31
+ - The all-answered close-out below carries the block in place of that line and needs no addition
23
32
 
24
33
  ```markdown
25
34
  **For the reviewer**
@@ -27,9 +36,16 @@ An unanswered bullet is owed the same way an unanswered Testing box is, so a pas
27
36
  - Confirm the 401 and 403 split reads correctly for the public API. Confirmed: `AuthService.authenticate()` returns 401 for an expired token and 403 for a missing scope, and both paths are covered under `## Testing`.
28
37
  ```
29
38
 
30
- A pass carrying nothing at all takes `## Review closed` and a short body, with the footer line included either way. On a first pass, post `✅ No findings. Reviewed against project docs and the board.` On a later pass, post `✅ Prior findings addressed. Re-reviewed <short-sha>, N commits since the prior pass.`
39
+ ## Close-out bodies
40
+
41
+ A pass carrying nothing at all takes `## Review closed` and a short body, with the footer line included either way. Post a close-out even with nothing to report, since a review left with no closing comment reads as one nobody answered.
42
+
43
+ - On a first pass, post `✅ No findings. Reviewed against project docs and the board.`
44
+ - On a later pass, post `✅ Prior findings addressed. Re-reviewed <short-sha>, N commits since the prior pass.`
31
45
 
32
- A pass carrying only minors is an ordinary finding-carrying pass, so it takes the open heading and the full shape rather than either short line, since the minors have to be readable and neither line reports them. A pass carrying only Testing questions, or only an unanswered reviewer request, takes the same route for the same reason. Keep whichever scope sentence the pass owes on the summary line:
46
+ ### Only minors
47
+
48
+ 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:
33
49
 
34
50
  ```markdown
35
51
  ## Review
@@ -45,8 +61,12 @@ A pass carrying only minors is an ordinary finding-carrying pass, so it takes th
45
61
  <!-- review-pr: commit=<headRefOid> read-at=<read-at> -->
46
62
  ```
47
63
 
48
- A pass that closed by withdrawing a finding rather than by reading its fix takes neither ✅ line, per the withdrawal rule in Step 3. Both claim a fix landed, and the second names it, so posting either over a withdrawal credits work nobody did on the one comment a reader treats as the verdict. Write the withdrawal and the fact that settled it in place of the canned line, keeping the heading and the footer.
64
+ ### A withdrawal
65
+
66
+ A pass that closed by withdrawing a finding rather than reading its fix takes neither ✅ line, per the withdrawal rule in Step 3. Both claim a fix landed, so either credits work nobody did on the comment a reader treats as the verdict.
67
+
68
+ Write the withdrawal and the fact that settled it in place of the canned line, keeping the heading and the footer.
49
69
 
50
- A pass whose only content is a `## For the reviewer` block with every bullet answered, and that owes nothing else, takes the same shape: `## Review closed`, the block in place of the canned line, and the footer. The heading reports what the branch author still owes rather than what the pass did, and an answer discharged in the same comment owes nothing back.
70
+ ### Only answered reviewer requests
51
71
 
52
- Post a close-out even when there is nothing to report. A review left with no closing comment reads as one nobody answered.
72
+ A pass whose only content is a `## For the reviewer` block with every bullet answered, owing nothing else, takes `## Review closed`, the block in place of the canned line, and the footer. The heading reports what the author still owes, and an answer discharged in the same comment owes nothing back.
@@ -9,22 +9,37 @@ Part of Step 4 of `review-pr`, reached only on the branch `post.md` names. Every
9
9
 
10
10
  When `<prior-heading>` from Step 2 reads `## Review closed` and this pass carries nothing owed, the thread already holds this verdict. Replace the standing close-out rather than posting a second one beside it.
11
11
 
12
+ ## The rewrite
13
+
12
14
  Resolve its numeric id off the REST listing, keeping the newest match across every page:
13
15
 
14
16
  ```bash
15
17
  gh api --paginate 'repos/{owner}/{repo}/pulls/<number>/reviews?per_page=100' --jq '.[] | select((.body // "") | split("\n")[0] | rtrimstr("\r") == "## Review closed") | .id' | tail -n 1
16
18
  ```
17
19
 
18
- Write the replacement body to `<body-file>`, the name `post.md` already derived at the top of Step 4, keeping the heading and the footer and naming what this pass covered on the scope line in place of what the old one covered. 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. The folder then gains a record of every covered head rather than losing the one the standing comment named. Then replace the comment:
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
+
22
+ - 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
+ - The folder then gains a record of every covered head rather than losing the one the standing comment named
19
24
 
20
25
  ```bash
21
26
  gh api -X PUT repos/{owner}/{repo}/pulls/<number>/reviews/<review-id> -F body=@<body-file>
22
27
  ```
23
28
 
24
- `PUT` keeps the comment's timestamp and its position in the thread, so the verdict stays where a reader already found it and the thread gains no second entry. A submitted review cannot be deleted, which is why this rewrites the standing comment rather than posting a fresh one.
29
+ `PUT` keeps the comment's timestamp and its position in the thread, so the verdict stays where a reader already found it. A submitted review cannot be deleted, which is why this rewrites the standing comment rather than posting a fresh one.
30
+
31
+ ## When the guard fires
32
+
33
+ The guard fires on `## Review closed` alone.
34
+
35
+ - Two open passes carry different findings and both are worth reading, so a repeated `## Review` posts normally
36
+ - A pass carrying anything owed posts normally under `## Review`, so a finding raised after a close-out is never swallowed by the guard
37
+
38
+ ## What the PUT leaves pinned
25
39
 
26
- The guard fires on `## Review closed` alone. Two open passes carry different findings and both are worth reading, so a repeated `## Review` posts normally. A pass carrying anything owed posts normally too, under `## Review`, which is what keeps a finding raised after a close-out from being swallowed by the guard that exists for a silent one.
40
+ `PUT` leaves the review's `commit.oid` pinned at the commit the close-out was first submitted against. Step 2's `<prior-oid>` and the prior commit `poll.ts` derives would read too wide a range from it, and the poll's `SEEN` branch, firing on `prior` equalling the head, could never be reached.
27
41
 
28
- The rewrite used to cost the review's `commit.oid`, which `PUT` leaves pinned at the commit the standing close-out was first submitted against. Step 2's `<prior-oid>` and the prior commit `poll.ts` derives both read that field, so the next pass read a range wider than its delta, and the poll's `SEEN` branch, which fires on `prior` equalling the head, could never be reached at all: an out-of-band pass reported as `MOVED` for the rest of the pull request's life.
42
+ The marker answers both, because `PUT` replaces the body and the marker is in it. It carries the commit this pass read, and every reader takes it in preference to the pinned field.
29
43
 
30
- The marker closes both, because `PUT` replaces the body and the marker is in it. The rewritten close-out carries the commit this pass read rather than the one the comment was first submitted against, and every reader now takes that in preference to the pinned field. The one thing `PUT` still cannot move is `submittedAt`, which stays at the original submission and is what the poll's age test reads, so a rewritten close-out ages from when it first landed rather than from when it was last rewritten. That is the correct reading for a thread waiting on a human, which is the question the age test asks.
44
+ - `submittedAt` still stays at the original submission, and the poll's age test reads it
45
+ - A rewritten close-out therefore ages from when it first landed, which is the right reading for a thread waiting on a human
@@ -7,10 +7,34 @@ description: How review-pr Step 3 tests what the pull request body's Key changes
7
7
 
8
8
  Part of Step 3 of `review-pr`. The session reads this file after the `## Testing` boxes and before the `## For the reviewer` bullets.
9
9
 
10
- Test what `## Key changes` claims against the diff it describes, with `canon pr key-changes <number> --json`. This repository squash-merges, so that section becomes the commit message and the record on the trunk once the branch is gone, which is the same thing a stale ticked box corrupts. Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
10
+ Test what `## Key changes` claims against the diff it describes, with `canon pr key-changes <number> --json`. This repository squash-merges, so that section becomes the commit message on the trunk once the branch is gone, the same record a stale ticked box corrupts.
11
11
 
12
- Read `unmet` as a `should-fix` finding under the same `**PR body**` block the stale box takes, one entry per path, quoting the bullet the record carries on `preview` and naming the `head` the comparison ran at. Read the bullet before filing it, since one class survives the reader: a bullet citing where something is defined while claiming an edit elsewhere, which puts a real path in the claim region and points the change at a locative the path does not name. Report that as the class rather than as a stale claim. A body is edited between pushes, so the reading is true at that commit and can be false a minute later. Read `unnamed` for yourself and raise nothing off the count. The class still covers a real omission and equally a generated asset or a regenerated index that earns no bullet, and the entries the reader can name as owing none are already held apart under `incidental`. Open the section and confirm no bullet names the file before putting a single one to the author, because this instruction has sent the question to three pull requests in one day over bullets that had named the files all along, and one of those authors added bullets nobody needed. Report `unresolved` and `incidental` nowhere: the first was judged in neither direction and the second owes no bullet, so neither says anything about the body.
12
+ ## Reading each field
13
13
 
14
- A `reason` of `no-claims` means the reader resolved no path out of a section that exists, which is the extractor failing over prose rather than the body being wrong. Say so and raise nothing. `no-section` is the body carrying no such heading, which is a finding only where the pull request template asks for one.
14
+ Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero. Each field the record carries takes its own reading.
15
+
16
+ | Field | Reading |
17
+ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
18
+ | `unmet` | a `should-fix` finding under the `**PR body**` block the stale box takes, one per path, quoting the `preview` bullet and the `head` |
19
+ | `unnamed` | read for yourself and raise nothing off the count |
20
+ | `unresolved` | reported nowhere, since it was judged in neither direction |
21
+ | `incidental` | reported nowhere, since it owes no bullet |
22
+
23
+ ### Before filing an unmet path
24
+
25
+ Read the bullet before filing it, since one class survives the reader. A bullet citing where something is defined while claiming an edit elsewhere puts a real path in the claim region, so report that as the class rather than as a stale claim.
26
+
27
+ A body is edited between pushes, so the reading is true at the `head` it names and can be false a minute later.
28
+
29
+ ### Before raising an unnamed path
30
+
31
+ The `unnamed` class covers a real omission and equally a generated asset or a regenerated index that earns no bullet. Open the section and confirm no bullet names the file before putting one to the author.
32
+
33
+ This instruction once sent the question to three pull requests in one day over bullets that had named the files all along, and one author added bullets nobody needed.
34
+
35
+ ## Refusals
36
+
37
+ - `no-claims` means the reader resolved no path out of a section that exists, the extractor failing over prose, so say so and raise nothing
38
+ - `no-section` means the body carries no such heading, a finding only where the pull request template asks for one
15
39
 
16
40
  Compose this on the body read Step 4 already performs rather than opening a second one. That step scans the title and body for a leaked phase label, and both reads answer from the same text at the same head.