@erclx/canon 4.116.0 → 4.117.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/canon-cli/SKILL.md +12 -12
- package/claude/skills/create-skill/REQUIREMENT.md +3 -0
- package/claude/skills/create-skill/SKILL.md +3 -1
- package/claude/skills/docs-fold/SKILL.md +1 -1
- package/claude/skills/memory-review/SKILL.md +4 -4
- package/claude/skills/review-pr/REQUIREMENT.md +2 -0
- package/claude/skills/review-pr/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/REQUIREMENT.md +2 -0
- package/claude/skills/role-orchestrator/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +14 -14
- package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +2 -2
- package/claude/skills/role-worker/REQUIREMENT.md +2 -0
- package/claude/skills/role-worker/SKILL.md +2 -2
- package/claude/skills/session-compact/REQUIREMENT.md +2 -1
- package/claude/skills/session-compact/SKILL.md +5 -1
- package/claude/skills/write-human/references/source-material.md +2 -2
- package/docs/agents/context-audit-checks.md +1 -1
- package/docs/agents/install-and-sync.md +1 -1
- package/docs/agents/markdown-audit.md +7 -3
- package/docs/agents/skills-audit.md +8 -1
- package/docs/workflow/ai-workflow.md +9 -9
- package/governance/rules/ui/440-surface-capture.md +2 -0
- package/package.json +1 -1
- package/scripts/core/check-capability-seeding.sh +4 -2
- package/src/claude/cases/misc.ts +5 -0
- package/src/claude/skills-audit.ts +59 -2
- package/src/commands/claude.ts +30 -0
- package/src/commands/markdown.ts +63 -2
- package/src/context/audit.ts +6 -8
- package/src/gate/measures.ts +60 -0
- package/src/gate/stages.ts +12 -2
- package/src/markdown/ceiling.ts +70 -0
- package/src/markdown/structure.ts +22 -0
- package/src/tooling/inject.ts +13 -5
- package/src/tooling/manifest.ts +9 -0
- package/src/tooling/scan.ts +23 -6
- package/src/tooling/subfolder.ts +3 -3
- package/standards/context.md +2 -2
- package/standards/markdown.md +2 -0
- package/standards/skill.md +1 -0
- package/tooling/base/reference.md +2 -2
- package/tooling/web/configs/e2e/screenshot.ts +43 -7
- package/tooling/web/reference.md +1 -1
- /package/tooling/base/{configs → seeds}/.github/pull_request_template.md +0 -0
- /package/tooling/base/{configs → seeds}/.github/workflows/verify.yml +0 -0
|
@@ -53,7 +53,7 @@ canon tasks plan-branch <argument> --json
|
|
|
53
53
|
- `conforms: false`: the plan's own filename breaks a cap in `${CLAUDE_SKILL_DIR}/../../standards/branch.md`. Pass `branch` anyway and say the cap it broke, since the alternative is a name this session shortened by hand, which is a second derivation and the thing this call exists to prevent. `git-branch` decides the rename at ship.
|
|
54
54
|
- Anything else, including a refusal, a record carrying no `branch` key, and an installed binary carrying no `plan-branch` subcommand: invoke `canon:session-worktree` bare and let its ladder derive the name. Say the verb did not answer, so a reader can tell a derived name from a fallback one.
|
|
55
55
|
|
|
56
|
-
The dispatch runbook runs the same verb on the same plan to pick the branch its collision check clears, so calling it here is what makes the checked branch and the taken branch one string. Deriving a name from `<plan>` by reading it was the alternative, and
|
|
56
|
+
The dispatch runbook runs the same verb on the same plan to pick the branch its collision check clears, so calling it here is what makes the checked branch and the taken branch one string. Deriving a name from `<plan>` by reading it was the alternative, and two readings of one plan part on the slug and on the type alike.
|
|
57
57
|
|
|
58
58
|
A caller that supplied a task path, or supplied nothing at all, has no plan to hand the verb here, since resolving either is Step 1's work and Step 1 has not run. Invoke `canon:session-worktree` bare in both cases. That is the ladder unchanged rather than a regression, and it leaves the hole open: a dispatched worker reaching this step through a task path derives its name from a tier rather than from the plan the dispatcher checked.
|
|
59
59
|
|
|
@@ -15,21 +15,21 @@ Consult before running an unfamiliar `canon` verb, before a sync or install, or
|
|
|
15
15
|
|
|
16
16
|
## Overwrite contract
|
|
17
17
|
|
|
18
|
-
| Surface
|
|
19
|
-
|
|
|
20
|
-
| Golden configs
|
|
21
|
-
| Dictionary seeds (`.cspell/*.txt`)
|
|
22
|
-
| Other seeds (`cspell.json`, `.lintstagedrc`, state docs) | `canon tooling sync` | Copy-once. Dropped on first install, untouched after. |
|
|
23
|
-
| Standards
|
|
24
|
-
| Seed docs and `CLAUDE.md`
|
|
25
|
-
| Seed docs
|
|
26
|
-
| Stack references
|
|
27
|
-
| `.gitignore`, deps, scripts
|
|
28
|
-
| Generated `index.md`
|
|
18
|
+
| Surface | Command | Effect on existing files |
|
|
19
|
+
| --------------------------------------------------------------------------------------------- | -------------------- | --------------------------------------------------------------------------- |
|
|
20
|
+
| Golden configs | `canon tooling sync` | Overwritten once `--write` is passed. Local edits are lost. |
|
|
21
|
+
| Dictionary seeds (`.cspell/*.txt`) | `canon tooling sync` | Merged and sorted. Existing terms preserved. |
|
|
22
|
+
| Other seeds (`cspell.json`, `.lintstagedrc`, base's `verify.yml` and PR template, state docs) | `canon tooling sync` | Copy-once. Dropped on first install, untouched after. |
|
|
23
|
+
| Standards | none | Nothing installs. `canon standards <name>` reads and never writes. |
|
|
24
|
+
| Seed docs and `CLAUDE.md` | `canon claude init` | Skipped when present. Never overwritten. |
|
|
25
|
+
| Seed docs | `canon claude sync` | Never touched. Only `.gitignore` is written. |
|
|
26
|
+
| Stack references | none | Nothing installs. `canon tooling reference <stack>` reads and never writes. |
|
|
27
|
+
| `.gitignore`, deps, scripts | any sync | Additive. Existing entries preserved. Deps re-pin on major skew. |
|
|
28
|
+
| Generated `index.md` | any sync or regen | Rewritten from target state. Hand edits are lost. |
|
|
29
29
|
|
|
30
30
|
## What a tooling sync can overwrite
|
|
31
31
|
|
|
32
|
-
A golden config is any file a stack ships under `configs/`, and the category is wider than its name suggests. It carries the CI workflow, the git hooks, the end-to-end harness, the shell scripts under `scripts/`, and the editor settings, alongside the linters and compilers a reader expects. A stack inherits its parent's configs, so syncing `astro` also writes everything `web` and `base` hold.
|
|
32
|
+
A golden config is any file a stack ships under `configs/`, and the category is wider than its name suggests. It carries the phase-label gate workflow, the web stack's CI workflow, the git hooks, the end-to-end harness, the shell scripts under `scripts/`, and the editor settings, alongside the linters and compilers a reader expects. A stack inherits its parent's configs, so syncing `astro` also writes everything `web` and `base` hold.
|
|
33
33
|
|
|
34
34
|
No list of those paths ships in this body, since a copy would go stale on a different cadence than the stacks. Run `canon tooling diff <stack> <target>` for the list resolved against a real target. It reports every path, writes nothing, and exits 1 when any differs. A binary older than that verb answers the same question through `canon tooling sync <stack> <target> --check`, which names each file it would replace.
|
|
35
35
|
|
|
@@ -13,6 +13,8 @@ A skill born without its requirement is the second failure, and it surfaces much
|
|
|
13
13
|
|
|
14
14
|
A third failure is a folder created for a moment a rule, a verb, or an existing skill already reaches, or for a procedure no session would get wrong from first principles. Nothing caught that before the folder existed, so the corpus grew by a skill an inward audit later had to argue for removing, with only a commit body recording the argument.
|
|
15
15
|
|
|
16
|
+
A fourth failure is dated provenance in the body. A target asked for an edit citing a measurement taken that day, and the draft carried "measured on" and the date inline, with the authoring standard, the markdown audit, and this skill all passing it. A body collecting those notes reads as history rather than as the rule a session follows.
|
|
17
|
+
|
|
16
18
|
## Must
|
|
17
19
|
|
|
18
20
|
- Read the authoring standard and the prose standard before drafting, so the draft starts conformant rather than getting corrected into shape
|
|
@@ -20,6 +22,7 @@ A third failure is a folder created for a moment a rule, a verb, or an existing
|
|
|
20
22
|
- Draft the sibling `REQUIREMENT.md` beside the body, from what the skill is for rather than from the drafted body
|
|
21
23
|
- Confirm the name and both files with the user before writing. The name is the routing key and a folder that disagrees with its frontmatter fails silently.
|
|
22
24
|
- Write to the conventional skills path, so discovery finds it without configuration
|
|
25
|
+
- Leave the skills audit's dated-provenance check clean for the new folder before reporting it, with each incident moved to the requirement's `Gap`
|
|
23
26
|
|
|
24
27
|
## Must not
|
|
25
28
|
|
|
@@ -21,10 +21,12 @@ Read these files in parallel:
|
|
|
21
21
|
- Does a rule, a verb, or an existing skill already reach this moment? A folder that only wraps something already reachable duplicates it rather than adding to the catalog.
|
|
22
22
|
- Does the body carry a procedure a session would get wrong from first principles? A one-line wrapper around a single command needs no skill.
|
|
23
23
|
- Will anything invoke it other than the author typing its name? Carry this one into the drafted `REQUIREMENT.md`'s `Must not` section as a review criterion instead, since nothing can answer it before the skill has run.
|
|
24
|
-
2. Draft the full `SKILL.md` from the user's description
|
|
24
|
+
2. Draft the full `SKILL.md` from the user's description, stating the current rule alone. An incident and the date it happened belong in the requirement's `Gap`, not in the body or a reference.
|
|
25
25
|
3. Draft the sibling `REQUIREMENT.md` from what the skill is for, in the shape the standard states. Write the gaps from the user's description rather than from the drafted body, since a requirement derived from the body records whatever the draft overfitted to.
|
|
26
26
|
4. Confirm the skill name and both files with the user before writing
|
|
27
27
|
5. Write to `.claude/skills/<name>/SKILL.md` and `.claude/skills/<name>/REQUIREMENT.md`
|
|
28
|
+
6. Run `canon claude skills audit --json` and read `findings.datedProvenance` for rows under `.claude/skills/<name>/`. Move each row's incident into `REQUIREMENT.md` under `Gap`, cut the date from the file the row names, and re-run until no row names the folder.
|
|
29
|
+
- When `canon` does not resolve or its record carries no `datedProvenance` key, say the check did not run rather than reporting the skill clean.
|
|
28
30
|
|
|
29
31
|
Every skill carries a requirement. A skill created without one is a gap someone closes in a later sweep, and the sweep has to reconstruct what the skill was for from the body it already shipped.
|
|
30
32
|
|
|
@@ -232,7 +232,7 @@ What that removes is a local-only review on a branch deleted before it opened a
|
|
|
232
232
|
|
|
233
233
|
Memory receipts sweep board-wide rather than by slug. Scan every `.canon/memory/review/memory-review-*.md`, not only the one matching this slug. `memory-review` writes its receipt after this skill has run in every ship chain, so a sweep keyed on the current slug looks for a file that does not exist yet, and no later branch looks for it either because a slug is unique per feature. Scanning the folder is what makes the sweep fire at all.
|
|
234
234
|
|
|
235
|
-
For each receipt, count the H2 items still
|
|
235
|
+
For each receipt, count the H2 items still pending. An item is pending when its H2 carries 📝, or when its H2 carries no status emoji and its `Decision:` slot holds nothing `memory-review` Apply would act on or skip, since a receipt written by hand or by an older binary may lack the marker, and Apply's parse leaves every other slot value undecided:
|
|
236
236
|
|
|
237
237
|
- No pending item: fold it per the collection rule in `${CLAUDE_SKILL_DIR}/../../standards/memory.md`, then delete the receipt.
|
|
238
238
|
- Any pending item: leave it and report the count. Pending items are decision state, and a branch shipping is not an operator deciding them.
|
|
@@ -176,11 +176,11 @@ As each item resolves, update its status in the review file: flip the H2 emoji f
|
|
|
176
176
|
|
|
177
177
|
### Sweep the receipt
|
|
178
178
|
|
|
179
|
-
Count the items still
|
|
179
|
+
Count the items still pending once the parse above has run. An item is pending when its H2 carries 📝, or when its H2 carries no status emoji and its `Decision:` slot holds nothing the parse above would act on or skip, since a receipt written by hand or by an older binary may lack the marker. Apply leaves one pending on `defer`, on empty, and on any unrecognized verb, so a receipt reaching this point may still be holding decisions.
|
|
180
180
|
|
|
181
181
|
Leave the receipt in place when any remain. When none do, collect it per the collection rule in `${CLAUDE_SKILL_DIR}/../../standards/memory.md`, which owns what a fold writes and which entry types take one.
|
|
182
182
|
|
|
183
|
-
`docs-fold` Step
|
|
183
|
+
`docs-fold` Step 9 sweeps the same folder on the same rule once per shipped branch, and either may reach a receipt first. Whichever does, the other finds no file and moves on.
|
|
184
184
|
|
|
185
185
|
End with: `✅ Applied: <nums> | ⏭ Skipped: <nums> | 📝 Pending: <nums>`. Omit empty buckets. If anything is pending, remind the user they can refine `Decision:` lines and re-ping, run "discuss" for question items, or commit a skip with `skip <nums>` in chat.
|
|
186
186
|
|
|
@@ -188,11 +188,11 @@ End with: `✅ Applied: <nums> | ⏭ Skipped: <nums> | 📝 Pending: <nums>`. Om
|
|
|
188
188
|
|
|
189
189
|
Trigger: user says "cleanup" or "delete the receipt" after Apply has run.
|
|
190
190
|
|
|
191
|
-
Cleanup folds one receipt's skips and removes that receipt, and does nothing else. It is the fallback route now that Apply and `docs-fold` Step
|
|
191
|
+
Cleanup folds one receipt's skips and removes that receipt, and does nothing else. It is the fallback route now that Apply and `docs-fold` Step 9 each collect a resolved receipt on their own, so it reaches a file those two left behind rather than being the only collector. Apply is still the only phase that moves a memory entry out of the pen, and it does so per approved item into `.canon/memory/archive/`. A user asking to sweep stale memories wants Propose, which classifies entries and writes a decision slot per entry.
|
|
192
192
|
|
|
193
193
|
If no `.canon/memory/review/memory-review-*.md` exists at the main root, stop: `✅ No review receipt to clean up.` Every other refusal in this skill carries a message, and the phase reads a receipt before it does anything else.
|
|
194
194
|
|
|
195
|
-
1. Read the latest `.canon/memory/review/memory-review-*.md` at the main root and confirm Apply has run against it. If any item is still
|
|
195
|
+
1. Read the latest `.canon/memory/review/memory-review-*.md` at the main root and confirm Apply has run against it. If any item is still pending, by the test the Apply sweep states, stop and name the pending numbers.
|
|
196
196
|
2. Collect it per the collection rule in `${CLAUDE_SKILL_DIR}/../../standards/memory.md`, folding each ⏭ skipped item before the file goes. The fold happens wherever a receipt is collected, so this phase runs the same rule the Apply sweep does.
|
|
197
197
|
3. Delete that one file. Leave every other receipt beside it in place, because the pending test above covers the file it read and nothing has tested the rest.
|
|
198
198
|
4. Leave every memory entry in the pen. A skip records the decline on the entry and keeps the file, and applied promotions, governance handoffs, and user-type memories each stay as the review left them.
|
|
@@ -13,6 +13,8 @@ A finding also stops being true by argument rather than by a fix. A worker namin
|
|
|
13
13
|
|
|
14
14
|
A request written under `## For the reviewer` also reached no reader. The authoring standard names it as what the reviewing session should confirm, and nothing on this side read the section by that name, so a branch author's question sat in the body until this skill answered it.
|
|
15
15
|
|
|
16
|
+
Scoping a later pass off GitHub's review stamp skipped real work. On 2026-09-07 an author pushed between a pass's read and its post, the stamp moved onto a commit that pass never saw, and the next pass scoped its delta past it and skipped a genuine fix. A closed review invites a second close-out too: on 2026-08-28 two passes over commits that raised nothing each posted a comment repeating the verdict above it, and the operator caught the pair rather than any check.
|
|
17
|
+
|
|
16
18
|
## Must
|
|
17
19
|
|
|
18
20
|
- Post until the review closes. A first pass opens against the whole change, and each later pass checks whether the prior findings landed.
|
|
@@ -63,7 +63,7 @@ The fields are `<prior-oid>` off `commit`, `<prior-heading>` off `heading`, and
|
|
|
63
63
|
|
|
64
64
|
`source` says which stamp answered. `marker` is the pass's own read-time record and is the authority. `fallback` is a pass posted before this mechanism shipped, so its commit is whatever the head was when GitHub recorded the review rather than what that session read, and a push inside its compose window is invisible. `none` is a thread carrying no pass at all.
|
|
65
65
|
|
|
66
|
-
Do not read `commit.oid` or `submittedAt` off `gh pr view --json reviews` here. Both are stamped at submission, so a push landing between a pass's read and its post moves them onto a commit that pass never saw, and this step then scopes the delta past it and reports it covered.
|
|
66
|
+
Do not read `commit.oid` or `submittedAt` off `gh pr view --json reviews` here. Both are stamped at submission, so a push landing between a pass's read and its post moves them onto a commit that pass never saw, and this step then scopes the delta past it and reports it covered.
|
|
67
67
|
|
|
68
68
|
A target whose CLI predates the verb meets a missing subcommand rather than a record. Fall back there to the jq below, which reads the stamps and carries the defect above, and say the fallback answered so a reader can tell a marker read from a stamped one:
|
|
69
69
|
|
|
@@ -119,7 +119,7 @@ A non-empty result carries the comment id Step 4 needs for the third filename se
|
|
|
119
119
|
|
|
120
120
|
A `## Post-review findings` reply carries no argued finding behind it, since it asserts a new defect rather than answering one, and this pass is its first independent reader. Restating it as a finding without opening anything is repeating the worker's claim rather than checking it. Read the file the comment names at `<headRefOid>`, the same `git show <headRefOid>:<path>` read Step 3 already runs to confirm a ticked box, and confirm the defect before it becomes a finding of this pass's own.
|
|
121
121
|
|
|
122
|
-
A moved head has its own way to add nothing, which the ancestor test cannot see. When `<prior-heading>` reads `## Review closed`, the standing verdict already reports the branch clear, so a pass over the new commits that raises nothing posts a comment saying what the one above it said.
|
|
122
|
+
A moved head has its own way to add nothing, which the ancestor test cannot see. When `<prior-heading>` reads `## Review closed`, the standing verdict already reports the branch clear, so a pass over the new commits that raises nothing posts a comment saying what the one above it said.
|
|
123
123
|
|
|
124
124
|
The producing shape is narrow. A prior pass tells the author a change is their own call, the author makes it, and the delta reaching the next pass has nothing left to say by construction.
|
|
125
125
|
|
|
@@ -43,6 +43,8 @@ Cost never enters the decision to widen a wave. The rule weighing how many track
|
|
|
43
43
|
|
|
44
44
|
The session also records nothing of what it learns. Both other callers of memory capture are ship-chain skills and this one never ships, so the session taking every operator correction is the session with no moment that writes one down. Hanging that moment on the merge sweep answers it and bills the operator a capture pass per batch of merges while nothing is being built, which is a cost paid on the days shipping is fastest.
|
|
45
45
|
|
|
46
|
+
Dispatch parts from what it checked in three measured ways. The gate and the worker each derived the branch from their own reading of one plan until 2026-09-06: one run checked `docs/remaining-skill-verdicts` against a worker that took `docs/skill-verdicts-decide`, another checked `fix/path-form-hook` against `feat/path-form-hook`, and four dispatches on 2026-09-05 produced three strings for one plan. A launched worker inherits its launcher's model rather than the configured default, measured on 2026-08-27 with `~/.claude/settings.json` set to `sonnet` while both dispatched workers ran `claude-opus-5`. A cleared file set does not hold either, since three tracks in the wave of 2026-09-08 crossed a set they had cleared against and every one merged clean.
|
|
47
|
+
|
|
46
48
|
## Must
|
|
47
49
|
|
|
48
50
|
- Read the priority file for execution order, since the index sorts by filename and states no order
|
|
@@ -129,7 +129,7 @@ What the worker owes on its own side is stated in the `role-worker` skill that s
|
|
|
129
129
|
|
|
130
130
|
What arrives there does not become a record by being read, so place it by what it changes. An answer that settles a finding goes onto the pull request through the next pass, which withdraws or regrades that finding and names the fact behind it, per `review-pr`. An answer that changes what this session believes about the world instead, which is a mapping correction or a constraint on what a worker can do, settles no finding and reaches no thread, so route it the way Boundaries below routes a change found while orchestrating, which lands it on the task owning the surface it describes. Writing a tracked file to hold either is forbidden here, which leaves the pull request and the board as the two surfaces this session writes.
|
|
131
131
|
|
|
132
|
-
Step 1 splits a rule that used to hold every plan in this session, and the evidence narrows it rather than retiring it. Two trials
|
|
132
|
+
Step 1 splits a rule that used to hold every plan in this session, and the evidence narrows it rather than retiring it. Two trials put a cold planner on four rows, and it reported ten things the task files got wrong, corrected this session's own premise twice, and overturned one row's closing conclusion. What that measures is finding quality. The rule's own claim is that a warm plan front-loads reasoning a cold worker would otherwise re-derive, which is a statement about a plan's downstream value, and no plan from either trial has been built. So the per-row measurement goes cold on the evidence and the cross-feature call stays here on the boundary the same trials confirmed, which is that a planner reading the board sees blockers and file sets and can write a confident merge order off a partial picture.
|
|
133
133
|
|
|
134
134
|
A plan written here is written against a tree several branches are already changing, so it names the file set of every track in flight as a constraint, one set per track, read from the Touches column of that track's row. State for each set which of the two acts it forbids, per Constraints in `${CLAUDE_SKILL_DIR}/../../standards/plan.md`. A bare path list leaves the worker guessing, which is how a plan ends up forbidding the repair of a citation the change broke.
|
|
135
135
|
|
|
@@ -259,7 +259,7 @@ or more open pull requests awaiting a first pass. Either one makes review the
|
|
|
259
259
|
bottleneck every track is waiting on, and both answer the same way: dispatch the
|
|
260
260
|
narrow re-review and keep the first pass here.
|
|
261
261
|
|
|
262
|
-
Three is the operator's number, set by hand
|
|
262
|
+
Three is the operator's number, set by hand and marked as such so a
|
|
263
263
|
measurement replaces it rather than argues with it. It is calibrated against a
|
|
264
264
|
session that ran five workers across two waves and reviewed five pull requests in
|
|
265
265
|
one stretch, several of them twice, where a sixth track was declined on judgment
|
|
@@ -16,9 +16,9 @@ Run `canon tasks plan-branch <plan> --json` against the row's plan file and read
|
|
|
16
16
|
|
|
17
17
|
Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
|
|
18
18
|
|
|
19
|
-
The worker calls the same verb on the same plan at `auto-ship` Step 0, so the branch this gate checks and the branch that session takes are one string by construction rather than two readings of one paragraph.
|
|
19
|
+
The worker calls the same verb on the same plan at `auto-ship` Step 0, so the branch this gate checks and the branch that session takes are one string by construction rather than two readings of one paragraph. Two readings of one plan part on the slug and on the type alike, and a check against a branch nobody uses verifies nothing.
|
|
20
20
|
|
|
21
|
-
The type the verb reports is fixed at `feat` whatever the row does, which is the half of the derivation that disagreed most. What makes that safe is that a branch type is cosmetic: `git-stage` reads a commit's type off the staged diff and `git-pr` reads a title off the diff, so nothing a release reads passes through the branch name. What it costs is a worktree listing where every dispatched branch reads `feat/`, which a person scanning one loses. Nothing renames it later,
|
|
21
|
+
The type the verb reports is fixed at `feat` whatever the row does, which is the half of the derivation that disagreed most. What makes that safe is that a branch type is cosmetic: `git-stage` reads a commit's type off the staged diff and `git-pr` reads a title off the diff, so nothing a release reads passes through the branch name. What it costs is a worktree listing where every dispatched branch reads `feat/`, which a person scanning one loses. Nothing renames it later, since `git-branch`'s conventions guard fires on a conforming `feat/` before reaching any type judgment.
|
|
22
22
|
|
|
23
23
|
## Check the plan waits on nobody
|
|
24
24
|
|
|
@@ -34,7 +34,7 @@ Branch on `launchable` rather than on the exit code, which a shell function wrap
|
|
|
34
34
|
|
|
35
35
|
This gate runs ahead of the two collision checks because it is the cheapest reading of the three, needing no roster and no ref, and because it is the only one asking about the row itself rather than about what else is in flight. A row nobody can launch does not need testing against the tracks already out.
|
|
36
36
|
|
|
37
|
-
It also reads the plan rather than a cell describing one, which is the input the gate below it does not have. The disjointness gate compares the sets a dispatcher wrote into the constraints and the Touches column, so a cell omitting a file clears a check the tree would fail
|
|
37
|
+
It also reads the plan rather than a cell describing one, which is the input the gate below it does not have. The disjointness gate compares the sets a dispatcher wrote into the constraints and the Touches column, so a cell omitting a file clears a check the tree would fail, and what catches it then is a worker's message rather than any check.
|
|
38
38
|
|
|
39
39
|
A blank `- Answer:` is not an unanswered question. `${CLAUDE_SKILL_DIR}/../../standards/plan.md` fixes an empty slot as accepting the `- Suggested:` line above it, which is what makes a plan decision-ready in one pass. The narrow case this reads is `- Suggested: needs your call, <why>` and its two demonstrated paraphrases, `needs operator's call` and `needs the operator's call`, over an empty slot, the form that same standard writes where the answer turns on preference rather than on a technical default. A gate reading every blank slot as open would refuse every plan in the folder.
|
|
40
40
|
|
|
@@ -66,19 +66,19 @@ No count binds this. List the files the candidate's plan touches, from its `**Fi
|
|
|
66
66
|
|
|
67
67
|
The board is not the whole set. A track a person launched by hand carries no row, so that column cannot see it, which is the ordinary shape whenever the operator is launching rather than dispatching. Read `canon sessions list --json` for the branches in flight, and take the file set of any branch no row names from the plan that branch is building. A candidate cleared against the board alone is cleared against a partial reading.
|
|
68
68
|
|
|
69
|
-
Take the comparison at the file path rather than at a folder above it. `canon tasks validate` compares the paths each row wrote, so a collision it reports on a folder means a row's Touches cell claimed that folder rather than the verb widening anything.
|
|
69
|
+
Take the comparison at the file path rather than at a folder above it. `canon tasks validate` compares the paths each row wrote, so a collision it reports on a folder means a row's Touches cell claimed that folder rather than the verb widening anything. A cell naming a bare folder collides with every row writing a file under it, which reads as the verb comparing path segments too coarsely and is not.
|
|
70
70
|
|
|
71
71
|
The finding names which row contributed the containing path, and a bare-folder cell reports as a claim of its own beside the findings. Read that output as a candidate list, settle each pair by file, and narrow the cell that over-claimed rather than discounting the collision it caused.
|
|
72
72
|
|
|
73
|
-
A declared set is what a branch sets out to write rather than a bound on it, so this gate clears against a prediction and the branch outgrows it hours later.
|
|
73
|
+
A declared set is what a branch sets out to write rather than a bound on it, so this gate clears against a prediction and the branch outgrows it hours later. A track can cross the set it cleared against and still merge clean, which is a gate reporting disjoint while a real overlap stood. Two classes account for nearly all of the crossing. The ship chain's own steps write past every plan, since `canon:docs-fold` refreshes whichever context entry a change reaches and `canon:docs-sync` reaches the public docs, and neither surface is one a planner can name before the change exists. The drift stages `bun run check` regenerates and asserts are the second, together with the test and sandbox siblings a source change drags in. Nothing here prevents either, because the files at issue are written long after this gate clears. What reads the other end is `canon tasks plan-reach`, which `canon:git-ship` runs at step 5 against the branch's own diff, so a crossing this gate could not see is named before the pull request opens rather than after it merges.
|
|
74
74
|
|
|
75
|
-
Disjointness is necessary and not sufficient, so hold a candidate whose sets do not touch when a stated reason serializes it, and write the reason on the hold. One row creating a skill and another auditing that catalog and counting it write nothing in common,
|
|
75
|
+
Disjointness is necessary and not sufficient, so hold a candidate whose sets do not touch when a stated reason serializes it, and write the reason on the hold. One row creating a skill and another auditing that catalog and counting it write nothing in common, and dispatching both still leaves the audit counting a denominator that moves underneath it. Nothing verifies that a reason was written, so the rule holds only while the dispatcher applies it.
|
|
76
76
|
|
|
77
77
|
What binds past that is review attention rather than a count, and `## Parallelism` in the skill body states it along with the cap an operator can set for a session. The one number this skill carries is the review fallback's count of three in `## Parallelism`, which moves a review rather than binding a track, and this runbook carries none.
|
|
78
78
|
|
|
79
79
|
## Pick the model
|
|
80
80
|
|
|
81
|
-
A `claude --bg` session inherits the model of whatever launched it rather than reading the machine's configured default.
|
|
81
|
+
A `claude --bg` session inherits the model of whatever launched it rather than reading the machine's configured default. An orchestrator on the larger model therefore spends it on every worker it launches, and the operator who set the default never sees the override.
|
|
82
82
|
|
|
83
83
|
Name `<model>` on the launch, and pick it against the task rather than copying whatever this session happens to run. Sizing the model to the row is the dispatcher's call, the same call it already makes on the branch. A mechanical row moving files under a written plan is not the row that needs the largest model, and one whose plan carries an open judgment is.
|
|
84
84
|
|
|
@@ -89,9 +89,9 @@ claude --bg --model <model> -n "worker-<project>-<slug>" "/canon:auto-ship <plan
|
|
|
89
89
|
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. Message it when the pull request opens, carrying the number, the branch, the head sha, the CI state, and every point you departed from the plan on, and message it again if you stop on a question."
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
`--bg, --background` starts the session as a background agent and returns immediately, `-n, --name` sets the display name that tells a self-dispatched worker from an operator's own launch in `canon sessions list`, and `--model` overrides the inheritance the section above
|
|
92
|
+
`--bg, --background` starts the session as a background agent and returns immediately, `-n, --name` sets the display name that tells a self-dispatched worker from an operator's own launch in `canon sessions list`, and `--model` overrides the inheritance the section above states. Pass `-n` on every dispatch rather than letting the client derive one. A launch that omits it leaves the session named for a fragment of its own identifier, which is both its address on the send channel and the whole of what the operator sees for it in agent view.
|
|
93
93
|
|
|
94
|
-
The prefix reads `worker-` because that is the role it marks.
|
|
94
|
+
The prefix reads `worker-` because that is the role it marks. A prefix naming a role the session does not hold sends a worker filtering the roster for that string to a sibling or itself on every row. Nothing matches the prefix programmatically, which keeps a rename down to three strings.
|
|
95
95
|
|
|
96
96
|
`<project>` is the basename of the main worktree root, not of wherever the dispatcher happens to be running. Resolve the main root first, the way `session-worktree` Step 1 does, since a bare `git rev-parse --show-toplevel` inside a linked worktree returns the worktree path rather than the project's.
|
|
97
97
|
|
|
@@ -118,7 +118,7 @@ model, which invokes it through the `Skill` tool, and that route answers a
|
|
|
118
118
|
flagged skill inconsistently. `auto-ship` has carried the flag since early in its life, and seven other
|
|
119
119
|
shipped skills carry it too.
|
|
120
120
|
|
|
121
|
-
Three launches
|
|
121
|
+
Three launches bound what makes a command take
|
|
122
122
|
that route. Observation A is the first refused worker, launched as `Run
|
|
123
123
|
/canon:session-worktree ..., then /canon:auto-ship ...`, which expanded
|
|
124
124
|
nothing. Observation B is a re-dispatch launched as `/canon:auto-ship
|
|
@@ -138,8 +138,8 @@ at no extra cost. Until it fails, the operational rule is the conjunction the
|
|
|
138
138
|
three observations support: put the flagged command at position zero, followed
|
|
139
139
|
by a space and its argument, with nothing before it.
|
|
140
140
|
|
|
141
|
-
Four sessions made the same tool call against the same plugin cache on
|
|
142
|
-
|
|
141
|
+
Four sessions made the same tool call against the same plugin cache on one
|
|
142
|
+
day. Two were answered with the body and shipped, and two were refused
|
|
143
143
|
with `Skill canon:auto-ship cannot be used with Skill tool due to
|
|
144
144
|
disable-model-invocation`. Prefixing separated nothing, since three of the
|
|
145
145
|
four carried the namespace and those three landed on both answers, so nothing
|
|
@@ -168,7 +168,7 @@ The same collapse reaches a human relay rather than a `claude --bg` string. A
|
|
|
168
168
|
controller that hands an operator two chained blocks to paste as separate
|
|
169
169
|
messages risks both landing as one, where everything after the first
|
|
170
170
|
command's name is read as that command's own argument and the second command
|
|
171
|
-
never fires, measured four times out of four
|
|
171
|
+
never fires, measured four times out of four.
|
|
172
172
|
A slash command expands as a user invocation only at position zero of a
|
|
173
173
|
prompt, and a later one in the same text reaches the session as prose instead.
|
|
174
174
|
The fix is what the template above already takes: one message, one command,
|
|
@@ -232,7 +232,7 @@ nothing the check can see.
|
|
|
232
232
|
|
|
233
233
|
`plan-feature` is a procedure rather than a role, so a launch naming it alone
|
|
234
234
|
reaches no `role-planner` and takes no role, which owes no message either.
|
|
235
|
-
Both trials
|
|
235
|
+
Both trials of this shape ran on prose the controller retyped into each launch,
|
|
236
236
|
which held every obligation those sessions took and is where the first one's
|
|
237
237
|
in-flight read went wrong. Reach the role directly on this launch, the way the
|
|
238
238
|
build shape above reaches `role-worker`.
|
|
@@ -27,7 +27,7 @@ The read is the report this session is holding. Retire a session whose delivery
|
|
|
27
27
|
|
|
28
28
|
### The act, and the order it runs in
|
|
29
29
|
|
|
30
|
-
`claude rm <id>` is meant to remove a background session and its worktree together, though its own report is not reliable, so `canon worktrees list` is the reading to trust afterward. It takes one target per call, so a wave costs one call per session. The argument is the id rather than the name, which `claude agents --json` carries beside the name on every row, so a session read off a roster by name is matched to its id there before the call.
|
|
30
|
+
`claude rm <id>` is meant to remove a background session and its worktree together, though its own report is not reliable, so `canon worktrees list` is the reading to trust afterward. It takes one target per call, so a wave costs one call per session. The argument is the id rather than the name, which `claude agents --json` carries beside the name on every row, so a session read off a roster by name is matched to its id there before the call. The name form answers `No job matching`.
|
|
31
31
|
|
|
32
32
|
The command belongs to the client rather than to this toolkit, so a target running another client performs whatever removal that client offers.
|
|
33
33
|
|
|
@@ -70,13 +70,13 @@ The state reaches every stalled dispatch, since one threshold governs the headin
|
|
|
70
70
|
|
|
71
71
|
The report is also where the count in `## Parallelism` is legible. That threshold trips on open pull requests awaiting a first pass, which is what `OPENED` and a pull request with no prior pass name here and what `SEEN` excludes, so read the count off these lines rather than off `gh pr list`, which counts a branch closed out and waiting on a merge the same as one nobody has read. It is a separate condition from the poll-start fallback above, which decides when this loop runs rather than where a review runs.
|
|
72
72
|
|
|
73
|
-
The count used to read low, and it erred in the direction that breaks the trigger. A review's `commit.oid` is stamped with the head at submission rather than with the commit the reviewer read, so an author pushing between the diff read and the post left the pass recorded against a commit it never saw, and `SEEN` then fired on a head still awaiting its first look at that delta.
|
|
73
|
+
The count used to read low, and it erred in the direction that breaks the trigger. A review's `commit.oid` is stamped with the head at submission rather than with the commit the reviewer read, so an author pushing between the diff read and the post left the pass recorded against a commit it never saw, and `SEEN` then fired on a head still awaiting its first look at that delta.
|
|
74
74
|
|
|
75
75
|
The pass now carries its own record instead. `review-pr` writes the commit it read and the instant it read it as a marker on the last line of every body it posts, and both this script and that skill resolve the covered state through `canon pr review-state`, which is the one place the marker is parsed. A commit pushed inside a pass's compose window falls outside the marked range, so it reads as `MOVED` rather than as `SEEN`. The manual double-check that used to hang off this paragraph is retired with the defect: a `SEEN` is now a claim about what a session read rather than about what GitHub stamped, so it wants no second read to be believed.
|
|
76
76
|
|
|
77
77
|
Read `source` on the record before trusting a `SEEN` on a thread whose newest pass is old. `marker` is the read-time record. `fallback` is a pass posted before the mechanism shipped, or a target whose CLI predates the verb, and it carries the defect above unchanged, so a `SEEN` under it is worth one `gh pr view --json reviews` before it is believed. `none` is a thread carrying no pass at all.
|
|
78
78
|
|
|
79
|
-
The other side of that comparison used to lag as well, which made the two errors compound rather than cancel. `gh pr view --json headRefOid` answers from the pull request object and that object trails the branch ref by up to a minute after a push, reporting nothing about the trail, so a pass stamped ahead of the commit it read was compared against a head stamped behind the commit that exists.
|
|
79
|
+
The other side of that comparison used to lag as well, which made the two errors compound rather than cancel. `gh pr view --json headRefOid` answers from the pull request object and that object trails the branch ref by up to a minute after a push, reporting nothing about the trail, so a pass stamped ahead of the commit it read was compared against a head stamped behind the commit that exists. The head now comes from `canon pr head`, which resolves the tip through `git ls-remote` and reports which commit each source names, and the object's head stays behind it as the fallback for a target whose CLI predates the verb. Both halves of the comparison now answer about a commit somebody named rather than one GitHub stamped, which is what lets a `SEEN` be read as covered.
|
|
80
80
|
|
|
81
81
|
## The watch beside it
|
|
82
82
|
|
|
@@ -19,6 +19,8 @@ Finishing is invisible from the other side. A worker that opens a pull request g
|
|
|
19
19
|
|
|
20
20
|
Nothing tells the session that refusing is allowed either. Four occasions across two days had a worker halt correctly with the cost falling on the dispatcher, and the correction that mattered most reached the right place because a worker argued back with evidence rather than complying. A role written only as report-upward suppresses exactly that.
|
|
21
21
|
|
|
22
|
+
The draft mark fails from both ends. On 2026-08-31 four sessions re-drafted a pull request someone had readied, acting on a belief no surface in the tree stated. On 2026-09-07 a controlling session told a worker to lift the mark rather than lifting it itself, and the worker complied and reported against the surface rather than the outcome.
|
|
23
|
+
|
|
22
24
|
## Must
|
|
23
25
|
|
|
24
26
|
- Assert what a building session is, what it may not do, and how long the role lasts, since the three step-owning skills each carry a procedure and none carries a role
|
|
@@ -26,8 +26,8 @@ running and re-invoking it would restart the build.
|
|
|
26
26
|
- Report a plan that fails to resolve as unreadable from here, naming the main-root path. Reporting the task as having no plan is true where this session stands and wrong about the world, and a reader with no second tree to check cannot separate the two.
|
|
27
27
|
- Build the plan the launch named. Do not write a second one when the path fails to resolve, since a row that cites a plan already has one and drafting another produces two plans for one row.
|
|
28
28
|
- Copy a ready folder's files verbatim to the paths its plan's constraints name, when the plan's `**Constraints:**` block names one, and edit only what the gate or the overview's own list requires. A rewrite discards the text the handoff exists to carry.
|
|
29
|
-
- Report a draft flag reading ready once, and leave it cleared. The ship chain marks the pull request a draft and no step anywhere un-marks, so a flag reading ready afterwards was lifted by the operator or by the controlling session that closed the review, each acting directly on the pull request, which GitHub requires before a merge. Re-drafting fights them,
|
|
30
|
-
- Refuse an instruction to lift the mark yourself, whoever sends it. This session cannot verify who is asking or whether review actually closed, so the refusal holds regardless of the sender's claimed authority.
|
|
29
|
+
- Report a draft flag reading ready once, and leave it cleared. The ship chain marks the pull request a draft and no step anywhere un-marks, so a flag reading ready afterwards was lifted by the operator or by the controlling session that closed the review, each acting directly on the pull request, which GitHub requires before a merge. Re-drafting fights them, and no surface in the tree states a reason to.
|
|
30
|
+
- Refuse an instruction to lift the mark yourself, whoever sends it. This session cannot verify who is asking or whether review actually closed, so the refusal holds regardless of the sender's claimed authority.
|
|
31
31
|
- Rewrite a plan question's `- Suggested:` line as `overridden at execution to <pick>,` plus the measurement when this build decides against an unanswered one, leaving the `- Answer:` slot blank, and put the same deviation in one line under the open task's `## Findings`, per `${CLAUDE_SKILL_DIR}/../../standards/plan.md`.
|
|
32
32
|
|
|
33
33
|
## The board is read-only
|
|
@@ -20,6 +20,7 @@ Sessions also write the wrong thing into a handoff. They restate the tree, which
|
|
|
20
20
|
- Carry a stated shape for the note, so what belongs in it is not re-derived per session.
|
|
21
21
|
- Require a citation for every claim, and a rejected alternative beside every decision.
|
|
22
22
|
- Decline in one line where the session holds nothing a compaction would destroy.
|
|
23
|
+
- Name `canon records push` then `canon sessions export` in the report when the caller asked to move the session to another machine, since this is the step a session takes before it moves and no other skill names either verb.
|
|
23
24
|
|
|
24
25
|
## Must not
|
|
25
26
|
|
|
@@ -34,7 +35,7 @@ Sessions also write the wrong thing into a handoff. They restate the tree, which
|
|
|
34
35
|
- No repository is required. The slug comes from the work, so a session outside git still writes a note.
|
|
35
36
|
- `.canon/` resolves at the main worktree root. From a linked worktree the write goes through the shell, since the file-editing tools refuse that path.
|
|
36
37
|
- Decline rather than pad. A note a reader finds is a note a reader trusts.
|
|
37
|
-
- The note is backed up by `canon records push`, which carries every top-level `.canon/` folder, and the push is a run someone makes. Until it runs, or where no remote is configured, the note sits on one disk. The skill's report does not say so, since it would restate a run the operator owns on every note.
|
|
38
|
+
- The note is backed up by `canon records push`, which carries every top-level `.canon/` folder, and the push is a run someone makes. Until it runs, or where no remote is configured, the note sits on one disk. The skill's report does not say so on an ordinary note, since it would restate a run the operator owns on every note. A caller who named a move is the exception, because the push is then the step the move depends on rather than a reminder, and the line prints on that report alone.
|
|
38
39
|
|
|
39
40
|
## Out of scope
|
|
40
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: session-compact
|
|
3
|
-
description: Captures a session's memory, then writes one handoff note to .canon/compact/ so the session after a compaction picks up where this one stopped. Use before running /compact, when asked to "write a handoff", "prepare for compaction", "save where we got to", or when a PreCompact hook blocks and names this skill. Do NOT use for a session holding the orchestrator role, whose board handoff is `session-map`. Do NOT use to record a decision a groundwork track owns, which is `plan-groundwork`.
|
|
3
|
+
description: Captures a session's memory, then writes one handoff note to .canon/compact/ so the session after a compaction picks up where this one stopped. Use before running /compact, when asked to "write a handoff", "prepare for compaction", "save where we got to", or before moving a session to another machine, or when a PreCompact hook blocks and names this skill. Do NOT use for a session holding the orchestrator role, whose board handoff is `session-map`. Do NOT use to record a decision a groundwork track owns, which is `plan-groundwork`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Session compact
|
|
@@ -36,8 +36,11 @@ Overwrite a note of the same name. One session's work has one note, and a second
|
|
|
36
36
|
```plaintext
|
|
37
37
|
✅ Handoff written: .canon/compact/<slug>.md
|
|
38
38
|
<the line memory-capture returned, where a fact routed>
|
|
39
|
+
Move: canon records push, then canon sessions export <id>
|
|
39
40
|
```
|
|
40
41
|
|
|
42
|
+
Print the `Move:` line only when the caller named moving the session to another machine, and leave it off every other report. Fill `<id>` from the `sessionId` of `canon sessions list --self --json`, and leave the literal `<id>` where that read refuses, since an id taken from any other row packs the wrong session. The push comes first so the note travels with the session. Name both commands for the operator and run neither: an export packs a transcript that can hold credentials, so the order belongs to the caller.
|
|
43
|
+
|
|
41
44
|
A decline reports itself so a caller can tell it from a failure:
|
|
42
45
|
|
|
43
46
|
```plaintext
|
|
@@ -57,4 +60,5 @@ A decline reports itself so a caller can tell it from a failure:
|
|
|
57
60
|
- The memory pass, its routing, and the pen's shape: `canon:memory-capture`
|
|
58
61
|
- The orchestrator's handoff, which carries a drift check and writes a task row: `canon:session-map`
|
|
59
62
|
- Reading a handoff back at the start of the next session: `canon:session-resume`
|
|
63
|
+
- Packing the session for another machine: `canon sessions export`, with `canon sessions import` and then `/canon:session-resume` on the arriving side
|
|
60
64
|
- A decision a groundwork track owns, which belongs in its own `06-decision.md`: `canon:plan-groundwork`
|
|
@@ -5,7 +5,7 @@ description: Which external writing patterns this skill adopted, which it declin
|
|
|
5
5
|
|
|
6
6
|
# Source material
|
|
7
7
|
|
|
8
|
-
Two external writing skills were read
|
|
8
|
+
Two external writing skills were read as source material for this one. The first catalogs 31 patterns of machine-sounding prose. The second layers document mode, sentence construction, information density, and ambiguity prevention.
|
|
9
9
|
|
|
10
10
|
Neither was imported wholesale, and one measurement settles why. Read this before adding a rule from outside this corpus, and extend the position below rather than deriving a second one.
|
|
11
11
|
|
|
@@ -24,7 +24,7 @@ The ambiguity rules travel with that layer for the same reason. Compression is w
|
|
|
24
24
|
- **Any restatement of a banned word set.** The bans ship as package data that `canon markdown audit` reads, and a copy here would be a second source for one list with a machine reading the other.
|
|
25
25
|
- **A voice register per surface.** One baseline covers every surface. A surface needing a warmer register states its own voice and claims the yield, which lifts voice alone and leaves the bans, the rhythm rules, and the tells in force.
|
|
26
26
|
|
|
27
|
-
The first of those four rests on a count. The source names `surface` among the nouns it bans, and tracked markdown in the authoring repository used `surface` 593 times, `harness` 95, `scaffolding` 17, and `primitive` 4
|
|
27
|
+
The first of those four rests on a count. The source names `surface` among the nouns it bans, and tracked markdown in the authoring repository used `surface` 593 times, `harness` 95, `scaffolding` 17, and `primitive` 4. Adopting the item either rewrites established vocabulary across the corpus or is ignored in silence, and a reader arriving later cannot tell which happened.
|
|
28
28
|
|
|
29
29
|
## Where this diverges
|
|
30
30
|
|
|
@@ -43,7 +43,7 @@ Every entry past the checkpoint carries the three questions the standard asks of
|
|
|
43
43
|
|
|
44
44
|
Only the third is mechanical, and it is the provenance check already reported below, so the length finding cites that count rather than measuring it again. What it cites is a count of change markers, which is why an entry recording what its own runs cost answers `no` there while carrying dates throughout. An entry outside the governed folder reads open there too, since provenance is scoped to the standard stating it and a clean list there is a scan that never ran. The other two stay open beside every entry, because whether a domain is still one domain is a judgment about its subject and recognizing reproduced content needs a reader who knows what those commands emit. Omitting them would read as an entry nothing found rather than one nothing checked.
|
|
45
45
|
|
|
46
|
-
It reports and never gates, which the standard settles rather than the usual judgment-against-fact line:
|
|
46
|
+
It reports and never gates, which the standard settles rather than the usual judgment-against-fact line: the 150 checkpoint is diagnostic, so an entry that answers all three and is still long is a correct outcome. The one hard cap is the 300-line whole-document ceiling, which `canon markdown audit` and the Document ceiling gate stage read rather than this check. The JSON record carries the joined findings as `length`, each with a `causes` array in the standard's order, and the jurisdiction the join reads as `entries[].governed`. That flag is what parts a provenance list measured and empty from one never scanned, which the count alone cannot say. The join is published rather than left to a consumer, since deriving it means restating which question the provenance count answers.
|
|
47
47
|
|
|
48
48
|
Depth and bullet weight are quoted from `standards/markdown.md`, which states both over every markdown file rather than over a context entry, so `canon markdown audit` measures them and this command no longer does. They share `renderedHeight` with the length measure, since the two checkpoints sit in one section of that standard and a reader compares them. What the split costs is that a session wanting both numbers for one entry runs two commands, and what it buys is that either number can be had for a file in a folder this audit refuses to resolve.
|
|
49
49
|
|
|
@@ -79,7 +79,7 @@ no copy to reconcile. `canon snippets list --json` carries the catalog and
|
|
|
79
79
|
|
|
80
80
|
`canon tooling diff <stack> [target]` is the read-only comparison, and a session asking what differs reaches for it by default. It writes nothing and exits 1 when anything differs and 0 when nothing does, so it gates CI. `--json` adds the scan record on stdout with `ok: true`, and the frame goes to stderr. A refusal such as an unknown stack, an excluded stack, or a toolkit-root target still exits 1 and answers `{ ok: false, reason, message }`, so a caller branches on the record and reads a typo'd stack name as a refusal rather than as drift.
|
|
81
81
|
|
|
82
|
-
The record carries a `withheld` array beside `configs`, each entry `{ rel, stack, present }`. It fills only when the target sits below its git root: every `.github/` config moves there rather than into `configs`, and none of them counts toward the exit, since GitHub never reads a workflow outside the root. `present` marks a copy an earlier sync already left in the subfolder, which the sync reports and never deletes. At a root, and for a target outside any repository, `withheld` is empty.
|
|
82
|
+
The record carries a `withheld` array beside `configs`, each entry `{ rel, stack, present }`. It fills only when the target sits below its git root: every `.github/` config or seed moves there rather than into `configs` or `seeds`, and none of them counts toward the exit, since GitHub never reads a workflow outside the root. `present` marks a copy an earlier sync already left in the subfolder, which the sync reports and never deletes. At a root, and for a target outside any repository, `withheld` is empty.
|
|
83
83
|
|
|
84
84
|
`canon tooling sync <stack> [target] --check` reports the same list and always exits 0. It stays for scripts that already call it. `sync` itself writes only under `--write`, and a headless run with neither flag reports and exits 1.
|
|
85
85
|
|
|
@@ -27,9 +27,9 @@ A bare run measures every markdown file git lists, tracked plus untracked-and-no
|
|
|
27
27
|
|
|
28
28
|
## Where the rules come from
|
|
29
29
|
|
|
30
|
-
The three ban sets and all
|
|
30
|
+
The three ban sets and all ten checkpoints ship with the `canon` package as data, in `src/markdown/bans.ts` and `src/markdown/structure.ts`. Every project is measured against the same sets whether or not it installed any standards, and no file has to resolve for a run to mean something.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Seven of the ten are stated in `markdown.md` and the three cadence numbers are stated in the `write-human` skill. That split is the content boundary rather than an accident: `markdown.md` carries the enforced rules a scan can decide, and the skill carries the rhythm rules a ban list cannot express. A cadence number moved in the skill and left in the code drifts the same way, so move both.
|
|
33
33
|
|
|
34
34
|
Reading them out of the standards per run was the original design. It put a parser contract on a document authored for people, and the standard had to carry a paragraph of its own warning an author that a one-word backticked example in a `- Do not use ` bullet would be lifted into a literal ban set and ban that word everywhere. A rule existing to protect a parser from the prose it parses is the argument for separating them.
|
|
35
35
|
|
|
@@ -128,9 +128,13 @@ Two of the rules `write-human` states are deliberately not implemented. A senten
|
|
|
128
128
|
|
|
129
129
|
The condition on that was something identifying a finite verb rather than guessing at one, and two parsers have now been run against it over 11,389 paragraph sentences. They disagree by a factor of four. `compromise` reports 2 percent and reads a fronted past participle as a finite verb, so `Measured at <sha> on <date>.` counts as carrying one. `wink-pos-tagger` reports 9 percent, fixes that class, and is still wrong on roughly three in four, because an imperative's verb tags as a proper noun and a noun-ambiguous predicate tags as a noun, which makes `Each maps to a skill.` read verbless. Separating those needs to know which token is the predicate, and that is syntax rather than a tag. The measure stays unimplemented, now against a mechanism rather than against the idea of one.
|
|
130
130
|
|
|
131
|
+
### Length
|
|
132
|
+
|
|
133
|
+
The Length step sums rendered lines over the whole source, frontmatter and fenced blocks included, and lists each document past the 300-line ceiling, longest first. A changelog and a document carrying a whole-line `<!-- canon-length-exempt: <reason> -->` outside a fence are counted in one line rather than listed. Each `--json` entry carries `renderedLines` and `exempt`, the stated reason or `null`, and `checkpoints` carries `ceiling`.
|
|
134
|
+
|
|
131
135
|
## Exit codes
|
|
132
136
|
|
|
133
|
-
Exit codes are `0` for a completed run with no gating finding, `1` for a refusal, `2` for a ban hit or a dead relative link, and `3` for a shipped ban set that arrived empty. A banned character, word, or spelling and a relative link resolving to nothing on disk each fail the run, both facts a scan settles rather than a reader judging. Bullet, paragraph, and depth weight are judgments a reader settles, and cadence is a distribution whose healthy range moves with the surface, so all four report under every code.
|
|
137
|
+
Exit codes are `0` for a completed run with no gating finding, `1` for a refusal, `2` for a ban hit or a dead relative link, and `3` for a shipped ban set that arrived empty. A banned character, word, or spelling and a relative link resolving to nothing on disk each fail the run, both facts a scan settles rather than a reader judging. Bullet, paragraph, and depth weight are judgments a reader settles, and cadence is a distribution whose healthy range moves with the surface, so all four report under every code. Length reports the same way, since the audit also runs on a plan kept out of version control, and the Document ceiling stage in `canon gate run` owns that verdict instead.
|
|
134
138
|
|
|
135
139
|
`3` is separate from `1` because the two want different responses from a caller. A refusal means no corpus was built, and the `Markdown bans` stage in `canon gate run` is right to report it as unmeasured rather than as a pass. An empty set means the corpus was walked and nothing was looked for, so that stage fails the push on `3` rather than skipping.
|
|
136
140
|
|
|
@@ -35,18 +35,25 @@ Each check traces to a line in the standard.
|
|
|
35
35
|
- No `README.md` inside a skill folder
|
|
36
36
|
- Folder name in kebab-case
|
|
37
37
|
- Each `REQUIREMENT.md` declaring `Gap` and `Must`, matched at any heading level
|
|
38
|
+
- No ISO date in `SKILL.md` or any file under `references/`, reported as `datedProvenance` in the JSON record's `findings`
|
|
38
39
|
|
|
39
40
|
A body whose frontmatter does not parse reports as declaring neither field rather than ending the run, so one malformed file cannot hide the corpus behind it. A key present with an empty value reads as absent, since a blank `name` would otherwise report as a name disagreeing with every folder. A folder carrying no requirement is reported once under presence rather than counted again for the sections it therefore lacks.
|
|
40
41
|
|
|
42
|
+
The date check reads prose alone. A date inside a fence or a code span is example data, such as a sample frontmatter block, and a date in the body's own frontmatter is metadata, so neither reports. `REQUIREMENT.md` and `EVAL.md` are not read, since the requirement's `Gap` is where the standard sends the incident and the date that earned a rule.
|
|
43
|
+
|
|
44
|
+
Each finding names the file and the line, such as `claude/skills/<name>/references/<file>.md line 12: <date>`. A date after "measured" or "verified" reports like any other, unlike the context audit, because a skill body states the rule a session follows today rather than a reading of the tree.
|
|
45
|
+
|
|
41
46
|
## What it leaves alone
|
|
42
47
|
|
|
43
48
|
The report names its own blind spots on every run, including the run where everything passed. Whether each `Must` traces to a stated gap is the rule in that standard worth the most and no parser reads it. Whether a gap states an observed failure rather than an intent, and whether a description routes, are the same kind of judgment. The 150-line body checkpoint is the one mechanical rule still absent here, and adding it would print a count rather than a defect, since the standard makes it a prompt to look with nothing enforcing it.
|
|
44
49
|
|
|
50
|
+
The date check reads ISO dates alone. A date written in words, a month with no day, and provenance carrying no date at all pass it.
|
|
51
|
+
|
|
45
52
|
A check with no rule behind it prints an opinion as a defect, which is where the list stops.
|
|
46
53
|
|
|
47
54
|
## Exit codes
|
|
48
55
|
|
|
49
|
-
Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a skill folder carrying no `REQUIREMENT.md`. Only presence sets a failing code. Name, description, folder,
|
|
56
|
+
Exit codes are `0` for a clean run, `1` for a refusal, and `2` for a skill folder carrying no `REQUIREMENT.md`. Only presence sets a failing code. Name, description, folder, requirement-section, and dated-provenance findings print and return `0`, because each is a judgment a reader settles and failing a push on one would make the check something to route around.
|
|
50
57
|
|
|
51
58
|
## The requirement gate
|
|
52
59
|
|
|
@@ -273,15 +273,15 @@ This section is the corpus the coverage claim is measured against: every name `c
|
|
|
273
273
|
|
|
274
274
|
### Run several tracks at once
|
|
275
275
|
|
|
276
|
-
| Skill | When to use
|
|
277
|
-
| ------------------------- |
|
|
278
|
-
| `canon:role-orchestrator` | To assert the control session that owns the queue and reviews each worker's PR
|
|
279
|
-
| `canon:role-planner` | To assert the thinking role for a cold session writing a plan, a groundwork track, or an intake pass
|
|
280
|
-
| `canon:role-worker` | To assert the worker role for a cold session building one branch under one plan
|
|
281
|
-
| `canon:session-relay` | When this session owes another session a message, with or without a role
|
|
282
|
-
| `canon:session-resume` | At the start of a session, to pick up what a previous one left
|
|
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
|
|
276
|
+
| Skill | When to use |
|
|
277
|
+
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
278
|
+
| `canon:role-orchestrator` | To assert the control session that owns the queue and reviews each worker's PR |
|
|
279
|
+
| `canon:role-planner` | To assert the thinking role for a cold session writing a plan, a groundwork track, or an intake pass |
|
|
280
|
+
| `canon:role-worker` | To assert the worker role for a cold session building one branch under one plan |
|
|
281
|
+
| `canon:session-relay` | When this session owes another session a message, with or without a role |
|
|
282
|
+
| `canon:session-resume` | At the start of a session, to pick up what a previous one left |
|
|
283
|
+
| `canon:session-compact` | Before a compaction or a move to another machine, 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 |
|
|
285
285
|
|
|
286
286
|
### Keep the project current with the toolkit
|
|
287
287
|
|
|
@@ -44,5 +44,7 @@ paths:
|
|
|
44
44
|
- Commit a flagged case's evidence output so the pull request carries the comparison, rather than attaching it by hand.
|
|
45
45
|
- A project whose own established convention already commits that same comparison may route a flagged case's evidence there instead of a separate evidence folder. Only an established convention already committing the same comparison qualifies, not a preference for skipping a second folder.
|
|
46
46
|
- Commit an evidence case for the first time only after running the capture twice with no code change between the runs and confirming the two outputs are byte-identical.
|
|
47
|
+
- Mask the element carrying a value that moves between runs, such as an elapsed-time counter, a clock, or a relative date, in the state's capture case before the double capture, so the check still runs on everything else in the frame. Mask the smallest element holding the value, never the state.
|
|
48
|
+
- Do not commit the state as evidence when the masked outputs still differ, which happens when the value's width reflows its neighbors. Name the variance in the pull request instead, and never re-run toward a match.
|
|
47
49
|
- Recommitting an unbounded sweep on every run reaches a gigabyte of repository history inside a hundred merges. A small, committed evidence set is what a reviewer needs and what lets GitHub draw its own before-and-after comparison on the pull request.
|
|
48
50
|
- `git-pr` and `git-followup` maintain a comparison comment on the pull request automatically once a case is committed, so a reviewer never has to open Files Changed to see it.
|