@erclx/canon 4.87.0 → 4.88.1
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/auto-ship/SKILL.md +1 -1
- package/claude/skills/docs-fold/SKILL.md +4 -4
- package/claude/skills/draft-and-pick/REQUIREMENT.md +10 -1
- package/claude/skills/draft-and-pick/SKILL.md +7 -3
- package/claude/skills/git-followup/SKILL.md +1 -1
- package/claude/skills/git-pr/SKILL.md +8 -8
- package/claude/skills/git-split/SKILL.md +19 -19
- package/claude/skills/memory-capture/SKILL.md +2 -2
- package/claude/skills/memory-review/SKILL.md +2 -2
- package/claude/skills/plan-groundwork/SKILL.md +1 -1
- package/claude/skills/review-address/SKILL.md +11 -11
- package/claude/skills/review-pr/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +1 -1
- package/claude/skills/role-orchestrator/scripts/poll.sh +1 -1
- package/claude/skills/teach-workspace/SKILL.md +2 -2
- package/claude/skills/ui-test/REQUIREMENT.md +1 -1
- package/claude/skills/ui-test/SKILL.md +2 -2
- package/docs/agents/commands.md +2 -1
- package/docs/agents/records.md +27 -0
- package/docs/agents/sandbox.md +1 -1
- package/docs/workflow/ai-workflow.md +4 -2
- package/governance/rules/core/055-scratch.md +1 -0
- package/package.json +1 -1
- package/scripts/tooling/verify.sh +2 -2
- package/src/claude/skills-headings.ts +1 -1
- package/src/commands/migrate.ts +1 -1
- package/src/commands/records.ts +159 -0
- package/src/migrate/record-layout.ts +2 -2
- package/src/migrate/scratch-evidence.ts +3 -5
- package/src/records/prune.ts +488 -0
- package/src/records/size.ts +24 -1
- package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +2 -2
- package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +2 -2
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +2 -2
- package/tooling/claude/seeds/CLAUDE.md +2 -6
|
@@ -121,7 +121,7 @@ The verb ships with the CLI and this body ships with the plugin, matching Step 6
|
|
|
121
121
|
|
|
122
122
|
If the diff touches UI files (JSX, TSX, Vue, Svelte, HTML, or CSS under `src/`), invoke `canon:ui-test`.
|
|
123
123
|
|
|
124
|
-
If `ui-test` produces a manual checklist, stop: `❌ UI requires visual verification. Checklist at .canon/tmp/ui-checklist/<slug>.md, which reaches the pull request once /git-ship runs. Verify manually, then run /git-ship.`
|
|
124
|
+
If `ui-test` produces a manual checklist, stop: `❌ UI requires visual verification. Checklist at .canon/tmp/handoff/ui-checklist/<slug>.md, which reaches the pull request once /git-ship runs. Verify manually, then run /git-ship.`
|
|
125
125
|
|
|
126
126
|
If all UI changes are covered by e2e tests, continue.
|
|
127
127
|
|
|
@@ -149,7 +149,7 @@ Read `canon/context/index.md` at `pwd` to see which domain entries exist. Skip t
|
|
|
149
149
|
|
|
150
150
|
Two sources feed this step, the same split Step 2 runs on. The diff carries what the repository changed. The routed facts carry what the session learned, which a diff cannot show.
|
|
151
151
|
|
|
152
|
-
**Routed facts.** Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`, falling back to `latest` on an empty result, and read `.canon/tmp/memory-routing/<slug>.md` at the main worktree root. `memory-capture` writes it, one H2 per target entry naming the path, with the fact underneath. Fold each fact into the entry its heading names, which for a nested `canon/context/<domain>/index.md` heading is the sibling file the fact belongs under rather than the generated index itself. Then delete the handoff file so a later run does not fold it twice.
|
|
152
|
+
**Routed facts.** Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`, falling back to `latest` on an empty result, and read `.canon/tmp/handoff/memory-routing/<slug>.md` at the main worktree root. `memory-capture` writes it, one H2 per target entry naming the path, with the fact underneath. Fold each fact into the entry its heading names, which for a nested `canon/context/<domain>/index.md` heading is the sibling file the fact belongs under rather than the generated index itself. Then delete the handoff file so a later run does not fold it twice.
|
|
153
153
|
|
|
154
154
|
This half is not diff-scoped and must not be. A gotcha a session hit while working is exactly the fact the diff never shows, and scoping it to changed files would drop the entries worth keeping. The handoff is a named input rather than a scan, so the reach stays bounded to what capture decided.
|
|
155
155
|
|
|
@@ -183,13 +183,13 @@ Write each updated entry immediately. Output one line per file, naming the path
|
|
|
183
183
|
|
|
184
184
|
Add a line naming the handoff when one was consumed:
|
|
185
185
|
|
|
186
|
-
`🧹 Folded: .canon/tmp/memory-routing/<slug>.md`
|
|
186
|
+
`🧹 Folded: .canon/tmp/handoff/memory-routing/<slug>.md`
|
|
187
187
|
|
|
188
188
|
The base lint-staged config runs `canon indexes regen` on every committed `*.md`, so `canon/context/index.md` refreshes automatically on commit. No manual step needed.
|
|
189
189
|
|
|
190
190
|
## Step 8: fold promoted pages
|
|
191
191
|
|
|
192
|
-
Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`, falling back to `latest` on an empty result, and read `.canon/tmp/teach-promotion/<slug>.md` at the main worktree root. `teach-workspace` writes it, one H2 per destination naming the path, with a source line under the heading and the page body in a fenced block below that. Read the body out of the fence rather than off the heading level, since a reference page carries headings of its own and only the fence separates them from the next destination. Skip this step silently when the file is absent, which is every run where nothing was promoted.
|
|
192
|
+
Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`, falling back to `latest` on an empty result, and read `.canon/tmp/handoff/teach-promotion/<slug>.md` at the main worktree root. `teach-workspace` writes it, one H2 per destination naming the path, with a source line under the heading and the page body in a fenced block below that. Read the body out of the fence rather than off the heading level, since a reference page carries headings of its own and only the fence separates them from the next destination. Skip this step silently when the file is absent, which is every run where nothing was promoted.
|
|
193
193
|
|
|
194
194
|
Each block is a page an operator already confirmed a destination for, so this step lands it rather than judging it again. Write to the destination the heading names, at `pwd` rather than at the main root, since every destination here is a tracked file that commits with the branch:
|
|
195
195
|
|
|
@@ -204,7 +204,7 @@ Output one line per page landed:
|
|
|
204
204
|
|
|
205
205
|
Add a line naming the handoff when one was consumed:
|
|
206
206
|
|
|
207
|
-
`🧹 Folded: .canon/tmp/teach-promotion/<slug>.md`
|
|
207
|
+
`🧹 Folded: .canon/tmp/handoff/teach-promotion/<slug>.md`
|
|
208
208
|
|
|
209
209
|
Report a block left unfolded rather than dropping it:
|
|
210
210
|
|
|
@@ -20,12 +20,21 @@ Without this skill, a session facing a decision nobody can settle from a diff:
|
|
|
20
20
|
- Reads a color without compositing its alpha, samples a round control at the corner of its bounding box, or confirms a panel's dimensions while it sits above the viewport. Each reads as a passing measurement about something no reader sees.
|
|
21
21
|
- Meets a machine with no browser binary and reports on the arms anyway, since nothing in the default flow separates a render that failed from one that was never attempted.
|
|
22
22
|
- Hand-writes a mockup of a surface a running app already serves, which drifts from what ships by a few pixels and a few words the moment the app's own markup or stylesheet moves on.
|
|
23
|
+
- Varies a property the decision is not about, which satisfies the one-property rule and produces a set differing in the layer a reader notices least. Five palettes over one identical page read as five skins of one design, and the operator rejected the whole set on sight.
|
|
24
|
+
- Renders each arm in whatever theme it happens to carry, so a set spanning both themes cannot be compared without the operator toggling every arm and holding the earlier ones in memory.
|
|
25
|
+
- Looks at a render to confirm it came out rather than to judge whether it is worth showing, which satisfies the rule against reporting an unseen result and still hands over weak work.
|
|
26
|
+
- Writes the run's renders to a scratch path outside the record that cites them, leaving the folder holding source markup and none of the images, so the judgment is unreachable to everyone except the session that made it.
|
|
27
|
+
- Narrows the page onto the picked arm in place on each iteration, so every earlier round is overwritten and a later pass cannot see what was already rejected.
|
|
23
28
|
|
|
24
29
|
## Must
|
|
25
30
|
|
|
26
31
|
- Produce three to five arms with the shipping state among them, each carrying an id, a label, and what the arm costs.
|
|
27
32
|
- Author the whole candidate set as one self-contained page and render it once, so the comparison arrives as one image, except where the surface under decision is a running app: there the page links a copy of the built stylesheet rather than inlining it, per `references/live-arms.md`.
|
|
28
|
-
-
|
|
33
|
+
- Name the layer the decision lives at before drafting, and vary the arms at that layer alone, so the answer names the difference that decided it rather than the cheapest one to change.
|
|
34
|
+
- Render every arm in one theme at a time, with a control that sets the whole set, so the comparison holds still while the operator reads it.
|
|
35
|
+
- Judge each render against a stated bar before handing it over, name the weakest thing on the page, and fix it where that sentence would embarrass the work.
|
|
36
|
+
- Write each iteration to its own folder rather than narrowing the previous one in place, so every earlier round stays openable.
|
|
37
|
+
- Write the run's renders inside the record that cites them wherever one exists, and reserve the scratch path for inputs that are re-runnable and cited by nothing.
|
|
29
38
|
- Render before reporting on any arm, and report a missing browser binary as a refusal naming the repair rather than describing an arm nobody has seen.
|
|
30
39
|
- Take the pick through the structured question surface, with the recommendation ranked first and marked, and every option carrying its cost.
|
|
31
40
|
- Keep every write inside the run's scratch folder until the pick is final.
|
|
@@ -15,7 +15,7 @@ Some decisions are settled by looking rather than by reasoning, and no draft is
|
|
|
15
15
|
|
|
16
16
|
## Step 1: name the decision and the arms
|
|
17
17
|
|
|
18
|
-
1. State the decision in one sentence, naming what changes between arms and what stays fixed.
|
|
18
|
+
1. State the decision in one sentence, naming what changes between arms and what stays fixed. Name the layer that sentence puts the decision at, and check that the arms will differ there rather than somewhere cheaper to change.
|
|
19
19
|
2. Derive a kebab slug from that sentence. Call the folder every file this run writes to `<dest>` below. `<dest>` is `.canon/tmp/<slug>/`, a nested `<slug>/` folder rather than a flat `<slug>-<file>.md`, which is the shape every temporary write in this project takes. Running inside a live `plan-groundwork` track is the one exception: `<dest>` is the track's own `evidence/<slug>/` instead, since a candidate render is evidence the track's decision file cites rather than spike input.
|
|
20
20
|
3. Write one arm per candidate, each carrying an id, a label, and what the arm costs. An arm with no stated cost is not an option.
|
|
21
21
|
4. Make the current state arm `0`, so the baseline is a candidate rather than an absence. A decision with nothing shipped yet says so and starts at arm `1`.
|
|
@@ -28,10 +28,12 @@ Write every arm side by side on one self-contained HTML page at `<dest>/candidat
|
|
|
28
28
|
- One page for the pick, never a set of separate images handed to the operator to compare from memory. The comparison they judge is `candidates.html`, which Step 3 renders and Step 4 asks about. The per-arm files exist only for Step 6's archival capture, once the pick is made, and only the last pass through this step is what Step 6 finds there.
|
|
29
29
|
- Wrap each arm's markup in the same class on both files, chosen once per run and reused everywhere, so one selector addresses an arm on the combined page and on its own standalone file alike.
|
|
30
30
|
- Label each arm on the page with its id and its cost, so the render carries what the question will ask about.
|
|
31
|
+
- Give the combined page one control that sets every arm's theme at once, beside whatever per-arm control the arms carry. A set spanning both themes cannot be compared, since the operator has to toggle each arm and hold the earlier ones in memory, which is the failure the single-page rule exists to prevent.
|
|
31
32
|
- Take the live-app branch instead when the surface under decision is a running app: lift the rendered markup and link a copy of the built stylesheet rather than inlining, per `${CLAUDE_SKILL_DIR}/references/live-arms.md`.
|
|
32
33
|
- On the default path, inline every style, script, and asset the page needs. The render reads the file off disk, so a page reaching for a build step or a network font renders without it and the arms differ by something nobody chose.
|
|
33
34
|
- On the default path, declare a font stack the machine resolves, such as `system-ui` behind a generic fallback. The render refuses a page that would rewrap against a substitute rather than shipping a false comparison, so a page naming no font at all is refused on whatever the default resolves to.
|
|
34
35
|
- Vary one property across the arms. A page whose arms differ in three ways answers no question, since the pick cannot say which difference decided it.
|
|
36
|
+
- Vary the property the decision is about, which the rule above is satisfiable without. Holding composition fixed and varying color obeys it exactly and produces five skins of one design, because a set differing in the layer a reader notices least answers nothing. A palette is chosen to serve a composition, so it cannot be picked ahead of one.
|
|
35
37
|
|
|
36
38
|
## Step 3: render and hand off
|
|
37
39
|
|
|
@@ -44,8 +46,10 @@ canon capture <dest>/candidates.html --selector <element>
|
|
|
44
46
|
- `--selector` has no default and the command refuses without it. Name the element wrapping the arms rather than `body`, which crops to whatever the page's own margins leave.
|
|
45
47
|
- `canon capture` and `canon drive` both need a browser binary the toolkit does not install. When either refuses for that reason, report the refusal and name `bunx playwright install chromium` as the repair, then stop rather than describing an arm nobody has seen.
|
|
46
48
|
- Serve the page instead of capturing it when the operator has to drive the decision, such as a hover response, a scroll-linked position, or a pace. Start `canon serve <dest> --entry candidates.html` in the background and read the link off its record, since the printed link opens `index.html` without that flag and `<dest>` holds no such file. A still answers how a thing looks and answers none of those.
|
|
49
|
+
- Write the render inside the record that cites it wherever one exists, by pointing `--out` at `<dest>/renders/` rather than at a session scratch path. A pick taken from an image the record does not hold is a judgment nobody but this session can check, and the archival capture in Step 6 covers the final round alone.
|
|
47
50
|
- Hand over the address rather than a description. Emit the PNG path on its own line, and the link beside it where the page is served.
|
|
48
51
|
- Never report a visual result you have not looked at. A claim about appearance with no render behind it is a guess.
|
|
52
|
+
- Look to judge rather than to confirm. Reading the image back to check it rendered satisfies the rule above and still hands over weak work, so name the weakest thing on the page in a sentence. Where that sentence would embarrass the work, fix it and hand over the second version. Say the remaining weakness out loud either way, so the operator is not hunting for what you already know.
|
|
49
53
|
|
|
50
54
|
## Step 4: take the pick
|
|
51
55
|
|
|
@@ -58,11 +62,11 @@ Put the choice to the operator through the structured question surface, since a
|
|
|
58
62
|
|
|
59
63
|
## Step 5: loop on the pick
|
|
60
64
|
|
|
61
|
-
1.
|
|
65
|
+
1. Write each iteration to its own `<dest>` rather than narrowing the previous one in place, suffixing the slug so the folders sort. An iteration overwritten is one a later pass cannot open, and the losing round is what stops a correction re-proposing something already rejected.
|
|
62
66
|
2. Write fresh arms off the pick and return to Step 2 where the correction opens a new question. Revise the one arm where it does not.
|
|
63
67
|
3. Re-render, hand off again, and take the next answer.
|
|
64
68
|
4. Repeat until the operator says it is right. The loop stops on their word and on nothing else, so a run stopping because the arms stopped differing has stopped early.
|
|
65
|
-
5. Hold the real surface untouched across every iteration. Nothing outside
|
|
69
|
+
5. Hold the real surface untouched across every iteration. Nothing outside the run's own folders changes until the pick is final.
|
|
66
70
|
|
|
67
71
|
## Step 6: close
|
|
68
72
|
|
|
@@ -31,7 +31,7 @@ A missing tracking ref is no longer a guard. An open pull request proves the bra
|
|
|
31
31
|
|
|
32
32
|
5. Post the evidence comparison, on every invocation including `reply-owned`, since this comment is not the reply step 8 owns. Run `canon pr evidence <number> --json`, resolving `<number>` from `gh pr view --json number`, and read `reason` on the record rather than the exit code.
|
|
33
33
|
- `no-evidence`: nothing changed under an `evidence/` segment this push. Say nothing and move on.
|
|
34
|
-
- `ok`: write `body` to `.canon/tmp/pr
|
|
34
|
+
- `ok`: write `body` to `.canon/tmp/pr/evidence/body-<number>.md` at the main worktree root (resolved the way `session-worktree` does), then post the comment with `gh pr comment <number> --body-file <main-root>/.canon/tmp/pr/evidence/body-<number>.md` when the record carries no `commentId`, or edit the existing one in place with `gh api -X PATCH repos/{owner}/{repo}/issues/comments/<commentId> -f body=@<main-root>/.canon/tmp/pr/evidence/body-<number>.md` when it does. Clean up the tmp file only after the call reports success.
|
|
35
35
|
- Any other reason is one of the mirrored git refusals (`gh-missing`, `gh-failed`, `no-base`, `unreadable-tree`, `unreadable-changes`). Report it and continue without stopping the chain.
|
|
36
36
|
6. Check for existing review comments: `gh api 'repos/{owner}/{repo}/pulls/<number>/comments' --jq 'length'`, resolving `<number>` from `gh pr view --json number`.
|
|
37
37
|
7. Sync the body and title on every invocation, before the routing below decides on the reply.
|
|
@@ -148,18 +148,18 @@ if [ -n "$pr_labels" ]; then
|
|
|
148
148
|
gh pr edit "$pr_number" --add-label "$pr_labels" >/dev/null ||
|
|
149
149
|
printf 'Label apply failed. Create a missing label with: gh label create <name>\n' >&2
|
|
150
150
|
fi
|
|
151
|
-
rm -rf .canon/tmp/pr
|
|
151
|
+
rm -rf .canon/tmp/pr/body
|
|
152
152
|
printf 'number=%s\nurl=%s\n' "$pr_number" "$pr_url"
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
### Post the UI checklist
|
|
156
156
|
|
|
157
|
-
`ui-test` writes a manual checklist to `.canon/tmp/ui-checklist/<slug>.md` at the main worktree root when a change needs visual verification, with `<slug>` derived per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. This step is the file's sole consumer. Resolve the main root the way `session-worktree` does (`git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd`) and check for the file there. A missing file means no checklist was produced, and there is nothing to post.
|
|
157
|
+
`ui-test` writes a manual checklist to `.canon/tmp/handoff/ui-checklist/<slug>.md` at the main worktree root when a change needs visual verification, with `<slug>` derived per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. This step is the file's sole consumer. Resolve the main root the way `session-worktree` does (`git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd`) and check for the file there. A missing file means no checklist was produced, and there is nothing to post.
|
|
158
158
|
|
|
159
159
|
When it exists, scan it against `${CLAUDE_SKILL_DIR}/../../standards/publish.md` before posting, the same as the pull request body above. Post it as its own comment on `<number>`, the number the final command above resolved, rather than folding it into the body, since a later push editing the body would overwrite checkboxes a reviewer already ticked:
|
|
160
160
|
|
|
161
161
|
```bash
|
|
162
|
-
gh pr comment <number> --body-file <main-root>/.canon/tmp/ui-checklist/<slug>.md
|
|
162
|
+
gh pr comment <number> --body-file <main-root>/.canon/tmp/handoff/ui-checklist/<slug>.md
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
Run the cleanup below only once that call reports success. On a failure, stop and leave the file in place: a retry needs the checklist to still be there, and deleting it on a failed post loses the only copy with nothing landed on the pull request.
|
|
@@ -167,11 +167,11 @@ Run the cleanup below only once that call reports success. On a failure, stop an
|
|
|
167
167
|
From a linked worktree the file-editing tools refuse a main-root path, so the cleanup goes out through `Bash` as two plain commands, the file and then the folder, rather than joined by `&&`, which is refused as compound:
|
|
168
168
|
|
|
169
169
|
```bash
|
|
170
|
-
rm <main-root>/.canon/tmp/ui-checklist/<slug>.md
|
|
170
|
+
rm <main-root>/.canon/tmp/handoff/ui-checklist/<slug>.md
|
|
171
171
|
```
|
|
172
172
|
|
|
173
173
|
```bash
|
|
174
|
-
rmdir <main-root>/.canon/tmp/ui-checklist 2>/dev/null || true
|
|
174
|
+
rmdir <main-root>/.canon/tmp/handoff/ui-checklist 2>/dev/null || true
|
|
175
175
|
```
|
|
176
176
|
|
|
177
177
|
The `rmdir` is a no-op when another branch's pending checklist still sits in the folder, which keeps this step from deleting a handoff that is not its own.
|
|
@@ -181,16 +181,16 @@ The `rmdir` is a no-op when another branch's pending checklist still sits in the
|
|
|
181
181
|
Run `canon pr evidence <number> --json` against the number the pull request step above resolved. Read `reason` on the record rather than the exit code.
|
|
182
182
|
|
|
183
183
|
- `no-evidence`: nothing changed under an `evidence/` segment. Say nothing and move on.
|
|
184
|
-
- `ok`: write `body` to `.canon/tmp/pr
|
|
184
|
+
- `ok`: write `body` to `.canon/tmp/pr/evidence/body-<number>.md` at the main worktree root (resolved the way `session-worktree` does), then post or update the comment:
|
|
185
185
|
|
|
186
186
|
```bash
|
|
187
|
-
gh pr comment <number> --body-file <main-root>/.canon/tmp/pr
|
|
187
|
+
gh pr comment <number> --body-file <main-root>/.canon/tmp/pr/evidence/body-<number>.md
|
|
188
188
|
```
|
|
189
189
|
|
|
190
190
|
When the record carries a `commentId`, edit that comment in place instead of posting a second one, reading the body field from the tmp file with `@`:
|
|
191
191
|
|
|
192
192
|
```bash
|
|
193
|
-
gh api -X PATCH repos/{owner}/{repo}/issues/comments/<commentId> -f body=@<main-root>/.canon/tmp/pr
|
|
193
|
+
gh api -X PATCH repos/{owner}/{repo}/issues/comments/<commentId> -f body=@<main-root>/.canon/tmp/pr/evidence/body-<number>.md
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
Clean up the tmp file the way the UI-checklist step does, only after the call reports success.
|
|
@@ -80,25 +80,25 @@ For independent mode, base every branch on `main`:
|
|
|
80
80
|
git branch -m <current_branch> <new_name>
|
|
81
81
|
|
|
82
82
|
# Create, cherry-pick, push, and open PR for each secondary branch
|
|
83
|
-
mkdir -p .canon/tmp/pr
|
|
83
|
+
mkdir -p .canon/tmp/pr/split
|
|
84
84
|
git switch -c <branch> main && git cherry-pick <sha> <sha> \
|
|
85
85
|
&& git push -u origin <branch> \
|
|
86
|
-
&& (cat <<'BODY' > .canon/tmp/pr
|
|
86
|
+
&& (cat <<'BODY' > .canon/tmp/pr/split/<branch>.md
|
|
87
87
|
<body following pr.md template, written from the cherry-picked commits>
|
|
88
88
|
BODY
|
|
89
|
-
) && gh pr create --title "<title>" --body-file .canon/tmp/pr
|
|
90
|
-
&& rm .canon/tmp/pr
|
|
89
|
+
) && gh pr create --title "<title>" --body-file .canon/tmp/pr/split/<branch>.md \
|
|
90
|
+
&& rm .canon/tmp/pr/split/<branch>.md
|
|
91
91
|
|
|
92
92
|
# Return to primary branch, push, and open its PR
|
|
93
93
|
git checkout <new_name> && git push -u origin <new_name> \
|
|
94
|
-
&& (cat <<'BODY' > .canon/tmp/pr
|
|
94
|
+
&& (cat <<'BODY' > .canon/tmp/pr/split/<new_name>.md
|
|
95
95
|
<body following pr.md template, written from the primary's commits>
|
|
96
96
|
BODY
|
|
97
|
-
) && gh pr create --title "<title>" --body-file .canon/tmp/pr
|
|
98
|
-
&& rm .canon/tmp/pr
|
|
97
|
+
) && gh pr create --title "<title>" --body-file .canon/tmp/pr/split/<new_name>.md \
|
|
98
|
+
&& rm .canon/tmp/pr/split/<new_name>.md
|
|
99
99
|
|
|
100
100
|
# Clean up the body-file dir if all PRs succeeded (no-op when non-empty)
|
|
101
|
-
rmdir .canon/tmp/pr
|
|
101
|
+
rmdir .canon/tmp/pr/split 2>/dev/null || true
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
For stacked mode, base each branch on the previous and cherry-pick only that group's commits:
|
|
@@ -107,36 +107,36 @@ For stacked mode, base each branch on the previous and cherry-pick only that gro
|
|
|
107
107
|
# Rename current branch to reflect primary concern
|
|
108
108
|
git branch -m <current_branch> <new_name>
|
|
109
109
|
|
|
110
|
-
mkdir -p .canon/tmp/pr
|
|
110
|
+
mkdir -p .canon/tmp/pr/split
|
|
111
111
|
|
|
112
112
|
# Group 1: based on main
|
|
113
113
|
git switch -c <branch-1> main && git cherry-pick <g1-sha> <g1-sha> \
|
|
114
114
|
&& git push -u origin <branch-1> \
|
|
115
|
-
&& (cat <<'BODY' > .canon/tmp/pr
|
|
115
|
+
&& (cat <<'BODY' > .canon/tmp/pr/split/<branch-1>.md
|
|
116
116
|
<body following pr.md template, written from the cherry-picked commits>
|
|
117
117
|
BODY
|
|
118
|
-
) && gh pr create --title "<title>" --body-file .canon/tmp/pr
|
|
119
|
-
&& rm .canon/tmp/pr
|
|
118
|
+
) && gh pr create --title "<title>" --body-file .canon/tmp/pr/split/<branch-1>.md \
|
|
119
|
+
&& rm .canon/tmp/pr/split/<branch-1>.md
|
|
120
120
|
|
|
121
121
|
# Group 2: based on <branch-1>, this group's commits only
|
|
122
122
|
git checkout -b <branch-2> && git cherry-pick <g2-sha> <g2-sha> \
|
|
123
123
|
&& git push -u origin <branch-2> \
|
|
124
|
-
&& (cat <<'BODY' > .canon/tmp/pr
|
|
124
|
+
&& (cat <<'BODY' > .canon/tmp/pr/split/<branch-2>.md
|
|
125
125
|
<body following pr.md template, written from the cherry-picked commits>
|
|
126
126
|
BODY
|
|
127
|
-
) && gh pr create --title "<title>" --body-file .canon/tmp/pr
|
|
128
|
-
&& rm .canon/tmp/pr
|
|
127
|
+
) && gh pr create --title "<title>" --body-file .canon/tmp/pr/split/<branch-2>.md \
|
|
128
|
+
&& rm .canon/tmp/pr/split/<branch-2>.md
|
|
129
129
|
|
|
130
130
|
# Return to primary branch, push, and open its PR
|
|
131
131
|
git checkout <new_name> && git push -u origin <new_name> \
|
|
132
|
-
&& (cat <<'BODY' > .canon/tmp/pr
|
|
132
|
+
&& (cat <<'BODY' > .canon/tmp/pr/split/<new_name>.md
|
|
133
133
|
<body following pr.md template, written from the primary's commits>
|
|
134
134
|
BODY
|
|
135
|
-
) && gh pr create --title "<title>" --body-file .canon/tmp/pr
|
|
136
|
-
&& rm .canon/tmp/pr
|
|
135
|
+
) && gh pr create --title "<title>" --body-file .canon/tmp/pr/split/<new_name>.md \
|
|
136
|
+
&& rm .canon/tmp/pr/split/<new_name>.md
|
|
137
137
|
|
|
138
138
|
# Clean up the body-file dir if all PRs succeeded (no-op when non-empty)
|
|
139
|
-
rmdir .canon/tmp/pr
|
|
139
|
+
rmdir .canon/tmp/pr/split 2>/dev/null || true
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
## After execution
|
|
@@ -44,7 +44,7 @@ For each project candidate, match its subject against `canon/context/index.md`.
|
|
|
44
44
|
|
|
45
45
|
Fail closed. A project candidate matching no entry stays a memory file, and so does one matching two entries where neither is clearly the owner. The residue is what the folder is for, and a fact filed under the wrong entry is worse than one in memory because a context entry is a surface sessions trust.
|
|
46
46
|
|
|
47
|
-
Do not edit a context entry here. `docs-fold` owns those edits and folds the routed facts in on its own pass, or two skills write one file at the same step. Write each routed fact to `.canon/tmp/memory-routing/<slug>.md` at the main worktree root instead, appending when the file exists. Name the heading with the entry's own path from `canon/context/index.md`, flat or the nested `index.md`, since that heading is what tells `docs-fold`'s routed-facts fold which file to open. An append is a whole-file operation the shell does directly, so send it as a plain single `Bash` command carrying a heredoc:
|
|
47
|
+
Do not edit a context entry here. `docs-fold` owns those edits and folds the routed facts in on its own pass, or two skills write one file at the same step. Write each routed fact to `.canon/tmp/handoff/memory-routing/<slug>.md` at the main worktree root instead, appending when the file exists. Name the heading with the entry's own path from `canon/context/index.md`, flat or the nested `index.md`, since that heading is what tells `docs-fold`'s routed-facts fold which file to open. An append is a whole-file operation the shell does directly, so send it as a plain single `Bash` command carrying a heredoc:
|
|
48
48
|
|
|
49
49
|
A flat domain takes:
|
|
50
50
|
|
|
@@ -96,7 +96,7 @@ Respond with one line per fact routed, written, or updated:
|
|
|
96
96
|
|
|
97
97
|
When anything routed, add a line naming the handoff so the caller knows a `docs-fold` pass is owed:
|
|
98
98
|
|
|
99
|
-
`→ Routed facts wait at .canon/tmp/memory-routing/<slug>.md. Run /docs-fold to fold them in.`
|
|
99
|
+
`→ Routed facts wait at .canon/tmp/handoff/memory-routing/<slug>.md. Run /docs-fold to fold them in.`
|
|
100
100
|
|
|
101
101
|
Omit that line when the caller runs `docs-fold` itself later in its own chain.
|
|
102
102
|
|
|
@@ -68,7 +68,7 @@ For each in-scope entry (see Scope), pick one action:
|
|
|
68
68
|
- In a target project, append the rule to an existing file under `.claude/rules/project/` with `Edit`, since a project rule is project-owned and sync never overwrites it, or hand off to the `create-rule` skill when no existing file fits.
|
|
69
69
|
- **Promote to a skill body**: the rule fires only when editing a specific path-scoped domain. Name the target skill.
|
|
70
70
|
- **Promote to a standards file**: the rule is an authoring reference that belongs in the project's own standards folder as `<domain>.md`.
|
|
71
|
-
- **Promote to a context entry**: the entry states a fact about a domain carrying an entry in `canon/context/index.md`. Append it to `.canon/tmp/memory-routing/<slug>.md` at the main worktree root, in the format `memory-capture` writes, and tell the user to run `/docs-fold` from a branch. Do not edit the context entry here. A project-identity or command fact takes this action when a context entry owns the subject, such as a development entry for commands, and **Retire** otherwise, naming a hand edit to `CLAUDE.md` as the reason. Memory review does not write the root file itself.
|
|
71
|
+
- **Promote to a context entry**: the entry states a fact about a domain carrying an entry in `canon/context/index.md`. Append it to `.canon/tmp/handoff/memory-routing/<slug>.md` at the main worktree root, in the format `memory-capture` writes, and tell the user to run `/docs-fold` from a branch. Do not edit the context entry here. A project-identity or command fact takes this action when a context entry owns the subject, such as a development entry for commands, and **Retire** otherwise, naming a hand edit to `CLAUDE.md` as the reason. Memory review does not write the root file itself.
|
|
72
72
|
- **Hand off to governance**: the rule is coding-standards class (typescript, testing, naming, error-handling, performance, logging, concurrency, planning), never a cross-domain behavior rule. Do not author the rule file inline. Never edit the synced `.claude/rules/` copies of toolkit rules, because `canon gov sync` overwrites them. Stop at handoff. This and **Promote to an always-loaded rule** never both claim one entry: class names the topic (coding-standards routes here), firing axis names the rest (applies-every-session routes to the rule promote).
|
|
73
73
|
- In the toolkit repo, point the user at `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md`, which own the source-of-truth rules under `governance/rules/`.
|
|
74
74
|
- In a target project, point the user at the `create-rule` skill, which scaffolds a project-local rule under `.claude/rules/`.
|
|
@@ -153,7 +153,7 @@ Free-form text after the verb is a reason. Capture it in the receipt but do not
|
|
|
153
153
|
Action by action type:
|
|
154
154
|
|
|
155
155
|
- **Promote**: use `Edit` to insert the rewritten rule into the target surface, then archive the memory file. A promote to an always-loaded rule in the toolkit repo never reaches this line: it stops as a handoff to `internal-governance`, the same as **Hand off** below, and archives only on the user's explicit confirmation.
|
|
156
|
-
- **Promote to a context entry**: append the fact to `.canon/tmp/memory-routing/<slug>.md` at the main worktree root, then archive the memory file. `docs-fold` folds it in on its next run from a branch, which is what keeps one skill writing context entries.
|
|
156
|
+
- **Promote to a context entry**: append the fact to `.canon/tmp/handoff/memory-routing/<slug>.md` at the main worktree root, then archive the memory file. `docs-fold` folds it in on its next run from a branch, which is what keeps one skill writing context entries.
|
|
157
157
|
- **Hand off**: do not edit governance. Archive the memory file only if the user confirmed the handoff explicitly. Otherwise leave it in place.
|
|
158
158
|
- **Retire**: archive the memory file.
|
|
159
159
|
|
|
@@ -21,7 +21,7 @@ Read `${CLAUDE_SKILL_DIR}/../../standards/groundwork.md` before writing any file
|
|
|
21
21
|
|
|
22
22
|
- Write only inside `.canon/groundwork/<nn>-<slug>/`. A feature plan, source changes, a standard, a rule, and a reference doc all live outside that folder, so this one rule forbids every one of them.
|
|
23
23
|
- One exception, at close only: write one task file recording what the track concluded.
|
|
24
|
-
- A second exception, for what a spike reads: write an input under `.canon/tmp/groundwork-fixtures/<slug>/`. Keep it out of `.canon/groundwork/` so mode detection never matches a fixture as a track. A fixture a headless run is pointed at goes outside the repository instead, per the rule in `## Running a spike`.
|
|
24
|
+
- A second exception, for what a spike reads: write an input under `.canon/tmp/runs/groundwork-fixtures/<slug>/`. Keep it out of `.canon/groundwork/` so mode detection never matches a fixture as a track. A fixture a headless run is pointed at goes outside the repository instead, per the rule in `## Running a spike`.
|
|
25
25
|
- What a spike produces stays inside the track rather than joining the two exceptions above: write evidence a spike file cites under `.canon/groundwork/<nn>-<slug>/evidence/`, which the first rule already permits. Mode detection matches entries at the top level of the tracks directory, so the sibling the fixtures rule guards against cannot be a folder nested inside a track, and the scratch path holds only what can be deleted without loss, which a recording a finding rests on is not.
|
|
26
26
|
- A `draft-and-pick` run invoked from inside a live track follows its own branch for this, writing to `evidence/` here rather than `.canon/tmp/<slug>/`.
|
|
27
27
|
- Reading is not restricted. External research is in scope, so read documentation, comparable projects, and papers whenever a live question needs them.
|
|
@@ -99,7 +99,7 @@ moved, which the earlier test could not see. Rebase again under
|
|
|
99
99
|
continue. The second push costs one extra force-push in a
|
|
100
100
|
case that needs the fix and the sibling to touch the same lines.
|
|
101
101
|
|
|
102
|
-
Then write a summary reply to `.canon/tmp/
|
|
102
|
+
Then write a summary reply to `.canon/tmp/pr/reply/reply-<number>.md`
|
|
103
103
|
mapping each finding to what changed, or to a one-line reason when it is a
|
|
104
104
|
conscious-accept rather than a defect. Key the filename on the PR number so two
|
|
105
105
|
sessions addressing different pull requests never overwrite each other between
|
|
@@ -154,7 +154,7 @@ Before posting, follow `${CLAUDE_SKILL_DIR}/../../standards/publish.md`
|
|
|
154
154
|
and run its scan against the reply:
|
|
155
155
|
|
|
156
156
|
```bash
|
|
157
|
-
canon labels scan --body-file .canon/tmp/
|
|
157
|
+
canon labels scan --body-file .canon/tmp/pr/reply/reply-<number>.md
|
|
158
158
|
```
|
|
159
159
|
|
|
160
160
|
The hook skips `.canon/tmp/`, so this scan is the only gate on the published
|
|
@@ -163,8 +163,8 @@ capture the posted comment's id, since Step 7 edits this exact comment rather
|
|
|
163
163
|
than trusting whichever one `gh` considers last:
|
|
164
164
|
|
|
165
165
|
```bash
|
|
166
|
-
comment_url=$(gh pr comment <number> --body-file .canon/tmp/
|
|
167
|
-
echo "${comment_url##*issuecomment-}" > .canon/tmp/
|
|
166
|
+
comment_url=$(gh pr comment <number> --body-file .canon/tmp/pr/reply/reply-<number>.md)
|
|
167
|
+
echo "${comment_url##*issuecomment-}" > .canon/tmp/pr/reply/reply-<number>.id
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
## Step 7: confirm resolution
|
|
@@ -176,7 +176,7 @@ the closing confirmation to the reply file Step 6 already posted, so the thread
|
|
|
176
176
|
carries one terminal state rather than a second comment under no heading:
|
|
177
177
|
|
|
178
178
|
```bash
|
|
179
|
-
printf '\n✅ All review findings addressed, CI green.\n' >> .canon/tmp/
|
|
179
|
+
printf '\n✅ All review findings addressed, CI green.\n' >> .canon/tmp/pr/reply/reply-<number>.md
|
|
180
180
|
```
|
|
181
181
|
|
|
182
182
|
A rebase-only run addressed no finding, so it appends its own confirmation
|
|
@@ -184,14 +184,14 @@ instead of that one. Claiming findings were addressed on a pull request that
|
|
|
184
184
|
carries none is false on a surface nothing else checks:
|
|
185
185
|
|
|
186
186
|
```bash
|
|
187
|
-
printf '\n✅ Rebased onto origin/main, CI green. No review findings were open.\n' >> .canon/tmp/
|
|
187
|
+
printf '\n✅ Rebased onto origin/main, CI green. No review findings were open.\n' >> .canon/tmp/pr/reply/reply-<number>.md
|
|
188
188
|
```
|
|
189
189
|
|
|
190
190
|
Re-run the scan against the updated file, since the appended line is new
|
|
191
191
|
content the Step 6 scan never saw:
|
|
192
192
|
|
|
193
193
|
```bash
|
|
194
|
-
canon labels scan --body-file .canon/tmp/
|
|
194
|
+
canon labels scan --body-file .canon/tmp/pr/reply/reply-<number>.md
|
|
195
195
|
```
|
|
196
196
|
|
|
197
197
|
Then edit the exact comment Step 6 posted, read back from the id it saved,
|
|
@@ -201,12 +201,12 @@ so read the id as its own plain command and write the printed value as a
|
|
|
201
201
|
literal in the `gh api` call rather than a substitution or a variable:
|
|
202
202
|
|
|
203
203
|
```bash
|
|
204
|
-
cat .canon/tmp/
|
|
204
|
+
cat .canon/tmp/pr/reply/reply-<number>.id
|
|
205
205
|
```
|
|
206
206
|
|
|
207
207
|
```bash
|
|
208
208
|
gh api -X PATCH "repos/{owner}/{repo}/issues/comments/<id printed above>" \
|
|
209
|
-
-F body=@.canon/tmp/
|
|
209
|
+
-F body=@.canon/tmp/pr/reply/reply-<number>.md
|
|
210
210
|
```
|
|
211
211
|
|
|
212
212
|
`--edit-last` was the first shape and it targets the wrong object here.
|
|
@@ -243,10 +243,10 @@ Do not merge. Hand back to the orchestrator for re-review.
|
|
|
243
243
|
|
|
244
244
|
## Post-review findings
|
|
245
245
|
|
|
246
|
-
Not everything worth reaching the reviewing session surfaces inside the numbered flow above. 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, follow `${CLAUDE_SKILL_DIR}/../../standards/markdown.md` for the banned words, and run the `${CLAUDE_SKILL_DIR}/../../standards/publish.md` scan before posting with `canon labels scan --body-file .canon/tmp/
|
|
246
|
+
Not everything worth reaching the reviewing session surfaces inside the numbered flow above. 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, follow `${CLAUDE_SKILL_DIR}/../../standards/markdown.md` for the banned words, and run the `${CLAUDE_SKILL_DIR}/../../standards/publish.md` scan before posting with `canon labels scan --body-file .canon/tmp/pr/reply/reply-<number>.md`.
|
|
247
247
|
|
|
248
248
|
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.
|
|
249
249
|
|
|
250
250
|
```bash
|
|
251
|
-
gh pr comment <number> --body-file .canon/tmp/
|
|
251
|
+
gh pr comment <number> --body-file .canon/tmp/pr/reply/reply-<number>.md
|
|
252
252
|
```
|
|
@@ -165,7 +165,7 @@ Use severity: `critical` (blocks merge), `should-fix` (fix before merge), `minor
|
|
|
165
165
|
|
|
166
166
|
## Step 4: post to the PR
|
|
167
167
|
|
|
168
|
-
Write the comment to `.canon/tmp/pr
|
|
168
|
+
Write the comment to `.canon/tmp/pr/review/body-<number>-<short-sha>.md` at the main worktree root, not the current worktree, which the rest of this step calls `<body-file>`. Resolve that root the way `session-worktree` does, and send the write as a plain single `Bash` command carrying a heredoc from a linked worktree, since `Edit` and `Write` refuse a main-root path there. The PR number stops two sessions reviewing different pull requests from overwriting each other between the write and the post, and the head commit stops a second pass overwriting the first one's body.
|
|
169
169
|
|
|
170
170
|
Derive both segments from Step 1. Never pick a suffix by hand, and never reuse a name the folder already holds.
|
|
171
171
|
|
|
@@ -279,7 +279,7 @@ Before posting, run the scan in `${CLAUDE_SKILL_DIR}/../../standards/publish.md`
|
|
|
279
279
|
Do not run the command below when `<prior-heading>` from Step 2 reads `## Review closed` and this pass carries nothing owed. That pass replaces the standing comment rather than adding one, under `### A close-out that repeats the standing one` at the end of this step. Posting first and reaching that section afterward leaves two close-outs both naming the new head, which is worse than the pair the guard exists against.
|
|
280
280
|
|
|
281
281
|
```bash
|
|
282
|
-
gh pr review <number> --comment --body-file .canon/tmp/pr
|
|
282
|
+
gh pr review <number> --comment --body-file .canon/tmp/pr/review/body-<number>-<short-sha>.md
|
|
283
283
|
```
|
|
284
284
|
|
|
285
285
|
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.`
|
|
@@ -54,7 +54,7 @@ Every classification line names a pull request and a state. A line starting `pol
|
|
|
54
54
|
|
|
55
55
|
The script exits non-zero and classifies nothing when the open pull request list itself fails to load. That case would otherwise report every tracked pull request as merged, so the baseline is left untouched and the run says so.
|
|
56
56
|
|
|
57
|
-
The baseline lives at `.canon/tmp/pr
|
|
57
|
+
The baseline lives at `.canon/tmp/pr/poll/baseline.txt` under the main worktree root and is per-machine. A first run against a board already in flight reports each open pull request once before it settles.
|
|
58
58
|
|
|
59
59
|
The five review headings the script matches are written by `review-pr` and `review-address`, and the whole set is stated once in the first. The reply family is matched by jq filters in the script, so a project posting its replies under different headings edits those to match. The review family is matched inside `canon pr review-state` instead, which the script and `review-pr` both read through, so a project renaming either review heading changes the verb rather than the script. Either way, a heading nothing matches reads as a pull request nobody has reviewed.
|
|
60
60
|
|
|
@@ -18,7 +18,7 @@ if [ -z "$MAIN_ROOT" ]; then
|
|
|
18
18
|
echo "poll: not a git repository, so nothing is classified" >&2
|
|
19
19
|
exit 1
|
|
20
20
|
fi
|
|
21
|
-
STATE_DIR="$MAIN_ROOT/.canon/tmp/pr
|
|
21
|
+
STATE_DIR="$MAIN_ROOT/.canon/tmp/pr/poll"
|
|
22
22
|
mkdir -p "$STATE_DIR"
|
|
23
23
|
STATE="$STATE_DIR/baseline.txt"
|
|
24
24
|
touch "$STATE"
|
|
@@ -217,7 +217,7 @@ Still owed: <what the destination expects that the page does not carry yet>
|
|
|
217
217
|
|
|
218
218
|
Then stop and let the operator strike, redirect, or confirm each block.
|
|
219
219
|
|
|
220
|
-
Write nothing to a destination here. One skill owns the durable writes, and two skills editing one file at one step is the failure that rule exists against. Record each confirmed block in `.canon/tmp/teach-promotion/<slug>.md` at the main worktree root instead, appending when the file exists, with one H2 per destination naming its path, the source page beneath it, and the page body fenced:
|
|
220
|
+
Write nothing to a destination here. One skill owns the durable writes, and two skills editing one file at one step is the failure that rule exists against. Record each confirmed block in `.canon/tmp/handoff/teach-promotion/<slug>.md` at the main worktree root instead, appending when the file exists, with one H2 per destination naming its path, the source page beneath it, and the page body fenced:
|
|
221
221
|
|
|
222
222
|
````markdown
|
|
223
223
|
## <destination path>
|
|
@@ -258,7 +258,7 @@ A promotion pass reports its own shape instead, one line per page the operator c
|
|
|
258
258
|
|
|
259
259
|
```plaintext
|
|
260
260
|
➡️ Promoting: .canon/teach/<nn>-<topic>/reference/<slug>.md → <destination path>
|
|
261
|
-
→ Confirmed pages wait at .canon/tmp/teach-promotion/<slug>.md. Run /docs-fold from a branch to fold them in.
|
|
261
|
+
→ Confirmed pages wait at .canon/tmp/handoff/teach-promotion/<slug>.md. Run /docs-fold from a branch to fold them in.
|
|
262
262
|
```
|
|
263
263
|
|
|
264
264
|
A pass where the operator confirmed nothing writes no handoff file and reports that alone.
|
|
@@ -19,7 +19,7 @@ Writing the checklist correctly to disk does not close the gap either. The file
|
|
|
19
19
|
- Read the project's existing test config and patterns before writing a test against them
|
|
20
20
|
- Assert a user action and its outcome per test, covering the happy path and the key edge cases
|
|
21
21
|
- Run the tests after writing them and fix what fails
|
|
22
|
-
- Write a produced checklist to the `.canon/tmp/ui-checklist/<slug>.md` handoff at the main worktree root, overwriting
|
|
22
|
+
- Write a produced checklist to the `.canon/tmp/handoff/ui-checklist/<slug>.md` handoff at the main worktree root, overwriting
|
|
23
23
|
- Report that everything is covered rather than manufacturing a checklist to show work
|
|
24
24
|
|
|
25
25
|
## Must not
|
|
@@ -59,7 +59,7 @@ If all changes are automatable, skip the manual checklist:
|
|
|
59
59
|
|
|
60
60
|
Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. Fall back to `latest` on an empty result.
|
|
61
61
|
|
|
62
|
-
When a manual checklist is produced, write it directly to `.canon/tmp/ui-checklist/<slug>.md` at the main worktree root, not the current worktree. Resolve that root the way `session-worktree` does. Create the directory if it does not exist. Always overwrite. This is a handoff file rather than a deliverable: `git-pr` posts it as a pull request comment once one opens, then removes it, and nothing here talks to `gh` directly.
|
|
62
|
+
When a manual checklist is produced, write it directly to `.canon/tmp/handoff/ui-checklist/<slug>.md` at the main worktree root, not the current worktree. Resolve that root the way `session-worktree` does. Create the directory if it does not exist. Always overwrite. This is a handoff file rather than a deliverable: `git-pr` posts it as a pull request comment once one opens, then removes it, and nothing here talks to `gh` directly.
|
|
63
63
|
|
|
64
64
|
From a linked worktree the file-editing tools refuse that path, so the checklist goes out through `Bash`. Send the `mkdir -p` and the heredoc as two plain commands rather than joining them with `&&`, which is refused as compound.
|
|
65
65
|
|
|
@@ -71,7 +71,7 @@ The `.canon/tmp/` directory is gitignored. Do not stage or commit the file.
|
|
|
71
71
|
|
|
72
72
|
1. Write and run e2e tests (report pass/fail)
|
|
73
73
|
2. If a manual checklist was produced, write it to file, then output only the file path in chat:
|
|
74
|
-
`📝 Wrote .canon/tmp/ui-checklist/<slug>.md`
|
|
74
|
+
`📝 Wrote .canon/tmp/handoff/ui-checklist/<slug>.md`
|
|
75
75
|
3. If no checklist was needed: `✅ All changes covered by e2e tests. No manual verification needed.`
|
|
76
76
|
|
|
77
77
|
Do not repeat the full checklist in chat.
|
package/docs/agents/commands.md
CHANGED
|
@@ -53,6 +53,7 @@ Full help: `canon <command> --help`. Behavior notes for the install and sync ver
|
|
|
53
53
|
| `canon records migrate` | Rewrite the records a validate finding names a recoverable transform for (`--write`, `--json`) |
|
|
54
54
|
| `canon records ordinal` | Report or claim the next ordinal `intake` and `groundwork` share, creating the folder atomically (`--claim`, `--json`) |
|
|
55
55
|
| `canon records size` | Report what each record folder holds and how much of it is recent, heaviest first (`--json`) |
|
|
56
|
+
| `canon records prune-tmp` | Report scratch nobody has touched inside an age window and delete it (`--write`, `--older-than`, `--json`) |
|
|
56
57
|
| `canon records push` | Commit every record folder less the exclusion set and push it to a private records remote (`--json`) |
|
|
57
58
|
| `canon records pull` | Fetch the records remote and write it back, refusing rather than discarding unpushed records (`--json`) |
|
|
58
59
|
| `canon migrate rename` | Rewrite every unprotected `aitk` token to `canon` and move the paths that carry the name, reporting the plan without `--write` (`--scope`, `--json`) |
|
|
@@ -190,7 +191,7 @@ A destination that already holds the same bytes marks the flat file a duplicate,
|
|
|
190
191
|
|
|
191
192
|
`migrate record-layout` folds two record folders under the memory pen: review receipts move from `.canon/review/memory/` to `.canon/memory/review/`, and retired entries move from `.canon/tmp/memory-archive/` to `.canon/memory/archive/`, backed for the first time since `memory/` is a folder `canon records push` carries and `tmp/` is not. Each move is refused independently when its destination already exists, and the citation repoint sweeps every folder `canon records push` carries, archives included, since an archived receipt still cites the row it retired. The same `canon-keep-record-root` marker and second-run idempotence check apply. A receipt sitting at the flat `review/` root, from before `.canon/review/memory/` existed, matches neither mapped move and is reported by name rather than moved.
|
|
192
193
|
|
|
193
|
-
The same verb leaves `.canon/review/` holding only reviews. Feedback moves to `.canon/feedback/`, and the design, board, slides, and diagram renders move to `.canon/tmp/render/<kind>/`, off the records remote since the commands regenerate them. Reference images move to `.canon/picks/references/`. A branch report flattens from `review/branch/review-<slug>.md` to `review/branch-<slug>.md`, and a flat `review/ui-checklist-<slug>.md` moves into `.canon/tmp/ui-checklist/`. Each folder under `review/evidence/` is decided from what it holds: one directly carrying an `arm-<id>` capture or a `design-handoff.md` is a pick and moves to `.canon/picks/<slug>/`, and any other is evidence and moves to `.canon/evidence/<nn>-<slug>/`. Evidence folders are numbered by the oldest file each holds, continuing past any ordinal `evidence/` already carries. The dry run labels each derived destination `(pick)` or `(evidence)`, so a misfiled folder shows before `--write`, and one filed wrong moves back by hand. A bare mention of an emptied folder, such as `review/evidence/` with no slug or `review/branch/` with no report name, matches no row and stays as written.
|
|
194
|
+
The same verb leaves `.canon/review/` holding only reviews. Feedback moves to `.canon/feedback/`, and the design, board, slides, and diagram renders move to `.canon/tmp/render/<kind>/`, off the records remote since the commands regenerate them. Reference images move to `.canon/picks/references/`. A branch report flattens from `review/branch/review-<slug>.md` to `review/branch-<slug>.md`, and a flat `review/ui-checklist-<slug>.md` moves into `.canon/tmp/handoff/ui-checklist/`. Each folder under `review/evidence/` is decided from what it holds: one directly carrying an `arm-<id>` capture or a `design-handoff.md` is a pick and moves to `.canon/picks/<slug>/`, and any other is evidence and moves to `.canon/evidence/<nn>-<slug>/`. Evidence folders are numbered by the oldest file each holds, continuing past any ordinal `evidence/` already carries. The dry run labels each derived destination `(pick)` or `(evidence)`, so a misfiled folder shows before `--write`, and one filed wrong moves back by hand. A bare mention of an emptied folder, such as `review/evidence/` with no slug or `review/branch/` with no report name, matches no row and stays as written.
|
|
194
195
|
|
|
195
196
|
## Version skew
|
|
196
197
|
|
package/docs/agents/records.md
CHANGED
|
@@ -152,6 +152,33 @@ The window counts read `mtime`, so what they report is a file written inside the
|
|
|
152
152
|
|
|
153
153
|
Exit codes: `0` the reading completed, `1` refused. The one refusal is `no-folder`, raised when the project holds neither record root. A project holding a root and no records is empty rather than absent: at the legacy `.claude` root each folder's own `present` flag says which ones it carries, and at a `.canon` root the folder list itself is already the answer, since nothing absent is named.
|
|
154
154
|
|
|
155
|
+
## Prune
|
|
156
|
+
|
|
157
|
+
`canon records prune-tmp` reports scratch nobody has touched inside an age window, and deletes it only with `--write`. Nothing removed an abandoned scratch folder before this, so a spike folder from months ago sat beside the ones a session still needs.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
canon records prune-tmp
|
|
161
|
+
canon records prune-tmp --write
|
|
162
|
+
canon records prune-tmp --older-than 30 --json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
| Option | Behavior |
|
|
166
|
+
| --------------------- | -------------------------------------------------------------- |
|
|
167
|
+
| `--json` | Add a machine-readable record on stdout |
|
|
168
|
+
| `--write` | Delete every candidate the report lists |
|
|
169
|
+
| `--older-than <days>` | Age a unit's newest file must clear to be offered (default 14) |
|
|
170
|
+
| `--root <path>` | Project root, defaulting to the main worktree |
|
|
171
|
+
|
|
172
|
+
A candidate is a unit whose newest file is older than the threshold: a `tmp/<slug>/` folder, a folder one level inside `runs/`, `render/`, or `pr/`, or a single marker file inside `hooks/<hook>/`. `pr/review/` groups its body files by pull request number rather than reporting as one folder, since a review pass leaves one file per pass and a folder holding thousands of them would otherwise report as a single row nobody can prune apart. A unit holding no files, empty subfolders included, is offered whatever its age.
|
|
173
|
+
|
|
174
|
+
`tmp/handoff/` and `tmp/pr/poll/` are never offered. A reader deletes a handoff themselves once it has been read, and a poll baseline is live state rather than scratch. The pre-split names the reserved split replaced, `memory-routing/`, `teach-promotion/`, `ui-checklist/`, and `pr-poll/` at the scratch root, are skipped the same way on a project the rename never reached, naming the folder they moved to, rather than being offered as ordinary slugs. An unread handoff is the one thing a wrong delete here loses for good.
|
|
175
|
+
|
|
176
|
+
A scratch-root name `canon migrate record-layout` or `canon migrate scratch-evidence` moves out of scratch for good, such as `memory-archive`, is skipped the same way on a project that has not yet run that migration, naming the verb to run. The retired-entry archive `memory-archive` holds is never deleted, per the memory standard, so it stays skipped rather than aging into an offer.
|
|
177
|
+
|
|
178
|
+
It writes nothing until `--write` is passed, matching `canon records migrate`: a session record has no history to undo a wrong delete from. It reads `mtime` the way `canon records size` does, so a machine restored by `canon records pull` reads its whole tree as new and fails safe by offering nothing.
|
|
179
|
+
|
|
180
|
+
Exit codes: `0` nothing to prune, or `--write` deleted every candidate. `1` refused, sharing `no-folder` with `size`, or a delete failed. `2` candidates exist and `--write` was not passed.
|
|
181
|
+
|
|
155
182
|
## Push and pull
|
|
156
183
|
|
|
157
184
|
`canon records push` commits the backed record folders to a private remote and pushes them. `canon records pull` fetches the other direction and writes them back. Both take `--json` and `--root` the way `validate` does, and both exit `0` on agreement and `1` on a refusal.
|
package/docs/agents/sandbox.md
CHANGED
|
@@ -69,7 +69,7 @@ A skill pairs to a scenario by filename, `<category>-<command>` first and bare `
|
|
|
69
69
|
|
|
70
70
|
`exempt` means no arm should be written and holds only with a reason, declared in `scripts/sandbox/exempt.toml` and limited to a harness limit the checker cannot reach past or a skill that writes no artifact. An armed arm outranks an exemption. An exemption naming no shipped skill, or naming one an arm now asserts, exits 1 without `--strict`. Each armed arm reports as `<category>:<command>/<arm>`, so two same-named arms under different scenarios stay distinct.
|
|
71
71
|
|
|
72
|
-
`scripts/sandbox/run.sh` calls this after a headless run and merges the verdict into the envelope it prints. It also writes that merged record to `.canon/tmp/sandbox
|
|
72
|
+
`scripts/sandbox/run.sh` calls this after a headless run and merges the verdict into the envelope it prints. It also writes that merged record to `.canon/tmp/runs/sandbox/<target>-<arm>-<timestamp>.json` with a `writes` array appended, and logs the path on stderr. Both fields are what a later re-score needs, since `--envelope` and `--writes` read files the run deletes on exit.
|
|
73
73
|
|
|
74
74
|
Two more fields ride alongside the verdict rather than inside it. `escapes` lists what the run wrote under a watched toolkit root, which the verdict cannot assert over because those files sit outside the sandbox tree. `sessions` reports the nested-dispatch bound, carrying `watched` for whether the client's session registry was there to read, `new` for the records that appeared while the run was in flight, `concurrent` for the records present both before and after, and `reap` for what the run found in the session's process group afterwards. Neither field fails a run on its own.
|
|
75
75
|
|