@erclx/canon 5.49.0 → 5.51.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 +4024 -0
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/auto-ship/references/fan-in.md +20 -6
- package/claude/skills/canon-cli/SKILL.md +9 -7
- package/claude/skills/canvas-board/REQUIREMENT.md +4 -3
- package/claude/skills/canvas-board/SKILL.md +8 -9
- package/claude/skills/ci-workflow/REQUIREMENT.md +3 -3
- package/claude/skills/ci-workflow/SKILL.md +26 -1
- package/claude/skills/ci-workflow/references/workflows.md +28 -0
- package/claude/skills/context-fold/references/context.md +4 -3
- package/claude/skills/design-record/REQUIREMENT.md +33 -0
- package/claude/skills/design-record/SKILL.md +84 -0
- package/claude/skills/design-record/references/folder.md +69 -0
- package/claude/skills/design-taste/SKILL.md +3 -2
- package/claude/skills/git-followup/SKILL.md +1 -0
- package/claude/skills/git-pr/references/pr.md +1 -1
- package/claude/skills/markdown-craft/REQUIREMENT.md +1 -0
- package/claude/skills/markdown-craft/SKILL.md +6 -1
- package/claude/skills/markdown-craft/references/markdown.md +3 -3
- package/claude/skills/markdown-craft/references/tables.md +30 -0
- package/claude/skills/pick-by-eye/SKILL.md +10 -8
- package/claude/skills/plan-feature/SKILL.md +13 -1
- package/claude/skills/plan-feature/references/batches.md +1 -1
- package/claude/skills/session-worktree/SKILL.md +1 -1
- package/claude/skills/target-check/references/domains.md +8 -6
- package/docs/agents/index.md +2 -2
- package/docs/agents/markdown-audit.md +5 -2
- package/docs/agents/migrate.md +2 -0
- package/docs/agents/targets.md +8 -1
- package/docs/target/migrations.md +8 -1
- package/docs/target/projects.md +1 -1
- package/docs/workflow/parallel-features.md +1 -1
- package/docs/workflow/skill-map.md +2 -1
- package/docs/workflow/visual-design-workflow.md +2 -1
- package/governance/detect.toml +1 -1
- package/governance/ledger/tooling/ci-workflow.md +6 -0
- package/governance/ledger/ui/design-reference.md +8 -12
- package/governance/rules/tooling/ci-workflow.md +4 -0
- package/governance/rules/ui/design-reference.md +4 -6
- package/package.json +2 -1
- package/scripts/ci/check-browsers.sh +13 -0
- package/scripts/ci/delete-previews.sh +34 -0
- package/scripts/ci/publish-check.sh +29 -0
- package/scripts/ci/refresh-frames-pr.sh +63 -0
- package/scripts/ci/report-baseline.sh +23 -0
- package/scripts/ci/stack-issues.sh +24 -0
- package/scripts/ci/verify-npm-token.sh +14 -0
- package/src/claude/cases/misc.ts +5 -0
- package/src/cli/help.ts +1 -0
- package/src/cli.ts +2 -0
- package/src/commands/changelog.ts +146 -0
- package/src/commands/claude.ts +26 -1
- package/src/commands/gate.ts +57 -16
- package/src/commands/hooks.ts +7 -5
- package/src/commands/markdown.ts +98 -19
- package/src/commands/migrate.ts +77 -0
- package/src/commands/worktrees.ts +273 -0
- package/src/context/audit.ts +6 -5
- package/src/design/budget.ts +1 -1
- package/src/design/components.ts +1 -1
- package/src/design/document.ts +1 -1
- package/src/design/regen.ts +1 -1
- package/src/design/render.ts +1 -1
- package/src/gate/hand-suite.ts +52 -0
- package/src/gate/measures.ts +9 -2
- package/src/gate/sequencer.ts +17 -8
- package/src/gov/detect.ts +28 -10
- package/src/hooks/post-merge.ts +45 -6
- package/src/markdown/gated.ts +5 -7
- package/src/markdown/scope.ts +60 -10
- package/src/markdown/structure.ts +1 -1
- package/src/markdown/tables.ts +150 -0
- package/src/migrate/status.ts +128 -0
- package/src/tasks/validate.ts +15 -12
- package/src/version/changelog.ts +156 -0
- package/src/worktrees/members.ts +540 -0
- package/tooling/base/configs/.husky/post-merge +7 -6
- package/tooling/claude/reference.md +1 -0
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +11 -1
- package/tooling/claude/seeds/CLAUDE.md +9 -0
- package/tooling/cloudflare/configs/.github/workflows/deploy.yml +10 -28
- package/tooling/cloudflare/configs/scripts/delete-previews.sh +36 -0
- package/tooling/cloudflare/reference.md +7 -4
- package/tooling/web/reference.md +5 -0
- package/tooling/web/seeds/canon/design/index.md +23 -0
- /package/claude/skills/{canvas-board → design-record}/references/design.md +0 -0
|
@@ -124,15 +124,16 @@ Read `canon canvas selection --json` when the answer points at the canvas, such
|
|
|
124
124
|
|
|
125
125
|
1. Apply the winning arm to the real surface, in one change.
|
|
126
126
|
2. Close out any document stating the decision as open, in the same change, naming the winner and the arms that stayed defensible. Skip this where nothing stated the decision.
|
|
127
|
-
3.
|
|
128
|
-
4.
|
|
129
|
-
5.
|
|
130
|
-
6.
|
|
131
|
-
7.
|
|
132
|
-
8.
|
|
133
|
-
9. Report
|
|
127
|
+
3. Hand the winning frame to `canon:design-record` by its page and frame, which lands it in the project's design record. Report it rather than proceeding silently when that skill does not resolve.
|
|
128
|
+
4. Capture the picked round's frames with `canon canvas capture <page> --out <archive-dir> --json`. Each lands as `arm-<id>.png`, keeping the losing arms as a revert record.
|
|
129
|
+
5. Resolve `<archive-dir>` as `.canon/picks/<slug>/` against the main worktree root, since shared session scratch resolves there rather than in a linked worktree.
|
|
130
|
+
6. Delete every page folder on the run's list from under the `content` folder `canon canvas list --json` reports. Touch no page the run did not add, whatever its name.
|
|
131
|
+
7. Stop the canvas server if this run started it, and leave one the operator started running.
|
|
132
|
+
8. Leave the renders in a live track's `evidence/<slug>/renders/`, since `plan-groundwork` treats evidence as durable.
|
|
133
|
+
9. Report each page whose delete is refused, naming the path for the operator to remove. The pick is applied either way.
|
|
134
|
+
10. Report every surface that changed, each on its own line, name the winning arm by its id and its cost, and report the archive path.
|
|
134
135
|
|
|
135
|
-
The picked round is the final one unless Step 4 resolved a revert. Archiving any other page lets item
|
|
136
|
+
The picked round is the final one unless Step 4 resolved a revert. Archiving any other page lets item 6 delete the winning arm with nothing kept, and a variant left on the canvas is a second design nobody maintains.
|
|
136
137
|
|
|
137
138
|
## Reading a measurement
|
|
138
139
|
|
|
@@ -149,6 +150,7 @@ A capture proves appearance and a measurement proves a relationship, so reach fo
|
|
|
149
150
|
Cite these rather than restating them. A step reimplemented here rots against the skill that owns it.
|
|
150
151
|
|
|
151
152
|
- `plan-feature` plans the work once the pick is made, and declares the pull request boundary that plan carries
|
|
153
|
+
- `design-record` lands the winning frame in the reference folder and its values in the design document, and runs before item 6 deletes the page its sync reads
|
|
152
154
|
- `design-taste` carries the layer catalog, the ordering, grey-boxing, and the defaults an arm should reach past
|
|
153
155
|
- `write-human` carries the voice for any copy an arm puts in front of a reader
|
|
154
156
|
|
|
@@ -152,9 +152,21 @@ Then output in chat:
|
|
|
152
152
|
1. <question>
|
|
153
153
|
- Suggested: <pick>, <reason or tradeoff>
|
|
154
154
|
|
|
155
|
-
Next:
|
|
155
|
+
Next: <line from the table below>
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
Pick the `Next:` line from the roster check `${CLAUDE_SKILL_DIR}/../task-board/SKILL.md` states under `### Check for a live orchestrator`, read there rather than restated here.
|
|
159
|
+
|
|
160
|
+
| Roster read | `Next:` line |
|
|
161
|
+
| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
162
|
+
| **Found** | `<name> places and dispatches <task>`, naming the orchestrator row the check matched |
|
|
163
|
+
| **Not found**, and this session's name starts with `worker-` or `planner-` | `Hand <task> to a worker`, naming no session, since none is live and the operator dispatches by hand |
|
|
164
|
+
| **Not found otherwise**, or the read fails | `/session-worktree` |
|
|
165
|
+
|
|
166
|
+
- With a live orchestrator and no planner role held, send the plan path to it through `canon:session-relay`, resolved from its `sessionId`
|
|
167
|
+
- A session holding `canon:role-planner` already owes that announcement through its own channel, so it sends none
|
|
168
|
+
- Say which row fired in one clause, since a line with no stated reason is one the next reader re-derives.
|
|
169
|
+
|
|
158
170
|
Show only the path line and the `Next:` line when there are no questions. The `.canon/plans/` directory is gitignored, so do not stage or commit the file.
|
|
159
171
|
|
|
160
172
|
Do not proceed to implementation until the user explicitly says to continue.
|
|
@@ -90,4 +90,4 @@ The integrator merges each member branch with `git merge --no-ff` in the order i
|
|
|
90
90
|
|
|
91
91
|
- Say in the pull request body that each member is one merge commit, so a reviewer reads `git log --first-parent` one member at a time
|
|
92
92
|
- Fix a semantic conflict a merge surfaces on the integrator's branch, since a member has no pull request to fix it on
|
|
93
|
-
-
|
|
93
|
+
- Name the members on the body's `Fan-in members:` line, which the post-merge hook reads to delete each member's branches and worktree, per `${CLAUDE_SKILL_DIR}/../auto-ship/references/fan-in.md`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: session-worktree
|
|
3
|
-
description: Enters a Claude Code worktree at `.claude/worktrees/<name>/` with a name derived from the active plan or branch. Use when asked to "enter a worktree", "start a worktree", "work in a worktree", or
|
|
3
|
+
description: Enters a Claude Code worktree at `.claude/worktrees/<name>/` with a name derived from the active plan or branch. Use when asked to "enter a worktree", "start a worktree", "work in a worktree", or before building an approved plan in this session. Also use when an `Edit` or `Write` to a main-root file such as a plan, a task, or a review report was refused for session isolation. Do NOT use to list, clean up, or rotate worktrees (use `git-worktree`).
|
|
4
4
|
metadata:
|
|
5
5
|
family: build
|
|
6
6
|
---
|
|
@@ -21,7 +21,7 @@ This domain is first because it conditions the other five. A target a release be
|
|
|
21
21
|
|
|
22
22
|
## 2. The `canon/` folder
|
|
23
23
|
|
|
24
|
-
Read four fields off the same report, then
|
|
24
|
+
Read four fields off the same report, then three verbs:
|
|
25
25
|
|
|
26
26
|
- `unmigrated`: a domain still at the root layout with nothing under `.claude/`. The most urgent finding, because such a domain reports no drift of its own while being entirely behind.
|
|
27
27
|
- `superseded`: a file a newer seed folder replaced. No command moves it, since the content is the project's own.
|
|
@@ -34,19 +34,21 @@ The fourth field and the two verbs cover what the toolkit stopped shipping and w
|
|
|
34
34
|
- `reverse`: `unclaimed` names folders at roots the toolkit stopped shipping, each with an `attribution` of `dropped`, `unattributed`, or `project`.
|
|
35
35
|
- `canon context audit --json`: required sections, entry length, citations, and index drift across the context folder.
|
|
36
36
|
- `canon records validate <kind> --json`, once per record folder the target carries.
|
|
37
|
+
- `canon migrate status --json`: every migration in run order, each with a `state` of `pending`, `clean`, or `refused`. It exits 2 while any is pending.
|
|
37
38
|
|
|
38
39
|
### Verdicts
|
|
39
40
|
|
|
40
|
-
Each of the three states takes all
|
|
41
|
+
Each of the three states takes all seven reads together.
|
|
41
42
|
|
|
42
|
-
- Current: `unmigrated`, `superseded`, and `unmovedSurfaces` all empty, `reverse.unclaimed` carrying nothing outside `project` attribution, and
|
|
43
|
-
- Behind: any of those carrying an entry. Name the entry and its attribution, and for `unmovedSurfaces` each `rel` with its `movesTo`.
|
|
44
|
-
- Unread: `historyUnavailable` set on `reverse`, a `reverse` or `unmovedSurfaces` key the report does not carry, or
|
|
43
|
+
- Current: `unmigrated`, `superseded`, and `unmovedSurfaces` all empty, `reverse.unclaimed` carrying nothing outside `project` attribution, and all three verbs finding nothing.
|
|
44
|
+
- Behind: any of those carrying an entry, or a `pending` migration. Name the entry and its attribution, and for `unmovedSurfaces` each `rel` with its `movesTo`.
|
|
45
|
+
- Unread: `historyUnavailable` set on `reverse`, a `reverse` or `unmovedSurfaces` key the report does not carry, or any verb refusing, a `refused` migration included
|
|
45
46
|
- A missing key is a binary predating the field rather than a target with nothing to report, so name the installed version beside it
|
|
46
47
|
|
|
47
48
|
Routes by finding.
|
|
48
49
|
|
|
49
|
-
- `
|
|
50
|
+
- A `pending` migration routes to its entry's `verb`, which only reports its plan until `--write` is passed. Name each in the order the status gives, and run neither form.
|
|
51
|
+
- `unmovedSurfaces` is the `surface-roots` migration. Name each entry's `rel` and `movesTo` beside its verb.
|
|
50
52
|
- `unmigrated` routes to a relocation the user runs, since no command moves it.
|
|
51
53
|
- `superseded` routes to a split the user makes, since no command moves the content and no skill proposes it.
|
|
52
54
|
- An `unclaimed` folder carrying `project` attribution routes nowhere, since history proved the project owns it.
|
package/docs/agents/index.md
CHANGED
|
@@ -12,10 +12,10 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
|
|
|
12
12
|
- [Context audit](context-audit.md): Running the context audit, its folder scope, the exit codes, every check with the folders it reaches and whether it gates, the seed gate, and the citation exclusions
|
|
13
13
|
- [Indexes](indexes.md): How canon indexes regen and canon indexes list resolve paths, what they stage, and the record and exit codes each emits
|
|
14
14
|
- [Install and sync](install-and-sync.md): What each install and sync verb writes, refuses, or leaves alone, the drift report and its seven side sections, the rule detection record, and version skew states
|
|
15
|
-
- [Markdown audit](markdown-audit.md): Running the markdown audit over any path, each finding
|
|
15
|
+
- [Markdown audit](markdown-audit.md): Running the markdown audit over any path, each finding with whether it gates, the density and table limits, the advisory measures, the exit codes, and where the audit runs
|
|
16
16
|
- [Migrate](migrate.md): What each canon migrate verb moves, the markers that keep an old spelling, and what each one refuses
|
|
17
17
|
- [Output shape](output-shape.md): The framed output shape every command renders into, the JSON and color modes, the exit discipline, the runtime catalogs, and a headless invocation per domain
|
|
18
18
|
- [Overview](overview.md): What this folder covers, the invocation rules every command inherits, and how canon docs resolves a topic name
|
|
19
19
|
- [Records](records.md): Backing the record folders to a private remote, restoring them on a new machine, reading folder size, and pruning stale scratch
|
|
20
20
|
- [Sessions transfer](sessions-transfer.md): Moving a session to another machine with export and import, the manifest fields, the refusal reasons, and what a resume on the target depends on
|
|
21
|
-
- [Targets](targets.md): The projects this toolkit installed into, where each answer comes from,
|
|
21
|
+
- [Targets](targets.md): The projects this toolkit installed into, where each answer comes from, the cross-target pull request read, and the releases since a target's install
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Markdown audit
|
|
3
|
-
description: Running the markdown audit over any path, each finding
|
|
3
|
+
description: Running the markdown audit over any path, each finding with whether it gates, the density and table limits, the advisory measures, the exit codes, and where the audit runs
|
|
4
4
|
kind: reference
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -28,11 +28,14 @@ The ban set and checkpoints ship in `src/markdown/bans.ts` and `src/markdown/str
|
|
|
28
28
|
| Links | A relative link resolving to nothing on disk | Always |
|
|
29
29
|
| Density | The six limits below, under `density` | On a gated prefix |
|
|
30
30
|
| Reference shape | A `kind: reference` page breaking its shape, under `referenceShape` | On a gated prefix |
|
|
31
|
+
| Tables | The four table limits below, under `tables` | Never |
|
|
31
32
|
| Colons | A colon followed by more text in a prose line, under `colons` | Never |
|
|
32
33
|
| Heading case | A capital after the first word of a heading or `title`, under `headingCase` | Never |
|
|
33
34
|
|
|
34
35
|
A page marked `kind: reference` holds its opening, then `Output`, `Refusals`, and `See also` as H2s, each at most once and in that order. It takes no H3 and no table headed `Flag` or `Option`. An unmarked page is never measured.
|
|
35
36
|
|
|
37
|
+
Tables take the four limits in the `markdown-craft` skill's `tables.md`, on the density paths but never `CLAUDE.md`. A cell past 120 visible characters reports as `table-cell`, more than 8 body rows as `table-rows`, a list after a table as `table-then-list`, and a code span with a run past 30 characters as `table-token`. None sets the exit code.
|
|
38
|
+
|
|
36
39
|
| Density limit | Finding |
|
|
37
40
|
| ------------------- | ---------------------------------------------------------- |
|
|
38
41
|
| Paragraph length | A prose paragraph past 350 characters |
|
|
@@ -45,7 +48,7 @@ A page marked `kind: reference` holds its opening, then `Output`, `Refusals`, an
|
|
|
45
48
|
- A paragraph under a list item counts toward that bullet, never as a paragraph
|
|
46
49
|
- A table, fence, list, heading, or bold section marker ends a run of paragraphs
|
|
47
50
|
- A run whose every item opens with a bold label or a link is exempt, and numbered items never count
|
|
48
|
-
- Only context entries, docs,
|
|
51
|
+
- Only context entries, docs, skills, tooling seeds and references, the wiki, `README.md`, and `CONTRIBUTING.md` take a finding, plus the Key paths of `CLAUDE.md` on the run and H3 limits
|
|
49
52
|
- A requirement gates only once the gated list below names its own file, never through its skill's prefix and never on a bullet run alone
|
|
50
53
|
|
|
51
54
|
| Root | A density or reference shape finding gates |
|
package/docs/agents/migrate.md
CHANGED
|
@@ -10,6 +10,7 @@ kind: reference
|
|
|
10
10
|
|
|
11
11
|
| Verb | Moves |
|
|
12
12
|
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `status` | Nothing. It lists every migration the project still needs, in run order. |
|
|
13
14
|
| `rename` | The retired `aitk` name. `--scope target` rewrites toolkit-owned folders only. |
|
|
14
15
|
| `skill-names` | Plugin skills off their `claude-` and `draft-` prefixes and other retired names, with every citation |
|
|
15
16
|
| `records` | Session records from `.claude/` to `.canon/`, with every tracked citation |
|
|
@@ -32,6 +33,7 @@ kind: reference
|
|
|
32
33
|
- `record-tree` reports closed groundwork and intake trails, the scratch folder, and backup history as counts and rewrites none
|
|
33
34
|
- `records` reports skipped record folders as a count on its own line
|
|
34
35
|
- A `record-tree` rewrite has no undo in version control, so read the report before `--write`
|
|
36
|
+
- `status` runs each verb as a dry run and exits 2 while any is pending, with a refusal outranking that. A clean entry can turn pending once an earlier one applies, so run it again after each `--write`.
|
|
35
37
|
|
|
36
38
|
Three markers keep an old spelling on a line. Each also covers the nearest non-blank line, below it for `canon-keep-record-root` and above it for `canon-keep-surface-root`.
|
|
37
39
|
|
package/docs/agents/targets.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Targets
|
|
3
|
-
description: The projects this toolkit installed into, where each answer comes from,
|
|
3
|
+
description: The projects this toolkit installed into, where each answer comes from, the cross-target pull request read, and the releases since a target's install
|
|
4
4
|
kind: reference
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -12,6 +12,13 @@ kind: reference
|
|
|
12
12
|
- Each stamping sync records its target in `$XDG_STATE_HOME/canon/targets.json`, and no row is removed
|
|
13
13
|
- A sweep finds installs the index never held
|
|
14
14
|
|
|
15
|
+
`canon changelog` prints the releases a target has not seen, from the `CHANGELOG.md` the installed package ships. It reads no registry and needs no `gh`.
|
|
16
|
+
|
|
17
|
+
- The start defaults to the oldest `version` across the target's stamp domains. `--since <version>` overrides it, and `--root <path>` names the target.
|
|
18
|
+
- `--json` carries `since`, `running`, and `entries`, each with its `version` and raw `markdown`, oldest first
|
|
19
|
+
- A start equal to the running version is the one empty range, since that target is current
|
|
20
|
+
- A refusal names its `reason` instead of printing an empty range, being `no-stamp`, `no-changelog`, or `unknown-version`. The last covers a pre-release and a start newer than the binary.
|
|
21
|
+
|
|
15
22
|
## Output
|
|
16
23
|
|
|
17
24
|
- `bound` on a sweep names the roots walked, the depth, and what it skipped. Read it before trusting a count.
|
|
@@ -6,7 +6,7 @@ category: Agent surface
|
|
|
6
6
|
|
|
7
7
|
# Target migrations
|
|
8
8
|
|
|
9
|
-
Each section is a move a project runs once, when it was scaffolded before the change it names. Run `canon upgrade` first. The recurring sync that follows is in [target sync](sync.md).
|
|
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
10
|
|
|
11
11
|
## Move the records first, once
|
|
12
12
|
|
|
@@ -180,3 +180,10 @@ A project holding `.claude/rules/canon/ui/a11y.md` adds the new rule alone:
|
|
|
180
180
|
```bash
|
|
181
181
|
canon gov install --add keyboard --yes .
|
|
182
182
|
```
|
|
183
|
+
|
|
184
|
+
## Group a long paths section in `CLAUDE.md`, once
|
|
185
|
+
|
|
186
|
+
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.
|
|
187
|
+
|
|
188
|
+
- Regroup the section by hand under H3s, each opening on a one-sentence lead.
|
|
189
|
+
- List `CLAUDE.md` in `canon/config/markdown.toml` to gate it from then on.
|
package/docs/target/projects.md
CHANGED
|
@@ -49,7 +49,7 @@ The chain runs these phases:
|
|
|
49
49
|
|
|
50
50
|
An existing project goes to `canon:target-check` first. For the Claude layer alone, run `canon claude init` and then `canon:target-setup indexes`. Run `canon:target-setup verify deep` by hand for the heavier smoke and screenshot pass.
|
|
51
51
|
|
|
52
|
-
Keep the `## Scripts` table in `canon/context/development/local-loop.md` current as scripts are added. A split entry keeps it in `
|
|
52
|
+
Keep the `## Scripts` table in `canon/context/development/local-loop.md` current as scripts are added. A split entry keeps it in any entry directly under `development/`, grouping a long set into short tables under lead sentences. <!-- audit-ignore-citations: canon/context/development/local-loop.md -->
|
|
53
53
|
|
|
54
54
|
### From scaffold to first feature
|
|
55
55
|
|
|
@@ -60,4 +60,4 @@ Merge order matters only when two branches touch the same file. Expect to rebase
|
|
|
60
60
|
|
|
61
61
|
`canon:task-board` creates a task file, and `canon:task-complete` marks outcomes. The `post-merge` hook archives the task a merged pull request closed, with its plan, and a `post-rewrite` hook does the same for a rebase pull. Both archive a closed task whose merge carried a PR number, and a slice merged before then moves only its own plan.
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
The hook also deletes the member branches and worktrees a merged fan-in integrator names, and `canon worktrees retire-members --pull-request <n>` does it for a merge the hook missed. `CANON_SKIP_RECLAIM` turns off that step and the reclaim, and `CANON_SKIP_UPGRADE` and `CANON_SKIP_PLUGIN_UPDATE` turn off the other two.
|
|
@@ -35,7 +35,7 @@ Groups run in the order a project meets them. Every name `canon claude skills li
|
|
|
35
35
|
|
|
36
36
|
| Skill | When to use |
|
|
37
37
|
| ----------------------------- | -------------------------------------------------- |
|
|
38
|
-
| `canon:session-worktree` |
|
|
38
|
+
| `canon:session-worktree` | Before building a plan in this session |
|
|
39
39
|
| `canon:auto-ship` | After plan approval, to chain implement through PR |
|
|
40
40
|
| `canon:test-first` | Before implementing a planned change |
|
|
41
41
|
| `canon:test-craft` | Writing or changing a test |
|
|
@@ -64,6 +64,7 @@ Groups run in the order a project meets them. Every name `canon claude skills li
|
|
|
64
64
|
| --------------------- | --------------------------------------------- |
|
|
65
65
|
| `canon:git-ship` | To run the post-feature chain through open PR |
|
|
66
66
|
| `canon:context-fold` | The diff left a context entry untrue |
|
|
67
|
+
| `canon:design-record` | A picked design needs landing in the record |
|
|
67
68
|
| `canon:task-complete` | After the fold, to mark shipped outcomes |
|
|
68
69
|
| `canon:docs-sync` | A change left `README.md` or `docs/` stale |
|
|
69
70
|
| `canon:git-stage` | Staged changes span several concerns |
|
|
@@ -20,7 +20,8 @@ These skills cover the tier:
|
|
|
20
20
|
|
|
21
21
|
- `canon:ui-checklist`, `canon:ux-audit`, and `canon:ux-measure` review and measure a UI
|
|
22
22
|
- `canon:pick-by-eye` drafts candidates as frames on the canvas and takes your pick
|
|
23
|
-
- `canon:canvas-board` works a direction out as HTML frames you edit in the browser
|
|
23
|
+
- `canon:canvas-board` works a direction out as HTML frames you edit in the browser, and hands the pick on
|
|
24
|
+
- `canon:design-record` lands a pick in `canon/design/` and carries its values into `canon/DESIGN.md`
|
|
24
25
|
- `canon:design-identity` drafts a logo mark and social card
|
|
25
26
|
|
|
26
27
|
Pick tier 0 when the UI is not the core product, decisions fit in sentences, and stakeholders review code.
|
package/governance/detect.toml
CHANGED
|
@@ -22,6 +22,12 @@ The field is continuous integration on GitHub Actions, and the authority is GitH
|
|
|
22
22
|
| Skip a fork's pull request | [Workflow syntax, permissions](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax): "if the workflow was triggered by a pull request event other than `pull_request_target` from a forked repository, and the Send write tokens to workflows from pull requests setting is not selected, the permissions are adjusted to change any write permissions to read only" | 2026-10-18 |
|
|
23
23
|
| Never commit back to the trunk | House choice: a workflow pushing onto the trunk lands a change no pull request carried and no reviewer read | 2026-10-18 |
|
|
24
24
|
|
|
25
|
+
## Steps
|
|
26
|
+
|
|
27
|
+
| Bullet | Source | Read |
|
|
28
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
29
|
+
| Long steps call a script, 5 max | House choice, from [issue 2531](https://github.com/erclx/canon/issues/2531): a 33-line publish check copied out of a workflow carried a program no laptop or shellcheck could run. [Workflow syntax, run](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax) documents `run` as a command run in the shell, with no limit on its size. One guard stays inline, since a placeholder or secrets guard of three lines gains nothing from a script | 2026-10-21 |
|
|
30
|
+
|
|
25
31
|
## Token scopes
|
|
26
32
|
|
|
27
33
|
| Bullet | Source | Read |
|
|
@@ -9,12 +9,14 @@ The rule states canon's own design handoff, generalized from Frameboard's projec
|
|
|
9
9
|
|
|
10
10
|
## Where the design lives
|
|
11
11
|
|
|
12
|
-
| Bullet | Source
|
|
13
|
-
| ------------------------------ |
|
|
14
|
-
| Build against the tracked copy | House choice: a canvas changes under the build with no history, so a reviewer cannot tell which version a surface was built against
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Build against the tracked copy | House choice: a canvas changes under the build with no history, so a reviewer cannot tell which version a surface was built against | 2026-10-18 |
|
|
15
|
+
| Load the keeper skill | House choice: the rule governs an edit and carries no procedure, so a session without the skill copies frames in by hand or skips the index row | 2026-10-21 |
|
|
16
|
+
| Land a design with its build | House choice: a design merged as its own pull request waits on a review that reads a picture with no build beside it, and the pick drifts before anything uses it | 2026-10-21 |
|
|
17
|
+
| No hand edits | House choice: a reference edited by hand drifts from its canvas source, and the next sync overwrites the edit or reports it as a change nobody made | 2026-10-18 |
|
|
18
|
+
| Index row before the sync | House choice: the sync writes only the surfaces the index names, so a missing row drops the new surface's reference | 2026-10-21 |
|
|
19
|
+
| Reference lifetime | House choice: a reference kept past its surface misleads the next build, and one redrawn without a design change hides a departure the review should see | 2026-10-21 |
|
|
18
20
|
|
|
19
21
|
## Building against a reference
|
|
20
22
|
|
|
@@ -31,12 +33,6 @@ The rule states canon's own design handoff, generalized from Frameboard's projec
|
|
|
31
33
|
| Name each departure | House choice: an unnamed departure reads as an accident, and the next build against the same reference repeats or reverts it | 2026-10-18 |
|
|
32
34
|
| Unexplained departure is a finding | House choice: without the finding, an unexplained departure merges as the new design with no one having chosen it | 2026-10-18 |
|
|
33
35
|
|
|
34
|
-
## Naming
|
|
35
|
-
|
|
36
|
-
| Bullet | Source | Read |
|
|
37
|
-
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
38
|
-
| One folder per app part | House choice: a fixed folder and theme suffix let the sync and a reviewer find a surface's pair of references without reading an index | 2026-10-18 |
|
|
39
|
-
|
|
40
36
|
## Declined
|
|
41
37
|
|
|
42
38
|
The rewrite kept every bullet. Each was already under 200 characters and states a step a reviewer can check against the pull request.
|
|
@@ -17,6 +17,10 @@ paths:
|
|
|
17
17
|
- Skip a pull request from a fork with `github.event.pull_request.head.repo.full_name == github.repository`, since the default token cannot push to a fork's branch.
|
|
18
18
|
- Never commit back to the trunk. A commit with no pull request and no reviewer needs an operator's decision, not a workflow's.
|
|
19
19
|
|
|
20
|
+
## Steps
|
|
21
|
+
|
|
22
|
+
- Keep an inline `run:` step to five lines at most with one guard allowed and no other branching, loop, trap, or function, and call a script for anything longer, per the `canon:ci-workflow` skill.
|
|
23
|
+
|
|
20
24
|
## Token scopes
|
|
21
25
|
|
|
22
26
|
- In a workflow with more than one job, add a write scope to the job that needs it, never to the workflow-level block, so every other job keeps the read-only token.
|
|
@@ -17,9 +17,11 @@ paths:
|
|
|
17
17
|
## Where the design lives
|
|
18
18
|
|
|
19
19
|
- Build UI against `canon/design/`, never against the canvas directly.
|
|
20
|
-
-
|
|
20
|
+
- Load the `canon:design-record` skill before adding, redrawing, or removing a reference under `canon/design/`, and report it rather than proceeding silently when it does not resolve.
|
|
21
|
+
- Land a picked or changed design in `canon/design/` through the project's design sync, in the first commit of the branch that builds against it or with its design round, never as its own pull request.
|
|
21
22
|
- Never copy, edit, or delete a reference file under `canon/design/` by hand.
|
|
22
|
-
- Add a new surface's row to the index in `canon/design/` before running the sync
|
|
23
|
+
- Add a new surface's row to the index in `canon/design/` before running the sync.
|
|
24
|
+
- Keep a reference while its surface exists, redraw it only on an intentional design change, and remove it with its index row in the change that removes the surface.
|
|
23
25
|
|
|
24
26
|
## Building against a reference
|
|
25
27
|
|
|
@@ -31,7 +33,3 @@ paths:
|
|
|
31
33
|
- Put each changed surface's reference image beside its built capture, light and dark, in the pull request description.
|
|
32
34
|
- Name each place the build departs from its reference, with its reason, in the pull request description.
|
|
33
35
|
- Report an unexplained departure from a reference as a review finding.
|
|
34
|
-
|
|
35
|
-
## Naming
|
|
36
|
-
|
|
37
|
-
- Give each app part one plain kebab-case folder under `canon/design/`, and name each file `<name>-dark` or `<name>-light`.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erclx/canon",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.51.0",
|
|
5
5
|
"description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"tooling",
|
|
15
15
|
"claude",
|
|
16
16
|
"docs",
|
|
17
|
+
"CHANGELOG.md",
|
|
17
18
|
"tsconfig.json",
|
|
18
19
|
"assets/brand/mark.svg",
|
|
19
20
|
"!scripts/eval",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fails when a Playwright browser named in the arguments is missing from the
|
|
3
|
+
# cache, called after the cache restore in pr-visual-checks.yml. A cache hit
|
|
4
|
+
# skips the install step, so a restore that came back empty would otherwise
|
|
5
|
+
# surface later as a launch error.
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
for browser in "$@"; do
|
|
9
|
+
if ! compgen -G "$HOME/.cache/ms-playwright/${browser}-*" >/dev/null; then
|
|
10
|
+
echo "Missing Playwright browser: ${browser}" >&2
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
done
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Deletes the Cloudflare Pages preview deployments minted for one branch, called
|
|
3
|
+
# by the cleanup-preview job in deploy-site.yml with the Pages project name and
|
|
4
|
+
# the branch. CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID come from the step
|
|
5
|
+
# env.
|
|
6
|
+
#
|
|
7
|
+
# Deployments are listed through the REST API rather than wrangler, whose
|
|
8
|
+
# listing carries no branch filter. `force=true` is what lets a deployment
|
|
9
|
+
# still holding its branch alias be deleted.
|
|
10
|
+
#
|
|
11
|
+
# tooling/cloudflare/configs/scripts/delete-previews.sh is the seeded copy,
|
|
12
|
+
# which adds a skip for an absent secret. Keep the two otherwise the same.
|
|
13
|
+
set -e
|
|
14
|
+
|
|
15
|
+
PROJECT_NAME=$1
|
|
16
|
+
BRANCH=$2
|
|
17
|
+
|
|
18
|
+
api="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/$PROJECT_NAME/deployments"
|
|
19
|
+
auth="Authorization: Bearer $CLOUDFLARE_API_TOKEN"
|
|
20
|
+
page=1
|
|
21
|
+
ids=""
|
|
22
|
+
while :; do
|
|
23
|
+
listing=$(curl -fsS -H "$auth" "$api?env=preview&per_page=25&page=$page")
|
|
24
|
+
[ "$(echo "$listing" | jq '.result | length')" -eq 0 ] && break
|
|
25
|
+
ids="$ids $(echo "$listing" | jq -r --arg branch "$BRANCH" '.result[] | select(.deployment_trigger.metadata.branch == $branch) | .id')"
|
|
26
|
+
page=$((page + 1))
|
|
27
|
+
done
|
|
28
|
+
deleted=0
|
|
29
|
+
for id in $ids; do
|
|
30
|
+
curl -fsS -X DELETE -H "$auth" "$api/$id?force=true" >/dev/null
|
|
31
|
+
echo "Deleted $id"
|
|
32
|
+
deleted=$((deleted + 1))
|
|
33
|
+
done
|
|
34
|
+
echo "Deleted $deleted preview deployments for $BRANCH."
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Reports whether the newest GitHub release reached npm, called by the
|
|
3
|
+
# publish-check job in release-please.yml. The step passes GH_TOKEN and GH_REPO
|
|
4
|
+
# for gh. A release inside the publish job's 15-minute window passes, since its
|
|
5
|
+
# publish may still be running.
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
package=@erclx/canon
|
|
9
|
+
|
|
10
|
+
if ! release=$(gh release view --json tagName,publishedAt --jq '"\(.tagName) \(.publishedAt)"') || [ -z "$release" ]; then
|
|
11
|
+
echo "::error::Could not read the newest GitHub release, so whether it reached npm is unknown."
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
read -r tag published <<<"$release"
|
|
15
|
+
age=$(($(date +%s) - $(date -d "$published" +%s)))
|
|
16
|
+
if [ "$age" -lt 900 ]; then
|
|
17
|
+
echo "$tag is ${age}s old, inside the 15-minute window its publish job runs in."
|
|
18
|
+
exit 0
|
|
19
|
+
fi
|
|
20
|
+
# A missing version is an E404, so read the whole list to tell it from an outage
|
|
21
|
+
if ! versions=$(npm view "$package" versions --json); then
|
|
22
|
+
echo "::error::Could not read $package from npm, so whether $tag reached it is unknown."
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
if ! jq -e --arg v "${tag#v}" '[.] | flatten | index($v) != null' <<<"$versions" >/dev/null; then
|
|
26
|
+
echo "::error::$tag has a GitHub release but npm does not serve it. Publish it with: gh workflow run release-please.yml -f tag=$tag"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
echo "$tag is on npm."
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Commits the regenerated capture frames onto the refresh branch and opens or
|
|
3
|
+
# updates its pull request, called by refresh-capture-frames.yml after the
|
|
4
|
+
# capture steps. The step holds GH_TOKEN and HUSKY, since the token scope is
|
|
5
|
+
# read off the workflow.
|
|
6
|
+
#
|
|
7
|
+
# Hooks stay off, since pre-push runs a check whose format step needs shfmt,
|
|
8
|
+
# which the runner lacks. verify.yml checks the pull request.
|
|
9
|
+
#
|
|
10
|
+
# The frames commit where they rendered, then only the files that commit wrote
|
|
11
|
+
# or dropped carry onto main fetched now, so a merge landing mid-run neither
|
|
12
|
+
# leaves the branch on an older base nor loses a frame it moved. The pull
|
|
13
|
+
# request resolves by number, since every merged refresh leaves a closed one on
|
|
14
|
+
# the same head.
|
|
15
|
+
set -e
|
|
16
|
+
|
|
17
|
+
branch=chore/refresh-capture-frames
|
|
18
|
+
title="chore(web): refresh capture frames"
|
|
19
|
+
|
|
20
|
+
if [ -z "$(git status --porcelain assets/captures assets/frames assets/evidence)" ]; then
|
|
21
|
+
echo "Frames are current, nothing to refresh."
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
git config user.name "github-actions[bot]"
|
|
25
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com" # canon-allow-reference: the bot account id, not a commit
|
|
26
|
+
git add assets/captures assets/frames assets/evidence
|
|
27
|
+
git commit -m "$title"
|
|
28
|
+
rendered=$(git rev-parse HEAD)
|
|
29
|
+
written=$(git diff --name-only --no-renames --diff-filter=d HEAD~1 HEAD)
|
|
30
|
+
dropped=$(git diff --name-only --no-renames --diff-filter=D HEAD~1 HEAD)
|
|
31
|
+
git fetch --depth=1 origin +refs/heads/main:refs/remotes/origin/main
|
|
32
|
+
git checkout -B "$branch" origin/main
|
|
33
|
+
if [ -n "$written" ]; then
|
|
34
|
+
printf '%s\n' "$written" | xargs -d '\n' git checkout "$rendered" --
|
|
35
|
+
fi
|
|
36
|
+
if [ -n "$dropped" ]; then
|
|
37
|
+
printf '%s\n' "$dropped" | xargs -d '\n' git rm -q --ignore-unmatch --
|
|
38
|
+
fi
|
|
39
|
+
if git diff --cached --quiet -- assets/captures assets/frames assets/evidence; then
|
|
40
|
+
echo "Main already carries these frames, nothing to refresh."
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
git commit -m "$title"
|
|
44
|
+
git push --force origin "$branch"
|
|
45
|
+
number=$(gh pr list --head "$branch" --state open --json number --jq '.[0].number')
|
|
46
|
+
if [ -z "$number" ]; then
|
|
47
|
+
gh pr create \
|
|
48
|
+
--base main \
|
|
49
|
+
--head "$branch" \
|
|
50
|
+
--title "$title" \
|
|
51
|
+
--body "## Summary
|
|
52
|
+
|
|
53
|
+
- Refreshes the capture frames from main so their counts and tokens stay current.
|
|
54
|
+
|
|
55
|
+
## Key changes
|
|
56
|
+
|
|
57
|
+
- Regenerates the count and token bearing frames, their images, and their stamps from main.
|
|
58
|
+
- Recaptures the home page baseline under assets/evidence/home.
|
|
59
|
+
- Merges itself by squash once its required check passes."
|
|
60
|
+
number=$(gh pr list --head "$branch" --state open --json number --jq '.[0].number')
|
|
61
|
+
fi
|
|
62
|
+
# Every run, so a force-push from a later run leaves it queued either way
|
|
63
|
+
gh pr merge "$number" --auto --squash
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Writes the step summary saying whether the captured landing page frames match
|
|
3
|
+
# the committed baseline, called by the capture job in pr-visual-checks.yml. A
|
|
4
|
+
# moved capture is expected when a catalog the page counts has moved, so it
|
|
5
|
+
# reports and never fails.
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
if git diff --quiet -- assets/evidence/home; then
|
|
9
|
+
echo "The captured frames match the committed baseline." >>"$GITHUB_STEP_SUMMARY"
|
|
10
|
+
exit 0
|
|
11
|
+
fi
|
|
12
|
+
{
|
|
13
|
+
echo "### The landing page capture moved"
|
|
14
|
+
echo
|
|
15
|
+
echo "These frames differ from the committed baseline. That is expected"
|
|
16
|
+
echo "when a catalog this page counts has moved, and it is not a failure."
|
|
17
|
+
echo
|
|
18
|
+
echo '```'
|
|
19
|
+
git diff --stat -- assets/evidence/home
|
|
20
|
+
echo '```'
|
|
21
|
+
echo
|
|
22
|
+
echo "Download the \`landing-page-capture\` artifact to view them."
|
|
23
|
+
} >>"$GITHUB_STEP_SUMMARY"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Opens, updates, or closes one issue per stack from the outcomes stack-verify.yml
|
|
3
|
+
# recorded under results/<stack>, called by its report job. Takes the stacks as
|
|
4
|
+
# arguments and reads GH_TOKEN, GH_REPO, and RUN_URL from the step env.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
for stack in "$@"; do
|
|
8
|
+
title="stack-verify: $stack"
|
|
9
|
+
# A job that died before recording counts as a failure
|
|
10
|
+
outcome=failure
|
|
11
|
+
[ -f "results/$stack" ] && outcome=$(cat "results/$stack")
|
|
12
|
+
number=$(gh issue list --state open --search "\"$title\" in:title" --json number,title \
|
|
13
|
+
--jq "[.[] | select(.title == \"$title\")][0].number // empty")
|
|
14
|
+
if [ "$outcome" = success ]; then
|
|
15
|
+
if [ -n "$number" ]; then
|
|
16
|
+
gh issue close "$number" --comment "Passing again in $RUN_URL"
|
|
17
|
+
fi
|
|
18
|
+
elif [ -n "$number" ]; then
|
|
19
|
+
gh issue comment "$number" --body "Still failing in $RUN_URL"
|
|
20
|
+
else
|
|
21
|
+
gh issue create --title "$title" \
|
|
22
|
+
--body "\`canon tooling verify $stack\` failed in $RUN_URL. The issue closes itself when a later run passes."
|
|
23
|
+
fi
|
|
24
|
+
done
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Checks that the npm publish credential is set and authenticates, so a release
|
|
3
|
+
# is never cut it cannot publish. release-please.yml passes NPM_TOKEN from the
|
|
4
|
+
# step env, which keeps the secret named in the workflow.
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
if [ -z "$NPM_TOKEN" ]; then
|
|
8
|
+
echo "::error::NPM_TOKEN is not set. Add the secret before a release can be cut."
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
if ! curl -sf -H "Authorization: Bearer $NPM_TOKEN" https://registry.npmjs.org/-/whoami >/dev/null; then
|
|
12
|
+
echo "::error::NPM_TOKEN did not authenticate. It is likely expired or revoked."
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|