@erclx/canon 4.87.0 → 4.88.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/auto-ship/SKILL.md +1 -1
- package/claude/skills/docs-fold/SKILL.md +4 -4
- 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
|
|
|
@@ -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
|
|
|
@@ -63,7 +63,7 @@ Work in Claude Code directly. It reads `CLAUDE.md` automatically and has full fi
|
|
|
63
63
|
|
|
64
64
|
The routing test is whether the repository can answer an item today. A session grepping handles the yes, and a groundwork track handles the no.
|
|
65
65
|
|
|
66
|
-
A groundwork track may run experiments to settle a question, writing a fixture it reads itself under `.canon/tmp/groundwork-fixtures/<slug>/` and spawning up to three billed headless runs before it asks. A fixture a headless run is pointed at sits outside the repository, since a session started under the project root inherits that project's `CLAUDE.md` and rules and would measure them instead of the arm.
|
|
66
|
+
A groundwork track may run experiments to settle a question, writing a fixture it reads itself under `.canon/tmp/runs/groundwork-fixtures/<slug>/` and spawning up to three billed headless runs before it asks. A fixture a headless run is pointed at sits outside the repository, since a session started under the project root inherits that project's `CLAUDE.md` and rules and would measure them instead of the arm.
|
|
67
67
|
|
|
68
68
|
What a spike produces goes somewhere else again. An input the run reads is re-runnable and cited by nothing, so the scratch path above is the right lifetime for it, while a recording or a render the track cites as evidence for a finding is what a later reader opens to check the claim. Evidence therefore lives in `evidence/` inside the track beside the file citing it, since the scratch tree holds only what can be deleted without loss.
|
|
69
69
|
|
|
@@ -81,7 +81,7 @@ When features are independent, run them in parallel instead of sequentially. Use
|
|
|
81
81
|
|
|
82
82
|
- Create a worktree per feature, then start a Claude Code session in each
|
|
83
83
|
- Invoke `canon:plan-feature` in each session. Plans land at the main worktree root as `.canon/plans/feature-<slug>.md`, one per feature, no collisions. Small features stay in chat and skip the file.
|
|
84
|
-
- Implement, verify, and review each feature independently. `review-branch` writes a per-branch report at the main worktree root (`review/branch-<slug>.md`), and `ui-test` writes a per-branch checklist handoff there too (`tmp/ui-checklist/<slug>.md`) that `git-pr` posts to the pull request and removes, so parallel sessions do not overwrite each other. The slug is the branch name with any leading type segment dropped, so `feat/jwt-expiration` and the plan at `feature-jwt-expiration.md` meet on one name
|
|
84
|
+
- Implement, verify, and review each feature independently. `review-branch` writes a per-branch report at the main worktree root (`review/branch-<slug>.md`), and `ui-test` writes a per-branch checklist handoff there too (`tmp/handoff/ui-checklist/<slug>.md`) that `git-pr` posts to the pull request and removes, so parallel sessions do not overwrite each other. The slug is the branch name with any leading type segment dropped, so `feat/jwt-expiration` and the plan at `feature-jwt-expiration.md` meet on one name
|
|
85
85
|
- Ship each worktree separately with `canon:git-ship`
|
|
86
86
|
- For full autonomy per worktree, invoke `canon:auto-ship` instead of the manual chain. Approve the plan, walk away, come back to a pull request the chain marked as a draft and then read the flag back on. The mark says the work has had no review yet, and it holds no window, since readying a pull request to merge lifts it directly, an act reserved to the operator or to the controlling session that closed the review.
|
|
87
87
|
|
|
@@ -111,6 +111,8 @@ A person points it at a private repository once and both verbs refuse until they
|
|
|
111
111
|
|
|
112
112
|
`canon records size` reports what each of these folders holds, heaviest first, along with `.canon/tmp`. Each row carries the file count, the bytes, how many files were written in the last 7 and 30 days, and the dates of the least and most recently written one. Nothing fails on a number, because a record folder has no correct size. What the verb replaces is a reading somebody had to remember to take: the memory pen went from 44 entries to 236 between two counts made by hand a fortnight apart, and nothing reported the rate in between.
|
|
113
113
|
|
|
114
|
+
`canon records prune-tmp` reports scratch under `.canon/tmp` nobody has touched inside an age window, and deletes it only with `--write`. A `tmp/handoff/` folder and the live `tmp/pr/poll/` baseline are never offered, since a reader deletes a handoff once it is read and a poll baseline is state a session still needs. See [records](../agents/records.md) for the candidate shapes and the exit codes.
|
|
115
|
+
|
|
114
116
|
A plan that ships is archived, never deleted. `canon tasks archive` moves it to `.canon/plans/archive/` alongside the task it belonged to and retargets that task's `Plan:` line at the new location, so a completed task still leads to the reasoning behind it. An archive sits inside the record folder it archives rather than beside it, so one ignore entry and one backed-folder entry cover a record and everything it has retired. The folder is gitignored, which is why a deleted plan had no recovery path. A plan cited by more than one task stays put until the last of them closes, since moving it early would strand every other pointer.
|
|
115
117
|
|
|
116
118
|
A branch review report takes the other route and is swept rather than archived. `review-branch` writes it flat into `.canon/review/` as `branch-<slug>.md`, the session addressing it reads it once, and the durable record of what a review found is the comment `review-pr` posts on the pull request, so `docs-fold` deletes any report whose branch is gone. The body that writes a report owns how long it lives, which leaves the shipping branch's own report on disk through the run that cites it and collects it a branch later. What that loses is a local-only review on a branch that never opened a pull request, which is why the report says so where a reader meets it.
|
|
@@ -9,3 +9,4 @@ description: Write temporary files to the scratch folder structure
|
|
|
9
9
|
- Write temporary files to `.canon/tmp/<slug>/<file>.md` in the project root, a nested `<slug>/` folder with a kebab-slug tied to the topic, not a flat `<slug>-<file>.md`. The scratch-guard hook enforces the location.
|
|
10
10
|
- Write to `.claude/.tmp/<slug>/` instead in a project that carries no `.canon/` root, which is one the record move has not reached. The hook accepts either, but the ignore file is the source: `.gitignore` and its manifest counterpart are what keep the fallback spelling out of `git status`, and the hook and this rule both defer to it. `canon migrate records` is what moves a project from this second spelling to the first. <!-- canon-keep-record-root -->
|
|
11
11
|
- "The project root" above means the current worktree by default. `085-worktrees.md` states the same mixed default for `.canon/tmp/` and defers to each skill's own body for which write resolves at the main root instead.
|
|
12
|
+
- `runs/`, `hooks/`, `handoff/`, `pr/`, and `render/` are reserved under `tmp/` for build and command output that can be regenerated, hook state, skill-to-skill handoff, pull request scratch, and render output. A session slug must not take one of these five names.
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@ show_help() {
|
|
|
16
16
|
echo -e "${GREY}│${NC} --keep ${GREY}# Keep tmp dir after run for inspection${NC}"
|
|
17
17
|
echo -e "${GREY}│${NC} -h, --help ${GREY}# Show this help message${NC}"
|
|
18
18
|
echo -e "${GREY}│${NC}"
|
|
19
|
-
echo -e "${GREY}│${NC} Scaffolds <stack> into .canon/tmp/verify-<stack>/, syncs the full"
|
|
19
|
+
echo -e "${GREY}│${NC} Scaffolds <stack> into .canon/tmp/runs/verify-<stack>/, syncs the full"
|
|
20
20
|
echo -e "${GREY}│${NC} layer chain, and runs check + test:e2e + screenshot."
|
|
21
21
|
echo -e "${GREY}└${NC}"
|
|
22
22
|
exit 0
|
|
@@ -102,7 +102,7 @@ main() {
|
|
|
102
102
|
open_timeline "canon tooling verify $stack"
|
|
103
103
|
trap close_timeline EXIT
|
|
104
104
|
|
|
105
|
-
local tmp_root="$PROJECT_ROOT/.canon/tmp"
|
|
105
|
+
local tmp_root="$PROJECT_ROOT/.canon/tmp/runs"
|
|
106
106
|
local tmp_dir="$tmp_root/verify-$stack"
|
|
107
107
|
|
|
108
108
|
mkdir -p "$tmp_root"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* `markdown.md` has a body backtick a named thing, so the natural spelling of
|
|
22
22
|
* the citation is `` See the `Parallel sessions` heading in `CLAUDE.md` `` and
|
|
23
23
|
* a class stopping at the first backtick never reaches the file. That heading
|
|
24
|
-
*
|
|
24
|
+
* was real in this repository's own root file when the check was written. Both classes returned the same
|
|
25
25
|
* twenty lines over `claude/skills/` at the stamp, so the backtick exclusion
|
|
26
26
|
* was carrying nothing and hiding the spelling an author would reach for.
|
|
27
27
|
*
|