@erclx/canon 5.46.0 → 5.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/git-followup/SKILL.md +1 -0
- package/claude/skills/git-pr/REQUIREMENT.md +2 -2
- package/claude/skills/git-pr/SKILL.md +2 -1
- package/claude/skills/git-pr/references/pr.md +58 -7
- package/claude/skills/review-address/SKILL.md +18 -6
- package/claude/skills/review-address/references/post-review-findings.md +4 -1
- package/claude/skills/review-address/references/rebase-only.md +14 -2
- package/claude/skills/review-pr/SKILL.md +9 -2
- package/claude/skills/review-pr/references/body-variants.md +29 -8
- package/claude/skills/review-pr/references/post.md +29 -10
- package/claude/skills/review-ui/SKILL.md +1 -0
- package/claude/skills/review-ui/references/post.md +2 -0
- package/claude/skills/review-ui/references/tick.md +3 -0
- package/claude/skills/role-reviewer/REQUIREMENT.md +1 -1
- package/claude/skills/role-reviewer/SKILL.md +3 -0
- package/claude/skills/role-worker/REQUIREMENT.md +1 -1
- package/claude/skills/role-worker/SKILL.md +1 -0
- package/claude/skills/task-board/references/decline.md +2 -2
- package/claude/skills/task-complete/references/archive.md +3 -2
- package/claude/skills/ui-checklist/SKILL.md +3 -3
- package/docs/workflow/zshrc-aliases.md +3 -0
- package/package.json +1 -1
- package/scripts/core/bootstrap.sh +1 -0
- package/src/commands/labels.ts +66 -1
- package/src/commands/pr.ts +113 -4
- package/src/commands/sessions.ts +25 -11
- package/src/commands/tasks.ts +2 -0
- package/src/labels/density.ts +363 -0
- package/src/pr/evidence.ts +1 -1
- package/src/pr/rest.ts +2 -0
- package/src/pr/tick.ts +89 -0
- package/src/sessions/live.ts +26 -0
- package/src/sessions/resolve.ts +62 -11
- package/src/tasks/archive.ts +15 -4
- package/tooling/base/seeds/.github/pull_request_template.md +1 -1
|
@@ -58,6 +58,7 @@ A missing tracking ref is not a guard, since an open pull request proves the bra
|
|
|
58
58
|
- Read the live title and body with `gh api repos/{owner}/{repo}/pulls/<number> --jq '{html_url, title, body}'`. Update the body with `gh api -X PATCH repos/{owner}/{repo}/pulls/<number> -F body=@<file> --silent` when the new commit changes scope, writing the new body to `<file>` first, and the title with `gh api -X PATCH repos/{owner}/{repo}/pulls/<number> -f title="<title>" --silent` when the scope shifted enough to make it inaccurate.
|
|
59
59
|
- A fix commit answering a review changes what shipped exactly as much as an ordinary followup does, so the sync cannot wait on the invocation or the comment count below.
|
|
60
60
|
- A body a person edited by hand between rounds gets no special handling: judge it against the tree the same way regardless of who wrote it last, since a hand-edit the fix commit has made stale is the exact drift this sync exists to close.
|
|
61
|
+
- Carry a `UI pass` line marked `<!-- pr-ui-pass -->` into any rewritten body unchanged, since `canon pr tick` owns it and a dropped marker defeats the reset on a new head.
|
|
61
62
|
8. Route on the invocation and the comment count for the reply alone.
|
|
62
63
|
- When invoked with `reply-owned`, skip this step: the caller posts its own reply.
|
|
63
64
|
- Otherwise, if the count is above zero, the followup addresses review feedback: write a one-line summary of the fix to `.canon/tmp/pr/followup/summary-<number>.md` in its own call, run `canon labels scan --body-file .canon/tmp/pr/followup/summary-<number>.md` against it, which applies the rule in `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md`, then post it with `gh api -X POST repos/{owner}/{repo}/issues/<number>/comments -F body=@.canon/tmp/pr/followup/summary-<number>.md --silent`. A hook on `Bash` runs the same scan on that body file and denies the post on a hit, and it refuses an inline `-f body=` value, so the body goes through a file. The `pull_request` check the git-pr surface carries triggers on a push or an open rather than a plain edit, so neither this comment nor the title and body step 7 synced reaches it.
|
|
@@ -7,13 +7,13 @@ description: What pull request generation is for, the gaps it closes, and what i
|
|
|
7
7
|
|
|
8
8
|
## Gap
|
|
9
9
|
|
|
10
|
-
Without this skill, a pull request body is written from memory rather than the diff, so it describes the intent and omits what the work became. Testing boxes get ticked from intent, a second push errors or opens a duplicate, and banned characters survive into a body the hook never sees.
|
|
10
|
+
Without this skill, a pull request body is written from memory rather than the diff, so it describes the intent and omits what the work became. Testing boxes get ticked from intent, a second push errors or opens a duplicate, and banned characters survive into a body the hook never sees. The body grows into a wall nothing measures.
|
|
11
11
|
|
|
12
12
|
Every pull request lands unlabelled, so a merged list cannot be filtered by surface. A head-branch lookup resolves a reused name to the merged pull request and rewrites it. A pull request changing a rendered surface reaches review with no screenshots, and a hand-written `Closes` line closes an issue on a middle slice or misses one the task names.
|
|
13
13
|
|
|
14
14
|
## Must
|
|
15
15
|
|
|
16
|
-
- The title and body come from the commits and the diff against main
|
|
16
|
+
- The title and body come from the commits and the lockfile-free diff against main, scanned for banned characters, phase labels, and density, and every ticked box records a check this run executed
|
|
17
17
|
- A follow-up run edits the one open pull request on the current head and default base, resolved once, and every write first proves that pull request's head and open state
|
|
18
18
|
- Closing lines come from `canon tasks closes`, one `Closes #NNN` per issue on the task's last slice only, with the scan failing a body that disagrees
|
|
19
19
|
- Labels come from the paths the branch changed against a map the project declares, applied after the pull request exists, with a refused label reported
|
|
@@ -105,7 +105,7 @@ Drop `--closes` when the closes step skipped a refusal, since no list was decide
|
|
|
105
105
|
|
|
106
106
|
### Reading the scan
|
|
107
107
|
|
|
108
|
-
Branch on the JSON record rather than the exit code, since a shell function wrapping `canon` can flatten a non-zero exit to 0. Stop and fix the title or body on any key below, and do not proceed to `### Final command` until a re-run comes back clean on all
|
|
108
|
+
Branch on the JSON record rather than the exit code, since a shell function wrapping `canon` can flatten a non-zero exit to 0. Stop and fix the title or body on any key below, and do not proceed to `### Final command` until a re-run comes back clean on all nine.
|
|
109
109
|
|
|
110
110
|
| Key | Fix |
|
|
111
111
|
| ------------------- | ------------------------------------------------------------------------------------ |
|
|
@@ -117,6 +117,7 @@ Branch on the JSON record rather than the exit code, since a shell function wrap
|
|
|
117
117
|
| `sections` | Rename or drop each heading in `outside`, and add each required heading in `missing` |
|
|
118
118
|
| `missingClosures` | Add a `Closes #NNN` line for each number listed |
|
|
119
119
|
| `strayClosures` | Delete the closing keyword for each number listed |
|
|
120
|
+
| `density` | Regroup or split the named section per `## Formatting` in `pr.md`, keeping each path |
|
|
120
121
|
|
|
121
122
|
Leave `cutsRelease` and `semverTags` alone, since a release-please pull request legitimately carries version references its own fixed shape explains.
|
|
122
123
|
|
|
@@ -52,8 +52,17 @@ A body describes the new behavior and nothing the diff or the commits already sa
|
|
|
52
52
|
|
|
53
53
|
## Sections
|
|
54
54
|
|
|
55
|
-
- `## Summary`:
|
|
56
|
-
- `## Key changes`:
|
|
55
|
+
- `## Summary`: one paragraph following `<Action Verb> <Direct Object> to <Result>`, then the closing lines
|
|
56
|
+
- `## Key changes`: bullets only, one change each, naming actual files, functions, or modules (e.g., `AuthService.verify()` not "auth handler")
|
|
57
|
+
- Name every changed path in a Key changes bullet. A trim may cut a clause and never a path, since `canon pr key-changes` reports a path no bullet claims.
|
|
58
|
+
|
|
59
|
+
### Key changes groups
|
|
60
|
+
|
|
61
|
+
Past 5 bullets, group Key changes under bold labels, 5 bullets a group at most.
|
|
62
|
+
|
|
63
|
+
- A label sits on its own line, names the area a reviewer opens together, and ends with no period
|
|
64
|
+
- Write a label in plain words, since a code span inside bold renders unbolded
|
|
65
|
+
- Open each group on a bullet, never on prose
|
|
57
66
|
|
|
58
67
|
### Closing lines
|
|
59
68
|
|
|
@@ -61,10 +70,12 @@ The closing lines end `## Summary`, one `Closes #NNN` per issue and no trailing
|
|
|
61
70
|
|
|
62
71
|
### Context section
|
|
63
72
|
|
|
64
|
-
`## Technical context` is optional and
|
|
73
|
+
`## Technical context` is optional and says why, naming the decisions the code can't show and any shortcoming of the approach.
|
|
65
74
|
|
|
66
75
|
- Omit Technical context for docs, config, or trivial changes
|
|
67
|
-
-
|
|
76
|
+
- Take one form per section, never bullets and paragraphs mixed
|
|
77
|
+
- Use bullets when every reason fits one sentence under 200 characters
|
|
78
|
+
- Otherwise write one paragraph per decision, opening on a bold lead-in that ends with a period and runs into the paragraph
|
|
68
79
|
|
|
69
80
|
### Testing section
|
|
70
81
|
|
|
@@ -72,6 +83,7 @@ The closing lines end `## Summary`, one `Closes #NNN` per issue and no trailing
|
|
|
72
83
|
|
|
73
84
|
- Omit Testing for docs, config, or trivial sync changes
|
|
74
85
|
- Use checkboxes, never prose. See Testing discipline for which box gets ticked.
|
|
86
|
+
- Write one box per command, pointing at a test file rather than listing its cases
|
|
75
87
|
|
|
76
88
|
### Reviewer section and visuals
|
|
77
89
|
|
|
@@ -90,6 +102,8 @@ The closing lines end `## Summary`, one `Closes #NNN` per issue and no trailing
|
|
|
90
102
|
- A ticked box describes the tree as it stands. A later commit removing or replacing what it names makes the box false.
|
|
91
103
|
- The branch that made that commit corrects the box in the same pass, as with any other stale claim in the body
|
|
92
104
|
|
|
105
|
+
Leave the `UI pass` line to `canon pr tick`, and carry it on a rewrite.
|
|
106
|
+
|
|
93
107
|
### Human-only boxes
|
|
94
108
|
|
|
95
109
|
Leave a box unchecked only when a human is required, and name which human and why on the same line. The agent runs everything else.
|
|
@@ -110,6 +124,17 @@ A live agent session is never the human a box waits on.
|
|
|
110
124
|
## Formatting
|
|
111
125
|
|
|
112
126
|
- End every bullet point with a period
|
|
127
|
+
- Carry no H3 or H4 anywhere in a body. GitHub draws an H3 one step under an H2, so stacked H3s compete with the sections.
|
|
128
|
+
- Group with a bold label or lead-in instead, in the forms the Sections rules above state
|
|
129
|
+
|
|
130
|
+
### Density
|
|
131
|
+
|
|
132
|
+
`canon labels scan` reports each section's density under `density`, naming the section, the kind, and the line in the whole body. Its help lists every limit.
|
|
133
|
+
|
|
134
|
+
- Every section but Testing holds bullets to 200 characters and 5 in a row, and paragraphs to 350 characters and 2 in a row
|
|
135
|
+
- Any section reports a `subheading`, and Technical context reports a `mixed-form`
|
|
136
|
+
- A bold label alone on a line breaks a Key changes run, and a bold lead-in starts a new Technical context run
|
|
137
|
+
- Fix a finding by regrouping or splitting, never by cutting a path
|
|
113
138
|
|
|
114
139
|
## Examples
|
|
115
140
|
|
|
@@ -122,19 +147,29 @@ Every section a body may carry, in order.
|
|
|
122
147
|
|
|
123
148
|
<Action Verb> <Direct Object> to <Result>.
|
|
124
149
|
|
|
150
|
+
Closes #<number>
|
|
151
|
+
|
|
125
152
|
## Key changes
|
|
126
153
|
|
|
154
|
+
**<Area a reviewer opens together>**
|
|
155
|
+
|
|
127
156
|
- <Verb> <specific component/file/function> (<reason if non-obvious>)
|
|
128
157
|
- <Verb> <specific component/file/function>
|
|
129
158
|
|
|
159
|
+
**<Next area>**
|
|
160
|
+
|
|
161
|
+
- <Verb> <specific component/file/function>
|
|
162
|
+
|
|
130
163
|
## Technical context
|
|
131
164
|
|
|
132
|
-
|
|
165
|
+
**<Decision in a few words>.** <Why this approach, what the code can't show, and any shortcoming>
|
|
166
|
+
|
|
167
|
+
**<Next decision>.** <Why>
|
|
133
168
|
|
|
134
169
|
## Testing
|
|
135
170
|
|
|
136
171
|
- [x] <Command run> <observed result>
|
|
137
|
-
- [x] <
|
|
172
|
+
- [x] <Test file run> <observed result>
|
|
138
173
|
- [ ] <Human-only check> (<which human, why>)
|
|
139
174
|
|
|
140
175
|
## For the reviewer
|
|
@@ -153,12 +188,20 @@ Update auth middleware to enforce jwt expiration checks. # imperative + direct o
|
|
|
153
188
|
|
|
154
189
|
## Key changes
|
|
155
190
|
|
|
191
|
+
**Validation** # bold label on its own line, no period
|
|
192
|
+
|
|
156
193
|
- Add `verifyExpiration()` to `src/auth/validators.ts`. # specific function + file path
|
|
194
|
+
- Add the clock skew window to `src/auth/config.ts`. # one change per bullet
|
|
195
|
+
|
|
196
|
+
**Session handling** # one label per area a reviewer opens together
|
|
197
|
+
|
|
157
198
|
- Refactor `AuthService.authenticate()` to handle 401 codes. # named component + clear change
|
|
158
199
|
|
|
159
200
|
## Technical context
|
|
160
201
|
|
|
161
|
-
|
|
202
|
+
**Stateless sessions.** Expiry lives in the token, so any instance can reject it without a shared store. # bold lead-in + why
|
|
203
|
+
|
|
204
|
+
**Clock skew.** A 30-second window absorbs drift between instances, so a token can outlive its expiry by that much. # decision + shortcoming
|
|
162
205
|
|
|
163
206
|
## Testing
|
|
164
207
|
|
|
@@ -182,9 +225,17 @@ This PR updates the authentication system to be more robust. # "This PR" opener
|
|
|
182
225
|
|
|
183
226
|
## Key changes
|
|
184
227
|
|
|
228
|
+
### Middleware # H3 inside a body
|
|
229
|
+
|
|
185
230
|
- Updated auth middleware files # vague, no specific component, no period
|
|
186
231
|
- The old system used to check tokens differently # describes historical behavior
|
|
187
232
|
|
|
233
|
+
## Technical context
|
|
234
|
+
|
|
235
|
+
- Tokens are stateless now. # a bullet
|
|
236
|
+
|
|
237
|
+
The skew window trades precision for no shared store. # mixed with a paragraph in one section
|
|
238
|
+
|
|
188
239
|
## Testing
|
|
189
240
|
|
|
190
241
|
- Tested manually # no specific command or case
|
|
@@ -88,33 +88,45 @@ Once the fixes are commits, re-run step 5's staleness test against the new head.
|
|
|
88
88
|
|
|
89
89
|
### Write the reply
|
|
90
90
|
|
|
91
|
-
Write a summary reply to `.canon/tmp/pr/reply/reply-<number>.md`, keyed on the PR number so two sessions addressing different pull requests never overwrite each other. Load `write-human` for voice, follow `${CLAUDE_SKILL_DIR}/../markdown-craft/references/markdown.md`, and keep each mapping to
|
|
91
|
+
Write a summary reply to `.canon/tmp/pr/reply/reply-<number>.md`, keyed on the PR number so two sessions addressing different pull requests never overwrite each other. Load `write-human` for voice, follow `${CLAUDE_SKILL_DIR}/../markdown-craft/references/markdown.md`, and keep each mapping to one line.
|
|
92
92
|
|
|
93
93
|
| Part | Content |
|
|
94
94
|
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
95
95
|
| Heading | `## Review response`, distinct from human threads and subordinate to the review's own `## Review` |
|
|
96
96
|
| Summary | One sentence. After a step 5 rebase, say so and name files resolved by hand and those the regen rebuilt, since the next review is a full pass. |
|
|
97
|
-
| Findings | One
|
|
97
|
+
| Findings | One line per finding under 200 characters, naming its outcome and the commit that carries it, in the shape below |
|
|
98
98
|
| Docs | Any `.claude/` docs refreshed as a result of the fixes |
|
|
99
99
|
| Close | `🤖 Addressed by Claude Code` on its own line, so the reply reads as a machine pass rather than a human sign-off |
|
|
100
100
|
|
|
101
|
+
```markdown
|
|
102
|
+
- **<severity>**, `<file>` line <n>: fixed in `<sha>`. <What now holds, in one sentence.>
|
|
103
|
+
- **<severity>**, `<file>` line <n>: declined. <The fact that settled it.>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The commit carries the fix and its test, so the line names neither.
|
|
107
|
+
|
|
101
108
|
A run the guard sent straight to step 5 has no findings to map, so it takes the `## Rebase` body in `${CLAUDE_SKILL_DIR}/references/rebase-only.md` instead. A run with findings skips that file.
|
|
102
109
|
|
|
103
110
|
### What a declined finding carries
|
|
104
111
|
|
|
105
|
-
A finding this run declined carries the fact that settled it inside its own
|
|
112
|
+
A finding this run declined carries the fact that settled it inside its own line, which may take a second sentence for it, such as the plan question that answered it, a constraint the diff does not show, or a measurement the reviewer lacked. Left in the channel, that fact is lost when both sessions end.
|
|
106
113
|
|
|
107
114
|
The rule reaches a finding and nothing else. A correction to what the reviewing session believes about the world changes no finding here, so answer it wherever the dispatch reached this session and keep it off the comment.
|
|
108
115
|
|
|
109
116
|
### Scan and post
|
|
110
117
|
|
|
111
|
-
Nothing tests the reply for either rule above. Follow `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md` and run its scan, which reads for banned characters and phase labels
|
|
118
|
+
Nothing tests the reply for either rule above. Follow `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md` and run its scan, which reads for banned characters and phase labels, and reports the reply's density:
|
|
112
119
|
|
|
113
120
|
```bash
|
|
114
|
-
canon labels scan --body-file .canon/tmp/pr/reply/reply-<number>.md
|
|
121
|
+
canon labels scan --body-file .canon/tmp/pr/reply/reply-<number>.md --json
|
|
115
122
|
```
|
|
116
123
|
|
|
117
|
-
The hook skips `.canon/tmp/`, and the `pull_request` check reads only a pull request's own title and body, so this scan is the only gate on the reply.
|
|
124
|
+
The hook skips `.canon/tmp/`, and the `pull_request` check reads only a pull request's own title and body, so this scan is the only gate on the reply.
|
|
125
|
+
|
|
126
|
+
- Fix any hit, and stop on a `density` entry too, shortening the line it names
|
|
127
|
+
- Read the record rather than the exit, since density never sets the exit code
|
|
128
|
+
|
|
129
|
+
Then post and keep the `id` the POST answers with, since Step 7 edits this exact comment:
|
|
118
130
|
|
|
119
131
|
```bash
|
|
120
132
|
gh api -X POST 'repos/{owner}/{repo}/issues/<number>/comments' -F body=@.canon/tmp/pr/reply/reply-<number>.md --jq .id > .canon/tmp/pr/reply/reply-<number>.id
|
|
@@ -12,10 +12,13 @@ Outside the numbered flow of `review-address`. The session reads this file when
|
|
|
12
12
|
Write the body the way Step 6 writes a reply.
|
|
13
13
|
|
|
14
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`
|
|
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 --json`
|
|
16
|
+
- Stop on a `density` entry as on any hit, since density never sets the exit code
|
|
16
17
|
- Open with `## Post-review findings` rather than `## Review response`, since nothing on the thread is being answered
|
|
17
18
|
- Close the body with `🤖 Addressed by Claude Code` on its own line, matching the reply's footer
|
|
18
19
|
|
|
20
|
+
Give each finding one line under 200 characters in the response's shape, naming its severity, its file and line, and what holds now or what was filed.
|
|
21
|
+
|
|
19
22
|
`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.
|
|
20
23
|
|
|
21
24
|
```bash
|
|
@@ -11,10 +11,22 @@ Read from Steps 6, 7, and 8 of `review-address` when the second guard sent the r
|
|
|
11
11
|
|
|
12
12
|
A rebase-only run has no findings to map, so it takes a different body rather than an empty list. Open it with `## Rebase`, since nothing on the pull request is being responded to and `## Review response` claims a review this run never read.
|
|
13
13
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
14
|
+
- Open on one sentence naming the commit rebased onto and what landed on `main`, and which half of the staleness test fired, being a branch that stopped merging or one `main` changed files under
|
|
15
|
+
- Give one short line each to the files resolved by hand, those the regen rebuilt, and those replayed with no conflict, each under 200 characters
|
|
16
16
|
- Close with `🤖 Addressed by Claude Code` on its own line, the same as a review response
|
|
17
17
|
|
|
18
|
+
```markdown
|
|
19
|
+
## Rebase
|
|
20
|
+
|
|
21
|
+
Rebased onto main at `<short-sha>`, because <why>.
|
|
22
|
+
|
|
23
|
+
- Resolved by hand: `<path>`. <What the resolution kept, in one sentence.>
|
|
24
|
+
- Rebuilt by the regen: `<path>`, `<path>`
|
|
25
|
+
- Replayed with no conflict: `<path>`
|
|
26
|
+
|
|
27
|
+
🤖 Addressed by Claude Code
|
|
28
|
+
```
|
|
29
|
+
|
|
18
30
|
The heading stays outside the `## Review` family, so the close-out's equality test on the first line never matches it.
|
|
19
31
|
|
|
20
32
|
## Closing confirmation
|
|
@@ -123,11 +123,15 @@ Whether a human is genuinely required is a reading the author may hold a reason
|
|
|
123
123
|
|
|
124
124
|
Read `## For the reviewer` the same way, bounded to the bullets under that heading. The Summary and Technical context carry the author's argument, and reading them while judging is what an independent pass exists to avoid.
|
|
125
125
|
|
|
126
|
-
Answer each bullet in the body with no severity and no count.
|
|
126
|
+
Answer each bullet in the body with no severity and no count.
|
|
127
|
+
|
|
128
|
+
- An unanswered request stays owed under the heading and dispatch, as an unanswered Testing box does
|
|
129
|
+
- An answered one is discharged in the same comment
|
|
130
|
+
- A request the diff answers against the author becomes a graded finding under the file that should have handled it, and enters the count, as an unconfirmed Review focus item does
|
|
127
131
|
|
|
128
132
|
### Review focus
|
|
129
133
|
|
|
130
|
-
On a first pass, confirm each `**Review focus:**` item the plan names against the diff, since only the planning session's list names the inputs that would break the change. Report them
|
|
134
|
+
On a first pass, confirm each `**Review focus:**` item the plan names against the diff, since only the planning session's list names the inputs that would break the change. Report them folded under `<details>` after the reviewer block, with the count in its summary as `Review focus, N checked`, one bullet each naming the case and what confirmed it.
|
|
131
135
|
|
|
132
136
|
- The block carries no severity and enters no count
|
|
133
137
|
- An item the pass cannot confirm becomes a `should-fix` finding under the file that should have handled it, so a checked item reads apart from a skipped one
|
|
@@ -156,6 +160,9 @@ Read `${CLAUDE_SKILL_DIR}/references/post.md` for the body file name, the body s
|
|
|
156
160
|
|
|
157
161
|
That file sends a pass repeating a standing close-out on to `${CLAUDE_SKILL_DIR}/references/close-out.md`, which rewrites the comment in place.
|
|
158
162
|
|
|
163
|
+
- Stop on a `density` entry in the body scan's record as on any other finding, and regroup or split the item it names before posting
|
|
164
|
+
- Read the record rather than the exit, since the scan exits 0 on density alone
|
|
165
|
+
|
|
159
166
|
## Step 5: output
|
|
160
167
|
|
|
161
168
|
```plaintext
|
|
@@ -23,17 +23,21 @@ A Testing box the Step 3 check raised goes in a `**Testing**` block placed after
|
|
|
23
23
|
|
|
24
24
|
## The reviewer block
|
|
25
25
|
|
|
26
|
-
Every `## For the reviewer` bullet Step 3 read
|
|
26
|
+
Every `## For the reviewer` bullet Step 3 read gets one bullet in the reviewer block, placed after the Testing block. It carries no severity and enters no count, and a request the pass answered against the author becomes a finding under its file block instead.
|
|
27
27
|
|
|
28
|
-
- An unanswered bullet is owed like an unanswered Testing box, so the pass takes `## Review` and the full body
|
|
29
28
|
- An answered bullet is not owed, since the answer is discharged in the same comment that carries it
|
|
29
|
+
- It folds under `<details>` with the count in its summary, as the request shortened and the read or test that settled it
|
|
30
|
+
- An unanswered bullet is owed like an unanswered Testing box, so it stays out of the fold in a `**For the reviewer**` block naming what would settle it, and the pass takes `## Review` and the full body
|
|
30
31
|
- 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
|
|
32
|
+
- The all-answered close-out below carries the folded block in place of that line and needs no addition
|
|
32
33
|
|
|
33
34
|
```markdown
|
|
34
|
-
|
|
35
|
+
<details>
|
|
36
|
+
<summary>For the reviewer, N confirmed</summary>
|
|
35
37
|
|
|
36
|
-
-
|
|
38
|
+
- The 401 and 403 split: `AuthService.authenticate()` returns 401 for an expired token and 403 for a missing scope
|
|
39
|
+
|
|
40
|
+
</details>
|
|
37
41
|
```
|
|
38
42
|
|
|
39
43
|
## Close-out bodies
|
|
@@ -41,7 +45,23 @@ Every `## For the reviewer` bullet Step 3 read goes in a `**For the reviewer**`
|
|
|
41
45
|
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
46
|
|
|
43
47
|
- On a first pass, post `✅ No findings. Reviewed against project docs and the board.`
|
|
44
|
-
- On a later pass, post
|
|
48
|
+
- On a later pass, post the count line, then one line per prior finding, minors included, so a closed review always says what it closed
|
|
49
|
+
|
|
50
|
+
```markdown
|
|
51
|
+
## Review closed
|
|
52
|
+
|
|
53
|
+
✅ N prior findings addressed at `<short-sha>`. Re-reviewed the N commits since the prior pass.
|
|
54
|
+
|
|
55
|
+
- **<severity>**, `<path>`: what now holds, in one line
|
|
56
|
+
|
|
57
|
+
🤖 Reviewed by Claude Code
|
|
58
|
+
|
|
59
|
+
<!-- review-pr: commit=<headRefOid> read-at=<read-at> -->
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- Each line names what holds now rather than the commit that fixed it, and stays under 200 characters
|
|
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
|
+
- A closing remark that fits no prior finding, such as a ticked Testing box, takes one line of its own after them
|
|
45
65
|
|
|
46
66
|
### Only minors
|
|
47
67
|
|
|
@@ -54,7 +74,8 @@ A pass carrying only minors is an ordinary finding-carrying pass, so it takes th
|
|
|
54
74
|
|
|
55
75
|
**`path/to/file.ext`**
|
|
56
76
|
|
|
57
|
-
- **minor** ([line 12](<repo-url>/blob/<headRefOid>/path/to/file.ext#L12)):
|
|
77
|
+
- **minor** ([line 12](<repo-url>/blob/<headRefOid>/path/to/file.ext#L12)): what breaks, in one sentence.
|
|
78
|
+
- Fix: the change
|
|
58
79
|
|
|
59
80
|
🤖 Reviewed by Claude Code
|
|
60
81
|
|
|
@@ -69,4 +90,4 @@ Write the withdrawal and the fact that settled it in place of the canned line, k
|
|
|
69
90
|
|
|
70
91
|
### Only answered reviewer requests
|
|
71
92
|
|
|
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.
|
|
93
|
+
A pass whose only content is a `## For the reviewer` block with every bullet answered, owing nothing else, takes `## Review closed`, the folded 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.
|
|
@@ -32,7 +32,7 @@ That satisfies both prohibitions above rather than carving an exception into eit
|
|
|
32
32
|
A finding bound to a line links to it, so a reader skips the hunt:
|
|
33
33
|
|
|
34
34
|
```markdown
|
|
35
|
-
- **should-fix** ([line 12](<repo-url>/blob/<headRefOid>/<path>#L12)): what breaks
|
|
35
|
+
- **should-fix** ([line 12](<repo-url>/blob/<headRefOid>/<path>#L12)): what breaks, in one sentence
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
- A span anchors as `#L12-L14`, with no spaces and no en dash, and a single line as `#L12`
|
|
@@ -60,12 +60,18 @@ X critical, Y should-fix, Z minor. Reviewed against project docs and the board.
|
|
|
60
60
|
|
|
61
61
|
**`path/to/file.ext`**
|
|
62
62
|
|
|
63
|
-
- **should-fix** ([line 12](<repo-url>/blob/<headRefOid>/path/to/file.ext#L12)): what breaks
|
|
64
|
-
-
|
|
63
|
+
- **should-fix** ([line 12](<repo-url>/blob/<headRefOid>/path/to/file.ext#L12)): what breaks, in one sentence.
|
|
64
|
+
- one fact of evidence, with its link
|
|
65
|
+
- Fix: the change
|
|
66
|
+
- **minor** ([lines 30-34](<repo-url>/blob/<headRefOid>/path/to/file.ext#L30-L34)): what breaks, in one sentence.
|
|
67
|
+
- Fix: the change
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
<details>
|
|
70
|
+
<summary>Review focus, N checked</summary>
|
|
67
71
|
|
|
68
|
-
-
|
|
72
|
+
- the case: the test or read that covers it
|
|
73
|
+
|
|
74
|
+
</details>
|
|
69
75
|
|
|
70
76
|
🤖 Reviewed by Claude Code
|
|
71
77
|
|
|
@@ -81,14 +87,24 @@ Re-reviewed `<short-sha>`, N commits since the prior pass. X critical, Y should-
|
|
|
81
87
|
|
|
82
88
|
**`path/to/file.ext`**
|
|
83
89
|
|
|
84
|
-
- **should-fix** ([line 12](<repo-url>/blob/<headRefOid>/path/to/file.ext#L12)): what breaks
|
|
90
|
+
- **should-fix** ([line 12](<repo-url>/blob/<headRefOid>/path/to/file.ext#L12)): what breaks, in one sentence.
|
|
91
|
+
- Fix: the change
|
|
85
92
|
|
|
86
93
|
🤖 Reviewed by Claude Code
|
|
87
94
|
|
|
88
95
|
<!-- review-pr: commit=<headRefOid> read-at=<read-at> -->
|
|
89
96
|
```
|
|
90
97
|
|
|
91
|
-
|
|
98
|
+
### The finding shape
|
|
99
|
+
|
|
100
|
+
A reader decides from the first line of a finding whether to open the rest, so the subject carries the failure alone.
|
|
101
|
+
|
|
102
|
+
- The subject is one sentence under 200 characters, naming what breaks rather than how the pass found it
|
|
103
|
+
- Each nested bullet carries one fact of evidence with its link, and the last one opens on `Fix:`
|
|
104
|
+
- A nested bullet stays under 200 characters too, since `canon labels scan` measures it as a bullet of its own
|
|
105
|
+
- A finding with nothing to add past its subject takes the `Fix:` bullet alone
|
|
106
|
+
|
|
107
|
+
A Testing question or a `## For the reviewer` bullet Step 3 read takes its own block after the file blocks, per `${CLAUDE_SKILL_DIR}/references/body-variants.md`. A request or a focus case that fails leaves `<details>` and becomes a finding.
|
|
92
108
|
|
|
93
109
|
### The PR body block
|
|
94
110
|
|
|
@@ -123,9 +139,9 @@ A minor the dispatched worker declines needs a surface that survives the merge,
|
|
|
123
139
|
Name the scope in every summary line after the first pass, since a reader cannot otherwise tell a narrow read from a full one. When the fallback in Step 2 fired, replace the commit count with `Re-reviewed the full change, the prior pass's commit is no longer on the branch`.
|
|
124
140
|
|
|
125
141
|
- Do not append the PR number to either heading, which GitHub already renders above the comment
|
|
126
|
-
- State each finding
|
|
142
|
+
- State each finding in the shape above, never as a paragraph of reasoning
|
|
127
143
|
- Omit files with no findings and do not lecture on process. The integration, contract, and consumer lenses stay as findings, not asides.
|
|
128
|
-
- `What is right`
|
|
144
|
+
- Leave `What is right` out unless the author asked a direct question it answers, then cap it at three bullets
|
|
129
145
|
- Close with `🤖 Reviewed by Claude Code` on its own line, so the review reads as an independent machine pass, not a human sign-off
|
|
130
146
|
|
|
131
147
|
## The marker, the scans, and the post
|
|
@@ -156,7 +172,10 @@ The marker is the read-time record those fields are not.
|
|
|
156
172
|
|
|
157
173
|
### The scans
|
|
158
174
|
|
|
159
|
-
Before posting, run `canon labels scan --body-file .canon/tmp/pr/review/body-<number>-<short-sha>.md`, which applies the rule in `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md`.
|
|
175
|
+
Before posting, run `canon labels scan --body-file .canon/tmp/pr/review/body-<number>-<short-sha>.md`, which applies the rule in `${CLAUDE_SKILL_DIR}/../git-pr/references/publish.md`. Pass `--json` and fix the body and scan again on a finding.
|
|
176
|
+
|
|
177
|
+
- A `density` entry is a finding here, naming a subject or nested bullet past 200 characters, a run past 5 plain bullets, or a paragraph past its limits, by its line in the whole body
|
|
178
|
+
- Density never sets the exit code, so the hook below lets the post through on it and this step is the one that stops
|
|
160
179
|
|
|
161
180
|
- A finding phrased against an internal phase label is what the label half of the scan catches here
|
|
162
181
|
- The call carries no `--title`, so the scan reads the text as a review
|
|
@@ -115,6 +115,7 @@ ${CLAUDE_SKILL_DIR}/scripts/pw.sh -s=<project>-<number> open <url>
|
|
|
115
115
|
|
|
116
116
|
Read `${CLAUDE_SKILL_DIR}/references/driving.md` on reaching this step for how a box's line maps to calls, how each value is read, and the four verdicts with the evidence each one carries.
|
|
117
117
|
|
|
118
|
+
- 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.
|
|
118
119
|
- Give every box exactly one verdict, in checklist order, since skipping a box silently reads the same as passing it
|
|
119
120
|
- 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
|
|
120
121
|
- Never judge a box ending in `(taste)`. Drive to the state, capture both themes, and mark it needs eyes.
|
|
@@ -76,6 +76,8 @@ Driven at `<short-sha>` on <address>, rendered by <renderer>.
|
|
|
76
76
|
<!-- review-ui: head=<head> -->
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
The post takes the length limits alone, with a box's reason under 350 characters and split into the reading and its cause where it runs longer, and keeps its H3 verdict groups and tables.
|
|
80
|
+
|
|
79
81
|
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.
|
|
80
82
|
|
|
81
83
|
- Drop each section holding no box, along with its heading. Order is fixed: Failed, Needs your eyes, Not driven, then the folded blocks.
|
|
@@ -18,5 +18,8 @@ Number the boxes off the `boxes` field Step 2 kept. Leave a box that failed, nee
|
|
|
18
18
|
The verb refuses a taste box and a head that is no longer the remote tip, writing nothing in either case. Branch on the record's `reason`, which reads `ticked` on success.
|
|
19
19
|
|
|
20
20
|
- `stale-head`: a push landed while this pass drove. Report it on the `Ticks:` line of Step 10 and never retry at the new tip, since the boxes were driven at the old one.
|
|
21
|
+
- `body-refused`: the comment is ticked and the body is not. Retry the same call once, then report the partial result on the `Ticks:` line rather than claiming both.
|
|
21
22
|
- Any other refusal: report the `reason` on the `Ticks:` line and tick nothing further.
|
|
22
23
|
- A binary whose `canon pr` lacks `tick` predates the verb. Report that on the `Ticks:` line and tick nothing.
|
|
24
|
+
|
|
25
|
+
The same call also writes the one UI pass line in the body's `## Testing`, such as `UI pass, 17 of 17 boxes passed at <sha>, 1 taste box left to the operator`, so the operator sees the result without opening the evidence comment. Never edit that line by hand.
|
|
@@ -14,7 +14,7 @@ Each launch also restates the reviewer's bounds, though they belong to the role.
|
|
|
14
14
|
## Must
|
|
15
15
|
|
|
16
16
|
- The role is asserted, with writes bounded to the comment and body file the pass posts, and the tracked file, branch, worktree, board, and commit named as outside them
|
|
17
|
-
- Every file read a finding rests on goes through `git show <head>:<path>` at the resolved head, and launch-carried cross-branch facts are inputs
|
|
17
|
+
- Every file read a finding rests on goes through `git show <head>:<path>` at the resolved head, and launch-carried cross-branch facts are inputs. A task list, if the host has one, tracks each step.
|
|
18
18
|
- The fix, a draft lift by any route but `canon pr lift`, and the merge are refused whoever asks, naming each owner, and the lift runs after a close is posted
|
|
19
19
|
- An open pass sends the worker the `review-address` dispatch and this session's id, else the controller the cause, and a close sends the lift's reason unless the other review is open or missing
|
|
20
20
|
- A re-review stays with the reviewer that took the first pass while it is live, and a block is messaged before it becomes an interactive prompt
|
|
@@ -31,6 +31,8 @@ The session writes its comment and nothing tracked. Each kind of reviewer writes
|
|
|
31
31
|
- Resolve `.canon/plans/`, `.canon/tasks/`, and `.canon/tmp/` at the main worktree root, and send the body file as a heredoc, the main-root route `session-worktree` states
|
|
32
32
|
- Report a plan or task that fails to resolve there as unreadable, naming the path, rather than reviewing as though the branch had none
|
|
33
33
|
|
|
34
|
+
Keep a task list from launch, one item per step of the pass, and tick each as it completes. Skip it on a host that offers no list tool, since the list is for the operator watching and not a gate.
|
|
35
|
+
|
|
34
36
|
Review the pull request the launch named. Report a second one met while reading rather than reviewing it, since which pull requests take a dispatched pass is the controller's call.
|
|
35
37
|
|
|
36
38
|
## What to read and what to leave
|
|
@@ -59,6 +61,7 @@ Send it to the worker holding the branch as `review-pr` Step 5 returns an open `
|
|
|
59
61
|
- Carry the pull request number, the heading, that step's count line verbatim, and this session's own sessionId for the worker's reply
|
|
60
62
|
- Resolve the worker with `canon sessions list --branch <branch> --json`, where `<branch>` is the pull request's head branch
|
|
61
63
|
- Send to it only when exactly one session comes back with its confidence reading `confirmed`
|
|
64
|
+
- Count a row reading `via: "listener"` as confirmed, since its messaging socket accepted a connection. Never read it as `via: "socket"`, which rests on a socket file existing alone.
|
|
62
65
|
- On zero, on any other count or confidence, or on a refusal, send the same message to the controller, naming the cause the roster read gives
|
|
63
66
|
- Say the roster rests on sockets from another pid namespace, so the worker may be live and unconfirmed, when a row reads `via: "socket"` or an empty roster carries a `foreignDead` count above zero
|
|
64
67
|
- Say the roster rests on a pid answering alone, since start times could not be read, when the confidence reads `unverified` and no row reads `via: "socket"`
|
|
@@ -13,7 +13,7 @@ The channel fails on the worker's side. Blocks open prompts nobody is attached t
|
|
|
13
13
|
|
|
14
14
|
## Must
|
|
15
15
|
|
|
16
|
-
- Session scratch resolves at the main worktree root, and a plan that fails to resolve is reported unreadable from there rather than absent
|
|
16
|
+
- Session scratch resolves at the main worktree root, and a plan that fails to resolve is reported unreadable from there rather than absent. A task list, where the host has one, tracks each slice.
|
|
17
17
|
- The announcement carries the number, branch, task, reach, any uncleared spend, and any row the build turned up, and a painted diff has its preview server running before the pull request opens
|
|
18
18
|
- A block goes out as a message before it becomes a prompt, a fan-in member's done message carries its branch and head commit, and a review wait that times out reaches the controller
|
|
19
19
|
- The plan's scope freezes at dispatch, and a late ask is sorted into a defect, a follow-up row, or an addition on the operator's word
|
|
@@ -23,6 +23,7 @@ This body states the role, the boundaries, and the channel, and starts no step o
|
|
|
23
23
|
- Resolve `.canon/plans/`, `.canon/tasks/`, `.canon/review/`, and `.canon/ready/` at the main worktree root, never the linked worktree. Being gitignored, they are absent there rather than empty.
|
|
24
24
|
- Report a plan that fails to resolve as unreadable from here, naming the main-root path. Reporting the task as having no plan is wrong about the world, and a reader cannot tell the two apart.
|
|
25
25
|
- Build the plan the launch named. Never write a second one when the path fails to resolve, since a row citing a plan already has one.
|
|
26
|
+
- Keep a task list from launch, one item per slice, and tick each as its slice lands. Skip it on a host that offers no list tool, since the list is for the operator watching and not a gate.
|
|
26
27
|
|
|
27
28
|
### Ready folders and the draft mark
|
|
28
29
|
|
|
@@ -33,11 +33,11 @@ A successful decline returns one record whose fields match `canon tasks archive`
|
|
|
33
33
|
| -------------------- | ------------------------------------------------------------------------ |
|
|
34
34
|
| `from`, `to` | The task's old and new path |
|
|
35
35
|
| `priorityRowRemoved` | Whether a `priority.md` row was dropped |
|
|
36
|
-
| `backlogRowRemoved` | Whether a `backlog.md` row was dropped
|
|
36
|
+
| `backlogRowRemoved` | Whether a `backlog.md` row was dropped |
|
|
37
37
|
| `indexRegenerated` | Whether the index was rewritten |
|
|
38
38
|
| `plans` | A `from` and `to` per live plan the task last cited, empty if none moved |
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
A task can be declined straight off `backlog.md`, so decline checks that file as well as `priority.md`. Archive does the same and reports the same two fields.
|
|
41
41
|
|
|
42
42
|
## Step 3: route on a refusal
|
|
43
43
|
|
|
@@ -8,7 +8,7 @@ description: How task-complete confirms a task's work reached main, runs canon t
|
|
|
8
8
|
The Archive mode of `task-complete`. The session reads this file when the request names a task file already shipped.
|
|
9
9
|
|
|
10
10
|
- The `post-merge` git hook archives the task a merge closed, so a request here is usually one the hook could not resolve. Run the steps against whatever it left in place.
|
|
11
|
-
- `canon tasks archive` moves the file, edits `priority.md`, and regenerates the index as one unit. The hook calls
|
|
11
|
+
- `canon tasks archive` moves the file, edits `priority.md` and `backlog.md`, and regenerates the index as one unit. The hook calls it too, so a hand-rolled move drifts from the unattended path.
|
|
12
12
|
|
|
13
13
|
## Step 1: confirm the work reached main
|
|
14
14
|
|
|
@@ -45,6 +45,7 @@ A successful archive returns one record naming what moved.
|
|
|
45
45
|
| -------------------- | --------------------------------------------------------------------- |
|
|
46
46
|
| `from`, `to` | The task's old and new path |
|
|
47
47
|
| `priorityRowRemoved` | Whether a `priority.md` row was dropped |
|
|
48
|
+
| `backlogRowRemoved` | Whether a `backlog.md` bullet was dropped |
|
|
48
49
|
| `indexRegenerated` | Whether the index changed |
|
|
49
50
|
| `plans` | A `from` and `to` per live plan the task last cited, in `Plan:` order |
|
|
50
51
|
|
|
@@ -80,4 +81,4 @@ Leave `TASK-ARCHIVE.md` alone when it is present in the archive folder. It recor
|
|
|
80
81
|
|
|
81
82
|
## Step 4: clear prose naming the task
|
|
82
83
|
|
|
83
|
-
The command drops the row from `.canon/tasks/priority.md` and leaves prose alone. Remove any sentence that still names the archived task or counts the changed rows, since a stale count reads as board state.
|
|
84
|
+
The command drops the row from `.canon/tasks/priority.md` and the bullet from `.canon/tasks/backlog.md`, and leaves prose alone. Remove any sentence that still names the archived task or counts the changed rows, since a stale count reads as board state.
|
|
@@ -23,11 +23,11 @@ An automatable change already covered by a test written during implementation is
|
|
|
23
23
|
|
|
24
24
|
## The checklist
|
|
25
25
|
|
|
26
|
-
Group visual items
|
|
26
|
+
Group visual items under a bold label per feature area, at most five boxes a group, and split a larger area into two labels. Use `- [ ]` checkbox syntax, and write each item as the action and the result a person is looking for, so a reviewer who did not build the change can run it.
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
**What to verify visually:**
|
|
28
|
+
The evidence comment already heads the list with `## What to look at`, so the file opens on its first feature label with no heading line of its own.
|
|
30
29
|
|
|
30
|
+
```markdown
|
|
31
31
|
**<Feature area>**
|
|
32
32
|
|
|
33
33
|
- [ ] <route>[, <width>]: <verb> <target> → <expected visual result>
|