@erclx/canon 4.89.0 → 4.92.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/design-taste/REQUIREMENT.md +78 -0
- package/claude/skills/design-taste/SKILL.md +148 -0
- package/claude/skills/design-taste/references/craft.md +58 -0
- package/claude/skills/design-taste/references/kinds.md +52 -0
- package/claude/skills/design-taste/references/preflight.md +77 -0
- package/claude/skills/design-taste/references/systems.md +49 -0
- package/claude/skills/design-taste/references/tells.md +101 -0
- package/claude/skills/design-taste/references/vocabulary.md +62 -0
- package/claude/skills/docs-fold/REQUIREMENT.md +2 -0
- package/claude/skills/docs-fold/SKILL.md +6 -0
- package/claude/skills/draft-and-pick/REQUIREMENT.md +8 -2
- package/claude/skills/draft-and-pick/SKILL.md +13 -5
- package/claude/skills/draft-ready/REQUIREMENT.md +53 -0
- package/claude/skills/draft-ready/SKILL.md +118 -0
- package/claude/skills/draft-ready/references/assembly.md +67 -0
- package/claude/skills/session-compact/REQUIREMENT.md +44 -0
- package/claude/skills/session-compact/SKILL.md +60 -0
- package/claude/skills/session-compact/references/handoff-note.md +68 -0
- package/claude/skills/session-map/REQUIREMENT.md +3 -2
- package/claude/skills/session-map/SKILL.md +2 -2
- package/claude/skills/session-resume/SKILL.md +3 -3
- package/docs/agents/audits.md +1 -1
- package/docs/agents/commands.md +1 -1
- package/docs/agents/context-audit-checks.md +4 -2
- package/docs/agents/context-audit.md +3 -1
- package/docs/agents/index.md +1 -1
- package/docs/agents/review-classification.md +1 -1
- package/docs/agents/routing.md +1 -1
- package/docs/agents/state-scoped-risk.md +1 -1
- package/docs/agents/teach.md +4 -2
- package/docs/workflow/ai-workflow.md +3 -1
- package/governance/rules/claude/540-architecture.md +1 -0
- package/governance/rules/claude/563-ready.md +4 -0
- package/governance/rules/ui/410-a11y.md +9 -0
- package/governance/rules/ui/420-forms.md +9 -0
- package/governance/rules/ui/460-design-taste.md +29 -0
- package/governance/rules/ui/470-motion.md +23 -0
- package/governance/stacks/astro.toml +1 -1
- package/governance/stacks/react.toml +1 -1
- package/package.json +1 -1
- package/src/audits/catalog.ts +6 -0
- package/src/autoship/paths.ts +1 -1
- package/src/claude/cases/authoring.ts +5 -0
- package/src/claude/cases/misc.ts +10 -0
- package/src/commands/capture.ts +1 -0
- package/src/commands/context.ts +22 -4
- package/src/commands/design.ts +5 -0
- package/src/commands/feedback.ts +1 -0
- package/src/commands/slides.ts +3 -0
- package/src/commands/transcripts.ts +1 -0
- package/src/context/architecture.ts +51 -1
- package/src/context/gate.ts +10 -4
- package/src/design/base.css +25 -19
- package/src/design/css.ts +4 -0
- package/src/design/fonts.ts +17 -0
- package/src/design/tokens.ts +46 -31
- package/src/gate/measures.ts +40 -0
- package/src/gate/stages.ts +9 -1
- package/src/teach/workspace.ts +8 -2
- package/standards/architecture.md +36 -11
- package/standards/context.md +2 -2
- package/standards/ready.md +2 -0
- package/standards/session.md +1 -0
- package/tooling/claude/seeds/canon/ARCHITECTURE.md +12 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: session-map
|
|
3
|
-
description: Writes the session map, the
|
|
3
|
+
description: Writes the session map, the board-side handoff at `.canon/tasks/session-<slug>.md`, for an orchestrating session or on a request naming the session map or the task board, running the skill-drift step the write procedure opens with. Use when asked to "write the session map", "write the board handoff", or when a session holding `canon:role-orchestrator` hands off before a compaction. Do NOT use for a plain session about to compact, which is `session-compact` and writes a note outside the board. Do NOT use to route session facts to a context entry or the memory folder, which is `memory-capture` and writes a different artifact, and do NOT use to read a handoff back, which is `session-resume`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Session map
|
|
@@ -13,7 +13,7 @@ Run every step rather than judging in advance that one has nothing to act on. Th
|
|
|
13
13
|
|
|
14
14
|
## What fires this
|
|
15
15
|
|
|
16
|
-
Being asked is
|
|
16
|
+
Being asked is the route for a plain session that wants a board-side map, and the orchestrator's handoff runbook is the route for that role. A plain session about to compact belongs to `session-compact`, which is what the `PreCompact` hook on the `manual` matcher names. That hook's message carries one sentence sending a session holding `canon:role-orchestrator` here.
|
|
17
17
|
|
|
18
18
|
Decline where the session holds no reasoning a reader could not get faster from git. Say so in one line and write nothing. The standard already names a `## State` filled from the tree as non-conforming, and the file that carries three such sections is worse than an absent one, because a reader who finds a map trusts it. Declining is a result the caller can act on, where a padded map is a result that misleads.
|
|
19
19
|
|
|
@@ -7,11 +7,11 @@ description: Resumes a previous session by reading the handoff it left behind, t
|
|
|
7
7
|
|
|
8
8
|
## Step 1: read tracked work
|
|
9
9
|
|
|
10
|
-
Resolve `.canon/plans/`, `.canon/memory/`, and `.canon/tasks/` at the main worktree root the way `session-worktree` does.
|
|
10
|
+
Resolve `.canon/plans/`, `.canon/memory/`, `.canon/compact/`, and `.canon/tasks/` at the main worktree root the way `session-worktree` does.
|
|
11
11
|
|
|
12
12
|
Read these in parallel, skipping any that do not exist:
|
|
13
13
|
|
|
14
|
-
- the newest `.canon/tasks/session-*.md`: the handoff a previous session wrote before a compaction, per `${CLAUDE_SKILL_DIR}/../../standards/session.md
|
|
14
|
+
- the newest `.canon/compact/*.md`, then the newest `.canon/tasks/session-*.md`: the handoff a previous session wrote before a compaction. The first is the note `session-compact` writes, and the second is the board-side map per `${CLAUDE_SKILL_DIR}/../../standards/session.md`, which stays readable. Whichever exists leads the report rather than the reads, and both lead it when both exist.
|
|
15
15
|
- `.canon/tasks/index.md`: the folder catalog. Read this before any individual task file, and take the task list from it by dropping the `index`, `priority`, `backlog`, and `session-` rows, which are siblings rather than tasks.
|
|
16
16
|
- `.canon/plans/*.md`: execution detail for in-progress tasks
|
|
17
17
|
- `.canon/memory/index.md` and any memory files relevant to the top backlog item
|
|
@@ -48,4 +48,4 @@ Memory is updated only when a recorded fact becomes wrong, never on resume. A do
|
|
|
48
48
|
|
|
49
49
|
This skill reads a handoff and never writes one. Reading and writing are two jobs, and the write happens at the close of a session rather than at its start.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Route a session asking how to leave a handoff behind to `session-compact`, which writes a note under `.canon/compact/`. A session holding the orchestrator role takes `session-map` instead, which writes the board-side map the standard governs, and a role carrying sections of its own adds them over the core per that role's own runbook.
|
package/docs/agents/audits.md
CHANGED
|
@@ -36,7 +36,7 @@ Each is invoked as the CLI the caller is running rather than as a global `canon`
|
|
|
36
36
|
|
|
37
37
|
Five findings fail the run: an unresolved context citation, a banned character, word, or spelling, a relative link resolving to nothing on disk, a skill folder carrying no `REQUIREMENT.md`, and a credential-shaped value in the tree the package ships. Each is a fact with no false-positive class behind it.
|
|
38
38
|
|
|
39
|
-
Four of the five are the ones `canon gate run` already fails a push on. The secret scan is the one entry gating without a stage behind it, added on that same fact-or-judgment test rather than as a side effect of registering a measure, and the
|
|
39
|
+
Four of the five are the ones `canon gate run` already fails a push on. The secret scan is the one entry gating without a stage behind it, added on that same fact-or-judgment test rather than as a side effect of registering a measure, and the toolkit already ranks content leaving the repository above content that stays.
|
|
40
40
|
|
|
41
41
|
Everything else reports. A heavy bullet, a long entry, a board row nothing resolves, a degradation term in a comment, and an implementation reaching history ahead of its test are judgments a reader settles. A push failing on one of those teaches contributors to route around the stage, which is the split recorded across every audit here and the one this command inherits rather than moves.
|
|
42
42
|
|
package/docs/agents/commands.md
CHANGED
|
@@ -68,7 +68,7 @@ Full help: `canon <command> --help`. Behavior notes for the install and sync ver
|
|
|
68
68
|
| `canon worktrees list` | Report which worktrees are reclaimable, keyed on the pull request having merged, with every refusal and the removal route named (`--json`) |
|
|
69
69
|
| `canon worktrees reclaim` | Remove every reclaimable worktree and the branch behind it, reporting without acting under `--dry-run` (`--json`) |
|
|
70
70
|
| `canon comments scan` | Measure comment density by language and comment kind, with a trend recomputed from git |
|
|
71
|
-
| `canon context audit` | Report required sections, length, cited paths, reference form, catalog tables, provenance, superseded-decision narration, index drift, the architecture record
|
|
71
|
+
| `canon context audit` | Report required sections, length, cited paths, reference form, catalog tables, provenance, superseded-decision narration, index drift, the architecture record against its own ceiling and entry cap and its word weight, and wireframe states against their evidence folders |
|
|
72
72
|
| `canon context classify diff` | Classify the chunks a git range changed, each with its enclosing section, as keep, replace, history, or move (`--base`, `--doc-types`, `--json`) |
|
|
73
73
|
| `canon context classify sweep` | Classify every section of the five canonical doc types, split at H3, as keep, rewrite, or move (`--doc-types`, `--json`) |
|
|
74
74
|
| `canon context classifier show` | Report the resolved classifier backend and model and which source decided them (`--json`) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Context audit checks
|
|
3
|
-
description: What each non-gating check reports, the unit each checkpoint is measured in, the architecture record's length
|
|
3
|
+
description: What each non-gating check reports, the unit each checkpoint is measured in, the architecture record's length and entry cap gates and claim coverage, which folders each check reaches, and what moved to the attribute tier
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Context audit checks
|
|
@@ -91,7 +91,9 @@ The JSON record carries the findings per entry as `entries[].narration` and the
|
|
|
91
91
|
|
|
92
92
|
## The architecture record
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
Four findings read `canon/ARCHITECTURE.md` rather than a folder, and only the first two are facts.
|
|
95
|
+
|
|
96
|
+
The entry cap check counts the record's decisions against the cap it states for itself, in a clause of the form `This record holds at most 12 decisions.` Like the length check, the cap belongs to the record rather than to the toolkit, so a record stating none is measured and never gated. A decision is a `###` heading outside a fenced block, so the template a standard shows does not count, and a heading carrying two decisions counts once. The JSON record carries what it read as `architecture.entryCap`, absent where the record states no cap, and the audit catalog counts a record past it as `recordOverCount`.
|
|
95
97
|
|
|
96
98
|
The length check compares the record against the ceiling it derives for itself, and only a record that states its own allowances has one. No standard sets a length rule for this document, so the numbers belong to whichever record declares them. The check reads a frame allowance and an allowance per decision out of the record's own prose and puts the ceiling at the frame plus the allowance times the decision count. The JSON record carries what it read as `architecture.allowances` and the reading as `architecture.lines` against `architecture.ceiling`.
|
|
97
99
|
|
|
@@ -37,7 +37,9 @@ A run where no requested name resolves refuses, whichever list it read. Naming t
|
|
|
37
37
|
|
|
38
38
|
## Exit codes
|
|
39
39
|
|
|
40
|
-
Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a gating finding. An unresolved citation gates under every mode. An architecture record that states its own line allowances gates when it is past the ceiling those derive, on any run that measures it, which is every mode except `--citations-only
|
|
40
|
+
Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a gating finding. An unresolved citation gates under every mode. An architecture record that states its own line allowances gates when it is past the ceiling those derive, on any run that measures it, which is every mode except `--citations-only`. A record that states an entry cap gates the same way when it holds more decisions than the cap, and a record stating neither limit is reported and never gated.
|
|
41
|
+
|
|
42
|
+
`bun run check` in the toolkit repository reads both limits through its own Architecture record stage rather than through this verb, since the verb's only gating stage there runs `--citations-only` and never opens the record.
|
|
41
43
|
|
|
42
44
|
Entry length, reference form, table, provenance, narration, the record's claim classification, and every word figure print and return `0` under every mode, because each is a judgment or a weight read alongside one, and failing a push on either would make the check something to route around. Narration is the weakest of the printed measures, since whether two bullets share a subject is a call the measure approximates from structure alone, and one of the shapes it matches is the rejected alternative the standard asks an entry to keep.
|
|
43
45
|
|
package/docs/agents/index.md
CHANGED
|
@@ -13,7 +13,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
|
|
|
13
13
|
- [Census](census.md): Tracked-plus-untracked file count, a breakdown by extension, and a line total that skips whatever reads as binary
|
|
14
14
|
- [Command catalog](commands.md): Every project-level command and every domain subcommand, plus the shape each domain exposes
|
|
15
15
|
- [Comments](comments.md): Comment density by language and kind, the two structural exclusions, and how the degradation sweep finds its vocabulary
|
|
16
|
-
- [Context audit checks](context-audit-checks.md): What each non-gating check reports, the unit each checkpoint is measured in, the architecture record's length
|
|
16
|
+
- [Context audit checks](context-audit-checks.md): What each non-gating check reports, the unit each checkpoint is measured in, the architecture record's length and entry cap gates and claim coverage, which folders each check reaches, and what moved to the attribute tier
|
|
17
17
|
- [Context audit](context-audit.md): Running the audit, its flags and folder scope, the exit codes, the citation gate, and the widened gate the seed stage runs
|
|
18
18
|
- [Doc classifier](context-classify.md): Classifying canonical-doc content as keep, replace/rewrite, history, or move in diff and sweep mode, the classifier setting pair, the record shape, and the exit codes
|
|
19
19
|
- [Self-stated counts](counts.md): Reading a sentence that asserts a closed catalog's size, how a match is decided, the plausibility filter that keeps a generic word from matching a subset, and why the sweep reports rather than gates
|
|
@@ -48,7 +48,7 @@ Most carry two spellings, the one a surface authors at and the one it reaches a
|
|
|
48
48
|
|
|
49
49
|
`CLAUDE.md` is matched as a whole path rather than as a prefix, because a prefix reaches nothing that sits in no folder. A nested `docs/CLAUDE.md` stays informational.
|
|
50
50
|
|
|
51
|
-
The set is data at `src/autoship/paths.ts`. That makes it permanently exempt from any later design that folds a machine-parsed list back into the surface citing it, per the machine-parsed clause
|
|
51
|
+
The set is data at `src/autoship/paths.ts`. That makes it permanently exempt from any later design that folds a machine-parsed list back into the surface citing it, per the toolkit's machine-parsed clause: a rule restating the list a parser reads is two sources for one list.
|
|
52
52
|
|
|
53
53
|
## Exit codes and refusals
|
|
54
54
|
|
package/docs/agents/routing.md
CHANGED
|
@@ -5,7 +5,7 @@ description: Reading per CLAUDE.md section how many bullets name a path, what co
|
|
|
5
5
|
|
|
6
6
|
# Routing report
|
|
7
7
|
|
|
8
|
-
`canon claude routing` reports, per `CLAUDE.md` section, how many top-level bullets name a path and how many of those a path-scoped rule already covers. It answers the firing axis of the tier test in `.claude/rules/canon/claude/592-claude-md.md`, whether a fact applies every session or fires on one path, and which until this verb existed was a judgment nothing counted.
|
|
8
|
+
`canon claude routing` reports, per `CLAUDE.md` section, how many top-level bullets name a path and how many of those a path-scoped rule already covers. It answers the firing axis of the tier test in `.claude/rules/canon/claude/592-claude-md.md`, whether a fact applies every session or fires on one path, and which until this verb existed was a judgment nothing counted. The tier test names two further axes, conditional presence and updatability, that this verb does not check.
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
canon claude routing
|
|
@@ -32,7 +32,7 @@ Three root files are read whether or not the field names them, being `package.js
|
|
|
32
32
|
|
|
33
33
|
The corpus answers what the package publishes, which is narrower than what the repository holds. Measured against this repository on 2026-08-21, the scan read 544 files and left 593 of the 1139 git lists unread. Those include everything under `.claude/`, `wiki/`, and `internal/`, the workflow definitions under `.github/`, and the trees the publish negations remove, being `scripts/sandbox/`, `scripts/eval/`, and every test file. `src/capture/` was a fourth negation when that reading was taken and is not one now. The folder holds 5 tracked files, 2 of them tests the publish still excludes, so the corpus is 3 wider than the numbers above describe and the unread count is 3 smaller.
|
|
34
34
|
|
|
35
|
-
A public repository makes that gap readable by anyone, so a clean run means no credential in the published tree rather than none in the repository. The run states the number on every pass, including a clean one, so the bound travels with the verdict. Widening the corpus to every tracked file is a separate decision, since the row this implements puts the shipped tree first on the
|
|
35
|
+
A public repository makes that gap readable by anyone, so a clean run means no credential in the published tree rather than none in the repository. The run states the number on every pass, including a clean one, so the bound travels with the verdict. Widening the corpus to every tracked file is a separate decision, since the row this implements puts the shipped tree first on the toolkit's rule that content leaving the repository gates harder than content that stays.
|
|
36
36
|
|
|
37
37
|
### What it keys on
|
|
38
38
|
|
package/docs/agents/teach.md
CHANGED
|
@@ -7,6 +7,8 @@ description: Listing learning workspaces with what their records schedule next,
|
|
|
7
7
|
|
|
8
8
|
Learning workspaces sit under `.canon/teach/<nn>-<topic>/`, and `standards/teach.md` fixes their layout, naming, and file formats, apart from the glossary, whose shape `standards/glossary.md` fixes, cited from the `teach-workspace` skill, so it travels with the file wherever a promotion lands it. Every verb here resolves that folder against the main worktree root rather than against the working directory, so a session standing in a linked worktree reaches the one workspace the learner has rather than opening a second.
|
|
9
9
|
|
|
10
|
+
Passing `--root` a path already named `teach` reaches that folder directly rather than wrapping it in the `.canon/`-or-`.claude/` record-root lookup, which is what lets a workspace committed outside the record root, such as a worked example kept in version control on purpose, take every verb below the same as one under `.canon/teach/`.
|
|
11
|
+
|
|
10
12
|
That root resolution is also why the writing verbs exist at all. The file-editing tools refuse a main-root path from a linked worktree and offer a worktree copy instead, and a caller naming only the destination reports a success that did not happen. A whole-file create still goes out as a shell heredoc. Changing a line inside a file that already exists has no shell route, because the stream editors are banned, so `resource` and `glossary` are the route for the two files a running workspace edits.
|
|
11
13
|
|
|
12
14
|
## List
|
|
@@ -25,7 +27,7 @@ canon teach list regular-expressions --json
|
|
|
25
27
|
|
|
26
28
|
With no topic it reports one line per workspace, carrying the lesson, learning-record, reference-page, and glossary-term counts, plus `next`, the ordinal an open would take. With one it reports the filenames behind each count and the glossary entries themselves.
|
|
27
29
|
|
|
28
|
-
A folder not named `NN-<topic>` is
|
|
30
|
+
A folder not named `NN-<topic>` is not a workspace, so the listing, `nav`, and every selector skip it and it moves no ordinal. That is what lets a teach root carry a sibling folder such as `evidence/` without it drawing as a stub row and receiving a contents page.
|
|
29
31
|
|
|
30
32
|
The listing also names the required files a workspace does not carry, which is `MISSION.md`, `RESOURCES.md`, and `GLOSSARY.md`. That is a report rather than a refusal, because a workspace missing one is still a workspace a session can resume.
|
|
31
33
|
|
|
@@ -139,7 +141,7 @@ The order is drawn here rather than instructed, and that is the point of the ver
|
|
|
139
141
|
|
|
140
142
|
## Render
|
|
141
143
|
|
|
142
|
-
`canon teach render` renders a lesson body's structural blocks to HTML, through the
|
|
144
|
+
`canon teach render` renders a lesson body's structural blocks to HTML, through the components `src/teach/render-fixture.tsx` composes by hand. It takes no topic and no `--root`, since the verb is a stateless transform reading nothing off a workspace on disk.
|
|
143
145
|
|
|
144
146
|
```bash
|
|
145
147
|
echo '[{"type":"heading","level":1,"text":"Compass bearings"}]' | canon teach render --json
|
|
@@ -280,7 +280,8 @@ This section is the corpus the coverage claim is measured against: every name `c
|
|
|
280
280
|
| `canon:role-worker` | To assert the worker role for a cold session building one branch under one plan |
|
|
281
281
|
| `canon:session-relay` | When a worker or planner owes its controller a message and holds no send tool |
|
|
282
282
|
| `canon:session-resume` | At the start of a session, to pick up what a previous one left |
|
|
283
|
-
| `canon:session-
|
|
283
|
+
| `canon:session-compact` | Before a compaction, to write a plain session's handoff note outside the board |
|
|
284
|
+
| `canon:session-map` | At the close of an orchestrating session, to write its board-side handoff |
|
|
284
285
|
|
|
285
286
|
### Keep the project current with the toolkit
|
|
286
287
|
|
|
@@ -304,6 +305,7 @@ This section is the corpus the coverage claim is measured against: every name `c
|
|
|
304
305
|
| `canon:create-snippet` | For a reusable prompt |
|
|
305
306
|
| `canon:create-standard` | For a new authoring convention |
|
|
306
307
|
| `canon:draft-docs` | For a brand-new `docs/*.md` page, drafted against `standards/docs.md` |
|
|
308
|
+
| `canon:draft-ready` | For finished files a worker should copy, written as a ready folder with its overview, thin plan, and task |
|
|
307
309
|
| `canon:draft-context` | For a brand-new `canon/context/<domain>.md` entry, drafted against `standards/context.md` |
|
|
308
310
|
| `canon:draft-wireframes` | For a brand-new `canon/wireframes/<surface>.md` file, drafted against `standards/wireframes.md` |
|
|
309
311
|
| `canon:draft-figure` | For a hand-drawn figure inside an existing doc, drafted against `standards/figures.md` and rendered through Mermaid or freehand SVG |
|
|
@@ -10,3 +10,4 @@ paths:
|
|
|
10
10
|
## Authority
|
|
11
11
|
|
|
12
12
|
- Follow the architecture standard for the overview, named decision entries, and risks. It is the single source. Read it with `canon standards architecture`.
|
|
13
|
+
- Add a decision here only when it fills one of the standard's slots, and write any other decision into the context entry for the domain it constrains. At the record's stated cap, merge or retire an entry before adding one.
|
|
@@ -6,6 +6,10 @@ paths:
|
|
|
6
6
|
|
|
7
7
|
# Ready standards
|
|
8
8
|
|
|
9
|
+
## Writing a ready folder
|
|
10
|
+
|
|
11
|
+
- Load the `canon:draft-ready` skill to write a ready folder, its overview, the thin plan, and the task row. It owns the procedure. Report it rather than proceeding silently when it does not resolve, since it ships with the plugin and this rule ships with the CLI.
|
|
12
|
+
|
|
9
13
|
## Authority
|
|
10
14
|
|
|
11
15
|
- Follow the ready standard for the folder layout, ordinal naming, the overview frontmatter, the mirrored tree, and the thin-plan contract. It is the single source. Read it with `canon standards ready`.
|
|
@@ -24,6 +24,12 @@ paths:
|
|
|
24
24
|
- Arrow keys: navigate within composite widgets. Do not let them exit the widget.
|
|
25
25
|
- Escape: closes any overlay, popover, dialog, or dropdown. Cancels in-progress edits.
|
|
26
26
|
- Do not introduce non-standard key bindings for common actions.
|
|
27
|
+
- Give every author-defined drag a single-pointer alternative and a keyboard path, such as move buttons or arrow-key reordering (WCAG 2.2 Dragging Movements, 2.5.7).
|
|
28
|
+
|
|
29
|
+
## Pointer targets
|
|
30
|
+
|
|
31
|
+
- Size a web pointer target at least 24 by 24 CSS pixels, or space it so a 24 pixel circle centered on it touches no other target (WCAG 2.2 Target Size Minimum, 2.5.8).
|
|
32
|
+
- Size a native touch target at the platform figure, 44 points on iOS and 48 dp on Android. Do not apply the web figure to native, and do not apply the native figure as the web minimum.
|
|
27
33
|
|
|
28
34
|
## Composite widgets
|
|
29
35
|
|
|
@@ -34,6 +40,7 @@ paths:
|
|
|
34
40
|
|
|
35
41
|
- Move focus into any overlay, popover, or dialog on open. Return focus to the trigger on close.
|
|
36
42
|
- Never move focus on hover. Only on keyboard input or explicit pointer activation.
|
|
43
|
+
- Do not let a sticky header, footer, or overlay fully cover the focused control. Scroll it into view clear of them (WCAG 2.2 Focus Not Obscured, 2.4.11).
|
|
37
44
|
|
|
38
45
|
## ARIA semantics
|
|
39
46
|
|
|
@@ -46,3 +53,5 @@ paths:
|
|
|
46
53
|
- Always provide `alt` on `<img>`. Use `alt=""` for decorative images.
|
|
47
54
|
- Describe the content or function of the image, not its appearance. Do not prefix with "Image of" or "Photo of".
|
|
48
55
|
- Alt text for functional images (icons, buttons) should describe the action, not the graphic.
|
|
56
|
+
- Give the visual boundary of a control and every graphic that carries meaning at least 3:1 contrast against adjacent colors (WCAG Non-text Contrast, 1.4.11).
|
|
57
|
+
- Never make color the only carrier of a state, error, or distinction. Pair it with text, an icon, or a pattern (WCAG Use of Color, 1.4.1).
|
|
@@ -26,6 +26,15 @@ paths:
|
|
|
26
26
|
- Show the error directly under the field that caused it, not under an unrelated field.
|
|
27
27
|
- When a change to field A causes a conflict involving field B, show the error under field A (the field the user edited), not under field B.
|
|
28
28
|
|
|
29
|
+
## Submission errors
|
|
30
|
+
|
|
31
|
+
- When a failed submission carries several errors, move focus to a summary that links to each failing field. Keep the inline error beside each field.
|
|
32
|
+
|
|
33
|
+
## Authentication and re-entry
|
|
34
|
+
|
|
35
|
+
- Let a password manager fill sign-in fields and accept a pasted value. Do not block paste or autofill on a password or code field (WCAG 2.2 Accessible Authentication, 3.3.8).
|
|
36
|
+
- Do not ask for information the user already gave earlier in the same flow. Prefill it or offer it for selection (WCAG 2.2 Redundant Entry, 3.3.7).
|
|
37
|
+
|
|
29
38
|
## Input sanitization
|
|
30
39
|
|
|
31
40
|
- Trim leading and trailing whitespace from text inputs on blur or submit, not on every keystroke.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Route edits that decide how a surface looks to the design-taste skill for the layer ordering, the coherence locks, and the defaults a model reaches for
|
|
3
|
+
paths:
|
|
4
|
+
- '**/*.css'
|
|
5
|
+
- '**/*.scss'
|
|
6
|
+
- 'canon/DESIGN.md'
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Design taste standards
|
|
10
|
+
|
|
11
|
+
## Authority
|
|
12
|
+
|
|
13
|
+
- Load the `canon:design-taste` skill for which design decision settles first, what must stay constant across a surface, and the defaults to reach past. It is the single source for all three.
|
|
14
|
+
- Load it before drafting, not after revising. A revision recovers a color and never recovers the composition a draft already settled.
|
|
15
|
+
- Report it rather than proceeding silently when the skill does not resolve. It ships with the plugin and this rule ships with the CLI, so a project that installed governance alone does not have it.
|
|
16
|
+
- Do not work the layer ordering or the coherence locks from memory.
|
|
17
|
+
|
|
18
|
+
## When this fires and when it does not
|
|
19
|
+
|
|
20
|
+
- This rule scopes to the files where an undecided visual choice lands, which is the stylesheet and the design document. Building a surface somebody already decided is implementation rather than design, and the rules below carry what implementation owes.
|
|
21
|
+
- Load the skill by name when a component edit makes a visual choice nobody has taken yet, such as a new section's shape, a spacing relationship, or a radius. The glob cannot see that intent and no glob can.
|
|
22
|
+
- Do not load it to ship a decided design. A worker wiring a picked composition pays the read and gets nothing back.
|
|
23
|
+
|
|
24
|
+
## Boundaries
|
|
25
|
+
|
|
26
|
+
- Accessibility, keyboard interaction and ARIA are a separate topic. `410-a11y` routes them, on every rendered path.
|
|
27
|
+
- States, empty and loading coverage, and destructive confirmation are a separate topic. `430-ux-completeness` routes them.
|
|
28
|
+
- Rendered copy casing, button labels and error wording are a separate topic. `400-ui` routes them.
|
|
29
|
+
- The floor those three carry is not restated in the skill, and it is not traded against a preference where the two collide.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Moving content controls and the reduced-motion preference for rendered UI
|
|
3
|
+
paths:
|
|
4
|
+
- '**/*.tsx'
|
|
5
|
+
- '**/*.jsx'
|
|
6
|
+
- '**/*.astro'
|
|
7
|
+
- '**/*.html'
|
|
8
|
+
- '**/*.css'
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Motion standards
|
|
12
|
+
|
|
13
|
+
## Moving content
|
|
14
|
+
|
|
15
|
+
- Give any content that moves, blinks, or scrolls automatically for more than five seconds a control to pause, stop, or hide it (WCAG Pause, Stop, Hide, 2.2.2).
|
|
16
|
+
- Halt an auto-advancing carousel or ticker while it holds keyboard focus or pointer hover.
|
|
17
|
+
- Do not flash content more than three times in any one second.
|
|
18
|
+
|
|
19
|
+
## Reduced motion
|
|
20
|
+
|
|
21
|
+
- Honor `prefers-reduced-motion: reduce` for every animation and transition that conveys no state. Remove it or replace it with an instant change.
|
|
22
|
+
- Keep essential state changes visible under reduced motion, using a fade or a static indicator instead of movement.
|
|
23
|
+
- Do not start video or animated backgrounds on their own when the preference is set.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
extends = "node"
|
|
2
|
-
rules = ["210-astro", "350-security-web", "400-ui", "410-a11y", "430-ux-completeness", "440-surface-capture", "450-link-behavior"]
|
|
2
|
+
rules = ["210-astro", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness", "440-surface-capture", "450-link-behavior", "460-design-taste", "470-motion"]
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
extends = "node"
|
|
2
|
-
rules = ["200-react", "230-nextjs", "250-tailwind", "300-testing-ts", "305-e2e-reliability", "306-test-scope", "310-zod", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness", "440-surface-capture", "450-link-behavior"]
|
|
2
|
+
rules = ["200-react", "230-nextjs", "250-tailwind", "300-testing-ts", "305-e2e-reliability", "306-test-scope", "310-zod", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness", "440-surface-capture", "450-link-behavior", "460-design-taste", "470-motion"]
|
package/package.json
CHANGED
package/src/audits/catalog.ts
CHANGED
|
@@ -168,6 +168,8 @@ function architectureCounts(
|
|
|
168
168
|
// zero there would read as one measured and found conforming.
|
|
169
169
|
const ceiling =
|
|
170
170
|
typeof record.ceiling === 'number' ? record.ceiling : undefined
|
|
171
|
+
const entryCap =
|
|
172
|
+
typeof record.entryCap === 'number' ? record.entryCap : undefined
|
|
171
173
|
|
|
172
174
|
let unverifiable = 0
|
|
173
175
|
let unchecked = 0
|
|
@@ -188,6 +190,10 @@ function architectureCounts(
|
|
|
188
190
|
...(ceiling !== undefined && {
|
|
189
191
|
recordOverLength: record.lines > ceiling ? 1 : 0,
|
|
190
192
|
}),
|
|
193
|
+
// Absent on a record stating no entry cap, for the same reason.
|
|
194
|
+
...(entryCap !== undefined && {
|
|
195
|
+
recordOverCount: decisions.length > entryCap ? 1 : 0,
|
|
196
|
+
}),
|
|
191
197
|
recordUnverifiable: unverifiable,
|
|
192
198
|
recordUnchecked: unchecked,
|
|
193
199
|
}
|
package/src/autoship/paths.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* application three times, so the set moved here and the body now calls a verb
|
|
8
8
|
* that reads it. Being machine-parsed makes it permanently exempt from any
|
|
9
9
|
* later design that folds a standard back into the surface citing it, per the
|
|
10
|
-
* machine-parsed clause in `canon/
|
|
10
|
+
* machine-parsed clause in `canon/context/standards/resolution.md`.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -42,6 +42,11 @@ export const AUTHORING_CASES: readonly SkillCase[] = [
|
|
|
42
42
|
'Write a brand-new docs page for the capture command, nothing under docs/ covers it yet.',
|
|
43
43
|
expect: 'draft-docs',
|
|
44
44
|
},
|
|
45
|
+
{
|
|
46
|
+
prompt:
|
|
47
|
+
'I already wrote the finished skill files, package them as a ready folder with a plan and a task for a worker to copy.',
|
|
48
|
+
expect: 'draft-ready',
|
|
49
|
+
},
|
|
45
50
|
{
|
|
46
51
|
prompt: 'This project has no README.md at all, write one from scratch.',
|
|
47
52
|
expect: 'draft-readme',
|
package/src/claude/cases/misc.ts
CHANGED
|
@@ -12,6 +12,11 @@ export const MISC_CASES: readonly SkillCase[] = [
|
|
|
12
12
|
'Show me a few different treatments for this callout so I can pick one by looking.',
|
|
13
13
|
expect: 'draft-and-pick',
|
|
14
14
|
},
|
|
15
|
+
{
|
|
16
|
+
prompt:
|
|
17
|
+
'Which layer of this landing page should I settle first, and what has to stay constant across it?',
|
|
18
|
+
expect: 'design-taste',
|
|
19
|
+
},
|
|
15
20
|
{
|
|
16
21
|
prompt:
|
|
17
22
|
'This project has no logo yet. Draft one and give me a social card to go with it.',
|
|
@@ -29,6 +34,11 @@ export const MISC_CASES: readonly SkillCase[] = [
|
|
|
29
34
|
{
|
|
30
35
|
prompt:
|
|
31
36
|
"We're about to hit the context limit, write the handoff before we lose state.",
|
|
37
|
+
expect: 'session-compact',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
prompt:
|
|
41
|
+
'I am the orchestrating session and about to compact, so write the board handoff as a session map on the task board.',
|
|
32
42
|
expect: 'session-map',
|
|
33
43
|
},
|
|
34
44
|
{
|
package/src/commands/capture.ts
CHANGED
|
@@ -42,6 +42,7 @@ type CaptureResult = Awaited<ReturnType<Renderer['captureSources']>>[number]
|
|
|
42
42
|
export function register(program: Command): void {
|
|
43
43
|
program
|
|
44
44
|
.command('capture')
|
|
45
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
45
46
|
.description('Render HTML capture sources to PNG')
|
|
46
47
|
.argument(
|
|
47
48
|
'[source]',
|
package/src/commands/context.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
architectureRel,
|
|
21
21
|
type ArchitectureReport,
|
|
22
22
|
coveredCount,
|
|
23
|
+
isOverCount,
|
|
23
24
|
isOverLength,
|
|
24
25
|
measureArchitecture,
|
|
25
26
|
testableCount,
|
|
@@ -98,7 +99,7 @@ export function register(program: Command): void {
|
|
|
98
99
|
context
|
|
99
100
|
.command('audit')
|
|
100
101
|
.description(
|
|
101
|
-
'Report required sections, entry length, citations, reference form, catalog tables, provenance, superseded-decision narration, index drift, the architecture record against its own ceiling and its word weight, and wireframe states against their evidence folders',
|
|
102
|
+
'Report required sections, entry length, citations, reference form, catalog tables, provenance, superseded-decision narration, index drift, the architecture record against its own ceiling, its own entry cap, and its word weight, and wireframe states against their evidence folders',
|
|
102
103
|
)
|
|
103
104
|
.argument('[path]', 'Project root, defaulting to the current directory')
|
|
104
105
|
.helpOption('-h, --help', 'Show this help message')
|
|
@@ -124,9 +125,11 @@ export function register(program: Command): void {
|
|
|
124
125
|
'An unresolved citation always gates. An architecture record that',
|
|
125
126
|
'states its own line allowances gates when it is past the ceiling',
|
|
126
127
|
'those derive, on any run except --citations-only, which never',
|
|
127
|
-
'measures it. A record stating
|
|
128
|
-
'
|
|
129
|
-
'
|
|
128
|
+
'measures it. A record stating an entry cap gates the same way when',
|
|
129
|
+
'it holds more decisions than the cap. A record stating neither is',
|
|
130
|
+
'reported and never gated. --gate widens the gate to the other two',
|
|
131
|
+
'findings that are facts rather than judgments: a missing required',
|
|
132
|
+
'section and index',
|
|
130
133
|
'drift. Entry length, reference form, table, provenance, narration,',
|
|
131
134
|
'and the record claim classification are judgments under both.',
|
|
132
135
|
'',
|
|
@@ -759,6 +762,7 @@ async function runAudit(
|
|
|
759
762
|
const gating = isGating({
|
|
760
763
|
unresolvedCitations: citations.unresolved.length,
|
|
761
764
|
recordOverLength: record !== undefined && isOverLength(record),
|
|
765
|
+
recordOverCount: record !== undefined && isOverCount(record),
|
|
762
766
|
sections,
|
|
763
767
|
drift,
|
|
764
768
|
wireframes,
|
|
@@ -1256,6 +1260,20 @@ function reportRecord(
|
|
|
1256
1260
|
)
|
|
1257
1261
|
}
|
|
1258
1262
|
|
|
1263
|
+
if (report.entryCap === undefined) {
|
|
1264
|
+
logInfo(
|
|
1265
|
+
`${plural(decisions, 'decision')} and no entry cap stated, so the count is reported and never gated.`,
|
|
1266
|
+
)
|
|
1267
|
+
} else if (isOverCount(report)) {
|
|
1268
|
+
logError(
|
|
1269
|
+
`${plural(decisions, 'decision')} against a cap of ${report.entryCap}. Merge two or retire one, never compress.`,
|
|
1270
|
+
)
|
|
1271
|
+
} else {
|
|
1272
|
+
logInfo(
|
|
1273
|
+
`${plural(decisions, 'decision')} against a cap of ${report.entryCap}.`,
|
|
1274
|
+
)
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1259
1277
|
if (decisions === 0) {
|
|
1260
1278
|
logWarn('The record declares no decision, so nothing was classified.')
|
|
1261
1279
|
return
|
package/src/commands/design.ts
CHANGED
|
@@ -26,12 +26,14 @@ import { mainWorktreeRoot } from '@/worktree'
|
|
|
26
26
|
export function register(program: Command): void {
|
|
27
27
|
const design = program
|
|
28
28
|
.command('design')
|
|
29
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
29
30
|
.description(
|
|
30
31
|
'Design system commands (regen, css, render, board, install, sync)',
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
design
|
|
34
35
|
.command('regen')
|
|
36
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
35
37
|
.description(
|
|
36
38
|
'Rewrite canon/DESIGN.md and the base stylesheet from src/design/tokens.ts',
|
|
37
39
|
)
|
|
@@ -84,6 +86,7 @@ export function register(program: Command): void {
|
|
|
84
86
|
|
|
85
87
|
design
|
|
86
88
|
.command('css')
|
|
89
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
87
90
|
.description('Emit the design tokens and components as CSS on stdout')
|
|
88
91
|
.option(
|
|
89
92
|
'--no-components',
|
|
@@ -104,6 +107,7 @@ export function register(program: Command): void {
|
|
|
104
107
|
|
|
105
108
|
design
|
|
106
109
|
.command('render')
|
|
110
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
107
111
|
.description('Render DESIGN.md tokens to HTML and CSS preview')
|
|
108
112
|
.option(
|
|
109
113
|
'-s, --source <path>',
|
|
@@ -137,6 +141,7 @@ export function register(program: Command): void {
|
|
|
137
141
|
|
|
138
142
|
design
|
|
139
143
|
.command('board')
|
|
144
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
140
145
|
.description(
|
|
141
146
|
'Generate the design board, an index over a project’s design surfaces',
|
|
142
147
|
)
|
package/src/commands/feedback.ts
CHANGED
|
@@ -71,6 +71,7 @@ function writeLocal(body: string, mismatch: string | undefined): string {
|
|
|
71
71
|
export function register(program: Command): void {
|
|
72
72
|
program
|
|
73
73
|
.command('feedback')
|
|
74
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
74
75
|
.description(
|
|
75
76
|
'Write toolkit feedback from stdin to .canon/feedback/, or open a GitHub issue with --github',
|
|
76
77
|
)
|
package/src/commands/slides.ts
CHANGED
|
@@ -11,10 +11,12 @@ import { intro, outro, palette } from '@/ui'
|
|
|
11
11
|
export function register(program: Command): void {
|
|
12
12
|
const slides = program
|
|
13
13
|
.command('slides')
|
|
14
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
14
15
|
.description('Slide deck commands (render, list)')
|
|
15
16
|
|
|
16
17
|
slides
|
|
17
18
|
.command('render')
|
|
19
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
18
20
|
.description('Render a SLIDES.md source into a PowerPoint deck')
|
|
19
21
|
.option('-s, --source <path>', 'Source SLIDES.md path', '.claude/SLIDES.md')
|
|
20
22
|
.option(
|
|
@@ -82,6 +84,7 @@ export function register(program: Command): void {
|
|
|
82
84
|
|
|
83
85
|
slides
|
|
84
86
|
.command('list')
|
|
87
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
85
88
|
.description('List the available slide layouts')
|
|
86
89
|
.option('--json', 'Output the layout catalog as JSON')
|
|
87
90
|
.action((opts: { json?: boolean }) => {
|
|
@@ -25,6 +25,7 @@ export async function resolveOutDir(opts: TranscriptOptions): Promise<string> {
|
|
|
25
25
|
export function register(program: Command): void {
|
|
26
26
|
program
|
|
27
27
|
.command('transcripts <url>')
|
|
28
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
28
29
|
.description('Fetch a YouTube transcript with metadata frontmatter')
|
|
29
30
|
.option(
|
|
30
31
|
'-o, --out <path>',
|