@erclx/canon 5.69.0 → 5.70.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/CHANGELOG.md +17 -0
- package/README.md +33 -159
- package/assets/brand/mark.svg +17 -9
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/app-drive/SKILL.md +3 -2
- package/claude/skills/app-drive/references/drive-record.md +52 -10
- package/claude/skills/app-drive/references/isolate.md +1 -1
- package/claude/skills/design-pick/SKILL.md +1 -0
- package/claude/skills/ui-craft/references/copy.md +4 -1
- package/claude/skills/ui-film/REQUIREMENT.md +4 -2
- package/claude/skills/ui-film/SKILL.md +24 -11
- package/claude/skills/ui-film/references/compose-craft.md +81 -0
- package/claude/skills/ui-film/references/config.md +6 -1
- package/claude/skills/ui-film/references/handoff.md +13 -3
- package/claude/skills/ui-film/references/take-craft.md +27 -1
- package/docs/index.md +1 -0
- package/docs/migrations/by-hand.md +107 -0
- package/docs/migrations/by-verb.md +118 -0
- package/docs/migrations/index.md +14 -0
- package/docs/target/index.md +1 -2
- package/docs/target/projects.md +2 -2
- package/docs/target/sync.md +22 -2
- package/docs/workflow/ai-workflow.md +1 -1
- package/governance/ledger/skills/ui-craft/copy.md +8 -4
- package/package.json +3 -1
- package/scripts/core/design-sync.ts +204 -0
- package/scripts/core/regen-web-favicon.ts +27 -12
- package/scripts/core/regen-web-previews.ts +8 -1
- package/scripts/core/render-film.ts +179 -0
- package/src/capture/frame-values.ts +12 -12
- package/src/commands/records.ts +3 -2
- package/src/commands/sandbox.ts +9 -2
- package/src/commands/take.ts +159 -11
- package/src/design/favicon.ts +85 -13
- package/src/design/reference.ts +98 -0
- package/src/design/render.ts +16 -14
- package/src/design/tokens.ts +4 -3
- package/src/gate/stages.ts +6 -0
- package/src/init/steps.ts +2 -2
- package/src/migrate/status.ts +3 -3
- package/src/records/validate.ts +14 -2
- package/src/sandbox/expect.ts +32 -0
- package/src/sandbox/headless/run.ts +6 -3
- package/src/take/camera.ts +99 -10
- package/src/take/film.ts +35 -2
- package/src/take/fixtures/input.html +138 -0
- package/src/take/keys.ts +193 -0
- package/src/take/log.ts +94 -1
- package/src/take/perform.ts +165 -10
- package/src/take/plan.ts +101 -6
- package/src/take/screencast.ts +28 -3
- package/src/take/stepper.ts +122 -18
- package/docs/target/migrations.md +0 -205
package/src/take/stepper.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { openFrameEncoder } from '@/take/encode'
|
|
2
|
+
import { describeKey, modifierBits } from '@/take/keys'
|
|
2
3
|
import { FPS, type LogEvent, START, VIEWPORT } from '@/take/log'
|
|
3
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type KeyInput,
|
|
6
|
+
type PointerInput,
|
|
7
|
+
type Stage,
|
|
8
|
+
TargetMissing,
|
|
9
|
+
performSteps,
|
|
10
|
+
} from '@/take/perform'
|
|
4
11
|
import type { Beat } from '@/take/plan'
|
|
5
12
|
import { type ShellPage, openShell } from '@/take/shell'
|
|
6
13
|
|
|
@@ -36,6 +43,9 @@ const LOAD_CAP_MS = 15_000
|
|
|
36
43
|
const SETTLE_MS = 2500
|
|
37
44
|
const LOADING_FRAME_CAP_MS = 250
|
|
38
45
|
|
|
46
|
+
/** How long a refusal waits on the frame it shows, since the page may be wedged. */
|
|
47
|
+
const STILL_CAP_MS = 2000
|
|
48
|
+
|
|
39
49
|
/** A frame that took longer than `STALL_MS`, so the beat is filmed another way. */
|
|
40
50
|
export class StepperStalled extends Error {
|
|
41
51
|
constructor(
|
|
@@ -68,6 +78,7 @@ export async function filmStepped(
|
|
|
68
78
|
const page = await openShell(request.executable, VIEWPORT)
|
|
69
79
|
const encoder = openFrameEncoder(request.clip, FPS)
|
|
70
80
|
const stallMs = request.stallMs ?? STALL_MS
|
|
81
|
+
let still: (() => Promise<Buffer | undefined>) | undefined
|
|
71
82
|
try {
|
|
72
83
|
let ticks = await load(page, request.url)
|
|
73
84
|
const beginFrame = (screenshot: boolean) =>
|
|
@@ -81,6 +92,7 @@ export async function filmStepped(
|
|
|
81
92
|
: {}),
|
|
82
93
|
},
|
|
83
94
|
)
|
|
95
|
+
still = () => frameStill(beginFrame)
|
|
84
96
|
await pump(beginFrame, SETTLE_MS)
|
|
85
97
|
|
|
86
98
|
const live = liveStage(page, beginFrame, stallMs)
|
|
@@ -128,16 +140,14 @@ export async function filmStepped(
|
|
|
128
140
|
hold: async (ms) => {
|
|
129
141
|
for (let i = 0; i < Math.round(ms / FRAME_MS); i += 1) await frame()
|
|
130
142
|
},
|
|
131
|
-
pointer:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
key:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
await sent
|
|
140
|
-
},
|
|
143
|
+
pointer: (input) =>
|
|
144
|
+
stepThen(
|
|
145
|
+
page.send('Input.dispatchMouseEvent', mouseEvent(input)),
|
|
146
|
+
frame,
|
|
147
|
+
),
|
|
148
|
+
key: (text) => stepThen(page.send('Input.insertText', { text }), frame),
|
|
149
|
+
press: (input) =>
|
|
150
|
+
stepThen(page.send('Input.dispatchKeyEvent', keyEvent(input)), frame),
|
|
141
151
|
evaluate: (expression) => page.evaluate(expression),
|
|
142
152
|
}
|
|
143
153
|
|
|
@@ -153,12 +163,31 @@ export async function filmStepped(
|
|
|
153
163
|
return { frames, events, still: last }
|
|
154
164
|
} catch (error) {
|
|
155
165
|
encoder.abort()
|
|
166
|
+
if (error instanceof TargetMissing && still) error.still = await still()
|
|
156
167
|
throw error
|
|
157
168
|
} finally {
|
|
158
169
|
page.close()
|
|
159
170
|
}
|
|
160
171
|
}
|
|
161
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Begins one frame with a screenshot for a refusal to show, through the same
|
|
175
|
+
* begin-frame call every kept frame uses, since a separate capture call can
|
|
176
|
+
* wait on a frame that never begins. Best effort, so a page that answers with
|
|
177
|
+
* nothing leaves the refusal standing without a still.
|
|
178
|
+
*/
|
|
179
|
+
async function frameStill(
|
|
180
|
+
beginFrame: (screenshot: boolean) => Promise<{ screenshotData?: string }>,
|
|
181
|
+
): Promise<Buffer | undefined> {
|
|
182
|
+
const reply = await Promise.race([
|
|
183
|
+
beginFrame(true).catch(() => undefined),
|
|
184
|
+
sleep(STILL_CAP_MS),
|
|
185
|
+
])
|
|
186
|
+
return reply?.screenshotData
|
|
187
|
+
? Buffer.from(reply.screenshotData, 'base64')
|
|
188
|
+
: undefined
|
|
189
|
+
}
|
|
190
|
+
|
|
162
191
|
/**
|
|
163
192
|
* Navigates and resolves with Chromium's monotonic clock in milliseconds, read
|
|
164
193
|
* off the load event's own timestamp. The runtime's clock cannot stand in for
|
|
@@ -217,7 +246,7 @@ function liveStage(
|
|
|
217
246
|
pointer: (input) => {
|
|
218
247
|
const sent = page.send('Input.dispatchMouseEvent', mouseEvent(input))
|
|
219
248
|
return withinStall(
|
|
220
|
-
beginFrame(false)
|
|
249
|
+
Promise.all([beginFrame(false), sent]),
|
|
221
250
|
0,
|
|
222
251
|
stallMs,
|
|
223
252
|
).then(() => {})
|
|
@@ -225,7 +254,15 @@ function liveStage(
|
|
|
225
254
|
key: (text) => {
|
|
226
255
|
const sent = page.send('Input.insertText', { text })
|
|
227
256
|
return withinStall(
|
|
228
|
-
beginFrame(false)
|
|
257
|
+
Promise.all([beginFrame(false), sent]),
|
|
258
|
+
0,
|
|
259
|
+
stallMs,
|
|
260
|
+
).then(() => {})
|
|
261
|
+
},
|
|
262
|
+
press: (input) => {
|
|
263
|
+
const sent = page.send('Input.dispatchKeyEvent', keyEvent(input))
|
|
264
|
+
return withinStall(
|
|
265
|
+
Promise.all([beginFrame(false), sent]),
|
|
229
266
|
0,
|
|
230
267
|
stallMs,
|
|
231
268
|
).then(() => {})
|
|
@@ -234,18 +271,83 @@ function liveStage(
|
|
|
234
271
|
}
|
|
235
272
|
}
|
|
236
273
|
|
|
274
|
+
/**
|
|
275
|
+
* A pointer input as CDP takes it. `buttons` carries the held button through a
|
|
276
|
+
* drag's moves, or the page reads them as a hover, and `modifiers` carries the
|
|
277
|
+
* held keys as a bit field beside the key events that pressed them.
|
|
278
|
+
*/
|
|
237
279
|
function mouseEvent(input: PointerInput): Record<string, unknown> {
|
|
238
280
|
const at = { x: input.x, y: input.y }
|
|
281
|
+
if (input.type === 'wheel') {
|
|
282
|
+
return { type: 'mouseWheel', ...at, deltaX: 0, deltaY: input.dy }
|
|
283
|
+
}
|
|
284
|
+
const modifiers = modifierBits(input.modifiers ?? [])
|
|
239
285
|
switch (input.type) {
|
|
240
286
|
case 'move':
|
|
241
|
-
return
|
|
287
|
+
return input.held
|
|
288
|
+
? { type: 'mouseMoved', ...at, button: 'left', buttons: 1, modifiers }
|
|
289
|
+
: { type: 'mouseMoved', ...at, button: 'none', buttons: 0, modifiers }
|
|
242
290
|
case 'down':
|
|
243
|
-
return { type: 'mousePressed', ...at, button: 'left', clickCount: 1 }
|
|
244
291
|
case 'up':
|
|
245
|
-
return {
|
|
246
|
-
|
|
247
|
-
|
|
292
|
+
return {
|
|
293
|
+
type: input.type === 'down' ? 'mousePressed' : 'mouseReleased',
|
|
294
|
+
...at,
|
|
295
|
+
button: 'left',
|
|
296
|
+
buttons: input.type === 'down' ? 1 : 0,
|
|
297
|
+
clickCount: input.clickCount,
|
|
298
|
+
modifiers,
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* A key input as CDP takes it. A key that types sends `keyDown` with its text
|
|
305
|
+
* only when no modifier but Shift is held, as Chromium's own keyboard does, so
|
|
306
|
+
* `Control+K` in a focused field inserts no K.
|
|
307
|
+
*/
|
|
308
|
+
function keyEvent(input: KeyInput): Record<string, unknown> {
|
|
309
|
+
const descriptor = describeKey(input.key, {
|
|
310
|
+
shift: input.modifiers.includes('Shift'),
|
|
311
|
+
})
|
|
312
|
+
if (!descriptor) throw new Error(`no key descriptor for ${input.key}`)
|
|
313
|
+
const event = {
|
|
314
|
+
key: descriptor.key,
|
|
315
|
+
code: descriptor.code,
|
|
316
|
+
windowsVirtualKeyCode: descriptor.keyCode,
|
|
317
|
+
nativeVirtualKeyCode: descriptor.keyCode,
|
|
318
|
+
location: descriptor.location,
|
|
319
|
+
modifiers: modifierBits(input.modifiers),
|
|
320
|
+
}
|
|
321
|
+
if (input.type === 'up') return { type: 'keyUp', ...event }
|
|
322
|
+
const types =
|
|
323
|
+
descriptor.text !== undefined &&
|
|
324
|
+
input.modifiers.every((modifier) => modifier === 'Shift')
|
|
325
|
+
return types
|
|
326
|
+
? {
|
|
327
|
+
type: 'keyDown',
|
|
328
|
+
...event,
|
|
329
|
+
text: descriptor.text,
|
|
330
|
+
unmodifiedText: descriptor.text,
|
|
331
|
+
}
|
|
332
|
+
: { type: 'rawKeyDown', ...event }
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Steps a frame, then awaits the input sent ahead of it. When the step fails,
|
|
337
|
+
* the input is left settling against a shell about to close, so its rejection
|
|
338
|
+
* is handled here rather than surfacing as an unhandled one.
|
|
339
|
+
*/
|
|
340
|
+
async function stepThen(
|
|
341
|
+
sent: Promise<unknown>,
|
|
342
|
+
step: () => Promise<void>,
|
|
343
|
+
): Promise<void> {
|
|
344
|
+
try {
|
|
345
|
+
await step()
|
|
346
|
+
} catch (error) {
|
|
347
|
+
sent.catch(() => {})
|
|
348
|
+
throw error
|
|
248
349
|
}
|
|
350
|
+
await sent
|
|
249
351
|
}
|
|
250
352
|
|
|
251
353
|
async function withinStall<T>(
|
|
@@ -257,6 +359,8 @@ async function withinStall<T>(
|
|
|
257
359
|
const stalled = new Promise<never>((_, fail) => {
|
|
258
360
|
timer = setTimeout(() => fail(new StepperStalled(frame, stallMs)), stallMs)
|
|
259
361
|
})
|
|
362
|
+
// A stall leaves the work pending until the shell closes and rejects it.
|
|
363
|
+
work.catch(() => {})
|
|
260
364
|
try {
|
|
261
365
|
return await Promise.race([work, stalled])
|
|
262
366
|
} finally {
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Target migrations
|
|
3
|
-
description: One-time moves a project scaffolded under an older toolkit runs before its next sync
|
|
4
|
-
category: Agent surface
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Target migrations
|
|
8
|
-
|
|
9
|
-
Each section is a move a project runs once, when it was scaffolded before the change it names. Run `canon upgrade` first, then `canon migrate status --json` to list the moves a project still needs in the order to run them. The recurring sync that follows is in [target sync](sync.md).
|
|
10
|
-
|
|
11
|
-
`canon:target-update` is the guided path through both. It runs the moves in that order once you approve its preview.
|
|
12
|
-
|
|
13
|
-
## Move the records first, once
|
|
14
|
-
|
|
15
|
-
Gitignored session records, being the task board, plans, review reports, and scratch, moved from `.claude/` to `.canon/`. Run these from inside the project, and read the `ok` field of the `--json` record rather than the exit code.
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
canon tooling sync claude . --write
|
|
19
|
-
canon migrate records --json
|
|
20
|
-
canon migrate records --write --json
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
The first line takes the `.canon/` ignore entry. The second reports and the third applies, repointing files that cite a moved folder and deleting a `.gitignore` line `.canon/` covers, as `dropped`. A line naming an old path keeps it via `canon-keep-record-root` on that line or the nearest non-blank one above.
|
|
24
|
-
|
|
25
|
-
Then sweep the citations inside the records, which the verb above never reaches.
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
canon migrate record-tree --json
|
|
29
|
-
canon migrate record-tree --write --json
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
It covers `diagrams`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`. Read the report before `--write`. The record tree is untracked, and a wrong rewrite has no undo.
|
|
33
|
-
|
|
34
|
-
## Back records up off this machine, once
|
|
35
|
-
|
|
36
|
-
Run `canon records push` once a private repository exists. It prints the one-time setup command when it finds none, and `canon init` prints the same reminder.
|
|
37
|
-
|
|
38
|
-
## Move rules off the flat layout, once
|
|
39
|
-
|
|
40
|
-
Installed rules moved from `.claude/rules/<subdir>/` to `.claude/rules/canon/<subdir>/`. On the flat layout `canon gov sync` exits 0 reporting `No governance surfaces found in target`, and `canon gov install` lands a second copy.
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
canon migrate rule-layout --json
|
|
44
|
-
canon migrate rule-layout --write --json
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Run it once, ahead of `canon gov sync` or `canon gov install`.
|
|
48
|
-
|
|
49
|
-
## Rename the skill citations, once
|
|
50
|
-
|
|
51
|
-
Plugin skills dropped their `claude-` prefix, so `canon:claude-docs` is now `canon:context-fold`. Five drafters merged into `write-doc`, seven `draft-` skills took verb-first names, and four joined their family. No old name keeps a pointer, and `canon migrate skill-names --write` moves a project. <!-- canon-keep-retired -->
|
|
52
|
-
|
|
53
|
-
| Old name | New name |
|
|
54
|
-
| ------------------ | ----------------------------------------------- |
|
|
55
|
-
| `draft-doc` | `write-doc` <!-- canon-keep-retired --> |
|
|
56
|
-
| `draft-figure` | `draw-figure` <!-- canon-keep-retired --> |
|
|
57
|
-
| `draft-identity` | `design-identity` <!-- canon-keep-retired --> |
|
|
58
|
-
| `draft-slides` | `make-deck` <!-- canon-keep-retired --> |
|
|
59
|
-
| `draft-and-pick` | `design-pick` <!-- canon-keep-retired --> |
|
|
60
|
-
| `draft-ready` | `plan-ready` <!-- canon-keep-retired --> |
|
|
61
|
-
| `draft-screencast` | `script-screencast` <!-- canon-keep-retired --> |
|
|
62
|
-
| `pick-by-eye` | `design-pick` <!-- canon-keep-retired --> |
|
|
63
|
-
| `ui-record` | `ui-film` <!-- canon-keep-retired --> |
|
|
64
|
-
| `ux-audit` | `ui-audit` <!-- canon-keep-retired --> |
|
|
65
|
-
| `ux-measure` | `ui-measure` <!-- canon-keep-retired --> |
|
|
66
|
-
|
|
67
|
-
Replace a citation of a skill with no successor by hand, or delete the sentence.
|
|
68
|
-
|
|
69
|
-
- `canvas` is now `canvas-board`. The sweep rewrites only `canon:canvas` and `skills/canvas`, so replace a bare mention by hand. <!-- canon-keep-retired -->
|
|
70
|
-
- `draft-ready` is rewritten only as `canon:draft-ready` and `skills/draft-ready`, since bare it also describes a pull request, so replace a bare mention by hand. <!-- canon-keep-retired -->
|
|
71
|
-
- `design-extract` and `sketch-design` are replaced by `canon:canvas-board`. <!-- canon-keep-retired -->
|
|
72
|
-
- `review-branch`, `seed-sync`, `restate-plainly`, `bash-cli-script`, `canon-operator`, `canon-rollout`, and `canon-feedback-triage` have no successor <!-- canon-keep-retired -->
|
|
73
|
-
- `create-standard`, `markdown-propose`, `memory-review`, `project-commands`, `repo-metadata`, `ux-walkthrough`, and `plan-ready` have no successor <!-- canon-keep-retired -->
|
|
74
|
-
|
|
75
|
-
The `memory` record kind left `canon records`, so `canon records validate memory` refuses as an unknown kind and `canon records migrate` is gone.
|
|
76
|
-
|
|
77
|
-
- `canon repo metadata`, `canon autoship`, and the `120-bash` rule are gone, and `canon gov sync` removes the rule <!-- canon-keep-retired -->
|
|
78
|
-
- `memory-capture`, the `603-memory` rule, and the `memory` standard are gone, so send a lesson to the context entry that owns the fact <!-- canon-keep-retired -->
|
|
79
|
-
|
|
80
|
-
Two held files run, `.husky/post-merge` and `.claude/hooks/pr-create-log.sh`, and a stale copy calls a missing binary or names a removed skill. Resync what the toolkit owns before sweeping what the project wrote, and run the second line once per stack, naming a held hook with `--replace`.
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
canon gov sync
|
|
84
|
-
canon tooling sync <stack> . --write
|
|
85
|
-
canon migrate skill-names --json
|
|
86
|
-
canon migrate skill-names --write --json
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Rename the pull request template headings, once
|
|
90
|
-
|
|
91
|
-
The template headings are now `## Key changes` and `## Technical context`, and `canon labels scan` compares case exactly. Rename them in `.github/pull_request_template.md`, or delete it and run `canon tooling sync base . --write`.
|
|
92
|
-
|
|
93
|
-
## Move the surfaces off `.claude/`, once
|
|
94
|
-
|
|
95
|
-
`context/`, `wireframes/`, `ARCHITECTURE.md`, `REQUIREMENTS.md`, `DESIGN.md`, and the install stamp folder, now `canon/config/`, moved under `canon/`. `rules/`, `skills/`, `hooks/`, and `settings.json` stay where Claude Code reads them.
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
canon migrate surface-roots --json
|
|
99
|
-
canon migrate surface-roots --write --json
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
It moves each surface with `git mv` and repoints every tracked citation. No other command notices a surface left at `.claude/`.
|
|
103
|
-
|
|
104
|
-
## Delete the retired snippets, once
|
|
105
|
-
|
|
106
|
-
A project scaffolded while snippets installed may hold `.claude/snippets/` or a root `snippets/`.
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
canon sync --check . --json | jq '.reverse'
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
- `dropped` is the toolkit's copy, unchanged, so delete it
|
|
113
|
-
- `unattributed` is often an edited copy, so read what changed first
|
|
114
|
-
- `project` is the project's own content, so leave it
|
|
115
|
-
|
|
116
|
-
From the registry the walk reports `historyUnavailable`, so check both paths by hand.
|
|
117
|
-
|
|
118
|
-
## Stop calling the standards verb, once
|
|
119
|
-
|
|
120
|
-
`canon standards <name>` is gone, and a call now fails with a too-many-arguments error. Replace each call with the skill that owns the standard, at `references/<name>.md`. <!-- canon-allow-reference: verb retired -->
|
|
121
|
-
|
|
122
|
-
| Skill | Standards |
|
|
123
|
-
| ----------------- | ------------------------------------------------------------- |
|
|
124
|
-
| `task-board` | `board`, `tasks`, `tasks-decline` |
|
|
125
|
-
| `context-fold` | `context` |
|
|
126
|
-
| `canvas-board` | `canvas`, `design` <!-- canon-keep-retired --> |
|
|
127
|
-
| `docs-sync` | `docs`, `readme` |
|
|
128
|
-
| `draw-figure` | `figures`, `mermaid` |
|
|
129
|
-
| `create-rule` | `rule` |
|
|
130
|
-
| `create-skill` | `skill`, `skill-paths`, `skill-practice`, `skill-requirement` |
|
|
131
|
-
| `git-branch` | `branch`, `slug` |
|
|
132
|
-
| `git-commit` | `commit`, `versioning` |
|
|
133
|
-
| `git-pr` | `pr`, `publish` |
|
|
134
|
-
| `markdown-craft` | `markdown` |
|
|
135
|
-
| `teach-workspace` | `glossary`, `teach` <!-- canon-keep-retired --> |
|
|
136
|
-
| `plan-groundwork` | `groundwork`, `groundwork-spikes` |
|
|
137
|
-
| `plan-intake` | `intake` |
|
|
138
|
-
| `plan-feature` | `plan`, `plan-lifecycle` |
|
|
139
|
-
| `plan-ready` | `ready` <!-- canon-keep-retired --> |
|
|
140
|
-
| `write-doc` | `requirements`, `wiki` |
|
|
141
|
-
| `session-map` | `session` |
|
|
142
|
-
|
|
143
|
-
The `standard` name became `skill-reference.md` in `create-skill`.
|
|
144
|
-
|
|
145
|
-
## Keep or delete the wireframes folder, your call
|
|
146
|
-
|
|
147
|
-
The wireframes record is retired, and nothing deletes what a project holds.
|
|
148
|
-
|
|
149
|
-
- `canon/wireframes/` stays yours and unaudited. Keep it as notes or delete it.
|
|
150
|
-
- Delete the `@canon/wireframes/index.md` line in your `CLAUDE.md` with the folder. `canon claude sync` does not rewrite it.
|
|
151
|
-
- `canon gov sync` removes `.claude/rules/canon/standards/wireframes.md`
|
|
152
|
-
|
|
153
|
-
## Keep or delete the retired product files, your call
|
|
154
|
-
|
|
155
|
-
The learning workspace, deck renderer, app recorder, canvas, and design install are retired with their verbs, and nothing deletes what a project holds.
|
|
156
|
-
|
|
157
|
-
- `.canon/teach/` holds HTML that opens as it is <!-- canon-keep-retired -->
|
|
158
|
-
- `.canon/slides/` and a `demos/` folder hold sources nothing renders any more, so delete them once nothing cites them <!-- canon-keep-retired -->
|
|
159
|
-
- Move the `.claude/design/project/` values, `fonts/`, and `fonts.css` to the stylesheet your `canon/DESIGN.md` lead names, then delete `.claude/design/`
|
|
160
|
-
|
|
161
|
-
A reference to a retired skill takes a hand edit.
|
|
162
|
-
|
|
163
|
-
- Replace a `canon:teach-workspace` reference with `canon:teach-craft` by hand, since `canon migrate skill-names` does not rewrite it <!-- canon-keep-retired -->
|
|
164
|
-
- Delete a `canon:make-deck`, `canon:script-screencast`, `canon:record-screencast`, `canon:read-frames`, or `canon:video-craft` reference, since none has a successor <!-- canon-keep-retired -->
|
|
165
|
-
- Delete a `canon:canvas-board` reference too. Frameboard, which `canon:design-pick` drives, reads each `.canon/canvas/` folder as a project, so move a kept page under one <!-- canon-keep-retired -->
|
|
166
|
-
|
|
167
|
-
## Move decisions out of a held architecture record, once
|
|
168
|
-
|
|
169
|
-
The toolkit no longer seeds `canon/ARCHITECTURE.md`, and a held file and its `CLAUDE.md` import stay. Move a trap a session would hit into that domain's `## Gotchas`, then delete the file and its `@canon/ARCHITECTURE.md` line.
|
|
170
|
-
|
|
171
|
-
## Deny subagents and merges, and turn auto memory off, once
|
|
172
|
-
|
|
173
|
-
The seeded `.claude/settings.json` now denies the `Agent` and `Task` tools and both merge commands, and sets `autoMemoryEnabled` to `false`. `canon claude sync` never rewrites a settings file you hold, so it warns while the file holds no default or lacks a merge entry. Removing `Agent` and `Task` later draws no warning.
|
|
174
|
-
|
|
175
|
-
```bash
|
|
176
|
-
canon migrate session-defaults --json
|
|
177
|
-
canon migrate session-defaults --write --json
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
The verb only adds a key that is absent. A person who wants auto memory back sets it in their own `.claude/settings.local.json`.
|
|
181
|
-
|
|
182
|
-
The verb adds `Bash(canon pr merge:*)` and `Bash(gh pr merge:*)` so a session cannot merge. A project migrated before them runs `--write` once more, which also restores `Agent` and `Task` for a person who removed them to allow subagents, so remove them again after.
|
|
183
|
-
|
|
184
|
-
## Move the capture cases out of the harness, once
|
|
185
|
-
|
|
186
|
-
A web project's `e2e/screenshot.ts` is a config. It now loads `CASES` from a seeded `e2e/capture/cases.ts` that sync copies once and never overwrites. A harness you edited is held on `--write`, so the new one lands only once you pass `--replace e2e/screenshot.ts`.
|
|
187
|
-
|
|
188
|
-
- Copy your cases from the old `e2e/screenshot.ts` into `e2e/capture/cases.ts`, then sync with `--replace e2e/screenshot.ts`.
|
|
189
|
-
- Evidence now defaults to `e2e/evidence/`. Set `export const EVIDENCE_ROOT = 'evidence'` in `e2e/capture/cases.ts` to keep a committed baseline at the old path.
|
|
190
|
-
- A run from a folder with no `e2e/capture/cases.ts` exits 1 and names the file.
|
|
191
|
-
|
|
192
|
-
## Fold the ui rules onto one pointer, once
|
|
193
|
-
|
|
194
|
-
Eleven ui rules moved unchanged into the `ui-craft` skill, being `a11y`, `keyboard`, `pointer`, `forms`, `motion`, `ux-controls`, `ux-completeness`, `surface-capture`, `copy`, `interface-casing`, and `link-behavior`. Update the plugin, then sync, which folds each one you hold and `design-taste.md` onto one `craft.md` pointer without local edits.
|
|
195
|
-
|
|
196
|
-
```bash
|
|
197
|
-
canon gov sync .
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
## Group a long paths section in `CLAUDE.md`, once
|
|
201
|
-
|
|
202
|
-
The `## Key paths` section of `CLAUDE.md` now keeps to 5 bullets in a row, and every other section stays exempt. `canon claude sync` never rewrites a `CLAUDE.md` you hold, so it warns with the line of each longer run.
|
|
203
|
-
|
|
204
|
-
- Regroup the section by hand under H3s, each opening on a one-sentence lead.
|
|
205
|
-
- List `CLAUDE.md` in `canon/config/markdown.toml` to gate it from then on.
|