@erclx/canon 5.48.0 → 5.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/auto-ship/SKILL.md +1 -1
- package/claude/skills/canvas-board/REQUIREMENT.md +4 -3
- package/claude/skills/canvas-board/SKILL.md +8 -9
- 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 +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 +31 -16
- package/claude/skills/plan-feature/references/batches.md +5 -5
- package/claude/skills/plan-feature/references/plan.md +82 -33
- package/claude/skills/plan-groundwork/SKILL.md +8 -0
- package/claude/skills/plan-groundwork/references/groundwork.md +2 -1
- package/claude/skills/plan-intake/SKILL.md +8 -0
- package/claude/skills/plan-intake/references/intake.md +9 -1
- package/claude/skills/plan-ready/REQUIREMENT.md +3 -3
- package/claude/skills/plan-ready/SKILL.md +3 -3
- package/claude/skills/plan-ready/references/assembly.md +7 -7
- package/claude/skills/plan-ready/references/ready.md +8 -6
- package/claude/skills/review-ui/REQUIREMENT.md +1 -1
- package/claude/skills/review-ui/SKILL.md +4 -11
- package/claude/skills/review-ui/references/close-out.md +1 -1
- package/claude/skills/review-ui/references/drive-copy.md +38 -0
- package/claude/skills/review-ui/references/post.md +24 -3
- package/claude/skills/review-ui/references/verb-absent.md +18 -0
- package/claude/skills/role-orchestrator/SKILL.md +3 -2
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-hand-launch.md +60 -0
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +5 -0
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +1 -1
- package/claude/skills/role-planner/SKILL.md +1 -1
- package/claude/skills/role-worker/SKILL.md +4 -2
- package/claude/skills/session-compact/references/handoff-note.md +3 -1
- package/claude/skills/session-map/references/session.md +1 -1
- package/claude/skills/session-worktree/SKILL.md +1 -1
- package/claude/skills/task-board/references/board.md +2 -2
- package/claude/skills/task-board/references/tasks.md +1 -1
- package/docs/agents/index.md +1 -1
- package/docs/agents/markdown-audit.md +5 -2
- package/docs/agents/records.md +1 -1
- package/docs/target/migrations.md +7 -0
- package/docs/target/projects.md +1 -1
- package/docs/workflow/parallel-features.md +2 -2
- package/docs/workflow/skill-map.md +2 -1
- package/docs/workflow/visual-design-workflow.md +2 -1
- package/governance/ledger/ui/design-reference.md +1 -0
- package/governance/rules/ui/design-reference.md +1 -0
- package/package.json +1 -1
- package/src/claude/cases/misc.ts +5 -0
- package/src/commands/claude.ts +26 -1
- package/src/commands/gate.ts +57 -16
- package/src/commands/markdown.ts +98 -19
- package/src/commands/pr.ts +411 -23
- package/src/commands/records.ts +45 -8
- package/src/commands/tasks.ts +12 -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/hooks/post-merge.ts +11 -0
- 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/pr/drive-copy.ts +218 -0
- package/src/pr/local.ts +26 -0
- package/src/pr/rest.ts +2 -0
- package/src/records/structure.ts +147 -0
- package/src/records/validate.ts +113 -12
- package/src/tasks/archive.ts +137 -4
- package/src/tasks/board.ts +68 -13
- package/src/tasks/validate.ts +54 -14
- package/tooling/base/configs/.github/workflows/pr-local-preview.yml +1 -1
- 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/claude/skills/{canvas-board → design-record}/references/design.md +0 -0
|
@@ -38,10 +38,11 @@ No surface carries cross-version sequencing, so report none, since reasoning spa
|
|
|
38
38
|
|
|
39
39
|
## Runbooks on request
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
The human asks for each side of a compaction, and this skill reads the runbook.
|
|
42
42
|
|
|
43
43
|
- On a request to write the handoff, follow `${CLAUDE_SKILL_DIR}/references/orchestrator-handoff.md`. It sends the generic half to `canon:session-map` and adds decisions taken under delegated authority.
|
|
44
44
|
- That map follows `${CLAUDE_SKILL_DIR}/../session-map/references/session.md`. Write nothing to the handoff that the board, a task file, or a groundwork folder already carries.
|
|
45
|
+
- Hand launch: `${CLAUDE_SKILL_DIR}/references/orchestrator-hand-launch.md`
|
|
45
46
|
- A lesson about how to work goes to the owning task's Findings, as a bold-labeled line per `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md`
|
|
46
47
|
- On a request to resume after a compaction, read `${CLAUDE_SKILL_DIR}/references/orchestrator-resume.md`, which reads that file back with the board and the groundwork behind the live work
|
|
47
48
|
|
|
@@ -108,7 +109,7 @@ The handback runbook holds how the message is addressed and worded, what to do w
|
|
|
108
109
|
|
|
109
110
|
### Constraints from the tracks in flight
|
|
110
111
|
|
|
111
|
-
A plan written here targets a tree several branches are changing, so it names each in-flight track's file set as a constraint, read from
|
|
112
|
+
A plan written here targets a tree several branches are changing, so it names each in-flight track's file set as a constraint, read from the plan its row links. State for each set which of the two acts it forbids, per Constraints in `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`.
|
|
112
113
|
|
|
113
114
|
- A bare path list leaves the worker guessing, and can end up forbidding the repair of a citation the change broke
|
|
114
115
|
- A shared path forbids an act only where the conflict check in `orchestrator-dispatch.md` holds the pair. Otherwise it names what the second branch to merge rebases over.
|
|
@@ -125,7 +125,7 @@ No practitioner source serializes on a shared file. Each builds in parallel and
|
|
|
125
125
|
Read `canon sessions list --json` for the branches in flight as well as the board, since a hand-launched track carries no row, and read the plan of any branch no row names.
|
|
126
126
|
|
|
127
127
|
- `canon tasks validate` and `canon tasks plan-reach` report the paths two rows share, a candidate list for the five holds and the paths a dispatch names
|
|
128
|
-
- A shared folder means a
|
|
128
|
+
- A shared folder means a plan declared it bare, so narrow that plan's list rather than reading the folder as a sweep
|
|
129
129
|
- A declared set predicts rather than bounds, since `canon:context-fold`, `canon:docs-sync`, and the drift stages write past every plan
|
|
130
130
|
- Such a crossing changes what the second branch rebases over, not whether a row holds, and `canon tasks plan-reach` at `canon:git-ship` step 5 names it
|
|
131
131
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Orchestrator hand launch
|
|
3
|
+
description: The one lean prompts file the orchestrator writes when the operator launches sessions by hand, with its path, overwrite rule, shape, and a filled example
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Run this when the operator launches sessions by hand rather than through `claude --bg`. The rules every launch shares stay in `orchestrator-launch.md`.
|
|
7
|
+
|
|
8
|
+
# Hand launch
|
|
9
|
+
|
|
10
|
+
When the operator launches sessions by hand, write every prompt to one file, `.canon/tmp/dispatch/prompts.md`, and overwrite it at each wave. It lists every row with a session not yet launched. A row launched from an older copy and again from a newer one would build twice, and the branch check in `orchestrator-dispatch.md` catches that.
|
|
11
|
+
|
|
12
|
+
The operator opens the file, reads one lead line, and pastes one fence per session. The shape is fixed:
|
|
13
|
+
|
|
14
|
+
- An H1, `# Dispatch prompts`, then one lead line carrying the measured head, the merge order, and the folder to open each session in
|
|
15
|
+
- An H2 per task as `## <label>: <title>`, an H3 per role as `### Worker` or `### Reviewer`, a `Model: <model>.` line, and a `text` fence
|
|
16
|
+
- In the fence, the command at position zero, then `Controller: <sessionId>`, then only context the plan does not carry, one line each
|
|
17
|
+
|
|
18
|
+
Keep the command at position zero per `### Position zero` in `orchestrator-launch.md`, and carry only what `### What the brief may carry` there allows. Leave out the answer-slot default and anything else the plan states.
|
|
19
|
+
|
|
20
|
+
The `Controller:` line replaces the background template's message clauses, which `role-worker` and `session-relay` already own. `auto-ship` Step 0 loads `role-worker`, so the fence needs nothing more.
|
|
21
|
+
|
|
22
|
+
- A reviewer launched by hand takes the `### Reviewer` H3 under its row's H2, with the sibling facts `orchestrator-reviewer-launch.md` lists
|
|
23
|
+
- Its `<number>` exists once the worker opened its pull request, so a later wave carries `### Reviewer` alone under the row, and the launched worker's H3 is gone
|
|
24
|
+
|
|
25
|
+
The first wave carries the worker. The wave after its pull request opens carries the reviewer alone.
|
|
26
|
+
|
|
27
|
+
````markdown
|
|
28
|
+
# Dispatch prompts
|
|
29
|
+
|
|
30
|
+
Head `<head>`. Merge `<label-a>` first, then `<label-b>`. Open each session in the repository root.
|
|
31
|
+
|
|
32
|
+
## <label-b>: <title>
|
|
33
|
+
|
|
34
|
+
### Worker
|
|
35
|
+
|
|
36
|
+
Model: opus.
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
/canon:auto-ship .canon/plans/feature-<slug>.md
|
|
40
|
+
Controller: <sessionId>
|
|
41
|
+
<branch-a> edits the section of <file> this plan leaves alone. Rebase over it when told.
|
|
42
|
+
```
|
|
43
|
+
````
|
|
44
|
+
|
|
45
|
+
````markdown
|
|
46
|
+
# Dispatch prompts
|
|
47
|
+
|
|
48
|
+
Head `<head>`. Merge `<label-a>` first, then `<label-b>`. Open each session in the repository root.
|
|
49
|
+
|
|
50
|
+
## <label-b>: <title>
|
|
51
|
+
|
|
52
|
+
### Reviewer
|
|
53
|
+
|
|
54
|
+
Model: opus.
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
Run /canon:role-reviewer, then /canon:review-pr <number>
|
|
58
|
+
Controller: <sessionId>
|
|
59
|
+
```
|
|
60
|
+
````
|
|
@@ -12,6 +12,7 @@ Run this once `orchestrator-dispatch.md` has cleared a row, or from its planning
|
|
|
12
12
|
- [Dispatch to address a review](#dispatch-to-address-a-review)
|
|
13
13
|
- [Dispatch to plan a row](#dispatch-to-plan-a-row)
|
|
14
14
|
- [Dispatch a talk session](#dispatch-a-talk-session)
|
|
15
|
+
- [Hand launch](#hand-launch)
|
|
15
16
|
- [Dispatch a reviewer](#dispatch-a-reviewer)
|
|
16
17
|
|
|
17
18
|
## Dispatch
|
|
@@ -156,6 +157,10 @@ Read the session's `sessionId` off the launch output, carry it in this session's
|
|
|
156
157
|
|
|
157
158
|
The map exists only once the handoff writes it, so this session's own launches, read with the prior map, are what the first wind-down reads talk sessions from.
|
|
158
159
|
|
|
160
|
+
## Hand launch
|
|
161
|
+
|
|
162
|
+
Read `${CLAUDE_SKILL_DIR}/references/orchestrator-hand-launch.md` when the operator launches sessions by hand. It states the single prompts file at `.canon/tmp/dispatch/prompts.md`, the lean shape of each fence, and a filled example, while the background templates above keep their full controller paragraph.
|
|
163
|
+
|
|
159
164
|
## Dispatch a reviewer
|
|
160
165
|
|
|
161
166
|
Read `${CLAUDE_SKILL_DIR}/references/orchestrator-reviewer-launch.md` when `orchestrator-review-dispatch.md` sends a pull request to a dispatched reviewer, for the code, cloud, and UI reviewer shapes.
|
|
@@ -32,7 +32,7 @@ The blocker cell states what the row waits on, and each kind is tested different
|
|
|
32
32
|
|
|
33
33
|
### A hold behind a track in flight
|
|
34
34
|
|
|
35
|
-
The validator tests the file the cell cites against the
|
|
35
|
+
The validator tests the file the cell cites against the files every `## Run now` row's plans declare. A track that merged since the row was parked is no longer in flight, whatever the sets still share.
|
|
36
36
|
|
|
37
37
|
- Re-test the cell under the conflict check in `orchestrator-dispatch.md` too, since a row parked on a shared file alone holds on nothing and moves up
|
|
38
38
|
- A cell naming the file in prose rather than backticks cites nothing, so rewrite the hold in the board's spelling and the next run picks it up
|
|
@@ -9,7 +9,7 @@ Run this once `orchestrator-review-dispatch.md` has sent a pull request to a dis
|
|
|
9
9
|
|
|
10
10
|
`review-pr` is a single pass rather than a role, so a launch naming it alone reaches no `role-reviewer` and owes no message. Reach the role first on this launch, the way the planning shape in `orchestrator-launch.md` reaches `role-planner`. `orchestrator-review-dispatch.md` decides whether a pull request takes this shape at all.
|
|
11
11
|
|
|
12
|
-
No branch and no worktree are entered here. The reviewer reads the pull request at the head `review-pr` resolves and writes the comment and its body file alone.
|
|
12
|
+
No branch and no worktree are entered here. The reviewer reads the pull request at the head `review-pr` resolves and writes the comment and its body file alone. A reviewer the operator launches by hand takes the `### Reviewer` H3 that `orchestrator-hand-launch.md` states.
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
claude --bg --model <model> -n "reviewer-<project>-<number>" "Run /canon:role-reviewer, then /canon:review-pr <number>. Your controller is the session whose sessionId is <dispatcher-id>. Resolve its current name from that id through canon sessions list --json, which carries sessionId per row, at the moment you send, and never resolve an addressee by name prefix. When the pass posts under ## Review, message the worker holding the branch to run review-address, carrying your own sessionId, and send it to the controller instead when no worker resolves. When it posts ## Review closed, run canon pr lift <number> --json and message the controller with the heading, the count line, and the reason the verb returned, unless that reason names the UI review as still open or missing. Message the controller again if you stop on a question."
|
|
@@ -39,7 +39,7 @@ Each item below is something a record needed and a launch string did not carry.
|
|
|
39
39
|
- The task file, and its `## Findings` before deciding anything, since a row can carry its own disproof under a title still stating the original claim
|
|
40
40
|
- The source files themselves, opened rather than summarized. Never a count quoted from the task file, which goes wrong or stale.
|
|
41
41
|
- `CLAUDE.md` and the context entries the row touches, for the decision and the alternative it was taken against rather than for the decision alone
|
|
42
|
-
- `.canon/tasks/priority.md` for the row's
|
|
42
|
+
- `.canon/tasks/priority.md` for the row's blocker and the plan each running row links. Read those plans for the constraint block's shared paths and holds, never as a gate a plan must clear.
|
|
43
43
|
|
|
44
44
|
### Reads for a feature plan alone
|
|
45
45
|
|
|
@@ -50,6 +50,8 @@ A painted pull request needs a server in this worktree for `canon pr local` to f
|
|
|
50
50
|
- Wait up to 60 seconds for the verb to read `ok`, then ship either way. A stack with no `dev` script gets no start, and `pr-evidence` reports `no-server`.
|
|
51
51
|
- Never detach it with `nohup`, `setsid`, or `disown`, since the owning session is the stop. `canon worktrees reclaim` ends any listener left in a worktree it removes.
|
|
52
52
|
|
|
53
|
+
Run `canon pr drive-copy --json` before starting `dev`. On `copied`, start it with `env` set to `copyRoot`, so the operator's address serves the copy. On `no-config`, start it as it is. On a refusal, start nothing and send the controller the `reason`, since a broken config would send drives into tracked folders.
|
|
54
|
+
|
|
53
55
|
A hand-run `git-ship` or `git-pr` starts no server, since only a worker has a lifetime to own one.
|
|
54
56
|
|
|
55
57
|
### Building against a design reference
|
|
@@ -86,7 +88,7 @@ The controlling session cannot watch this one build, so the messages below are o
|
|
|
86
88
|
|
|
87
89
|
### The pull request announcement
|
|
88
90
|
|
|
89
|
-
Announce the pull request as the ship chain's pull request step returns, carrying the number, the branch,
|
|
91
|
+
Announce the pull request as the ship chain's pull request step returns, carrying the number, the branch, the head sha, the CI state, the task it closes, and each departure from the plan. That transition is the one moment only this session knows, and the controller's review poll no longer starts on a dispatch.
|
|
90
92
|
|
|
91
93
|
- Carry the reach in that message, being the paths this branch wrote that another live plan or `## Run now` row holds, and who holds each, which `canon tasks plan-reach` reports as `claimed`
|
|
92
94
|
- Report each as a rebase the controller should order, never as a breach, since sharing a path is not a hold
|
|
@@ -123,7 +125,7 @@ A worker that improvised this read polled issue comments, never saw a UI close p
|
|
|
123
125
|
|
|
124
126
|
### Addressing
|
|
125
127
|
|
|
126
|
-
Address the session the launch named, and the reviewer by its dispatch's sessionId, each as a `sessionId` rather than a name. `canon:session-relay` turns that id into an address and carries the send, falling back to a copyable block where no send tool exists.
|
|
128
|
+
Address the session the launch named, and the reviewer by its dispatch's sessionId, each as a `sessionId` rather than a name. `canon:session-relay` turns that id into an address and carries the send, falling back to a copyable block where no send tool exists. A `Controller: <sessionId>` line names the controller.
|
|
127
129
|
|
|
128
130
|
Ask the operator when the launch named nobody and a person is there, putting candidate rows through the structured question surface. The halt is the cheaper error, since a message sent to the wrong session reports success and is lost.
|
|
129
131
|
|
|
@@ -39,7 +39,9 @@ Name what was not touched too. A session that changed no source says so, since a
|
|
|
39
39
|
|
|
40
40
|
One bullet per decision, each naming what it beat. A decision with no rejected alternative reads as arbitrary and gets reopened by the next session that dislikes it.
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
- Keep each bullet under 200 characters, and give a decision needing more an H3 of its own and a short paragraph
|
|
43
|
+
- The note holds to the six criteria under `### Structure` in `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`
|
|
44
|
+
- Where a decision rests on a measurement, give the number rather than the conclusion. `27 spacing values became 7` survives a reader disagreeing with it, where `the spacing was tidied` does not.
|
|
43
45
|
|
|
44
46
|
### `## What is open`
|
|
45
47
|
|
|
@@ -66,7 +66,7 @@ Three sections form the core, in this order, and every session fills all three.
|
|
|
66
66
|
- `## Mistakes worth not repeating`: what went wrong and the rule it yields
|
|
67
67
|
- `## Standing cautions`: commands that lie, tools that measure the wrong tree, and anything unbacked
|
|
68
68
|
|
|
69
|
-
Add a section only for content that fits none of the three and would otherwise be lost.
|
|
69
|
+
Add a section only for content that fits none of the three and would otherwise be lost. Every section holds to the six criteria under `### Structure` in `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`.
|
|
70
70
|
|
|
71
71
|
## Writing one
|
|
72
72
|
|
|
@@ -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
|
---
|
|
@@ -91,9 +91,9 @@ A collision with running work blocks a task only where no merge settles it, bein
|
|
|
91
91
|
|
|
92
92
|
### The `Touches` cell
|
|
93
93
|
|
|
94
|
-
A `Touches` cell
|
|
94
|
+
A `Touches` cell counts its plans' declared files and names the two fullest folders, as in ``12 files under `src/tasks/`, `claude/skills/` ``. The linked plan holds the file list, which the overlap checks read.
|
|
95
95
|
|
|
96
|
-
- `canon tasks plan-reach <plan>` reads the
|
|
96
|
+
- `canon tasks plan-reach <plan>` reads the reach a running branch grew to
|
|
97
97
|
- Change the plan's list to change the cell. A worker never edits this board.
|
|
98
98
|
|
|
99
99
|
### Cell width
|
|
@@ -83,7 +83,7 @@ Two headings, `## Outcomes` and `## Findings`. Outcomes are future and checkable
|
|
|
83
83
|
|
|
84
84
|
- Add no third heading. Status stays inline on an outcome rather than becoming an "In progress" section.
|
|
85
85
|
- Outcomes stay a checklist, since the board tools read them by their `- [ ]` line
|
|
86
|
-
- Open each finding with a bold label naming its source and date, as in `- **planner, 2026-10-11:** <finding>`.
|
|
86
|
+
- Open each finding with a bold label naming its source and date, as in `- **planner, 2026-10-11:** <finding>`. Past 200 characters it becomes a paragraph, and the plan holds anything longer.
|
|
87
87
|
|
|
88
88
|
### Sizing outcomes
|
|
89
89
|
|
package/docs/agents/index.md
CHANGED
|
@@ -12,7 +12,7 @@ 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
|
|
@@ -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/records.md
CHANGED
|
@@ -57,5 +57,5 @@ Branch on `reason` in the `--json` record, not the exit code.
|
|
|
57
57
|
## See also
|
|
58
58
|
|
|
59
59
|
- `canon records ordinal <kind> <slug> --claim`: reserve the next intake or groundwork ordinal
|
|
60
|
-
- `canon records validate <kind> --help`: where a record
|
|
60
|
+
- `canon records validate <kind> [record] --help`: where a record disagrees with its standard, plus a `structure` report that never sets the exit
|
|
61
61
|
- `canon records stale --help`: when the requirements record was last reviewed
|
|
@@ -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
|
|
|
@@ -32,7 +32,7 @@ Assert the orchestrator role in one warm session with `canon:role-orchestrator`.
|
|
|
32
32
|
|
|
33
33
|
- To pick otherwise, rewrite that line as `overridden at execution to <pick>,` plus the measurement, and add one line to the open task's `## Findings`
|
|
34
34
|
|
|
35
|
-
- `canon records validate plans` reports a plan breaking its standard, and takes `groundwork`, `intake`, and `teach`
|
|
35
|
+
- `canon records validate plans [plan]` reports a plan breaking its standard, plus `structure` misses that never set the exit, and takes `groundwork`, `intake`, and `teach`
|
|
36
36
|
- `canon records push` and `canon records pull` carry `.canon/` to and from a private repository. See [records](../agents/records.md).
|
|
37
37
|
- `canon records size`, `canon records prune-tmp`, and `canon records stale canonical` report folder size, idle scratch, and releases since the requirements review
|
|
38
38
|
- `canon tasks archive` moves a shipped plan to `.canon/plans/archive/` and retargets the task's `Plan:` line
|
|
@@ -58,6 +58,6 @@ Merge order matters only when two branches touch the same file. Expect to rebase
|
|
|
58
58
|
- A parked row is reported when its cited task reached the trunk, whether the cell links it or names its bare label
|
|
59
59
|
- A group header must be the columns the group owns, a `Touches` cell must match its plan, and a `Waiting on` cell renders at most 120 characters
|
|
60
60
|
|
|
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
|
|
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
|
`CANON_SKIP_RECLAIM`, `CANON_SKIP_UPGRADE`, and `CANON_SKIP_PLUGIN_UPDATE` turn off the three hook steps that act on the machine.
|
|
@@ -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.
|
|
@@ -12,6 +12,7 @@ The rule states canon's own design handoff, generalized from Frameboard's projec
|
|
|
12
12
|
| Bullet | Source | Read |
|
|
13
13
|
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
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 |
|
|
15
16
|
| Land a design first | House choice: a design landing inside a feature pull request is reviewed as code, and a second feature building against it meanwhile reads a design nobody accepted | 2026-10-18 |
|
|
16
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 |
|
|
17
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, and a stale row keeps a removed surface's | 2026-10-18 |
|
|
@@ -17,6 +17,7 @@ paths:
|
|
|
17
17
|
## Where the design lives
|
|
18
18
|
|
|
19
19
|
- Build UI against `canon/design/`, never against the canvas directly.
|
|
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.
|
|
20
21
|
- Land a picked or changed canvas design in `canon/design/` through the project's design sync, as its own pull request, before any feature builds against it.
|
|
21
22
|
- Never copy, edit, or delete a reference file under `canon/design/` by hand.
|
|
22
23
|
- Add a new surface's row to the index in `canon/design/` before running the sync, and remove it only in the change that removes the surface.
|
package/package.json
CHANGED
package/src/claude/cases/misc.ts
CHANGED
|
@@ -39,6 +39,11 @@ export const MISC_CASES: readonly SkillCase[] = [
|
|
|
39
39
|
'Draw the signup screen as a frame on the canvas, then restyle the element I just selected there.',
|
|
40
40
|
expect: 'canvas-board',
|
|
41
41
|
},
|
|
42
|
+
{
|
|
43
|
+
prompt:
|
|
44
|
+
'The operator picked the new settings panel, so land it in canon/design with an index row for it.',
|
|
45
|
+
expect: 'design-record',
|
|
46
|
+
},
|
|
42
47
|
{
|
|
43
48
|
prompt:
|
|
44
49
|
"We're about to hit the context limit, write the handoff before we lose state.",
|
package/src/commands/claude.ts
CHANGED
|
@@ -71,6 +71,9 @@ import {
|
|
|
71
71
|
serializeSettings,
|
|
72
72
|
writeSettings,
|
|
73
73
|
} from '@/claude/settings'
|
|
74
|
+
import { bodyLines } from '@/markdown/scan'
|
|
75
|
+
import { scopedDensityFindings } from '@/markdown/scope'
|
|
76
|
+
import { CAP } from '@/markdown/stacks'
|
|
74
77
|
import { readSessionDefaults } from '@/migrate/session-defaults'
|
|
75
78
|
import { copyPreservingMode } from '@/sync/copy'
|
|
76
79
|
import { checkoutMismatchWarning, PROJECT_ROOT } from '@/roots/project'
|
|
@@ -176,6 +179,22 @@ export function seededDirPath(resolved: string, name: string): string {
|
|
|
176
179
|
? surfaceDir(resolved, name)
|
|
177
180
|
: recordDir(resolved, name)
|
|
178
181
|
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Each bullet run past the limit in the target's `CLAUDE.md` Key paths, read
|
|
185
|
+
* through the scope the audit gates on. Sync warns rather than gates, since a
|
|
186
|
+
* target gates only the prefixes its own config lists.
|
|
187
|
+
*/
|
|
188
|
+
export async function longKeyPathsRuns(
|
|
189
|
+
resolved: string,
|
|
190
|
+
): Promise<{ line: number; count: number }[]> {
|
|
191
|
+
const path = join(resolved, 'CLAUDE.md')
|
|
192
|
+
if (!existsSync(path)) return []
|
|
193
|
+
const lines = bodyLines(await readFile(path, 'utf8'))
|
|
194
|
+
return scopedDensityFindings('CLAUDE.md', lines)
|
|
195
|
+
.filter((finding) => finding.kind === 'bullet-run')
|
|
196
|
+
.map((finding) => ({ line: finding.line, count: finding.value ?? 0 }))
|
|
197
|
+
}
|
|
179
198
|
const USER_DIR = join('tooling', 'claude', 'user')
|
|
180
199
|
const STATUSLINE = 'statusline-command.sh'
|
|
181
200
|
const PLUGIN_MANIFEST = join('claude', '.claude-plugin', 'plugin.json')
|
|
@@ -244,7 +263,8 @@ export function register(program: Command): void {
|
|
|
244
263
|
'Writes .gitignore alone: adds the entries the claude manifest lists',
|
|
245
264
|
'and drops ones under its headers that the manifest no longer lists.',
|
|
246
265
|
'A seeded file is reported, never rewritten, and a missing one points',
|
|
247
|
-
'at `canon claude init`.',
|
|
266
|
+
'at `canon claude init`. A CLAUDE.md Key paths running past 5 bullets',
|
|
267
|
+
'is reported with the H3 grouping that fixes it.',
|
|
248
268
|
'',
|
|
249
269
|
'Exit codes:',
|
|
250
270
|
' 0 synced, already up to date, or the prompt was cancelled',
|
|
@@ -665,6 +685,11 @@ async function runSync(target: string): Promise<number> {
|
|
|
665
685
|
if (isDirectory(seededDirPath(resolved, name))) logInfo(`${name}/`)
|
|
666
686
|
else logWarn(`${name}/ missing. Run \`canon claude init\``)
|
|
667
687
|
}
|
|
688
|
+
for (const run of await longKeyPathsRuns(resolved)) {
|
|
689
|
+
logWarn(
|
|
690
|
+
`CLAUDE.md:${run.line} runs ${run.count} Key paths bullets past ${CAP}. Group them under H3s, each opening on a lead sentence`,
|
|
691
|
+
)
|
|
692
|
+
}
|
|
668
693
|
|
|
669
694
|
// Sync never rewrites an existing settings file, so a target seeded before
|
|
670
695
|
// the session defaults learns of them only here.
|
package/src/commands/gate.ts
CHANGED
|
@@ -130,22 +130,9 @@ async function runGate(opts: RunCommandOptions): Promise<number> {
|
|
|
130
130
|
process.stderr.write(`${failed.label}: ${failed.failure}\n`)
|
|
131
131
|
}
|
|
132
132
|
process.stdout.write(
|
|
133
|
-
`${JSON.stringify(
|
|
134
|
-
ok: code === 0,
|
|
135
|
-
|
|
136
|
-
scoped: changed.scoped,
|
|
137
|
-
changed: changed.files.length,
|
|
138
|
-
summary,
|
|
139
|
-
ms: results.reduce((total, result) => total + result.ms, 0),
|
|
140
|
-
stages: results.map(({ id, label, status, failure, ms, queuedMs }) => ({
|
|
141
|
-
id,
|
|
142
|
-
label,
|
|
143
|
-
status,
|
|
144
|
-
failure,
|
|
145
|
-
ms,
|
|
146
|
-
queuedMs,
|
|
147
|
-
})),
|
|
148
|
-
})}\n`,
|
|
133
|
+
`${JSON.stringify(
|
|
134
|
+
gateRecord({ ok: code === 0, root, changed, summary, results }),
|
|
135
|
+
)}\n`,
|
|
149
136
|
)
|
|
150
137
|
return code
|
|
151
138
|
}
|
|
@@ -154,6 +141,60 @@ async function runGate(opts: RunCommandOptions): Promise<number> {
|
|
|
154
141
|
return code
|
|
155
142
|
}
|
|
156
143
|
|
|
144
|
+
/** Lines of a failed stage's output the record keeps, counted from the end. */
|
|
145
|
+
const OUTPUT_TAIL_LINES = 200
|
|
146
|
+
|
|
147
|
+
interface GateRecordInput {
|
|
148
|
+
readonly ok: boolean
|
|
149
|
+
readonly root: string
|
|
150
|
+
readonly changed: {
|
|
151
|
+
readonly scoped: boolean
|
|
152
|
+
readonly files: readonly string[]
|
|
153
|
+
}
|
|
154
|
+
readonly summary: Summary
|
|
155
|
+
readonly results: readonly StageResult[]
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The `--json` record. A failed stage carries the tail of the output its
|
|
160
|
+
* checks printed, since a caller reading the record alone otherwise reruns
|
|
161
|
+
* the stage by hand to learn which test failed. The tail rather than a parse
|
|
162
|
+
* of the `FAIL` lines, so a generic stage stays free of one runner's format,
|
|
163
|
+
* and a dot-reporter run prints its failures and summary last.
|
|
164
|
+
*/
|
|
165
|
+
export function gateRecord(input: GateRecordInput) {
|
|
166
|
+
const { ok, root, changed, summary, results } = input
|
|
167
|
+
return {
|
|
168
|
+
ok,
|
|
169
|
+
root,
|
|
170
|
+
scoped: changed.scoped,
|
|
171
|
+
changed: changed.files.length,
|
|
172
|
+
summary,
|
|
173
|
+
ms: results.reduce((total, result) => total + result.ms, 0),
|
|
174
|
+
stages: results.map(
|
|
175
|
+
({ id, label, status, failure, ms, queuedMs, emissions }) => ({
|
|
176
|
+
id,
|
|
177
|
+
label,
|
|
178
|
+
status,
|
|
179
|
+
failure,
|
|
180
|
+
ms,
|
|
181
|
+
queuedMs,
|
|
182
|
+
output: status === 'failed' ? outputTail(emissions) : undefined,
|
|
183
|
+
}),
|
|
184
|
+
),
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function outputTail(emissions: StageResult['emissions']): string {
|
|
189
|
+
const lines = emissions
|
|
190
|
+
.filter((emission) => emission.kind === 'output')
|
|
191
|
+
.map((emission) => emission.text.trimEnd())
|
|
192
|
+
.filter((text) => text !== '')
|
|
193
|
+
.join('\n')
|
|
194
|
+
.split('\n')
|
|
195
|
+
return lines.slice(-OUTPUT_TAIL_LINES).join('\n')
|
|
196
|
+
}
|
|
197
|
+
|
|
157
198
|
function report(result: StageResult): void {
|
|
158
199
|
logStep(result.label)
|
|
159
200
|
for (const emission of result.emissions) {
|