@erclx/canon 4.3.0 → 4.5.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/claude-markdown-propose/REQUIREMENT.md +0 -1
- package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +34 -9
- package/claude/skills/repo-metadata/REQUIREMENT.md +37 -0
- package/claude/skills/repo-metadata/SKILL.md +51 -0
- package/docs/agents/audits.md +6 -6
- package/docs/agents/commands.md +64 -62
- package/docs/agents/context-audit.md +2 -2
- package/docs/agents/index.md +1 -1
- package/docs/agents/records.md +17 -7
- package/docs/agents/sandbox.md +3 -1
- package/docs/agents/tasks.md +36 -1
- package/docs/operating-model.md +1 -1
- package/package.json +1 -1
- package/scripts/core/check-ignore-parity.sh +9 -3
- package/scripts/lib/sandbox-dispatch.sh +182 -0
- package/src/audits/baseline.ts +1 -1
- package/src/claude/cases/misc.ts +4 -0
- package/src/cli.ts +4 -0
- package/src/commands/claude.ts +7 -1
- package/src/commands/context.ts +3 -3
- package/src/commands/design.ts +6 -1
- package/src/commands/feedback.ts +5 -1
- package/src/commands/gov.ts +2 -1
- package/src/commands/repo.ts +393 -0
- package/src/commands/slides.ts +6 -1
- package/src/commands/tasks.ts +100 -0
- package/src/context/citations.ts +16 -5
- package/src/context/folders.ts +18 -8
- package/src/gate/measures.ts +1 -1
- package/src/intake/folder.ts +2 -1
- package/src/paths.ts +16 -0
- package/src/record-root.ts +134 -0
- package/src/records/backup.ts +40 -22
- package/src/records/size.ts +12 -7
- package/src/records/validate.ts +35 -17
- package/src/repo/metadata.ts +206 -0
- package/src/tasks/answers.ts +202 -0
- package/src/tasks/archive.ts +33 -30
- package/src/teach/workspace.ts +2 -1
- package/tooling/claude/manifest.toml +1 -1
- package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +8 -1
- package/tooling/claude/seeds/.claude/hooks/memory-index.sh +28 -11
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +12 -3
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +4 -0
- package/tooling/claude/seeds/.claude/hooks/tasks-index.sh +28 -10
|
@@ -23,7 +23,6 @@ A second failure compounds the first. A claim copied across several files is cor
|
|
|
23
23
|
- Carry three labelled variants on a change whose replacement was invented, and one on a change that corrects text to a recorded fact
|
|
24
24
|
- Write one proposal file per source file under `.claude/proposals/<slug>/` and stop, leaving `You:` empty on every change
|
|
25
25
|
- Apply only a change carrying an answer, one file at a time, and re-grep its anchor before applying it
|
|
26
|
-
- Name a folder that does not collide with `.claude/audits/`, which holds generated data rather than decision state
|
|
27
26
|
- Report what each file leaves alone, so a proposal cannot be read as finding everything wanting
|
|
28
27
|
|
|
29
28
|
## Must not
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Orchestrator dispatch runbook
|
|
3
|
-
description: The collision check before a self-dispatch, the file-set disjointness gate, the branch and model the launch names, the planning and handback dispatch shapes, and the loop's stopping condition
|
|
3
|
+
description: The plan-answer gate, the collision check before a self-dispatch, the file-set disjointness gate, the branch and model the launch names, the planning and handback dispatch shapes, and the loop's stopping condition
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Run this at loop step 4, for a `## Run now` row whose plan is verified, in place of handing the worktree to a human. The disjointness gate below is where that row's file set is tested against every track in flight.
|
|
@@ -11,6 +11,25 @@ Resolve `<slug>` from `<plan>`, the row's plan file, the way `claude-worktree` S
|
|
|
11
11
|
|
|
12
12
|
This is the branch the worker takes, not a guess at one it will derive for itself. Carry the exact string into the launch below. Both halves of that derivation have already disagreed in production: one run checked `docs/remaining-skill-verdicts` against a worker that took `docs/skill-verdicts-decide`, and a later one checked `fix/path-form-hook` against a worker that took `feat/path-form-hook`. A check against a branch nobody uses verifies nothing, and a slug mismatch no longer fails the run downstream on its own, since `claude-autoship` now takes `<plan>` directly rather than resolving it from the worker's own branch. The check above is what has to catch a wrong candidate now.
|
|
13
13
|
|
|
14
|
+
## Check the plan waits on nobody
|
|
15
|
+
|
|
16
|
+
Run `canon tasks plan-answers <plan> --json` and read `launchable` off the record.
|
|
17
|
+
|
|
18
|
+
- `launchable: true`: the plan answers itself, so proceed to the branch check.
|
|
19
|
+
- `launchable: false`: the row is not dispatchable. Report every entry in `open`, each carrying the question label and the reason its suggestion gave for needing a person, and hand the row to the human-launch line below. Never fill the slot on the operator's behalf, which is the one move the plan standard forbids outright.
|
|
20
|
+
- `reason: archived`: the row's plan sits in `.claude/plans/archive/` and describes work that already shipped. Repoint the row at a live plan rather than dispatching, since `claude-autoship` Step 1 refuses the same file and the worker would meet that refusal after the launch spent.
|
|
21
|
+
- The command refuses for any other reason, or the record carries no `launchable` key: treat the row as unverified rather than clear, name what could not be read, and fall back to the human. A gate that reads nothing and proceeds is the gate not running.
|
|
22
|
+
|
|
23
|
+
Branch on `launchable` rather than on the exit code, which a shell function wrapping `canon` can flatten to zero and so read a held row as a clear one.
|
|
24
|
+
|
|
25
|
+
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.
|
|
26
|
+
|
|
27
|
+
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. That happened on 2026-08-31, when two rows were cleared against each other with one constraints block leaving out the context entry both were about to write, and what caught it was a worker sending a message rather than any check.
|
|
28
|
+
|
|
29
|
+
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>` 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.
|
|
30
|
+
|
|
31
|
+
What it prevents is a halt nobody is watching for. `claude-worker` instructs a session to stop on a question written as needing the operator's call, correctly and by its own body, so a dispatch that never reads the plan lands a worker in a wait for a person who does not know it is waiting. The worker's halt is not the defect, and the dispatch that made it necessary is.
|
|
32
|
+
|
|
14
33
|
## Check the branch is unclaimed
|
|
15
34
|
|
|
16
35
|
Run `canon sessions list --branch <type>/<slug> --json` and read `claimed` off the record.
|
|
@@ -43,7 +62,7 @@ The finding names which row contributed the containing path, and a bare-folder c
|
|
|
43
62
|
|
|
44
63
|
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, measured 2026-08-27, 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.
|
|
45
64
|
|
|
46
|
-
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.
|
|
65
|
+
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.
|
|
47
66
|
|
|
48
67
|
## Pick the model
|
|
49
68
|
|
|
@@ -186,12 +205,16 @@ resolved off the row the way the build shape resolves one off a plan.
|
|
|
186
205
|
reads `planner-` for the reason the worker's reads `worker-`, which is that it
|
|
187
206
|
marks the role of the session it names rather than the one that launched it.
|
|
188
207
|
|
|
189
|
-
|
|
190
|
-
and the disjointness gate has nothing to compare, since a
|
|
191
|
-
no track in flight can hold.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
208
|
+
None of the three checks above binds this shape. The branch check has no
|
|
209
|
+
candidate to read, and the disjointness gate has nothing to compare, since a
|
|
210
|
+
planner writes one file no track in flight can hold. The plan-answer gate
|
|
211
|
+
reaches no plan at all, because the planner is dispatched to write the file a
|
|
212
|
+
build would later read, so running it here would refuse every planning dispatch
|
|
213
|
+
over a plan nobody has written yet.
|
|
214
|
+
|
|
215
|
+
What a planning dispatch owes instead is the reverse reading, because the plan
|
|
216
|
+
it produces carries a constraint per track in flight and a row planned during a
|
|
217
|
+
wave is planned against a tree that wave is changing. `claude-planner` states that read as a command over open pull
|
|
195
218
|
requests, which is why the brief carries no branch list for it.
|
|
196
219
|
|
|
197
220
|
One row per dispatch. A session reused across a batch pays the context load once
|
|
@@ -202,7 +225,9 @@ session where the saving is worth it and say what the cap was.
|
|
|
202
225
|
|
|
203
226
|
## Fall back to the human
|
|
204
227
|
|
|
205
|
-
Hand the row to the human-launch line in step 4 instead of dispatching when any of these hold, and name which one: the collision check refused, the row's file set overlaps a track already out, or a stated reason holds the row behind one.
|
|
228
|
+
Hand the row to the human-launch line in step 4 instead of dispatching when any of these hold, and name which one: the plan still waits on the operator, the plan-answer read could not be taken, the collision check refused, the row's file set overlaps a track already out, or a stated reason holds the row behind one.
|
|
229
|
+
|
|
230
|
+
The first of those five is the one that reaches a person rather than the board. A row held for a collision or for a serialize reason waits on the wave clearing, where a row held on its plan waits on an answer only the operator can give, so hand that one over with the question label and its stated reason attached rather than as a name and a refusal.
|
|
206
231
|
|
|
207
232
|
## Stop the loop
|
|
208
233
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: repo-metadata
|
|
3
|
+
description: Why an About text nobody re-derives drifts from the README it summarizes, and why writing it stays a second, explicit step
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Repo metadata requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, a repository's GitHub About description, homepage, and topics are set once by hand and never checked again. Nothing in either skill corpus reads or writes them, so a README rewrite carries no signal back to the three fields a stranger reads before the README, and they drift silently. That already happened here: the About text disagreed with the README's own opening line, and nobody noticed until someone read both side by side.
|
|
11
|
+
|
|
12
|
+
A session that fixes this by hand skips the gate a repeatable proposal would have given it. It reads the README once, writes a description from memory, and never records what it compared against or gives anyone else a chance to see the diff before it goes out. The write is also public and immediate: `gh repo edit` changes what every visitor to the repository page sees, with no draft state and no second confirmation from GitHub itself.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Compute the proposal from the tree alone, through `canon repo metadata propose`, and report the diff against the remote before any write is discussed
|
|
17
|
+
- Route the choice of which fields to write through the structured question surface, since which field to accept is the operator's preference to decide
|
|
18
|
+
- Run `canon repo metadata apply` only in a turn separate from the one that ran propose, carrying only the fields the operator answered in this conversation
|
|
19
|
+
|
|
20
|
+
## Must not
|
|
21
|
+
|
|
22
|
+
- Call apply in the same invocation that ran propose, whatever the operator's request already names
|
|
23
|
+
- Write a field the operator has not answered in this conversation, even when the CLI's own proposal suggested a value for it
|
|
24
|
+
- Read an absent field in the proposal as a suggestion to clear what the remote already carries
|
|
25
|
+
- Edit `README.md` or any repository setting other than description, homepage, and topics
|
|
26
|
+
- Fire from a request with no repository-metadata content, since a skill invoked on a vague signal risks presenting a write path the operator never asked for
|
|
27
|
+
|
|
28
|
+
## Guards
|
|
29
|
+
|
|
30
|
+
- No `gh` remote resolves. Stop, naming the refusal `canon repo metadata propose` reports, since a proposal with nothing to compare against is a suggestion nobody asked for.
|
|
31
|
+
- `canon repo metadata propose` or `apply` does not resolve on an older install. Report that rather than proceeding, since the fix without the verb reintroduces the hand-written write this skill exists to gate.
|
|
32
|
+
|
|
33
|
+
## Out of scope
|
|
34
|
+
|
|
35
|
+
- Editing the README's own content, which is a request against that file rather than against repository metadata
|
|
36
|
+
- Repository settings outside description, homepage, and topics, such as visibility or branch protection, which carry a different blast radius and no local source to propose from
|
|
37
|
+
- Any repeat run outside a target project's own tree, since the proposal is computed from that tree's README and manifest rather than from this toolkit's shape
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: repo-metadata
|
|
3
|
+
description: Proposes a GitHub About description, homepage, and topic set computed from the README's opening line and package.json, reports the difference against what the repository's remote already carries, and stops without writing. A later invocation, carrying the operator's answered fields, writes the change through gh repo edit. Use when asked to "propose repo metadata", "check the github description", "does our github about text match the readme", "sync github topics", "update the repo homepage", or "apply the repo metadata answers". Do NOT use to edit README.md itself, or to change a repository setting other than description, homepage, or topics.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Repo metadata
|
|
7
|
+
|
|
8
|
+
Reads what `canon repo metadata propose` computes locally and reports it against the remote. Writing is a separate step: `canon repo metadata apply` never runs in the same invocation that ran propose, whatever the operator answers in the same breath. The gate is the two invocations, not a prompt inside one.
|
|
9
|
+
|
|
10
|
+
## Guards
|
|
11
|
+
|
|
12
|
+
- Report it rather than proceeding silently when `canon repo metadata propose` or `canon repo metadata apply` does not resolve. Both ship with the CLI, so a project on an older install meets a missing subcommand rather than a refusal.
|
|
13
|
+
- The propose read refuses when the repository has no remote `gh` can read. Report that refusal verbatim and stop. A proposal with nothing to compare against is a suggestion nobody asked for.
|
|
14
|
+
- Never call apply in the same turn that ran propose, even when the operator's request already names the fields to write. Surface the proposal, get the answer, then run apply as its own step.
|
|
15
|
+
- Never write a field the operator did not confirm in this conversation. A field the proposal computed and the operator has not answered stays unwritten, whatever the CLI's own suggestion for it was.
|
|
16
|
+
- A field absent from the proposal has no local source and is never a suggestion to clear what the remote already carries. Report it as unchanged, not as a removal.
|
|
17
|
+
|
|
18
|
+
## Propose
|
|
19
|
+
|
|
20
|
+
1. Run `canon repo metadata propose --json`.
|
|
21
|
+
2. Read `diff` and `repo` from the record. `repo` is the `--repo` value the later apply step must carry. An empty `diff` means the remote already matches what this run computed: report that and stop, since there is nothing to answer.
|
|
22
|
+
3. For each field `diff` carries, put the change to the operator through the structured question surface: which of the differing fields to write. Rank accepting the proposed value first for a field whose current value is stale or wrong, and give the reject option the cost of leaving the remote as it stands. Never pre-select an answer for the operator.
|
|
23
|
+
4. Report the fields the proposal left absent as unchanged, naming that neither the README nor `package.json` carried a source for them.
|
|
24
|
+
5. Stop. Do not run apply here even when the operator answers immediately, since answering is not yet an apply invocation.
|
|
25
|
+
|
|
26
|
+
## Apply
|
|
27
|
+
|
|
28
|
+
1. Confirm every field about to be written was answered by the operator in this conversation. Carry no field forward unanswered.
|
|
29
|
+
2. Run `canon repo metadata apply`, always passing `--repo <owner/name>` from the propose record's `repo` field, plus the flags for the answered fields: `--description <text>`, `--homepage <url>`, `--topics <comma-separated list>`. `--topics` is the full desired set, and the command reads the current set itself to compute what to add and remove. The command refuses rather than writing when `--repo` does not match what `--root` resolves to, so never omit it and never guess it from anything but the propose record's `repo` field.
|
|
30
|
+
3. Report the written state from the JSON record.
|
|
31
|
+
|
|
32
|
+
## Output
|
|
33
|
+
|
|
34
|
+
Propose:
|
|
35
|
+
|
|
36
|
+
```plaintext
|
|
37
|
+
📋 Repo metadata proposal
|
|
38
|
+
description: "<current>" → "<proposed>"
|
|
39
|
+
homepage: unchanged, no local source
|
|
40
|
+
topics: +<added>, -<removed>
|
|
41
|
+
|
|
42
|
+
Answer which fields to write, then ask again to apply.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Apply:
|
|
46
|
+
|
|
47
|
+
```plaintext
|
|
48
|
+
✅ Applied: <fields written>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Omit a line from the proposal block for a field `diff` did not carry.
|
package/docs/agents/audits.md
CHANGED
|
@@ -14,11 +14,11 @@ canon audits run --record
|
|
|
14
14
|
canon audits list --json
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
| Option | Behavior
|
|
18
|
-
| --------------- |
|
|
19
|
-
| `--json` | Add a machine-readable record on stdout, keeping the frame
|
|
20
|
-
| `--root <path>` | Measure this tree instead of the current worktree
|
|
21
|
-
| `--record` | Write this run's tracked counts to `.claude/
|
|
17
|
+
| Option | Behavior |
|
|
18
|
+
| --------------- | ---------------------------------------------------------------- |
|
|
19
|
+
| `--json` | Add a machine-readable record on stdout, keeping the frame |
|
|
20
|
+
| `--root <path>` | Measure this tree instead of the current worktree |
|
|
21
|
+
| `--record` | Write this run's tracked counts to `.claude/canon/baseline.json` |
|
|
22
22
|
|
|
23
23
|
## What it runs
|
|
24
24
|
|
|
@@ -73,7 +73,7 @@ Every run states how many corpora it measured against how many it skipped, inclu
|
|
|
73
73
|
|
|
74
74
|
## The retained baseline
|
|
75
75
|
|
|
76
|
-
`.claude/
|
|
76
|
+
`.claude/canon/baseline.json` holds the counts from the last run recorded with `--record`, alongside the day it was taken and the commit it was read at. Each later run reports which counts moved and by how much.
|
|
77
77
|
|
|
78
78
|
The file is committed. A per-machine record cannot answer the question this half exists for, which is whether a number grew since anyone last looked, and a fresh checkout has to inherit that answer rather than start over. The cost is that a branch moving a count either fixes it or re-records it and says why, which is the shape the consumed-copy assertion already carries.
|
|
79
79
|
|
package/docs/agents/commands.md
CHANGED
|
@@ -9,68 +9,70 @@ Full help: `canon <command> --help`. Behavior notes for the install and sync ver
|
|
|
9
9
|
|
|
10
10
|
## Project-level
|
|
11
11
|
|
|
12
|
-
| Command
|
|
13
|
-
|
|
|
14
|
-
| `canon init [path]`
|
|
15
|
-
| `canon sync [path]`
|
|
16
|
-
| `canon sync --check`
|
|
17
|
-
| `canon sandbox [cat:cmd]`
|
|
18
|
-
| `canon sandbox reset`
|
|
19
|
-
| `canon sandbox clean`
|
|
20
|
-
| `canon sandbox check`
|
|
21
|
-
| `canon sandbox coverage`
|
|
22
|
-
| `canon indexes regen`
|
|
23
|
-
| `canon docs [topic]`
|
|
24
|
-
| `canon design render`
|
|
25
|
-
| `canon slides render`
|
|
26
|
-
| `canon slides list`
|
|
27
|
-
| `canon feedback`
|
|
28
|
-
| `canon transcripts <url>`
|
|
29
|
-
| `canon tasks archive`
|
|
30
|
-
| `canon tasks pull-request`
|
|
31
|
-
| `canon tasks outcome`
|
|
32
|
-
| `canon tasks validate`
|
|
33
|
-
| `canon intake list`
|
|
34
|
-
| `canon intake answer`
|
|
35
|
-
| `canon teach list`
|
|
36
|
-
| `canon teach open`
|
|
37
|
-
| `canon teach resource`
|
|
38
|
-
| `canon teach glossary`
|
|
39
|
-
| `canon records validate`
|
|
40
|
-
| `canon records migrate`
|
|
41
|
-
| `canon records size`
|
|
42
|
-
| `canon records push`
|
|
43
|
-
| `canon records pull`
|
|
44
|
-
| `canon migrate rename`
|
|
45
|
-
| `canon sessions list`
|
|
46
|
-
| `canon worktrees list`
|
|
47
|
-
| `canon comments scan`
|
|
48
|
-
| `canon context audit`
|
|
49
|
-
| `canon markdown audit`
|
|
50
|
-
| `canon claude skills audit`
|
|
51
|
-
| `canon standards audit`
|
|
52
|
-
| `canon claude skills drift`
|
|
53
|
-
| `canon claude skills reach`
|
|
54
|
-
| `canon claude skills rank`
|
|
55
|
-
| `canon claude routing`
|
|
56
|
-
| `canon gov test-order`
|
|
57
|
-
| `canon gov superseded`
|
|
58
|
-
| `canon gov restated`
|
|
59
|
-
| `canon gov citations`
|
|
60
|
-
| `canon secrets scan`
|
|
61
|
-
| `canon deps audit`
|
|
62
|
-
| `canon labels audit`
|
|
63
|
-
| `canon labels scan`
|
|
64
|
-
| `canon autoship classify`
|
|
65
|
-
| `canon pr key-changes`
|
|
66
|
-
| `canon
|
|
67
|
-
| `canon
|
|
68
|
-
| `canon
|
|
69
|
-
| `canon
|
|
70
|
-
| `canon
|
|
71
|
-
| `canon
|
|
72
|
-
| `canon
|
|
73
|
-
| `canon
|
|
12
|
+
| Command | Purpose |
|
|
13
|
+
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
14
|
+
| `canon init [path]` | Bootstrap a project with selected toolkit domains |
|
|
15
|
+
| `canon sync [path]` | Sync all installed domains in a target project |
|
|
16
|
+
| `canon sync --check` | Report toolkit drift and the installed version against the newest published (`--json`, `--exit-code`) |
|
|
17
|
+
| `canon sandbox [cat:cmd]` | Run sandbox scenarios (interactive or routed), toolkit-only like the tree it reads |
|
|
18
|
+
| `canon sandbox reset` | Reset sandbox to baseline |
|
|
19
|
+
| `canon sandbox clean` | Wipe the sandbox |
|
|
20
|
+
| `canon sandbox check` | Score a provisioned sandbox against a scenario expectation (`--json` for the verdict) |
|
|
21
|
+
| `canon sandbox coverage` | Report which scenarios declare expectations (`--json`, `--strict`, `--skills`) |
|
|
22
|
+
| `canon indexes regen` | Regenerate `index.md` files from sibling frontmatter |
|
|
23
|
+
| `canon docs [topic]` | Emit toolkit reference docs (`list`, or a topic by name) |
|
|
24
|
+
| `canon design render` | Render `.claude/DESIGN.md` tokens to HTML and CSS |
|
|
25
|
+
| `canon slides render` | Render a `.claude/SLIDES.md` source into a PowerPoint deck, reporting any unrecognized layout name on stderr |
|
|
26
|
+
| `canon slides list` | List the available slide layouts (`--json` for the catalog) |
|
|
27
|
+
| `canon feedback` | Write toolkit feedback from stdin to `.claude/review/feedback/`, or open a GitHub issue with `--github` |
|
|
28
|
+
| `canon transcripts <url>` | Fetch a YouTube transcript with metadata frontmatter (needs `yt-dlp`) |
|
|
29
|
+
| `canon tasks archive` | Move a shipped task off the board, clear its ordering row, and regenerate the index |
|
|
30
|
+
| `canon tasks pull-request` | Record a pull request number on the task a branch closes, by stem or `--plan` (`--json`) |
|
|
31
|
+
| `canon tasks outcome` | Mark outcomes `[x]` on a task by position, repeating `--close` (`--json`) |
|
|
32
|
+
| `canon tasks validate` | Report board rows whose shape, order, plan, task file, group, file set, or blocker does not hold (`--json`) |
|
|
33
|
+
| `canon intake list` | Report intake folder counts, or one folder's items, keeping what is unread with `--unread` (`--json`) |
|
|
34
|
+
| `canon intake answer` | Write selections into one cluster's answer slots, repeating `--set <item>=<answer>` (`--json`) |
|
|
35
|
+
| `canon teach list` | Report learning workspaces and the ordinal a new one takes, or what one workspace holds (`--json`) |
|
|
36
|
+
| `canon teach open` | Open a workspace at the next ordinal with its mission, resources, and glossary files (`--json`) |
|
|
37
|
+
| `canon teach resource` | Record sources and leads in a workspace, repeating `--read` or `--lead` as `<title>=<url>` (`--json`) |
|
|
38
|
+
| `canon teach glossary` | Add terms to a workspace glossary alphabetically, repeating `--term <term>=<definition>` (`--json`) |
|
|
39
|
+
| `canon records validate` | Report a session record or a standard against the standard governing it, per kind (`--json`) |
|
|
40
|
+
| `canon records migrate` | Rewrite the records a validate finding names a recoverable transform for (`--write`, `--json`) |
|
|
41
|
+
| `canon records size` | Report what each record folder holds and how much of it is recent, heaviest first (`--json`) |
|
|
42
|
+
| `canon records push` | Commit the nine backed record folders and push them to a private records remote (`--json`) |
|
|
43
|
+
| `canon records pull` | Fetch the records remote and write it back, refusing rather than discarding unpushed records (`--json`) |
|
|
44
|
+
| `canon migrate rename` | Rewrite every unprotected `aitk` token to `canon` and move the paths that carry the name, reporting the plan without `--write` (`--scope`, `--json`) |
|
|
45
|
+
| `canon sessions list` | Resolve live sessions to the worktree and branch each holds, filtered by `--branch` (`--json`) |
|
|
46
|
+
| `canon worktrees list` | Report which worktrees are reclaimable, keyed on the pull request having merged, with every refusal and the removal route named (`--json`) |
|
|
47
|
+
| `canon comments scan` | Measure comment density by language and comment kind, with a trend recomputed from git |
|
|
48
|
+
| `canon context audit` | Report required sections, length, cited paths, reference form, catalog tables, provenance, superseded-decision narration, and index drift |
|
|
49
|
+
| `canon markdown audit` | Fail any markdown path on a banned character, word, or spelling, and report the structural checkpoints |
|
|
50
|
+
| `canon claude skills audit` | Report both skill corpora against the mechanical rules in `standards/skill.md` |
|
|
51
|
+
| `canon standards audit` | Report the corpus against the `## Success criterion` gate, failing only on a standard new to the branch (`--json`, `--arrivals-only`) |
|
|
52
|
+
| `canon claude skills drift` | Name the shipped skill bodies rewritten between a given ref and `HEAD`, and the installed version against the newest published (`--json`) |
|
|
53
|
+
| `canon claude skills reach` | Report the bodies in either skill corpus citing a toolkit path no target project receives, exiting 2 on an unqualified one |
|
|
54
|
+
| `canon claude skills rank` | Score either skill corpus's descriptions against a case corpus by TF-IDF cosine similarity, reporting rank-one and top-three (`--cases <path>`) |
|
|
55
|
+
| `canon claude routing` | Report per `CLAUDE.md` section how many bullets name a path and how many of those a path-scoped rule already covers (`--json`) |
|
|
56
|
+
| `canon gov test-order` | Report where an implementation reached history ahead of the test covering it (`--json`) |
|
|
57
|
+
| `canon gov superseded` | Report where the tree still asserts a value a changed convention no longer produces, keyed on the value and on the family stem behind a templated citation (`--json`) |
|
|
58
|
+
| `canon gov restated` | Report every instruction the always-loaded file or a rule shares with the seed, a shipped skill body, or another rule, classed and with its anchors named (`--json`) |
|
|
59
|
+
| `canon gov citations` | Resolve every path a rule cites and every internal frontmatter glob, failing on one reaching nothing (`--json`) |
|
|
60
|
+
| `canon secrets scan` | Report credential-shaped values in the tree the package ships, keyed on issued values rather than on words (`--json`) |
|
|
61
|
+
| `canon deps audit` | Report published advisories against the resolved dependency set, refusing rather than reporting clean when the index is unreachable (`--json`) |
|
|
62
|
+
| `canon labels audit` | Report the labels a changed set earns from the pull request label map and the paths no row reaches (`--json`) |
|
|
63
|
+
| `canon labels scan` | Fail a pull request whose title or body carries a phase label, sorting a release pull request's tokens as semver rather than as a leak (`--event`, `--json`) |
|
|
64
|
+
| `canon autoship classify` | Decide whether a changed set needs the review pass, naming the file and the test that decided it (`--json`) |
|
|
65
|
+
| `canon pr key-changes` | Compare the files a pull request body's Key Changes names against its own diff, in both directions (`--body`, `--base`, `--json`) |
|
|
66
|
+
| `canon repo metadata propose` | Compare a description, homepage, and topic set computed from the README and `package.json` against what the remote carries, writing nothing (`--root`, `--json`) |
|
|
67
|
+
| `canon repo metadata apply` | Write an explicitly supplied description, homepage, or topic set to the remote through `gh repo edit` (`--description`, `--homepage`, `--topics`, `--root`, `--json`) |
|
|
68
|
+
| `canon census [path]` | Report tracked file count, a breakdown by extension, and a line total that skips whatever reads as binary (`--json`) |
|
|
69
|
+
| `canon audits run` | Run every audit as one set, report per check under one verdict, and compare each count to the recorded baseline (`--json`, `--record`) |
|
|
70
|
+
| `canon audits list` | List every audit the set runs, with the corpus each reads and whether it gates (`--json`) |
|
|
71
|
+
| `canon gate run` | Run every stage that guards a branch here, scoping shell, types, and tests to the changed set (`--all`, `--no-write`, `--nested`, `--json`) |
|
|
72
|
+
| `canon inventory [subject]` | Walk every route a project declares and group its elements by the property each computes, as a listing rather than a gate (`--json`) |
|
|
73
|
+
| `canon capture [source]` | Render HTML capture sources to PNG, toolkit-only and absent from an installed package |
|
|
74
|
+
| `canon serve [dir]` | Serve a directory on the loopback interface and print the link that opens it, running until interrupted (`--port`, `--entry`, `--json`) |
|
|
75
|
+
| `canon upgrade` | Reinstall the CLI globally with the package manager the install path names (`--json`) |
|
|
74
76
|
|
|
75
77
|
`canon serve` ships and drives no browser, which is what separates it from the two that do. A generated page loses its stylesheet and its script to an editor preview and to a `file://` open, so the link is the delivery rather than a convenience, and every generated surface here reaches a reader through one. It binds `127.0.0.1` and never a wildcard, because what it is pointed at is routinely a gitignored record tree. It sends `cache-control: no-store`, since a preview exists to be edited and reloaded and a cached stylesheet reads as a fix that did not work.
|
|
76
78
|
|
|
@@ -31,7 +31,7 @@ canon context audit tooling/base/seeds --gate
|
|
|
31
31
|
|
|
32
32
|
Scope defaults to `context`, `diagrams`, and `wireframes`, and a folder the project does not carry is skipped rather than reported. A domain that outgrew one file and split into `<domain>/` is audited as its own folder, so a split entry measures at the same grain as a flat one.
|
|
33
33
|
|
|
34
|
-
A name passed to `--folder` resolves under `.
|
|
34
|
+
A name passed to `--folder` resolves under the record roots first, `.canon/` ahead of `.claude/`, and at the project root last, which is what puts `docs/` and any later corpus in reach of the same engine. The root base is reached only by a name the caller passes, so the default list still resolves under a record root alone and a project holding a root `wireframes/` is not audited against a standard it never adopted. `diagrams` is the one default name that is a session record and moves with them, which is why both record roots are in the list rather than one, and `docs/agents/records.md` states the read order every verb shares. The scope line prints the resolved path, so a project carrying both spellings reads which one was taken. The JSON record carries the base per folder as `folders[].base`.
|
|
35
35
|
|
|
36
36
|
A run where no requested name resolves refuses, whichever list it read. Naming the absent ones narrows to `--folder`, since a project carrying one of the three default folders is the ordinary case and a name it never asked for is not a typo. The JSON record carries those names as `unresolvedFolders`.
|
|
37
37
|
|
|
@@ -80,4 +80,4 @@ Append `<!-- audit-ignore-citations: <path> -->` to the source line in either ca
|
|
|
80
80
|
|
|
81
81
|
The marker itself stays out of anything that installs. A seed, a plugin skill body, and a stack reference all reach a target, so a marker there lands as toolkit bookkeeping in someone else's tree. Reword those lines to drop the path instead, and where a stop message has to spell it, move that message into a fenced block, which this check already skips.
|
|
82
82
|
|
|
83
|
-
The pattern spells
|
|
83
|
+
The pattern spells both record-root prefixes, so a citation into a folder that has moved still resolves and a folder resolved at the project root is measured by every other check while contributing nothing here. A pattern fixed at one root matches nothing after a move and reports nothing, which is a stale reference passing the check written to find it. Widening it to a bare `docs/x.md` would match prose that references nothing, which is a separate decision from where entries come from. A run whose folders all resolved at the root says the check is out of scope rather than reporting that zero paths resolved, and the same run under `--citations-only` refuses, because a gate exiting clean on a scope it could not build is the failure the gate exists to catch.
|
package/docs/agents/index.md
CHANGED
|
@@ -27,7 +27,7 @@ CLI catalog and invocation rules for agents, split by command domain. Start with
|
|
|
27
27
|
- [Markdown audit](markdown-audit.md): Running the audit over any markdown path, where its bans and checkpoints are read from, what each check reports, and why the ban half gates while the structural half reports
|
|
28
28
|
- [Output shape](output-shape.md): Two framed shapes every command renders into, how JSON and --names modes keep stdout clean, and the exit discipline that lets piped output drain
|
|
29
29
|
- [Overview](overview.md): What this folder covers, the invocation rules every command inherits, and where domain behavior is documented instead
|
|
30
|
-
- [Records](records.md):
|
|
30
|
+
- [Records](records.md): The two roots a record folder resolves at, validating the session records and the standards corpus, the per-kind checks, the refusal reasons, migrating a record a frontmatter change orphaned, reading each folder's size and growth, backing the folders to a private remote, and which root each kind defaults to
|
|
31
31
|
- [Restated instructions](restated.md): Counting the instructions the always-loaded file and every path-scoped rule share with the seed, the shipped skill bodies, and each other, how a match is decided, the three classes, which surface a later edit starts from, and why the sweep reports rather than gates
|
|
32
32
|
- [Review classification](review-classification.md): Deciding whether a changed set needs the review pass, the two tests it runs, why an empty set refuses rather than skipping, and the written fallback a target on an older binary falls back to
|
|
33
33
|
- [Routing report](routing.md): Reading per CLAUDE.md section how many bullets name a path, what counts as naming one, when a rule counts as covering it, the two refusals, and why the verb reports rather than gates
|
package/docs/agents/records.md
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Records
|
|
3
|
-
description:
|
|
3
|
+
description: The two roots a record folder resolves at, validating the session records and the standards corpus, the per-kind checks, the refusal reasons, migrating a record a frontmatter change orphaned, reading each folder's size and growth, backing the folders to a private remote, and which root each kind defaults to
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Records
|
|
7
7
|
|
|
8
|
+
## Record roots
|
|
9
|
+
|
|
10
|
+
Every verb here resolves a record folder at two roots rather than one. `.canon/<folder>` is read first, `.claude/<folder>` second, and a folder neither root carries resolves to the creation default, which is `.claude/`. The scratch folder is the one name that differs by root, spelled `.claude/.tmp` and `.canon/tmp`, since inside a dotted root the leading dot hides nothing already hidden.
|
|
11
|
+
|
|
12
|
+
The read order and the creation default disagree deliberately. The gitignored record folders are moving to a root of their own, and the CLI learns to read both roots in a release that ships ahead of the move, so the binary a session already holds knows where to look by the time a tree relocates. Creating under the new root before then would write records to a root whose ignore line may not have reached a project yet, and it would split one project's records across two roots with no verb able to reconcile them. The move flips the default and nothing else.
|
|
13
|
+
|
|
14
|
+
A caller never spells a record root by hand for the same reason. A path written as `.claude/plans/...` resolves against one root and reports nothing when it is wrong, which is the quiet failure this ordering exists to prevent: a stale binary meeting a moved layout, writing to the old path, and reporting success. Read a folder through the verb that owns it, and where a skill needs the path itself, take it from that verb's record rather than composing one.
|
|
15
|
+
|
|
16
|
+
A refusal names every root it looked at, so a message reading `no-folder` says where a write would land as well as where the read failed.
|
|
17
|
+
|
|
8
18
|
## Validate
|
|
9
19
|
|
|
10
20
|
`canon records validate <kind>` reports where a file and the standard governing it disagree. Five kinds are gitignored folders under `.claude/`: `plans`, `groundwork`, `intake`, `memory`, and `teach`. The sixth is `standards`, the authoring corpus, which is tracked and installed rather than scratch.
|
|
@@ -56,7 +66,7 @@ A section runs to the next marker-shaped line whatever it names, so a plan carry
|
|
|
56
66
|
|
|
57
67
|
### Exit codes and refusals
|
|
58
68
|
|
|
59
|
-
Exit codes: `0` every check passed, `1` refused, `2` at least one record carries a finding. A `reason` field carries which gate fired: `no-folder` when none of the kind's directories exist, and `unknown-kind` when the argument names no published kind. A `no-folder` message names every candidate, so the `standards` refusal names
|
|
69
|
+
Exit codes: `0` every check passed, `1` refused, `2` at least one record carries a finding. A `reason` field carries which gate fired: `no-folder` when none of the kind's directories exist, and `unknown-kind` when the argument names no published kind. A `no-folder` message names every candidate, so a record kind's refusal names both record roots and the `standards` refusal names the authoring root and the installed copy.
|
|
60
70
|
|
|
61
71
|
An exit code says nothing about a call made from a session, since a shell profile may wrap the binary in a function taking its status from a later command. Read the record's `findings` array and its `reason` rather than the exit when a skill consumes this.
|
|
62
72
|
|
|
@@ -96,7 +106,7 @@ Exit codes: `0` nothing carried a known transform, or `--write` repaired everyth
|
|
|
96
106
|
|
|
97
107
|
## Size
|
|
98
108
|
|
|
99
|
-
`canon records size` reports what each record folder holds and how much of it is recent. It reads the ten backed folders named under Push and pull, plus
|
|
109
|
+
`canon records size` reports what each record folder holds and how much of it is recent. It reads the ten backed folders named under Push and pull, plus the scratch folder, and it gates nothing.
|
|
100
110
|
|
|
101
111
|
```bash
|
|
102
112
|
canon records size
|
|
@@ -116,11 +126,11 @@ The reading carries two windows rather than one, at 7 and 30 days. A single wind
|
|
|
116
126
|
|
|
117
127
|
Nothing fails on a number here. A record folder has no correct size, so the reading is a number to notice rather than a threshold to gate, and the point of the verb is that the next reading is taken by a command instead of by someone remembering to count the folder. The memory pen went from 44 entries to 236 between two readings taken by hand two weeks apart, which is the measurement this replaces.
|
|
118
128
|
|
|
119
|
-
|
|
129
|
+
The scratch folder is read here and skipped by a backup, because deletable without loss is not the same as empty. The routing handoffs and the memory archive both sit there and both accumulate. `.claude/.records.git` stays out because it is the backup history rather than a record, and `.claude/worktrees/` stays out because each entry is a checkout of the project with its own removal verb, and one of them outweighs every record folder combined.
|
|
120
130
|
|
|
121
131
|
The window counts read `mtime`, so what they report is a file written inside the window rather than one created there. An entry edited long after it landed reads as recent, which overstates growth and never understates it, and these folders are append-mostly so the two readings agree on nearly every file. The one reading that is wrong rather than early is a machine restored by `canon records pull`, which resets the work tree hard and re-dates every file it writes, so a window taken there counts the restore. Nothing on the filesystem separates the two, since a restored file is new by every stamp it carries.
|
|
122
132
|
|
|
123
|
-
Exit codes: `0` the reading completed, `1` refused. The one refusal is `no-folder`, raised when the
|
|
133
|
+
Exit codes: `0` the reading completed, `1` refused. The one refusal is `no-folder`, raised when the project holds neither record root. A project holding a root and no records is empty rather than absent, and each folder's own `present` flag already says which of the ten it carries.
|
|
124
134
|
|
|
125
135
|
## Push and pull
|
|
126
136
|
|
|
@@ -132,9 +142,9 @@ canon records push --json
|
|
|
132
142
|
canon records pull
|
|
133
143
|
```
|
|
134
144
|
|
|
135
|
-
The backed folders are `diagrams`, `groundwork`, `intake`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`, all under
|
|
145
|
+
The backed folders are `diagrams`, `groundwork`, `intake`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`, all under whichever record root the project carries. Eight of them are the Claude ignore group the claude manifest ships, minus three entries: `.claude/.tmp`, which is deletable without loss, `.claude/worktrees/`, whose contents belong to the project repository already, and `.claude/.records.git/`, which is the history the rest are pushed into. `diagrams` is the one that group does not carry at all, since a target still tracks its own copies, which is why the list is spelled out rather than derived. Each name is a top-level record folder and every archive sits inside the one it archives, so the list stays at one entry per surface however many archives appear. It is a constant rather than configuration, and it deliberately does not match the six record kinds `validate` hardcodes.
|
|
136
146
|
|
|
137
|
-
Records are gitignored by design, so the history lives in a second git directory at `.
|
|
147
|
+
Records are gitignored by design, so the history lives in a second git directory at `.records.git` inside the record root, with that root as its work tree. Both resolve off the root together rather than folder by folder, since a history opened at one root beside a work tree at the other would stage the deletion of every folder a move relocated. Every path stays where it is, which is what a separate checkout could not do. The verbs stage the nine folders by explicit pathspec with `--force`, so nothing outside them can enter the index however the ignore rules read, and the project working tree and its index are never touched. Each pathspec is a bare folder name and git reads it against the current directory rather than against the work tree the same call names, so the invocation carries `-C` at the work tree beside the other two flags. That is what lets either verb run from a linked worktree under `.claude/worktrees/`, which sits inside the records work tree and would otherwise prefix every name with its own path.
|
|
138
148
|
|
|
139
149
|
### Setup
|
|
140
150
|
|
package/docs/agents/sandbox.md
CHANGED
|
@@ -68,4 +68,6 @@ A skill pairs to a scenario by filename, `<category>-<command>` first and bare `
|
|
|
68
68
|
|
|
69
69
|
`exempt` means no arm should be written and holds only with a reason, declared in `scripts/sandbox/exempt.toml` and limited to a harness limit the checker cannot reach past or a skill that writes no artifact. An armed arm outranks an exemption. An exemption naming no shipped skill, or naming one an arm now asserts, exits 1 without `--strict`. Each armed arm reports as `<category>:<command>/<arm>`, so two same-named arms under different scenarios stay distinct.
|
|
70
70
|
|
|
71
|
-
`scripts/sandbox/run.sh` calls this after a headless run and merges the verdict into the envelope it prints. It also writes that merged record to `.claude/.tmp/sandbox-runs/<target>-<arm>-<timestamp>.json` with a `writes` array appended, and logs the path on stderr. Both fields are what a later re-score needs, since `--envelope` and `--writes` read files the run deletes on exit.
|
|
71
|
+
`scripts/sandbox/run.sh` calls this after a headless run and merges the verdict into the envelope it prints. It also writes that merged record to `.claude/.tmp/sandbox-runs/<target>-<arm>-<timestamp>.json` with a `writes` array appended, and logs the path on stderr. Both fields are what a later re-score needs, since `--envelope` and `--writes` read files the run deletes on exit.
|
|
72
|
+
|
|
73
|
+
Two more fields ride alongside the verdict rather than inside it. `escapes` lists what the run wrote under a watched toolkit root, which the verdict cannot assert over because those files sit outside the sandbox tree. `sessions` reports the nested-dispatch bound, carrying `watched` for whether the client's session registry was there to read, `new` for the records that appeared while the run was in flight, and `reap` for what the run found in the session's process group afterwards. Neither field fails a run on its own.
|
package/docs/agents/tasks.md
CHANGED
|
@@ -51,7 +51,7 @@ canon tasks archive --pull-request 673 --json | jq -r 'if .ok then .task else .r
|
|
|
51
51
|
|
|
52
52
|
The record carries `location`, one of `unstated`, `live`, `archived`, or `outside`, and `citedBy`, the other live tasks whose `Plan:` line lands on the same file. Exit codes: `0` read, `1` refused with `no-board` or `no-match`.
|
|
53
53
|
|
|
54
|
-
The target resolves against
|
|
54
|
+
The target resolves against the board folder and against the project root both, so `../plans/x.md` and `.claude/plans/x.md` land on the same file and one plan two tasks spelled differently counts once. Containment is tested at both record roots rather than at the one this tree resolves at, since a line somebody wrote against a root the tree has since left is still a path into the plans folder, and reading it as outside would report a shipped plan as still live. `docs/agents/records.md` states the read order.
|
|
55
55
|
|
|
56
56
|
`canon tasks archive` gates on this same answer, so a caller wanting the count reads it here rather than scanning the board. The `claude-docs` plans sweep is the exception and still states the rule in its own body, because a plugin skill reaches a target on merge while the CLI reaches one on release, so a sweep calling a verb the installed `canon` predates gets no record back and archives nothing.
|
|
57
57
|
|
|
@@ -63,6 +63,41 @@ A `live` location with an empty `citedBy` is the sweep to run. One whose `citedB
|
|
|
63
63
|
canon tasks plan-citations v28.1-trigger-escalation --json | jq -r '.location'
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
## Plan answers
|
|
67
|
+
|
|
68
|
+
`canon tasks plan-answers <plan>` answers whether a plan is launchable, which is whether it still waits on the operator for a call only they can make. It reports and never writes.
|
|
69
|
+
|
|
70
|
+
Name the plan by its path or by its slug, which resolve to the same file:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
canon tasks plan-answers dispatch-answer-gate
|
|
74
|
+
canon tasks plan-answers .claude/plans/feature-dispatch-answer-gate.md
|
|
75
|
+
canon tasks plan-answers ../plans/feature-dispatch-answer-gate.md
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
A relative path resolves against the project root first and against `.claude/tasks/` second. The third form above is what a board row writes, since its link is relative to the board, and a dispatcher copying the reference out of the row it is dispatching has that spelling to hand rather than either of the other two. A refusal names every base it looked under.
|
|
79
|
+
|
|
80
|
+
`canon tasks plan-citations` reads a task's `Plan:` line against those same two bases in the opposite order, and tests that the target lands under the live plans folder, which this verb does not. Both answer the same file for every spelling a board writes. Liveness is a separate refusal here: a plan resolving inside `.claude/plans/archive/` returns `archived` rather than a launchable reading, since it answers every question and describes work that already shipped.
|
|
81
|
+
|
|
82
|
+
| Option | Effect |
|
|
83
|
+
| --------------- | ------------------------------------------- |
|
|
84
|
+
| `--json` | Emit a machine-readable record on stdout |
|
|
85
|
+
| `--root <path>` | Board root, defaulting to the main worktree |
|
|
86
|
+
|
|
87
|
+
The record carries `launchable` and `open`, the questions still waiting, each with the `label` that names it and the `why` its suggestion gave for needing a person. Exit codes: `0` launchable, `1` refused with `no-plan`, `archived`, or `bad-input`, `2` waiting on the operator.
|
|
88
|
+
|
|
89
|
+
A blank `- Answer:` is not a waiting question. The plan standard fixes an empty slot as accepting the `- Suggested:` line above it, so the one shape this reads is `- Suggested: needs your call, <why>` over an empty slot, which is what that standard writes where the answer turns on preference rather than on a technical default. A verb reading every blank slot as open would report every plan in the folder.
|
|
90
|
+
|
|
91
|
+
The question block is read through the same parser `canon tasks validate` runs, so the gate and the conformance check cannot drift into disagreeing about what a question is. A question carrying no suggestion at all is that check's finding rather than this one's, and it goes unread here.
|
|
92
|
+
|
|
93
|
+
Branch on `launchable` rather than on the exit code, for the reason the section above states: a shell profile wrapping `canon` in a function can take a later command's status and mask every non-zero exit, which reads a waiting plan as a launchable one.
|
|
94
|
+
|
|
95
|
+
The orchestrator dispatch runbook calls this before it checks the branch or the file sets, so a row whose plan still needs a person is handed back rather than launched into a worker that halts on the same question.
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
canon tasks plan-answers dispatch-answer-gate --json | jq -r '.launchable'
|
|
99
|
+
```
|
|
100
|
+
|
|
66
101
|
## Pull request
|
|
67
102
|
|
|
68
103
|
`canon tasks pull-request` records the number a branch's pull request carries onto the task that branch closes. It adds `Pull request: #NNN` under the `Plan:`, `Groundwork:`, `Intake:`, or `Issue:` lines the task already holds, and corrects the number in place when the line exists.
|
package/docs/operating-model.md
CHANGED
|
@@ -70,7 +70,7 @@ and no later session recovers that vantage.
|
|
|
70
70
|
One feature travels this path end to end.
|
|
71
71
|
|
|
72
72
|
1. The next feature is planned with `claude-feature`, writing a plan to `.claude/plans/`. The orchestrator runs it warm when the row turns on a contract other features consume or a shared wiring seam, and dispatches a planner under `claude-planner` otherwise. A cold planner measures the row against the tree rather than trusting what the row claims, and it reads what is in flight from open pull requests rather than from branches and worktrees, which this repository leaves behind after a squash merge.
|
|
73
|
-
2. Orchestrator checks the branch is unclaimed and the plan's file set is disjoint from every track in flight, then dispatches a background worker with `claude --bg` against the plan, naming the branch and the model on the launch rather than leaving the worker to derive either. No count caps how many run at once. The branch travels as the argument to the worker's own worktree call, which is the one place the name is read rather than inferred. It falls back to naming the invocation for a human to run through `claude-worktree` and `claude-autoship` when the check refuses, the sets overlap, or a stated reason serializes the plan behind a track already in flight. Either way, the worker enters its own worktree, builds, self-checks, opens a PR, and stops at the PR boundary.
|
|
73
|
+
2. Orchestrator checks the plan waits on nobody, checks the branch is unclaimed, and checks the plan's file set is disjoint from every track in flight, then dispatches a background worker with `claude --bg` against the plan, naming the branch and the model on the launch rather than leaving the worker to derive either. No count caps how many run at once. The branch travels as the argument to the worker's own worktree call, which is the one place the name is read rather than inferred. It falls back to naming the invocation for a human to run through `claude-worktree` and `claude-autoship` when the plan still waits on an answer only the operator can give, the check refuses, the sets overlap, or a stated reason serializes the plan behind a track already in flight. Either way, the worker enters its own worktree, builds, self-checks, opens a PR, and stops at the PR boundary.
|
|
74
74
|
3. Orchestrator reviews the PR with `claude-pr-review` and posts findings to it.
|
|
75
75
|
4. Orchestrator tells the session holding that branch to run `claude-address-review` once the pass posted a finding at any severity, resolving the target then with `canon sessions list --branch` and reporting the invocation for the human when no live session holds it. The worker addresses the findings, rebases onto `origin/main` when a sibling landed first and left the branch unable to merge, then pushes a follow-up. A pass carrying only minor findings dispatches too, since the grade runs low often enough that a floor at should-fix loses fixes a worker would have made. `claude-pr-review` states that threshold and the heading follows it, so an open heading is itself the signal to send.
|
|
76
76
|
5. Orchestrator closes the review out with `claude-pr-review` again. The second pass reads only the commits the follow-up added, or the worker's response alone when the follow-up added none, and posts under `## Review` when it finds anything and under `## Review closed` when it finds nothing, so a reader learns from the heading whether work is still owed and takes the merge decision from the counts on the line under it. A pass finding nothing where a close-out already stands rewrites that comment to cover what it read rather than posting a second one, so the thread carries one live verdict. Repeat from step 4 until a pass closes the review.
|