@erclx/canon 5.22.0 → 5.24.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.
Files changed (44) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/canon-operator/REQUIREMENT.md +1 -0
  3. package/claude/skills/canon-operator/SKILL.md +5 -1
  4. package/claude/skills/draft-doc/REQUIREMENT.md +1 -0
  5. package/claude/skills/draft-doc/references/readme/agent-facing.md +59 -0
  6. package/claude/skills/draft-doc/references/readme/application.md +66 -0
  7. package/claude/skills/draft-doc/references/readme/cli.md +64 -0
  8. package/claude/skills/draft-doc/references/readme/library.md +57 -0
  9. package/claude/skills/draft-doc/references/readme/plugin.md +66 -0
  10. package/claude/skills/draft-doc/references/readme.md +33 -4
  11. package/claude/skills/target-check/SKILL.md +9 -9
  12. package/claude/skills/target-check/references/domains.md +11 -5
  13. package/docs/agents/architecture-record.md +2 -2
  14. package/docs/agents/canvas.md +5 -1
  15. package/docs/agents/index.md +1 -1
  16. package/docs/agents/tasks-validate.md +4 -2
  17. package/docs/agents/tasks.md +1 -1
  18. package/docs/target/projects.md +1 -1
  19. package/docs/workflow/visual-design-workflow.md +1 -1
  20. package/package.json +1 -1
  21. package/src/canvas/client/inspector.tsx +46 -4
  22. package/src/canvas/client/shell.css +7 -0
  23. package/src/canvas/tokens.ts +9 -1
  24. package/src/commands/canvas.ts +29 -0
  25. package/src/commands/tasks.ts +16 -0
  26. package/src/design/budget.ts +149 -0
  27. package/src/design/document.ts +22 -10
  28. package/src/design/render.ts +11 -1
  29. package/src/design/tokens.ts +40 -61
  30. package/src/gate/measures.ts +49 -0
  31. package/src/gate/stages.ts +5 -1
  32. package/src/tasks/validate.ts +65 -0
  33. package/standards/architecture.md +2 -2
  34. package/standards/board.md +2 -0
  35. package/standards/design.md +13 -3
  36. package/standards/requirements.md +3 -1
  37. package/tooling/claude/seeds/.claude/hooks/path-form.sh +2 -2
  38. package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +2 -2
  39. package/tooling/claude/seeds/.claude/hooks/{tasks-index.sh → records-index.sh} +42 -35
  40. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +1 -1
  41. package/tooling/claude/seeds/.claude/settings.json +1 -5
  42. package/tooling/claude/seeds/canon/ARCHITECTURE.md +1 -1
  43. package/tooling/claude/seeds/canon/REQUIREMENTS.md +2 -0
  44. package/tooling/claude/seeds/.claude/hooks/memory-index.sh +0 -95
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
4
- "version": "5.22.0",
4
+ "version": "5.24.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -27,6 +27,7 @@ The last failure is a section no route reaches. `## Route` maps an intent or a d
27
27
  - Run the CLI non-interactively and report the command run, what changed, and the full path of anything written
28
28
  - Read the overwrite contract from the body before any install or sync, and name each surface it lists as overwritten before running one
29
29
  - Read the reverse walk beside the forward sections, so a folder the toolkit stopped shipping reaches the user
30
+ - Read `unmovedSurfaces` from the report itself, since the drift status ignores it by design, and name `canon migrate surface-roots` without running either form
30
31
  - Name an unmigrated domain's root and install path for the user to move themselves, since no skill covers the move
31
32
  - Route a measurement intent to the audit offers from the route table itself, rather than leaving the section reachable only by reading past the table
32
33
  - Rank a lifecycle row against the audit offers where the route table itself states it, so a session acting on the lifecycle row reads the ranking without opening the section being ranked
@@ -22,10 +22,11 @@ Load a domain doc with `canon docs <topic>` only when the intent touches that do
22
22
 
23
23
  Run `canon sync --check . --json` before routing. It reports what a target is behind on across every surface, so the intent comes from the project state rather than from the user having to know it already. Skip only when the user named a single operation to run.
24
24
 
25
- Read seven fields off the report and carry each to `## Route`:
25
+ Read eight fields off the report and carry each to `## Route`:
26
26
 
27
27
  - `unmigrated`: a domain sitting at the root layout with nothing under `.claude/`. The most urgent finding, because that domain reports no drift of its own while being entirely behind
28
28
  - `superseded`: a file a newer seed folder replaced. No command moves it, since the content is the project's own, so this routes to `canon:target-check` for the full per-domain reading rather than ending at the report
29
+ - `unmovedSurfaces`: a surface held only at its `.claude/` spelling, each entry carrying `rel` and `movesTo`. Every other field reads clean for such a target, and the report's drift status ignores it by design, so read the array itself rather than an exit status
29
30
  - `seeds`: entries are `matching`, `stale`, `drifted`, or `missing`. Anything but `matching` needs the seed handoff
30
31
  - `domains[].entries`: per-file `stale`, `customized`, `stranded`, and `orphaned` as before, plus `retired` for a rule the toolkit no longer ships, which the next sync deletes
31
32
  - `reverse`: what the target holds that no live catalog claims. `unclaimed` lists folders at roots the toolkit stopped shipping, each with an `attribution` of `dropped`, `unattributed`, or `project`. Act on the first two and leave `project` alone, which history proved the project owns
@@ -34,6 +35,8 @@ Read seven fields off the report and carry each to `## Route`:
34
35
 
35
36
  State what the report found in one line per finding before acting on any of it.
36
37
 
38
+ An `unmovedSurfaces` key the report does not carry at all is likewise a CLI predating the field rather than a target with nothing to move. Say the section was unread and name the installed CLI version as the cause.
39
+
37
40
  A `reverse` key the report does not carry at all is a CLI predating the field rather than a target with nothing unclaimed. Say the walk did not run and name the CLI version as the cause. Reading the absent key as an empty answer reports a clean target to a project nobody has looked at, and answering it from a filesystem walk of your own is worse, since that reaches a verdict the attribution the field carries is the only thing entitled to make.
38
41
 
39
42
  ## Route
@@ -44,6 +47,7 @@ The two can name different rows, and a reply answers both rather than picking on
44
47
 
45
48
  - A domain in `unmigrated`: name it, the root path, and the install path. No command moves it, so the user runs the relocation themselves
46
49
  - Anything in `superseded`: name which files and what replaced them, then hand off to `canon:target-check`, which reports and never moves or deletes the file
50
+ - Anything in `unmovedSurfaces`: name `canon migrate surface-roots`, which only reports its plan until `--write` is passed, with each entry's `rel` and `movesTo`, and run neither form, since the move rewrites tracked paths
47
51
  - A folder in `unclaimed`: name it and the attribution it carries. No command moves it and the content may be the project's own, so the decision is the user's
48
52
 
49
53
  - First-time scaffold of a fresh project: hand off to `target-setup`
@@ -19,6 +19,7 @@ The same procedure ran as five skills, one per kind, each restating the read, th
19
19
  - Check the catalog by exact name and by title and description, since a subject can be covered under a name the request does not guess
20
20
  - Confirm the resolved path, the kind's detections, and the full content with the user before writing, since placement and detection are judgment calls with no diff to preview them against
21
21
  - Regenerate the catalog of the folder the document landed in, where the kind keeps one, so the next run's collision check sees it
22
+ - Draft a README from the template for every detected project type and combine them, never the closest one, since a project that is a CLI and a plugin owes its reader both
22
23
  - Carry each kind's own musts in its reference: the root check for context and wireframes, tier detection and the two modes for wireframes, both placement tests and a fetched source for the wiki, and project detection and rendered-value badges for the readme
23
24
 
24
25
  ## Must not
@@ -0,0 +1,59 @@
1
+ ![<Project name> logo](<committed logo path>)
2
+
3
+ # <Project name>
4
+
5
+ [![<build status>](<badge image URL>)](<run history URL>)
6
+ [![<license>](<badge image URL>)](<license URL>)
7
+
8
+ <One-line claim taken from the repository description.>
9
+
10
+ <Two or three sentences expanding the claim: the conventions or tooling an agent working here picks up, and what a session gains from them.>
11
+
12
+ ## Installation
13
+
14
+ ### Requirements
15
+
16
+ - <Agent tool and minimum version>
17
+ - <Runtime the listing command needs>
18
+
19
+ ### Set up
20
+
21
+ ```bash
22
+ <clone command>
23
+ <setup command, when a session needs one>
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### What an agent loads first
29
+
30
+ `<file an agent reads at session start>` carries <what that file holds>. It points at <the files it imports or links>.
31
+
32
+ ### List what the project exposes
33
+
34
+ ```bash
35
+ <command that lists skills, rules, or commands>
36
+ ```
37
+
38
+ ### Where things live
39
+
40
+ | Path | Holds |
41
+ | ---------- | ------------ |
42
+ | `<folder>` | <What it is> |
43
+
44
+ ## Documentation
45
+
46
+ <Link to the full conventions reference.>
47
+
48
+ ## Support
49
+
50
+ - <Where to open an issue>
51
+ - <Where to ask a question>
52
+
53
+ ## Contributing
54
+
55
+ See the [contributing guidelines](<CONTRIBUTING.md path>).
56
+
57
+ ## License
58
+
59
+ [<License name>](<LICENSE path>)
@@ -0,0 +1,66 @@
1
+ ![<Project name> logo](<committed logo path>)
2
+
3
+ # <Project name>
4
+
5
+ [![<build status>](<badge image URL>)](<run history URL>)
6
+ [![<license>](<badge image URL>)](<license URL>)
7
+
8
+ <One-line claim taken from the manifest or repository description.>
9
+
10
+ [<Live site name>](<live URL>)
11
+
12
+ ![<What the screenshot depicts>](<committed image path>)
13
+
14
+ <Two or three sentences expanding the claim: who the product is for, then what they do with it.>
15
+
16
+ ## Features
17
+
18
+ - <Benefit the user gets>
19
+ - <Benefit the user gets>
20
+
21
+ ## Installation
22
+
23
+ ### Requirements
24
+
25
+ - <Runtime and version>
26
+ - <Service the app depends on>
27
+
28
+ ### Set up
29
+
30
+ ```bash
31
+ <clone command>
32
+ <dependency install command>
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ### Run locally
38
+
39
+ ```bash
40
+ <dev server command>
41
+ ```
42
+
43
+ <Address the app serves on.>
44
+
45
+ ### Configuration
46
+
47
+ | Variable | Purpose | Default |
48
+ | -------- | ------------------ | ----------- |
49
+ | `<NAME>` | <What it controls> | `<default>` |
50
+
51
+ ### Deploy
52
+
53
+ <Where the app deploys and the command or workflow that does it.>
54
+
55
+ ## Support
56
+
57
+ - <Where to open an issue>
58
+ - <Where to ask a question>
59
+
60
+ ## Contributing
61
+
62
+ See the [contributing guidelines](<CONTRIBUTING.md path>).
63
+
64
+ ## License
65
+
66
+ [<License name>](<LICENSE path>)
@@ -0,0 +1,64 @@
1
+ ![<Project name> logo](<committed logo path>)
2
+
3
+ # <Project name>
4
+
5
+ [![<package version>](<badge image URL>)](<registry page URL>)
6
+ [![<build status>](<badge image URL>)](<run history URL>)
7
+ [![<license>](<badge image URL>)](<license URL>)
8
+
9
+ <One-line claim taken from the manifest description.>
10
+
11
+ <Two or three sentences expanding the claim: the task a reader repeats by hand, then what the command does about it.>
12
+
13
+ ## Features
14
+
15
+ - <Benefit the reader gets>
16
+ - <Benefit the reader gets>
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ <global install or one-shot run command>
22
+ ```
23
+
24
+ <Runtime or platform requirement, when one exists.>
25
+
26
+ ## Usage
27
+
28
+ ### Run a command
29
+
30
+ ```bash
31
+ <command> <argument>
32
+ ```
33
+
34
+ ### Flags
35
+
36
+ ```bash
37
+ <command> --<flag> <value>
38
+ <command> --<flag>
39
+ ```
40
+
41
+ | Flag | Effect |
42
+ | ---------- | -------------- |
43
+ | `--<flag>` | <What it does> |
44
+
45
+ ### Exit codes
46
+
47
+ <What each non-zero exit means, when a script depends on it.>
48
+
49
+ ## Documentation
50
+
51
+ <Link to the full command reference.>
52
+
53
+ ## Support
54
+
55
+ - <Where to open an issue>
56
+ - <Where to ask a question>
57
+
58
+ ## Contributing
59
+
60
+ See the [contributing guidelines](<CONTRIBUTING.md path>).
61
+
62
+ ## License
63
+
64
+ [<License name>](<LICENSE path>)
@@ -0,0 +1,57 @@
1
+ ![<Project name> logo](<committed logo path>)
2
+
3
+ # <Project name>
4
+
5
+ [![<package version>](<badge image URL>)](<registry page URL>)
6
+ [![<build status>](<badge image URL>)](<run history URL>)
7
+ [![<license>](<badge image URL>)](<license URL>)
8
+
9
+ <One-line claim taken from the manifest description.>
10
+
11
+ [<Live site name>](<live URL>)
12
+
13
+ ![<What the screenshot depicts>](<committed image path>)
14
+
15
+ <Two or three sentences expanding the claim: the problem a reader recognizes, then what the library does about it.>
16
+
17
+ ## Features
18
+
19
+ - <Benefit the reader gets>
20
+ - <Benefit the reader gets>
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ <install command for the package manager the project uses>
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ### Quickstart
31
+
32
+ ```<language>
33
+ <Import and one call that shows the core behavior>
34
+ ```
35
+
36
+ ### <Second common task>
37
+
38
+ ```<language>
39
+ <Snippet for the task>
40
+ ```
41
+
42
+ ## Documentation
43
+
44
+ <Link to the full API reference.>
45
+
46
+ ## Support
47
+
48
+ - <Where to open an issue>
49
+ - <Where to ask a question>
50
+
51
+ ## Contributing
52
+
53
+ See the [contributing guidelines](<CONTRIBUTING.md path>).
54
+
55
+ ## License
56
+
57
+ [<License name>](<LICENSE path>)
@@ -0,0 +1,66 @@
1
+ ![<Project name> logo](<committed logo path>)
2
+
3
+ # <Project name>
4
+
5
+ [![<build status>](<badge image URL>)](<run history URL>)
6
+ [![<license>](<badge image URL>)](<license URL>)
7
+
8
+ <One-line claim taken from the plugin manifest description.>
9
+
10
+ <Two or three sentences expanding the claim: the work a session repeats, then what the plugin adds to it.>
11
+
12
+ ## Features
13
+
14
+ - <Skill, command, or hook and what it gives the user>
15
+ - <Skill, command, or hook and what it gives the user>
16
+
17
+ ## Installation
18
+
19
+ ### <First channel name>
20
+
21
+ ```bash
22
+ <install command for this channel>
23
+ ```
24
+
25
+ ### <Second channel name>
26
+
27
+ ```bash
28
+ <install command for this channel>
29
+ ```
30
+
31
+ ### Update
32
+
33
+ ```bash
34
+ <update command>
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### Invoke a skill
40
+
41
+ ```text
42
+ /<plugin name>:<skill name> <argument>
43
+ ```
44
+
45
+ ### What the plugin adds
46
+
47
+ | Name | Kind | Purpose |
48
+ | -------- | ------------------------- | -------------- |
49
+ | `<name>` | <skill, command, or hook> | <What it does> |
50
+
51
+ ### Configuration
52
+
53
+ <Setting the plugin reads and where the user sets it, when one exists.>
54
+
55
+ ## Support
56
+
57
+ - <Where to open an issue>
58
+ - <Where to ask a question>
59
+
60
+ ## Contributing
61
+
62
+ See the [contributing guidelines](<CONTRIBUTING.md path>).
63
+
64
+ ## License
65
+
66
+ [<License name>](<LICENSE path>)
@@ -24,16 +24,44 @@ The target path is the placement. A README keeps no catalog, so there is no coll
24
24
 
25
25
  ## Detect
26
26
 
27
- - Read the project's manifest (`package.json`, `pyproject.toml`, `Cargo.toml`, or equivalent) for a `bin` field or CLI entry point, an installable package name, and its declared dependencies.
28
- - Check for a `claude/skills/` or `.claude/skills/` folder, a `plugin.json`, or a marketplace manifest, each naming an agent-facing or marketplace-distributed surface.
29
- - Note every type that applies rather than stopping at the first match, since a project is often several at once and the draft covers each one.
27
+ Read the project's manifest (`package.json`, `pyproject.toml`, `Cargo.toml`, or equivalent) and the tree, then test each type on its own signal. Note every type that matches rather than stopping at the first, since a project is often several at once.
28
+
29
+ - Library: an installable package name, counted only when no application, CLI, or plugin signal matches, since a named package is also what a site, a CLI, or a plugin carries. An agent-facing match leaves it in place, so a library with a `CLAUDE.md` still gets its quickstart.
30
+ - CLI: a `bin` field, a `[project.scripts]` table in `pyproject.toml`, a `[[bin]]` table in `Cargo.toml`, or a CLI entry point.
31
+ - Application: a site framework dependency, a site config file, or an app entry point.
32
+ - Agent-facing: a `.claude/skills/` folder, or a file an agent loads such as `CLAUDE.md`.
33
+ - Plugin: a `plugin.json` or a marketplace manifest.
34
+
35
+ A match on no type falls back to the standard's generic template. Say so in the preview, and never force the nearest type.
36
+
30
37
  - Check for a page. A dependency on a site framework or a site config file in the manifest or the tree marks a project as having one. Read a live URL separately, from a `homepage` field or a deploy config, since a project can have a page and no known URL.
31
38
  - A monorepo can carry a page in one package and a CLI in another, so both signals may fire. Report each rather than picking one.
32
39
  - Look for the mark and the screenshot among images the project already commits: scan the existing README for image references, then the asset and public folders.
33
40
 
41
+ ## Pick
42
+
43
+ Read the template for each detected type from `readme/`, beside this file:
44
+
45
+ - `readme/library.md`
46
+ - `readme/cli.md`
47
+ - `readme/application.md`
48
+ - `readme/agent-facing.md`
49
+ - `readme/plugin.md`
50
+
51
+ Each is a whole page of slots and structure, so fill it from the manifest and the tree rather than from memory. The standard still owns voice and content, and a template carries no rule of its own.
52
+
53
+ A project matching one type drafts from that template. A project matching several combines them:
54
+
55
+ - Keep one H1, one header block, and one description.
56
+ - Order the sections as the standard orders them.
57
+ - Add each extra type's usage or install subsection under the shared `## Installation` or `## Usage` H2, never as a second H2 of the same name.
58
+ - Drop a template section the page already carries rather than repeating it.
59
+
60
+ Between agent-facing and plugin alone, a repository with no plugin manifest takes only the agent-facing template, and a plugin that is not agent-facing in its own repository takes only the plugin template. A CLI or an application that also matches agent-facing keeps its own template beside it.
61
+
34
62
  ## Draft
35
63
 
36
- - Draft the H1, a 2-3 sentence description in plain text, then the required sections, then whichever optional sections and per-type content Detect found. A README takes no frontmatter.
64
+ - Start from the picked or combined template. Draft the H1, a 2-3 sentence description in plain text, then the required sections, then whichever optional sections and per-type content Detect found. A README takes no frontmatter.
37
65
  - Open the page with the standard's header block, filling each slot from what Detect found: mark, title, badges, a one-line claim from the manifest description, the live link, then the product screenshot.
38
66
  - Fill the screenshot slot only with an image the project already commits, referenced with alt text naming what it depicts. This kind cannot capture one. Omit the slot when no such image exists and say so in the preview. Never write a placeholder path or invent a mark.
39
67
  - Omit the link and the screenshot for a project with no page, and the mark for a project with none, without a note in the drafted page. A project with a page and no known URL gets the screenshot slot, and the Confirm step asks for the link.
@@ -48,6 +76,7 @@ Add these lines, then confirm them with the path:
48
76
 
49
77
  - `**Target:** <root | nested>`
50
78
  - `**Detected:** <project types>`
79
+ - `**Templates:** <each template read, or the generic fallback>`
51
80
  - `**Header:** <each slot as filled (source) or skipped (reason)>`
52
81
  - `**Badges:** <candidates and what backs each one, or none>`
53
82
 
@@ -26,7 +26,7 @@ CANON_NON_INTERACTIVE=1 canon sync --check . --json
26
26
  CANON_NON_INTERACTIVE=1 canon context audit --json
27
27
  ```
28
28
 
29
- The first answers five domains from one read, being `skew`, `domains`, `seeds`, `tooling`, and the layout fields `unmigrated`, `superseded`, and `reverse`. The second deepens a domain that first read only counts.
29
+ The first answers five domains from one read, being `skew`, `domains`, `seeds`, `tooling`, and the layout fields `unmigrated`, `superseded`, `unmovedSurfaces`, and `reverse`. The second deepens a domain that first read only counts.
30
30
 
31
31
  Then run `CANON_NON_INTERACTIVE=1 canon records validate <kind> --json` once per record folder the target carries, taking `<kind>` from what is present under the record root rather than from a list held here.
32
32
 
@@ -76,14 +76,14 @@ Lead with the table, then one detail block per domain that is not current:
76
76
  ```markdown
77
77
  Target check: `<path>`, against canon `<version>`.
78
78
 
79
- | Domain | State | Evidence |
80
- | ---------------- | ------------------------- | --------------------------------- |
81
- | Toolkit version | current / behind / unread | `skew.installed` against `latest` |
82
- | `canon/` folder | current / behind / unread | what the layout fields named |
83
- | Governance rules | current / behind / unread | the per-state counts |
84
- | Tooling | current / behind / unread | what a sync would change |
85
- | Seeds | current / behind / unread | the per-state counts |
86
- | Claude harness | unchecked | no verb reads it |
79
+ | Domain | State | Evidence |
80
+ | ---------------- | ------------------------- | ---------------------------------------------- |
81
+ | Toolkit version | current / behind / unread | `skew.installed` against `latest` |
82
+ | `canon/` folder | current / behind / unread | the layout fields, including `unmovedSurfaces` |
83
+ | Governance rules | current / behind / unread | the per-state counts |
84
+ | Tooling | current / behind / unread | what a sync would change |
85
+ | Seeds | current / behind / unread | the per-state counts |
86
+ | Claude harness | unchecked | no verb reads it |
87
87
 
88
88
  No overall verdict. The harness row is unchecked rather than passing.
89
89
  ```
@@ -21,19 +21,25 @@ 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 three fields off the same report, then two verbs:
24
+ Read four fields off the same report, then two 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.
28
+ - `unmovedSurfaces`: a surface held only at its `.claude/` spelling, each entry carrying `rel` and `movesTo`. Every other field reads clean for such a target, and the report's drift status ignores it by design, so read the array itself rather than an exit status.
28
29
  - `reverse`: `unclaimed` names folders at roots the toolkit stopped shipping, each with an `attribution` of `dropped`, `unattributed`, or `project`.
29
30
  - `canon context audit --json`: required sections, entry length, citations, and index drift across the context folder.
30
31
  - `canon records validate <kind> --json`, once per record folder the target carries.
31
32
 
32
- - Current: `unmigrated` and `superseded` both empty, `reverse.unclaimed` carrying nothing outside `project` attribution, and both verbs finding nothing.
33
- - Behind: any of those carrying an entry. Name the entry and its attribution.
34
- - Unread: `historyUnavailable` set on `reverse`, a `reverse` key the report does not carry at all, or either verb refusing. A missing `reverse` key is a binary predating the field rather than a target with nothing unclaimed.
33
+ - Current: `unmigrated`, `superseded`, and `unmovedSurfaces` all empty, `reverse.unclaimed` carrying nothing outside `project` attribution, and both verbs finding nothing.
34
+ - Behind: any of those carrying an entry. Name the entry and its attribution, and for `unmovedSurfaces` each `rel` with its `movesTo`.
35
+ - Unread: `historyUnavailable` set on `reverse`, a `reverse` or `unmovedSurfaces` key the report does not carry at all, or either verb refusing. A missing key is a binary predating the field rather than a target with nothing to report, so name the installed version beside it.
35
36
 
36
- Routes by finding. `unmigrated` routes to a relocation the user runs, since no command moves it. `superseded` routes to a split the user makes, since no command moves the content and no skill proposes it. An `unclaimed` folder carrying `project` attribution routes nowhere, since history proved the project owns it.
37
+ Routes by finding.
38
+
39
+ - `unmovedSurfaces` routes to `canon migrate surface-roots`, which only reports its plan until `--write` is passed. Name the verb with each entry's `rel` and `movesTo`, and run neither form.
40
+ - `unmigrated` routes to a relocation the user runs, since no command moves it.
41
+ - `superseded` routes to a split the user makes, since no command moves the content and no skill proposes it.
42
+ - An `unclaimed` folder carrying `project` attribution routes nowhere, since history proved the project owns it.
37
43
 
38
44
  ## 3. Governance rules
39
45
 
@@ -19,7 +19,7 @@ What the derivation costs where it does apply is that the ceiling rises when a d
19
19
 
20
20
  The decision word check counts each decision's prose against the cap the record states in a clause of the form `at most 150 words a decision`. The count runs below the `###` heading with fenced lines dropped, so neither the heading nor a template block inside a decision counts, and the report and the gate read the same number. The JSON record carries the clause as `architecture.wordCap`, and the audit catalog counts the decisions past it as `recordOverWords`, absent where the record states no clause.
21
21
 
22
- The risk bullet check counts every list item under `## Risks / open questions`, nested ones included, against a clause of the form `at most 6 risk bullets`, since a nested bullet is weight the reader pays for like any other. Both clauses can share the entry cap's sentence, and each gates only a record stating it, for the reason the cap does. A record stating a risk cap and carrying no Risks section is reported as absent rather than passed as zero bullets. The JSON record carries the clause as `architecture.riskCap` and the count as `architecture.risksBullets`, and the audit catalog counts a section past its cap as `recordOverRisks`, absent where the record states no clause.
22
+ The risk bullet check counts every list item under `## Risks / open questions`, nested ones included, against a clause of the form `at most 6 risk bullets`, since a nested bullet is weight the reader pays for like any other. Both clauses can share the entry cap's sentence, and each gates only a record stating it, for the reason the cap does. The record `canon claude init` seeds into a new project states all three caps in that one sentence, so a new project gates on them from its first decision, and a project that installed earlier keeps whatever its own record states. A record stating a risk cap and carrying no Risks section is reported as absent rather than passed as zero bullets. The JSON record carries the clause as `architecture.riskCap` and the count as `architecture.risksBullets`, and the audit catalog counts a section past its cap as `recordOverRisks`, absent where the record states no clause.
23
23
 
24
24
  The coverage report classifies each decision as carrying a countable claim, a structural invariant, or neither, then reports each testable entry against whether it names a check that exists. A countable claim carries a figure a run could recompute, and an invariant quantifies over a named tree closely enough that a walk could falsify it. A check is a `scripts/**.sh` path the entry spells that is on disk, or a `canon` invocation matching a registered audit, so coverage reads the entry rather than the tree and a claim some check happens to cover without the entry saying so reads as unchecked.
25
25
 
@@ -33,4 +33,4 @@ The `## Risks / open questions` section is weighed the same way, reported apart
33
33
 
34
34
  ## The requirements record
35
35
 
36
- The audit reads `canon/REQUIREMENTS.md` under its own step and counts every word below the frontmatter against the cap the record states in a clause of the form `This record holds at most 600 words.` The cap is the record's own, so a record stating none is measured and never gated, and a project with no record is reported as out of scope. The JSON record carries the reading as `requirements`, null where the project carries no record and absent under `--citations-only`, and the audit catalog counts a record past its cap as `requirementsOverWords`.
36
+ The audit reads `canon/REQUIREMENTS.md` under its own step and counts every word below the frontmatter against the cap the record states in a clause of the form `This record holds at most 600 words.` The cap is the record's own, so a record stating none is measured and never gated, and a project with no record is reported as out of scope. The seeded record states the 600-word cap. The JSON record carries the reading as `requirements`, null where the project carries no record and absent under `--citations-only`, and the audit catalog counts a record past its cap as `requirementsOverWords`.
@@ -11,6 +11,10 @@ description: The canvas server, its content format, and the verbs that list, arr
11
11
 
12
12
  `canvas serve` serves the canvas on `127.0.0.1`, walks ports like `canon serve`, refuses a `Host` other than loopback on its own port, and runs until interrupted. Each frame gets the project's token stylesheet injected, being this toolkit's tokens in its own checkout, else `.claude/design/base.css` plus anything under `.claude/design/project/`, else none with a notice. An open canvas reloads a frame whose file changes.
13
13
 
14
+ The toolkit's own tokens arrive with the font faces they name embedded, so a frame renders in them on a machine that never installed them. An installed base owns its fonts and gets no faces added.
15
+
16
+ Before it binds a port, `canvas serve` checks that `preact` and `@preact/signals` resolve from the package the CLI runs from, never from the served project. When either is missing it refuses with reason `missing-client-deps` and names `bun install` in the detail, rather than serving a shell that loads as a blank page.
17
+
14
18
  The write routes the shell posts to also refuse a request carrying an `Origin` other than the server's own, and a body that is not JSON.
15
19
 
16
20
  ## List and add
@@ -37,7 +41,7 @@ A pick is refused as `address-mismatch` when the browser builds elements the fil
37
41
 
38
42
  The operator edits the same set from the inspector, which posts to the same writer with the hash the frame was served with. An edit made against a version of the file Claude has since rewritten is refused as `stale-address` and the shell reloads the frame, so nothing lands on a shifted element. A color picked from the project's tokens is written as `var(--<name>)`. Both writers take the frame file's lock, and an edit that keeps the selected element where it was keeps the selection fresh.
39
43
 
40
- Inline style beats a class, so an operator's edit masks a class change Claude makes later. Read a frame's inline styles before restyling it, and remove one the change should replace. A property outside the set, or a value carrying `;`, `{`, `}`, `<`, `>`, or a line break, is refused as `invalid-edit`.
44
+ Inline style beats a class, so an operator's edit masks a class change Claude makes later. Read a frame's inline styles before restyling it, and remove one the change should replace. The inspector marks a color or background set inline with the word `raw` when its value names no `var()` and is not `currentColor` or a CSS-wide keyword, since that value stays fixed when the theme changes. A property outside the set, or a value carrying `;`, `{`, `}`, `<`, `>`, or a line break, is refused as `invalid-edit`.
41
45
 
42
46
  The Theme tab in the shell lists what the token stylesheet defines, grouped as color, spacing, radius, font family, font size, and other. The page record at `/api/pages` carries the same list as `tokens.groups`.
43
47
 
@@ -62,7 +62,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
62
62
  - [Targets](targets.md): The projects this toolkit installed into, the record the install writes against the sweep that backs it, what bounds each answer, and the cross-target pull request read
63
63
  - [Tasks archive](tasks-archive.md): Archiving a shipped task by stem or pull request, declining one decided against, the refusal reasons each carries, and how the plan and ready folder move with the task
64
64
  - [Tasks plans](tasks-plans.md): The five verbs that read or write a task's plan, being where it sits and who cites it, whether it waits on the operator, the branch it derives, what a branch reached against what it declared, and writing its Plan line
65
- - [Tasks validate](tasks-validate.md): Validating the board against the tree, the seven checks, the untested, claims, and unplaced arrays, the exit codes, and the refusal reasons
65
+ - [Tasks validate](tasks-validate.md): Validating the board against the tree, the seven checks, the untested, claims, wide, and unplaced arrays, the exit codes, and the refusal reasons
66
66
  - [Tasks](tasks.md): Claiming the next phase label, recording a pull request number and closing an outcome, listing each task's readiness, and where archiving, the plan verbs, and board validation are described
67
67
  - [Teach authoring](teach-authoring.md): Resolving what the next lesson needs before it is written, rendering a lesson body's blocks to HTML, and rewriting the root listing, a contents page, and each lesson's chrome and quiz stepper from its marker regions
68
68
  - [Teach](teach.md): Listing learning workspaces with what their records schedule next, opening one with its required files, recording sources and glossary terms, viewing one with its chrome refreshed, where the lesson authoring verbs are described, the refusal reasons, and why every write here runs through a verb
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Tasks validate
3
- description: Validating the board against the tree, the seven checks, the untested, claims, and unplaced arrays, the exit codes, and the refusal reasons
3
+ description: Validating the board against the tree, the seven checks, the untested, claims, wide, and unplaced arrays, the exit codes, and the refusal reasons
4
4
  ---
5
5
 
6
6
  # Tasks validate
@@ -103,7 +103,9 @@ That claim collides with every row a later session writes under the folder, and
103
103
 
104
104
  The scan reaches `## Run now` and stops, where the collision check stops. A cell in another group describes work nobody has planned, written as a sentence and rewritten once a plan exists, so a claim read off one reports on prose rather than on a file set. A parked folder claim surfaces when its row is promoted, which is when the cell becomes something a dispatcher can act on.
105
105
 
106
- A task file neither surface names lands in a fourth array, on the same reasoning:
106
+ A code span in any cell that the preview cannot break lands in a fourth array, `wide`, with the same `group`, `subject`, and `message` fields. The scan measures a span's longest whitespace-free run and flags one past 60 characters, or a brace-glob at any width. A 57-character span fit the pane and a 78-character one did not, so the limit is a heuristic and the array moves no exit code. A link target is skipped, since only its label renders, and the scan reaches every group, since the overflow it exists for sat under `## Up next`.
107
+
108
+ A task file neither surface names lands in a fifth array, on the same reasoning:
107
109
 
108
110
  ```json
109
111
  {
@@ -118,6 +118,6 @@ canon tasks list --json | jq -r '.tasks[] | select(.readiness == "backlog") | .s
118
118
 
119
119
  ## Validate
120
120
 
121
- `canon tasks validate` reports what each row of `priority.md` and each line of `backlog.md` claims against what the tree holds. Its seven checks, the three arrays that move no exit code, and the refusals are in `tasks-validate.md`.
121
+ `canon tasks validate` reports what each row of `priority.md` and each line of `backlog.md` claims against what the tree holds. Its seven checks, the four arrays that move no exit code, and the refusals are in `tasks-validate.md`.
122
122
 
123
123
  For the board format, see `standards/board.md`. For the `Pull request:` line and the archive rules, see `standards/tasks.md`.
@@ -71,7 +71,7 @@ Keep the `## Scripts` table in `canon/context/development.md` current as scripts
71
71
 
72
72
  Scaffold installs tooling and seeds. It does not fill the planning docs or the design system. Complete those before the first feature session:
73
73
 
74
- 1. Fill `canon/REQUIREMENTS.md` and `canon/ARCHITECTURE.md`. The seed provides the files, the scope and decisions are yours to write.
74
+ 1. Fill `canon/REQUIREMENTS.md` and `canon/ARCHITECTURE.md`. The seed provides the files, the scope and decisions are yours to write. Each seed states its own caps, 12 decisions of at most 150 words and 6 risk bullets for the architecture record and 600 words for the requirements, and `canon context audit` gates on them. Edit a number in the record to loosen it.
75
75
  2. For a UI project, invoke `canon:design-extract` to draft `canon/DESIGN.md`. With no UI code yet it takes the greenfield path and proposes tokens from the requirements and a `## Personality` section. Skip for non-UI projects.
76
76
  3. Optionally invoke `canon:draft-diagram` to draft entries under `.canon/diagrams/` from the architecture and the requirements. One file per diagram kind, so a later refresh of one kind leaves the others untouched. It renders each diagram it writes to verify the layout, which downloads the Mermaid CLI on first use and takes about 15 seconds.
77
77
  4. Start the feature loop. See [AI workflow](../workflow/ai-workflow.md) for the per-feature sequence.
@@ -14,7 +14,7 @@ The tier framework sits alongside [Claude Design](../../wiki/claude/design.md),
14
14
 
15
15
  ## Tier 0: prose only
16
16
 
17
- The default. `canon/DESIGN.md` holds visual intent as prose plus token tables for color, typography, spacing, borders, motion, and iconography. `.claude/WIREFRAMES.md` holds each surface's regions and states.
17
+ The default. `canon/DESIGN.md` holds visual intent as token tables for color, typography, spacing, and borders, with one Personality paragraph and at most three one-line rules a section, motion and iconography included. `.claude/WIREFRAMES.md` holds each surface's regions and states.
18
18
 
19
19
  Claude Code reads both and writes the implementation. Works for CLI tools, internal dashboards, admin panels, and backend-focused projects.
20
20
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "5.22.0",
4
+ "version": "5.24.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": {