@erclx/canon 4.123.0 → 4.125.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 +2 -4
- package/claude/skills/codebase-layout/REQUIREMENT.md +1 -1
- package/claude/skills/codebase-layout/SKILL.md +1 -1
- package/claude/skills/create-rule/SKILL.md +5 -2
- package/claude/skills/design-taste/SKILL.md +1 -1
- package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
- package/claude/skills/memory-review/SKILL.md +1 -1
- package/claude/skills/memory-review/references/later-phases.md +1 -1
- package/claude/skills/memory-review/references/receipt-format.md +1 -1
- package/claude/skills/role-orchestrator/REQUIREMENT.md +2 -1
- package/claude/skills/role-orchestrator/SKILL.md +11 -9
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +2 -0
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +18 -3
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +4 -4
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +29 -0
- package/claude/skills/role-reviewer/REQUIREMENT.md +75 -0
- package/claude/skills/role-reviewer/SKILL.md +73 -0
- package/claude/skills/role-worker/SKILL.md +1 -1
- package/claude/skills/test-first/REQUIREMENT.md +1 -1
- package/docs/agents/counts.md +3 -3
- package/docs/agents/drift-surfaces.md +2 -2
- package/docs/agents/rule-citations.md +4 -4
- package/docs/agents/test-order.md +1 -1
- package/docs/index.md +3 -0
- package/docs/target-migrations.md +93 -0
- package/docs/target-projects.md +4 -216
- package/docs/target-scenarios.md +56 -0
- package/docs/target-sync.md +93 -0
- package/docs/workflow/ai-workflow.md +2 -201
- package/docs/workflow/index.md +2 -0
- package/docs/workflow/operating-model.md +28 -58
- package/docs/workflow/parallel-features.md +77 -0
- package/docs/workflow/skill-map.md +149 -0
- package/governance/renames.toml +34 -0
- package/governance/rules/{claude/560-diagrams.md → canon/660-diagrams.md} +1 -1
- package/governance/rules/{claude/562-session.md → canon/662-session.md} +1 -1
- package/governance/rules/code/000-code.md +21 -0
- package/governance/rules/lib/300-testing-ts.md +0 -4
- package/governance/rules/lib/330-testing-py.md +0 -4
- package/governance/rules/{claude/500-prose.md → writing/800-prose.md} +1 -1
- package/governance/rules/{claude/501-markdown.md → writing/801-markdown.md} +1 -1
- package/governance/rules/{claude/502-mermaid.md → writing/802-mermaid.md} +1 -1
- package/governance/stacks/base.toml +7 -6
- package/governance/stacks/node.toml +1 -1
- package/governance/stacks/python.toml +1 -1
- package/package.json +1 -1
- package/scripts/core/install-check.sh +3 -5
- package/src/claude/cases/workflow.ts +5 -0
- package/src/claude/routing.ts +1 -1
- package/src/commands/gov.ts +1 -1
- package/src/gate/measures.ts +1 -6
- package/src/gov/citations.ts +1 -1
- package/src/gov/list.ts +1 -1
- package/src/gov/restated.ts +2 -2
- package/src/init/steps.ts +1 -1
- package/src/labels/spelling.ts +1 -1
- package/src/records/prune.ts +1 -1
- package/src/shipped/references.ts +1 -1
- package/standards/rule.md +4 -6
- package/tooling/base/reference.md +1 -1
- package/tooling/claude/seeds/.claude/hooks/path-form.sh +1 -1
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +1 -1
- package/tooling/web/reference.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-review-fallback.md +0 -18
- package/governance/rules/core/000-constitution.md +0 -30
- package/governance/rules/core/020-concurrency.md +0 -22
- package/governance/rules/core/030-error-handling.md +0 -31
- package/governance/rules/core/040-performance.md +0 -20
- package/governance/rules/core/050-logging.md +0 -20
- package/governance/rules/core/060-naming.md +0 -19
- package/governance/rules/snippets/600-at-references.md +0 -9
- /package/governance/rules/{core/025-indexes.md → canon/601-indexes.md} +0 -0
- /package/governance/rules/{core/035-tasks.md → canon/602-tasks.md} +0 -0
- /package/governance/rules/{core/045-memory.md → canon/603-memory.md} +0 -0
- /package/governance/rules/{core/055-scratch.md → canon/604-scratch.md} +0 -0
- /package/governance/rules/{core/085-worktrees.md → canon/605-worktrees.md} +0 -0
- /package/governance/rules/{core/087-git.md → canon/606-git.md} +0 -0
- /package/governance/rules/{claude/510-context.md → canon/610-context.md} +0 -0
- /package/governance/rules/{claude/511-indexes.md → canon/611-indexes.md} +0 -0
- /package/governance/rules/{claude/520-wireframes.md → canon/620-wireframes.md} +0 -0
- /package/governance/rules/{claude/530-requirements.md → canon/630-requirements.md} +0 -0
- /package/governance/rules/{claude/540-architecture.md → canon/640-architecture.md} +0 -0
- /package/governance/rules/{claude/545-decisions.md → canon/645-decisions.md} +0 -0
- /package/governance/rules/{claude/550-design.md → canon/650-design.md} +0 -0
- /package/governance/rules/{claude/555-tasks.md → canon/655-tasks.md} +0 -0
- /package/governance/rules/{claude/556-groundwork.md → canon/656-groundwork.md} +0 -0
- /package/governance/rules/{claude/557-intake.md → canon/657-intake.md} +0 -0
- /package/governance/rules/{claude/558-plan.md → canon/658-plan.md} +0 -0
- /package/governance/rules/{claude/559-memory.md → canon/659-memory.md} +0 -0
- /package/governance/rules/{claude/561-teach.md → canon/661-teach.md} +0 -0
- /package/governance/rules/{claude/563-ready.md → canon/663-ready.md} +0 -0
- /package/governance/rules/{core/005-behavior.md → claude/565-behavior.md} +0 -0
- /package/governance/rules/{core/015-output.md → claude/566-output.md} +0 -0
- /package/governance/rules/{core/070-planning.md → claude/567-planning.md} +0 -0
- /package/governance/rules/{core → code}/010-testing.md +0 -0
- /package/governance/rules/{ci → tooling}/700-ci-workflow.md +0 -0
- /package/governance/rules/{core/065-spelling.md → tooling/765-spelling.md} +0 -0
- /package/governance/rules/{core/075-dependencies.md → tooling/775-dependencies.md} +0 -0
- /package/governance/rules/{core/080-config-comments.md → tooling/780-config-comments.md} +0 -0
- /package/governance/rules/{core/090-code-comments.md → tooling/790-code-comments.md} +0 -0
- /package/governance/rules/{claude/580-readme.md → writing/880-readme.md} +0 -0
- /package/governance/rules/{claude/591-standard-authoring.md → writing/891-standard-authoring.md} +0 -0
|
@@ -80,67 +80,7 @@ Start a fresh Claude Code session. The diff is sufficient context for both revie
|
|
|
80
80
|
|
|
81
81
|
### Parallel features
|
|
82
82
|
|
|
83
|
-
When features are independent, run them in parallel
|
|
84
|
-
|
|
85
|
-
- Create a worktree per feature, then start a Claude Code session in each
|
|
86
|
-
- Invoke `canon:plan-feature` in each session. Plans land at the main worktree root as `.canon/plans/feature-<slug>.md`, one per feature, no collisions. Small features stay in chat and skip the file.
|
|
87
|
-
- Implement, verify, and review each feature independently. `review-branch` writes a per-branch report at the main worktree root (`review/branch-<slug>.md`), and `ui-checklist` writes a per-branch checklist handoff there too (`tmp/handoff/ui-checklist/<slug>.md`) that `git-pr` posts to the pull request and removes, so parallel sessions do not overwrite each other. The slug is the branch name with any leading type segment dropped, so `feat/jwt-expiration` and the plan at `feature-jwt-expiration.md` meet on one name
|
|
88
|
-
- Ship each worktree separately with `canon:git-ship`
|
|
89
|
-
- For full autonomy per worktree, invoke `canon:auto-ship` instead of the manual chain. Approve the plan, walk away, come back to a pull request the chain marked as a draft and then read the flag back on. The mark says the work has had no review yet, and it holds no window, since readying a pull request to merge lifts it directly, an act reserved to the operator or to the controlling session that closed the review.
|
|
90
|
-
|
|
91
|
-
To run several worktrees as a coordinated flow rather than ad hoc, assert the orchestrator role in one warm session with `canon:role-orchestrator`. It holds the cross-feature call, plans each feature itself or dispatches a cold planner under `canon:role-planner` to write the plan, refills the ready queue so a free worker never waits, and reviews each worker's PR with `canon:review-pr`, then tells the session holding that branch to run `canon:review-address` whenever the pass posted a finding at any severity, which is the same threshold `canon:review-pr` states and posts its open heading under. The human launches workers and merges. See [operating model](operating-model.md) for the full loop.
|
|
92
|
-
|
|
93
|
-
Execution order comes off `.canon/tasks/priority.md` and nothing sequences work into versions. Scope stays in `canon/REQUIREMENTS.md` as a statement of what is wanted, and it reaches the board as discrete tasks the orchestrator orders by readiness.
|
|
94
|
-
|
|
95
|
-
Run one orchestrator at a time. The board is gitignored, so a second session reads none of the first one's writes and the two collide on labels and archives.
|
|
96
|
-
|
|
97
|
-
No fixed number caps the worker tracks underneath it. Collision between file sets is what binds, so a candidate opens only when its files are disjoint from every track in flight, compared at the file path rather than at a folder above it, and the ceiling in practice is how many outputs one session can still review properly. Disjointness is necessary and not sufficient, since two tracks interact in ways no file-set comparison reads, so a stated reason can still hold a disjoint candidate behind another. An operator caps a session's workers by saying so, and that cap binds for the session rather than standing as a number in a file.
|
|
98
|
-
|
|
99
|
-
Before a handoff, the orchestrator checks the plan against the tree rather than reading it: grep each construct it names and count the sites, confirm every phase label it cites is still open, and open each file it describes. A plan goes stale from whatever merged after it was written, and reading cannot catch that.
|
|
100
|
-
|
|
101
|
-
A constraint naming a track in flight carries the same problem past the handoff, so the block opens with the commit it was measured against. A worker re-tests before honoring one, fetching and then logging that commit against `origin/main` over the paths the constraint names, and any merge there means the track landed and the constraint is dead. An unstamped block reads as unverified rather than as live, which covers every plan written before the rule.
|
|
102
|
-
|
|
103
|
-
`.canon/plans/`, `.canon/review/`, and `.canon/memory/` all resolve at the main worktree root, so artifacts created in any session are visible from any sibling worktree. A session inside a worktree reads them directly, since the file-editing tools refuse a main-root path but `Read` resolves normally. It writes a whole file through the shell and makes a change inside an existing file through a `canon` verb, which resolves the main root in-process. See [Claude Code and git worktrees](../../wiki/claude/claude-worktrees.md) for the full rule and the domain-level fan-out guidance.
|
|
104
|
-
|
|
105
|
-
The plan's shape is fixed by `standards/plan.md`: the section list, the filename, the lifecycle, and the contract its questions keep. Every question carries a `- Suggested:` line and an empty `- Answer:` slot, and a blank answer accepts the suggestion at execution time. That default is what makes a plan decision-ready in one pass, and it is the opposite of the contract an intake folder keeps, where an empty slot means nobody reached the item.
|
|
106
|
-
|
|
107
|
-
An execution that picks other than the suggestion rewrites the `- Suggested:` line as `overridden at execution to <pick>,` followed by the measurement that moved it, and leaves the slot blank. That fixed phrase is how a reader of the archived plan tells an override from a suggestion the execution accepted, since an authored suggestion often carries a number of its own. The same deviation takes one line in the open task's `## Findings`, which is the register that survives the plan being archived. A deviation from a question somebody already answered goes back to whoever answered it instead, since a filled slot is a decision already made.
|
|
108
|
-
|
|
109
|
-
`canon records validate plans` reports where a plan and that standard disagree: a filename that is not `feature-<slug>.md`, a missing required section, a files-to-touch entry naming no file, and a question carrying a suggestion with no answer slot. The same verb takes `groundwork`, `intake`, `memory`, and `teach`, which are governed the same way and were unreachable for the same reason. Nothing fires it automatically, because all five folders are gitignored and every check the repository runs reads changed files from git. It reports and never writes, since the folders are per-machine scratch with no history to recover a wrong repair from.
|
|
110
|
-
|
|
111
|
-
`canon records push` carries these folders off the disk they live on, and `canon records pull` brings them back. Every top-level entry under `.canon/` is backed except `tmp`, `ordinal-locks`, and `.records.git`, so a folder added later enters the payload on its own rather than waiting on a name written into a list. Push names each folder it carries the first time it sees it, which is what keeps a stray folder visible instead of silently entering the payload. It refuses the whole push, naming each path, when a new or changed file is over 25 MB or carries a credential `canon secrets scan` would report. The history lives in a second git directory at `.canon/.records.git` with `.canon/` as its work tree, so every path a task file cites stays where it is.
|
|
112
|
-
|
|
113
|
-
A person points it at a private repository once and both verbs refuse until they have, and `push` refuses when that origin is also a remote of the project, since the payload is the memory pen and the groundwork trails. `.husky/post-merge` runs the push after its archive loop, on every merge rather than only on one that closed a task. See [records push](../agents/records-push.md) for the refusal table.
|
|
114
|
-
|
|
115
|
-
`canon records size` reports what each of these folders holds, heaviest first, along with `.canon/tmp`. Each row carries the file count, the bytes, how many files were written in the last 7 and 30 days, and the dates of the least and most recently written one. Nothing fails on a number, because a record folder has no correct size. What the verb replaces is a reading somebody had to remember to take: the memory pen went from 44 entries to 236 between two counts made by hand a fortnight apart, and nothing reported the rate in between.
|
|
116
|
-
|
|
117
|
-
`canon records prune-tmp` reports scratch under `.canon/tmp` nobody has touched inside an age window, and deletes it only with `--write`. A `tmp/handoff/` folder and the live `tmp/pr/poll/` baseline are never offered, since a reader deletes a handoff once it is read and a poll baseline is state a session still needs. See [records](../agents/records.md) for the candidate shapes and the exit codes.
|
|
118
|
-
|
|
119
|
-
`canon records stale memory` reads the memory pen as a review queue. It names each entry due for review, meaning one carrying no `reviewed` date or one older than 30 days, and every backticked path an entry cites that the tree no longer holds. Due entries come first, with the ones citing a missing path ahead of the rest, so a review takes the first batch rather than the whole pen. It writes nothing. See [records stale](../agents/records-stale.md) for how a path is read and the refusals.
|
|
120
|
-
|
|
121
|
-
A plan that ships is archived, never deleted. `canon tasks archive` moves it to `.canon/plans/archive/` alongside the task it belonged to and retargets that task's `Plan:` line at the new location, so a completed task still leads to the reasoning behind it. An archive sits inside the record folder it archives rather than beside it, so one ignore entry and one backed-folder entry cover a record and everything it has retired. The folder is gitignored, which is why a deleted plan had no recovery path. A plan cited by more than one task stays put until the last of them closes, since moving it early would strand every other pointer.
|
|
122
|
-
|
|
123
|
-
A branch review report takes the other route and is swept rather than archived. `review-branch` writes it flat into `.canon/review/` as `branch-<slug>.md`, the session addressing it reads it once, and the durable record of what a review found is the comment `review-pr` posts on the pull request, so `context-fold` deletes any report whose branch is gone. The body that writes a report owns how long it lives, which leaves the shipping branch's own report on disk through the run that cites it and collects it a branch later. What that loses is a local-only review on a branch that never opened a pull request, which is why the report says so where a reader meets it.
|
|
124
|
-
|
|
125
|
-
`canon:context-fold` decides which task closed by reading the diff rather than the conversation. It resolves a merge base against `origin/main`, unions the committed diff with the working tree and untracked files, then matches unchecked outcomes on the board against what shipped. A task that shipped without ever being discussed still gets marked. Requirements, architecture, and design stay session-sourced, because a diff cannot carry a judgment.
|
|
126
|
-
|
|
127
|
-
`.canon/tasks/` is gitignored and resolves at the main worktree root, so every session shares one board. One file per task is what keeps concurrent sessions from overwriting each other, since a gitignored board has no history to recover a lost write from. Its `index.md` is generated by a hook rather than by `bun run check`, because the whole-repo index walk skips gitignored folders.
|
|
128
|
-
|
|
129
|
-
`.canon/memory/` carries the same arrangement, its own hook regenerating `index.md` from each entry's `title`, `description`, and `category`. A hand-maintained `priority.md` sits beside it carrying execution order and what each task is waiting on, which the alphabetical index cannot express.
|
|
130
|
-
|
|
131
|
-
`canon tasks validate` reads a row against its own table before it reads anything the row claims. A blank or prose line closes the table above it, so a row stranded there is checked against the line behind it rather than parsed as a continuation, and a row that clears that test still has its cell count checked against its header. A `## Needs a plan` row that states its own position, searched for `<ordinal> here` or the bare word `last` anywhere in the cell rather than at its start, is checked against where it actually sits, which is what catches a gap, a duplicate, and a sequence starting somewhere other than first alike.
|
|
132
|
-
|
|
133
|
-
Past that shape, it checks what a surviving row claims against what the tree holds: every plan pointer resolves, every task file is named by a board row or a backlog line and never by both, no task sits in two groups, and no two rows marked ready touch the same file. One check across both surfaces is what lets a task move between the board and the backlog without the move reading as a dropped file. The collision check is the half a reader cannot run by eye, and it is what keeps two workers from being handed colliding work. Blockers re-takes what a parked row waits on, reporting one whose cited task reached the trunk and one whose cited file nothing running still holds. A cited task settles the row by being archived, or by closing every outcome and naming a pull request the trunk carries, since the checkbox alone is marked while the branch is still in review. Both halves read a citation out of the blocker cell, so a row citing neither is reported as untested rather than counted clean, and so is a cited task the trunk could not answer for. It reports and never writes, because a row is the orchestrator's claim and a validator repairing one would assert the claim it exists to test. Nothing fires it automatically, since the board is gitignored per-machine scratch with no shared moment to hang a hook on, so the orchestrator's sweep calls it at the point the readiness claim is made and follows it with the parked re-test.
|
|
134
|
-
|
|
135
|
-
`canon:task-board` owns the two operations that bracket a task's life. It creates the file, holding the filename convention and the frontmatter contract so a malformed write cannot break the index for every sibling, and it moves a shipped task to `.canon/tasks/archive/`. Creation is where the origin invariant is enforced: every task names a plan, a groundwork folder, an intake folder, or an issue, since a task with no origin is either lost context or work nobody decided to do.
|
|
136
|
-
|
|
137
|
-
Archiving a task carries its plan with it, in the same act. The merge is what settles a plan, and the hook below reaches the archive with nobody watching, so a second call after it would be a second failure point leaving the task archived and the plan live. A task whose plan a sibling still cites archives on its own and leaves the plan live, since moving it on the first task to close strands every other pointer at a path that has gone.
|
|
138
|
-
|
|
139
|
-
Nothing chained that archive until the `post-merge` git hook landed. Every earlier step fires from `canon:auto-ship` or `canon:git-ship`, both of which finish while the pull request is still open, so a task archived there would close for work that may be abandoned. The board is gitignored, which rules out reading it from anywhere but the machine that pulled. The hook names the board's archive candidates and stays silent otherwise, including on a project with no board.
|
|
140
|
-
|
|
141
|
-
Candidates rather than closed tasks, because outcomes are marked on the branch. A task can read all `[x]` while its pull request is still open, so `canon:task-board` confirms the work reached `main` before it moves anything, and the hook's own output says that check is still owed. A companion `post-rewrite` hook carries the same announcement for anyone pulling with rebase, which fires that event instead of `post-merge`.
|
|
142
|
-
|
|
143
|
-
It announces and moves nothing, so `canon:task-board` stays the only writer. A shell-side archive would change a gitignored board with no diff to review and no session watching, and `index.md` regenerates from a session hook that a shell `mv` never fires. Both hooks ship with the `base` tooling stack, so a target project running this workflow gets the same trigger.
|
|
83
|
+
When features are independent, run them in parallel, one worktree and one session per feature. See [parallel features](parallel-features.md) for the loop in each worktree, the orchestrated flow, and the records and task board every session shares.
|
|
144
84
|
|
|
145
85
|
### Autonomous ship
|
|
146
86
|
|
|
@@ -197,146 +137,7 @@ Before the first feature session on a UI-heavy project, pick a design tier. The
|
|
|
197
137
|
|
|
198
138
|
## Skills
|
|
199
139
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
This section is the corpus the coverage claim is measured against: every name `canon claude skills list --names` reports takes exactly one row here. A skill serving two moments sits at the earlier one, and mentions elsewhere in this file are prose rather than routing.
|
|
203
|
-
|
|
204
|
-
### Set up a project
|
|
205
|
-
|
|
206
|
-
| Skill | When to use |
|
|
207
|
-
| ---------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
208
|
-
| `canon:target-setup` | On a fresh scaffold, to detect the stack and run the install chain, or to reach one phase of it alone |
|
|
209
|
-
| `canon:canon-operator` | On a project that already exists, to read what it carries before an install is picked |
|
|
210
|
-
| `canon:sketch-design` | Before `design-extract`'s greenfield path, to trace a design direction from reference images or URLs |
|
|
211
|
-
| `canon:design-extract` | Before the first UI feature, to draft `canon/DESIGN.md` |
|
|
212
|
-
| `canon:draft-diagram` | Once the architecture is written, to draft per-kind entries under `.canon/diagrams/` |
|
|
213
|
-
| `canon:repo-metadata` | When the GitHub About text, homepage, or topics may have drifted, to reconcile them against the README |
|
|
214
|
-
|
|
215
|
-
### Decide what to build
|
|
216
|
-
|
|
217
|
-
| Skill | When to use |
|
|
218
|
-
| -------------------------- | --------------------------------------------------------------------------------- |
|
|
219
|
-
| `canon:plan-intake` | When the input is a pile of findings rather than one feature |
|
|
220
|
-
| `canon:plan-intake-answer` | When an intake folder holds unread slots waiting on your decision |
|
|
221
|
-
| `canon:backlog-triage` | When the backlog needs pruning, to file a verdict per row and apply approved ones |
|
|
222
|
-
| `canon:plan-groundwork` | When the state is unmeasured and more than one approach is live |
|
|
223
|
-
| `canon:decision-escalate` | When open decisions turn on your preference and want batching into one set |
|
|
224
|
-
| `canon:draft-and-pick` | When the call is taste and wants several candidates rendered side by side |
|
|
225
|
-
| `canon:task-board` | When a decided item needs a file on the board, or a shipped one needs archiving |
|
|
226
|
-
| `canon:plan-feature` | When the approach is settled and the next step is a plan |
|
|
227
|
-
| `canon:codebase-layout` | When a plan or change adds a file and you need to decide which folder holds it |
|
|
228
|
-
|
|
229
|
-
### Build the feature
|
|
230
|
-
|
|
231
|
-
| Skill | When to use |
|
|
232
|
-
| ---------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
233
|
-
| `canon:session-worktree` | At the plan-to-execute boundary, to get an isolated tree and branch, or to route a refused main-root write |
|
|
234
|
-
| `canon:auto-ship` | After plan approval, to chain implement, verify, review, draft PR |
|
|
235
|
-
| `canon:project-commands` | When the project's own command needs running |
|
|
236
|
-
| `canon:test-first` | Before implementing a planned change, to run its test red, green, then refactor |
|
|
237
|
-
| `canon:test-craft` | When writing or changing any test, to pick its layer and filter what it asserts |
|
|
238
|
-
| `canon:review-craft` | When reviewing a change, for what to look for and how much evidence a finding needs |
|
|
239
|
-
| `canon:systematic-debugging` | When a test fails or a bug surfaces, to force root cause first |
|
|
240
|
-
| `canon:ui-checklist` | After a UI change, to write what to look at and name what ships untested |
|
|
241
|
-
|
|
242
|
-
### Check the work before it leaves the branch
|
|
243
|
-
|
|
244
|
-
| Skill | When to use |
|
|
245
|
-
| ------------------------ | --------------------------------------------------------------------------------------- |
|
|
246
|
-
| `canon:review-branch` | On the local branch diff, before anything is pushed |
|
|
247
|
-
| `canon:standards-audit` | When changed markdown has to answer to the authoring standards |
|
|
248
|
-
| `canon:document-health` | When the documents themselves have to answer for length, placement, and staleness |
|
|
249
|
-
| `canon:markdown-propose` | When a markdown claim needs rewriting and the change should wait for an answer per file |
|
|
250
|
-
| `canon:ux-audit` | To read UI source for missing states, edge cases, and inconsistencies |
|
|
251
|
-
| `canon:ux-measure` | To start the interface and measure paint, processor, and layout cost |
|
|
252
|
-
| `canon:ux-walkthrough` | To run a multi-finding inspection pass over a running app with the operator |
|
|
253
|
-
|
|
254
|
-
### Ship it
|
|
255
|
-
|
|
256
|
-
| Skill | When to use |
|
|
257
|
-
| ---------------------- | ------------------------------------------------------------------------------------------------- |
|
|
258
|
-
| `canon:git-ship` | To run the whole post-feature chain from the verify gate through open PR |
|
|
259
|
-
| `canon:memory-capture` | First skill in that chain, to route what the session learned to the surface owning it |
|
|
260
|
-
| `canon:context-fold` | When decisions diverged from the plan, or a shipped task needs its outcomes marked |
|
|
261
|
-
| `canon:docs-fold` | Never on purpose. A pointer at the old name that names `context-fold` <!-- canon-keep-retired --> |
|
|
262
|
-
| `canon:docs-sync` | When a change since main left `README.md` or `docs/` stale |
|
|
263
|
-
| `canon:git-stage` | When the staged set spans several concerns and wants one commit each |
|
|
264
|
-
| `canon:git-commit` | When the staged set is one concern, or was staged hunk by hand |
|
|
265
|
-
| `canon:git-branch` | When a branch name needs generating or renaming to conventional form |
|
|
266
|
-
| `canon:git-pr` | When a pull request needs a title and body written from the diff |
|
|
267
|
-
| `canon:memory-review` | When the pen has grown, to propose where each entry belongs |
|
|
268
|
-
|
|
269
|
-
### After the pull request opens
|
|
270
|
-
|
|
271
|
-
| Skill | When to use |
|
|
272
|
-
| ---------------------- | -------------------------------------------------------------------- |
|
|
273
|
-
| `canon:review-pr` | From an independent session, to post findings on the PR itself |
|
|
274
|
-
| `canon:review-address` | On the worker's side, to fix posted findings and push a follow-up |
|
|
275
|
-
| `canon:git-followup` | For a small self-review edit on a branch whose PR is already open |
|
|
276
|
-
| `canon:git-split` | When a branch turns out to carry unrelated commits |
|
|
277
|
-
| `canon:git-issue` | When something surfaced that belongs on the tracker rather than here |
|
|
278
|
-
| `canon:git-worktree` | After a PR merges, to list worktrees and reclaim the slot |
|
|
279
|
-
|
|
280
|
-
### Run several tracks at once
|
|
281
|
-
|
|
282
|
-
| Skill | When to use |
|
|
283
|
-
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
284
|
-
| `canon:role-orchestrator` | To assert the control session that owns the queue and reviews each worker's PR |
|
|
285
|
-
| `canon:role-planner` | To assert the thinking role for a cold session writing a plan, a groundwork track, or an intake pass |
|
|
286
|
-
| `canon:role-worker` | To assert the worker role for a cold session building one branch under one plan |
|
|
287
|
-
| `canon:session-relay` | When this session owes another session a message, with or without a role |
|
|
288
|
-
| `canon:session-resume` | At the start of a session, to pick up what a previous one left |
|
|
289
|
-
| `canon:session-compact` | Before a compaction or a move to another machine, to write a plain session's handoff note outside the board |
|
|
290
|
-
| `canon:session-map` | At the close of an orchestrating session, to write its board-side handoff |
|
|
291
|
-
|
|
292
|
-
### Keep the project current with the toolkit
|
|
293
|
-
|
|
294
|
-
| Skill | When to use |
|
|
295
|
-
| ----------------------------- | -------------------------------------------------------------------------------------- |
|
|
296
|
-
| `canon:target-check` | In a target project, to report per domain what it holds against what the toolkit ships |
|
|
297
|
-
| `canon:seed-sync` | After a toolkit update, to reconcile installed seeds without losing customizations |
|
|
298
|
-
| `canon:canon-feedback` | When something in the toolkit is broken, missing, or off, to open an issue on it |
|
|
299
|
-
| `canon:canon-feedback-triage` | In the toolkit repo, to work through the open feedback issues |
|
|
300
|
-
| `canon:canon-rollout` | In the toolkit repo, to take one change out to every consuming project at once |
|
|
301
|
-
|
|
302
|
-
### Generate an artifact on demand
|
|
303
|
-
|
|
304
|
-
| Skill | When to use |
|
|
305
|
-
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
306
|
-
| `canon:create-rule` | For a project-specific governance rule the toolkit does not ship |
|
|
307
|
-
| `canon:create-skill` | For a new `SKILL.md` |
|
|
308
|
-
| `canon:create-snippet` | For a reusable prompt |
|
|
309
|
-
| `canon:create-standard` | For a new authoring convention |
|
|
310
|
-
| `canon:draft-docs` | For a brand-new `docs/*.md` page, drafted against `standards/docs.md` |
|
|
311
|
-
| `canon:draft-ready` | For finished files a worker should copy, written as a ready folder with its overview, thin plan, and task |
|
|
312
|
-
| `canon:draft-context` | For a brand-new `canon/context/<domain>.md` entry, drafted against `standards/context.md` |
|
|
313
|
-
| `canon:draft-wireframes` | For a brand-new `canon/wireframes/<surface>.md` file, drafted against `standards/wireframes.md` |
|
|
314
|
-
| `canon:draft-wiki` | For a brand-new wiki reference page on a subject owned outside the project, drafted against `standards/wiki.md` |
|
|
315
|
-
| `canon:draft-figure` | For a hand-drawn figure inside an existing doc, drafted against `standards/figures.md` and rendered through Mermaid or freehand SVG |
|
|
316
|
-
| `canon:draft-readme` | For a project's `README.md`, drafted against `standards/readme.md` |
|
|
317
|
-
| `canon:bash-script` | For an interactive, human-facing shell tool |
|
|
318
|
-
| `canon:bash-cli-script` | For a non-interactive automation, CI, or pipeline script |
|
|
319
|
-
| `canon:ci-workflow` | For a GitHub Actions workflow file |
|
|
320
|
-
| `canon:draft-slides` | For a deck, drafted as `.claude/SLIDES.md` and rendered to PowerPoint |
|
|
321
|
-
| `canon:draft-screencast` | For a recording script with beats and defaults already seeded |
|
|
322
|
-
| `canon:record-screencast` | For compiling and running a screencast draft into a recording and a still |
|
|
323
|
-
| `canon:draft-identity` | For a project's logo mark and its social card, drafted through `draft-and-pick`'s pick loop |
|
|
324
|
-
|
|
325
|
-
### Answer a question at any point
|
|
326
|
-
|
|
327
|
-
| Skill | When to use |
|
|
328
|
-
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
329
|
-
| `canon:canon-cli` | Before running an unfamiliar verb, a sync, or an install, to learn which command to run, which reference doc covers it, or what it overwrites, merges, or leaves alone |
|
|
330
|
-
| `canon:index-lookup` | To find where a topic is documented across the tracked `index.md` catalogs |
|
|
331
|
-
| `canon:youtube-transcripts` | When a video transcript is wanted in the repo as context |
|
|
332
|
-
| `canon:read-frames` | To read a recorded demo back frame by frame and report what each one shows, with no verdict on whether the recording looks right |
|
|
333
|
-
| `canon:teach-workspace` | To learn a subject across sessions, in a workspace that holds the progress |
|
|
334
|
-
| `canon:write-human` | Before drafting or revising prose, for voice, rhythm, and density |
|
|
335
|
-
| `canon:restate-plainly` | When an answer or a document has to be read again in plain words |
|
|
336
|
-
|
|
337
|
-
Every row answers a question rather than marking a point in a project's life, so a phase above would send a reader to the wrong group.
|
|
338
|
-
|
|
339
|
-
A learning workspace produces two halves and only one of them leaves. A lesson is worked through once and stays in the workspace, and a reference page or a glossary carries no learner, so it belongs wherever the project already keeps prose on that subject. Asking `canon:teach-workspace` to promote sorts each durable page by who owns its subject, sending an Anthropic-owned subject to the wiki, an internal one to the matching context entry, and everything else, subject-neutral material included, to the public docs. It proposes and waits, because a promoted page is public prose that needs a line naming who owns the subject, and it writes nothing to a destination: each page the operator confirms goes to a handoff file that `canon:context-fold` folds in from a branch. A project with no wiki folder gets a refusal naming `canon wiki init` rather than a folder it never asked for.
|
|
140
|
+
The [skill map](skill-map.md) gives every skill the plugin ships one row saying when to reach for it, grouped by the moment a project meets it.
|
|
340
141
|
|
|
341
142
|
## Feedback routing
|
|
342
143
|
|
package/docs/workflow/index.md
CHANGED
|
@@ -14,5 +14,7 @@ How a person and their agents run a feature, from the design tier through the pa
|
|
|
14
14
|
## Workflow
|
|
15
15
|
|
|
16
16
|
- [Operating model](operating-model.md): Orchestrator, planner, and worker roles for building across parallel sessions
|
|
17
|
+
- [Parallel features](parallel-features.md): Run independent features in parallel worktrees, with the records and board every session shares
|
|
18
|
+
- [Skill map](skill-map.md): When to reach for each plugin skill, grouped by the moment a project meets it
|
|
17
19
|
- [Visual design workflow](visual-design-workflow.md): Tiered guide for design and wireframe authoring with Claude Code
|
|
18
20
|
- [Zshrc aliases for Claude Code](zshrc-aliases.md): Shell aliases that shorten common Claude Code invocations
|
|
@@ -14,13 +14,14 @@ Cold worker sessions build. The human launches workers and merges.
|
|
|
14
14
|
This page covers the roles and the loop. For the worktree mechanism (isolation, merge
|
|
15
15
|
order, port collisions), see [Claude Code and git worktrees](../../wiki/claude/claude-worktrees.md).
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## Four roles
|
|
18
18
|
|
|
19
|
-
The split is by vantage, not by capability. All
|
|
19
|
+
The split is by vantage, not by capability. All four are Claude Code sessions.
|
|
20
20
|
|
|
21
21
|
| Role | Session | Owns | Does not |
|
|
22
22
|
| ------------ | ------------------------------------- | ------------------------------------------------------- | --------------------------------- |
|
|
23
23
|
| Orchestrator | One warm, long-lived session | The cross-feature call, deep PR review, merge order | Edit tracked files, merge PRs |
|
|
24
|
+
| Reviewer | One cold session per pull request | The per-pull-request read, posted through `review-pr` | Fix findings, lift a draft, merge |
|
|
24
25
|
| Planner | One session per row, warm or cold | Measure the row against the tree, write one plan | Enter a worktree, write the board |
|
|
25
26
|
| Worker | One cold worktree session per feature | Implement, self-check, open PR, answer the orchestrator | Write the shared board, merge |
|
|
26
27
|
|
|
@@ -28,19 +29,17 @@ Each role is asserted explicitly rather than inferred. The orchestrator loads
|
|
|
28
29
|
`role-orchestrator` at the start of its session, a worker loads `role-worker`,
|
|
29
30
|
which `auto-ship` invokes at Step 0 so a dispatched build and a
|
|
30
31
|
hand-launched one reach it on the same path, and a planner loads
|
|
31
|
-
`role-planner` from the launch that dispatches
|
|
32
|
-
boundaries rather than logic.
|
|
32
|
+
`role-planner` and a reviewer `role-reviewer` from the launch that dispatches
|
|
33
|
+
it. All four are framing and boundaries rather than logic.
|
|
33
34
|
|
|
34
35
|
The planner is the one role the orchestrator also performs. Per-row planning
|
|
35
36
|
runs warm inside the orchestrator's own session or cold in a dispatched one, and
|
|
36
37
|
the boundary between them is the cross-feature call: which rows collide, what
|
|
37
38
|
merges before what, and whether a row should run at all stay with the
|
|
38
39
|
orchestrator, because a session reading the board sees blockers and file sets
|
|
39
|
-
and can write a confident merge order off a partial picture.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
cold. No plan from either trial has been built, so a cold plan's value to the
|
|
43
|
-
worker reading it is still unmeasured.
|
|
40
|
+
and can write a confident merge order off a partial picture. The per-row
|
|
41
|
+
measurement is free to go cold, since a cold planner reads the tree rather than
|
|
42
|
+
trusting what the task file claims.
|
|
44
43
|
|
|
45
44
|
A dispatched planner owes two messages: the plan's path as the file lands, with
|
|
46
45
|
what the task file got wrong beside it, and a block before that block becomes an
|
|
@@ -48,16 +47,10 @@ interactive prompt. The plan file is its only write. A stale count, a moved line
|
|
|
48
47
|
or a path that no longer resolves is reported rather than repaired, since the
|
|
49
48
|
task file and the board stay the orchestrator's to write.
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
What the worker may not do is write `.canon/tasks/priority.md` or the backlog
|
|
57
|
-
beside it. Those are gitignored, so an overwrite drops a row with no history to
|
|
58
|
-
recover it from, and a worker cannot pick a free task label without reading
|
|
59
|
-
every task file and every archive entry. It reports the row it needs and lets
|
|
60
|
-
the orchestrator write it.
|
|
50
|
+
A worker that halts on a plan question it may not answer, or argues back against
|
|
51
|
+
an instruction the tree contradicts, is working correctly. It never writes
|
|
52
|
+
`.canon/tasks/priority.md` or the backlog beside it, and reports the row it
|
|
53
|
+
needs for the orchestrator to write. `role-worker` carries the reason for each.
|
|
61
54
|
|
|
62
55
|
The orchestrator's cell reads every tracked file rather than every feature, and
|
|
63
56
|
it offers no exception for a small one. A correction found while orchestrating
|
|
@@ -79,7 +72,7 @@ One feature travels this path end to end.
|
|
|
79
72
|
|
|
80
73
|
1. The next feature is planned with `plan-feature`, writing a plan to `.canon/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 `role-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 by composing the live session roster with open pull requests. A bare branch or worktree is not evidence on its own, since this repository leaves both behind after a squash merge.
|
|
81
74
|
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 `session-worktree` and `auto-ship` 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.
|
|
82
|
-
3.
|
|
75
|
+
3. A reviewer dispatched under `role-reviewer`, or the orchestrator in place, reviews the PR with `review-pr` and posts findings to it.
|
|
83
76
|
4. Orchestrator tells the session holding that branch to run `review-address` 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. `review-pr` states that threshold and the heading follows it, so an open heading is itself the signal to send.
|
|
84
77
|
5. Orchestrator closes the review out with `review-pr` 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.
|
|
85
78
|
6. The human reads the result and merges. The orchestrator tells any trailing worker whose branch shares a seam with the merged one to run `review-address`, which rebases whether or not the review left anything open.
|
|
@@ -123,43 +116,24 @@ delta against, and `canon pr checks` reports the runs belonging to that tip, so
|
|
|
123
116
|
follow-up push cannot be read as green off the predecessor's completed run. See
|
|
124
117
|
[Head-sensitive pull request reads](../agents/pr-reads.md).
|
|
125
118
|
|
|
119
|
+
Three messages travel from the worker to its controller, and `role-worker`
|
|
120
|
+
states each with its reason: the worker's pull request as it opens, the end of
|
|
121
|
+
each address-review pass with the new CI state, and a block before it becomes an
|
|
122
|
+
interactive prompt. Nothing is sent on progress. An open pull request is what
|
|
123
|
+
starts the controller's review poll, with a dispatch still silent after thirty
|
|
124
|
+
minutes kept as a fallback.
|
|
125
|
+
|
|
126
126
|
What the session channel carries is the handback instruction and the worker's
|
|
127
127
|
reply to it, which is a notification layer over a record that stays on the PR. A
|
|
128
128
|
reply that changes an outcome, such as a worker naming the plan question that
|
|
129
129
|
already declined a finding, still belongs back on the PR, since the session
|
|
130
130
|
holding it ends and the thread is what a later reader opens.
|
|
131
131
|
|
|
132
|
-
Three messages travel the other way and the worker owes all three. It announces
|
|
133
|
-
its own pull request as the ship chain opens one, naming the number, the
|
|
134
|
-
branch, and the task it closes. That is the one transition only that session
|
|
135
|
-
can observe, since a worker that finishes goes idle rather than exiting, and
|
|
136
|
-
one sat unnoticed for eighteen minutes before the announcement existed.
|
|
137
|
-
|
|
138
|
-
It announces again when an address-review pass finishes, naming what it
|
|
139
|
-
addressed and the pull request's new CI state. That transition is the other
|
|
140
|
-
one only the worker can observe, and it replaces the same idle-poll gap on the
|
|
141
|
-
review's return leg, after a worker once addressed a posted review by
|
|
142
|
-
answering on the thread alone and telling its controller nothing.
|
|
143
|
-
|
|
144
|
-
The third reports a block before that block becomes an interactive prompt. A
|
|
145
|
-
queued message drains at the next tool round and a session already waiting on
|
|
146
|
-
input never reaches one, so an answer relayed to an open prompt renders beneath
|
|
147
|
-
the question and changes nothing. Nothing is sent on progress, which would
|
|
148
|
-
rebuild the poll on the sender's side of the channel.
|
|
149
|
-
|
|
150
|
-
The announcement is what let the review poll narrow. It used to start on a
|
|
151
|
-
dispatch as well as on an open pull request, and the script reads pull requests
|
|
152
|
-
while a building worker has none, so five consecutive runs reported no movement
|
|
153
|
-
across roughly fifteen minutes while one worker built. An open pull request is
|
|
154
|
-
now the whole trigger, with a dispatch still out after thirty minutes and silent
|
|
155
|
-
kept as a fallback.
|
|
156
|
-
|
|
157
132
|
Two rules put it there rather than leaving that to whoever remembers. A finding
|
|
158
133
|
the worker declines carries the fact that settled it in the same reply body that
|
|
159
134
|
already maps every finding, and a pass accepting that argument states the
|
|
160
135
|
withdrawal or the regrade with what produced it instead of dropping the finding
|
|
161
|
-
from its next comment.
|
|
162
|
-
asks a session to judge mid-reply whether its own message mattered.
|
|
136
|
+
from its next comment.
|
|
163
137
|
|
|
164
138
|
A reply that corrects the reviewing session rather than a finding stays off the
|
|
165
139
|
thread. Which session holds which branch, or what gate a worker's edits pass
|
|
@@ -199,10 +173,8 @@ may hold a reason for, and it still opens the heading and sends the dispatch,
|
|
|
199
173
|
because the author is the only party who can answer it.
|
|
200
174
|
|
|
201
175
|
A minor the worker declines goes to the findings of the task the
|
|
202
|
-
branch closes, since a thread does not survive the merge.
|
|
203
|
-
|
|
204
|
-
anchors to the change. That removes the copy-paste that otherwise routes review
|
|
205
|
-
through the human between two sessions.
|
|
176
|
+
branch closes, since a thread does not survive the merge. Feedback kept on the
|
|
177
|
+
PR outlives both sessions, so no human copies review between them.
|
|
206
178
|
|
|
207
179
|
## Feature sizing
|
|
208
180
|
|
|
@@ -235,13 +207,11 @@ above it, and stop adding once you can no longer review every output properly.
|
|
|
235
207
|
Serialize a track sharing a wiring seam with another, and serialize one whose
|
|
236
208
|
sets are disjoint when a stated reason still puts it behind another.
|
|
237
209
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
carries none of that reading. Nothing counts the pull requests, so the switch
|
|
244
|
-
holds only while the session applies it to itself.
|
|
210
|
+
Review splits by vantage. A reviewer under `role-reviewer` takes a pull
|
|
211
|
+
request's read, first pass included, when three await one (a number set by
|
|
212
|
+
hand), when a diff is too large to hold, or when it changes code or a skill or
|
|
213
|
+
rule body. Prose stays with the orchestrator, which keeps one cross-branch pass
|
|
214
|
+
per wave over file sets, the board, and merge order.
|
|
245
215
|
|
|
246
216
|
Inbound turns cost the controlling session as well, so weigh the spend before
|
|
247
217
|
widening. A message from one of your other sessions arrives as a new turn
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Parallel features
|
|
3
|
+
description: Run independent features in parallel worktrees, with the records and board every session shares
|
|
4
|
+
category: Workflow
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Parallel features
|
|
8
|
+
|
|
9
|
+
When features are independent, run them in parallel instead of sequentially. Use one git worktree per feature so each session has its own working tree and branch.
|
|
10
|
+
|
|
11
|
+
## One worktree per feature
|
|
12
|
+
|
|
13
|
+
- Create a worktree per feature, then start a Claude Code session in each
|
|
14
|
+
- Invoke `canon:plan-feature` in each session. Plans land at the main worktree root as `.canon/plans/feature-<slug>.md`, one per feature, no collisions. Small features stay in chat and skip the file.
|
|
15
|
+
- Implement, verify, and review each feature independently. `review-branch` writes a per-branch report at the main worktree root (`review/branch-<slug>.md`), and `ui-checklist` writes a per-branch checklist handoff there too (`tmp/handoff/ui-checklist/<slug>.md`) that `git-pr` posts to the pull request and removes, so parallel sessions do not overwrite each other. The slug is the branch name with any leading type segment dropped, so `feat/jwt-expiration` and the plan at `feature-jwt-expiration.md` meet on one name
|
|
16
|
+
- Ship each worktree separately with `canon:git-ship`
|
|
17
|
+
- For full autonomy per worktree, invoke `canon:auto-ship` instead of the manual chain. Approve the plan, walk away, come back to a pull request the chain marked as a draft and then read the flag back on. The mark says the work has had no review yet, and it holds no window, since readying a pull request to merge lifts it directly, an act reserved to the operator or to the controlling session that closed the review.
|
|
18
|
+
|
|
19
|
+
## A coordinated flow
|
|
20
|
+
|
|
21
|
+
To run several worktrees as a coordinated flow rather than ad hoc, assert the orchestrator role in one warm session with `canon:role-orchestrator`. It holds the cross-feature call, plans each feature itself or dispatches a cold planner under `canon:role-planner` to write the plan, refills the ready queue so a free worker never waits, and reviews each worker's PR with `canon:review-pr` or dispatches a cold reviewer under `canon:role-reviewer` to run it, then tells the session holding that branch to run `canon:review-address` whenever the pass posted a finding at any severity, which is the same threshold `canon:review-pr` states and posts its open heading under. The human launches workers and merges. See [operating model](operating-model.md) for the full loop.
|
|
22
|
+
|
|
23
|
+
Execution order comes off `.canon/tasks/priority.md` and nothing sequences work into versions. Scope stays in `canon/REQUIREMENTS.md` as a statement of what is wanted, and it reaches the board as discrete tasks the orchestrator orders by readiness.
|
|
24
|
+
|
|
25
|
+
Run one orchestrator at a time. The board is gitignored, so a second session reads none of the first one's writes and the two collide on labels and archives.
|
|
26
|
+
|
|
27
|
+
See [operating model](operating-model.md) for what caps the worker tracks under it and when a disjoint one still waits. An operator caps a session's workers by saying so, and that cap binds for the session rather than standing as a number in a file.
|
|
28
|
+
|
|
29
|
+
Before a handoff, the orchestrator checks the plan against the tree rather than reading it: grep each construct it names and count the sites, confirm every phase label it cites is still open, and open each file it describes. A plan goes stale from whatever merged after it was written, and reading cannot catch that.
|
|
30
|
+
|
|
31
|
+
A constraint naming a track in flight carries the same problem past the handoff, so the block opens with the commit it was measured against. A worker re-tests before honoring one, fetching and then logging that commit against `origin/main` over the paths the constraint names, and any merge there means the track landed and the constraint is dead. An unstamped block reads as unverified rather than as live, which covers every plan written before the rule.
|
|
32
|
+
|
|
33
|
+
## Records at the main root
|
|
34
|
+
|
|
35
|
+
`.canon/plans/`, `.canon/review/`, and `.canon/memory/` all resolve at the main worktree root, so artifacts created in any session are visible from any sibling worktree. A session inside a worktree reads them directly, since the file-editing tools refuse a main-root path but `Read` resolves normally. It writes a whole file through the shell and makes a change inside an existing file through a `canon` verb, which resolves the main root in-process. See [Claude Code and git worktrees](../../wiki/claude/claude-worktrees.md) for the full rule and the domain-level fan-out guidance.
|
|
36
|
+
|
|
37
|
+
The plan's shape is fixed by `standards/plan.md`: the section list, the filename, the lifecycle, and the contract its questions keep. Every question carries a `- Suggested:` line and an empty `- Answer:` slot, and a blank answer accepts the suggestion at execution time. That default is what makes a plan decision-ready in one pass, and it is the opposite of the contract an intake folder keeps, where an empty slot means nobody reached the item.
|
|
38
|
+
|
|
39
|
+
An execution that picks other than the suggestion rewrites the `- Suggested:` line as `overridden at execution to <pick>,` followed by the measurement that moved it, and leaves the slot blank. That fixed phrase is how a reader of the archived plan tells an override from a suggestion the execution accepted, since an authored suggestion often carries a number of its own. The same deviation takes one line in the open task's `## Findings`, which is the register that survives the plan being archived. A deviation from a question somebody already answered goes back to whoever answered it instead, since a filled slot is a decision already made.
|
|
40
|
+
|
|
41
|
+
`canon records validate plans` reports where a plan and that standard disagree: a filename that is not `feature-<slug>.md`, a missing required section, a files-to-touch entry naming no file, and a question carrying a suggestion with no answer slot. The same verb takes `groundwork`, `intake`, `memory`, and `teach`, which are governed the same way and were unreachable for the same reason. Nothing fires it automatically, because all five folders are gitignored and every check the repository runs reads changed files from git. It reports and never writes, since the folders are per-machine scratch with no history to recover a wrong repair from.
|
|
42
|
+
|
|
43
|
+
`canon records push` carries these folders off the disk they live on, and `canon records pull` brings them back. Every top-level entry under `.canon/` is backed except `tmp`, `ordinal-locks`, and `.records.git`, so a folder added later enters the payload on its own rather than waiting on a name written into a list. Push names each folder it carries the first time it sees it, which is what keeps a stray folder visible instead of silently entering the payload. It refuses the whole push, naming each path, when a new or changed file is over 25 MB or carries a credential `canon secrets scan` would report. The history lives in a second git directory at `.canon/.records.git` with `.canon/` as its work tree, so every path a task file cites stays where it is.
|
|
44
|
+
|
|
45
|
+
A person points it at a private repository once and both verbs refuse until they have, and `push` refuses when that origin is also a remote of the project, since the payload is the memory pen and the groundwork trails. `.husky/post-merge` runs the push after its archive loop, on every merge rather than only on one that closed a task. See [records push](../agents/records-push.md) for the refusal table.
|
|
46
|
+
|
|
47
|
+
`canon records size` reports what each of these folders holds, heaviest first, along with `.canon/tmp`. Each row carries the file count, the bytes, how many files were written in the last 7 and 30 days, and the dates of the least and most recently written one. Nothing fails on a number, because a record folder has no correct size. What the verb replaces is a reading somebody had to remember to take: the memory pen went from 44 entries to 236 between two counts made by hand a fortnight apart, and nothing reported the rate in between.
|
|
48
|
+
|
|
49
|
+
`canon records prune-tmp` reports scratch under `.canon/tmp` nobody has touched inside an age window, and deletes it only with `--write`. A `tmp/handoff/` folder and the live `tmp/pr/poll/` baseline are never offered, since a reader deletes a handoff once it is read and a poll baseline is state a session still needs. See [records](../agents/records.md) for the candidate shapes and the exit codes.
|
|
50
|
+
|
|
51
|
+
`canon records stale memory` reads the memory pen as a review queue. It names each entry due for review, meaning one carrying no `reviewed` date or one older than 30 days, and every backticked path an entry cites that the tree no longer holds. Due entries come first, with the ones citing a missing path ahead of the rest, so a review takes the first batch rather than the whole pen. It writes nothing. See [records stale](../agents/records-stale.md) for how a path is read and the refusals.
|
|
52
|
+
|
|
53
|
+
A plan that ships is archived, never deleted. `canon tasks archive` moves it to `.canon/plans/archive/` alongside the task it belonged to and retargets that task's `Plan:` line at the new location, so a completed task still leads to the reasoning behind it. An archive sits inside the record folder it archives rather than beside it, so one ignore entry and one backed-folder entry cover a record and everything it has retired. The folder is gitignored, which is why a deleted plan had no recovery path. A plan cited by more than one task stays put until the last of them closes, since moving it early would strand every other pointer.
|
|
54
|
+
|
|
55
|
+
A branch review report takes the other route and is swept rather than archived. `review-branch` writes it flat into `.canon/review/` as `branch-<slug>.md`, the session addressing it reads it once, and the durable record of what a review found is the comment `review-pr` posts on the pull request, so `context-fold` deletes any report whose branch is gone. The body that writes a report owns how long it lives, which leaves the shipping branch's own report on disk through the run that cites it and collects it a branch later. What that loses is a local-only review on a branch that never opened a pull request, which is why the report says so where a reader meets it.
|
|
56
|
+
|
|
57
|
+
`canon:context-fold` decides which task closed by reading the diff rather than the conversation. It resolves a merge base against `origin/main`, unions the committed diff with the working tree and untracked files, then matches unchecked outcomes on the board against what shipped. A task that shipped without ever being discussed still gets marked. Requirements, architecture, and design stay session-sourced, because a diff cannot carry a judgment.
|
|
58
|
+
|
|
59
|
+
## The task board
|
|
60
|
+
|
|
61
|
+
`.canon/tasks/` is gitignored and resolves at the main worktree root, so every session shares one board. One file per task is what keeps concurrent sessions from overwriting each other, since a gitignored board has no history to recover a lost write from. Its `index.md` is generated by a hook rather than by `bun run check`, because the whole-repo index walk skips gitignored folders.
|
|
62
|
+
|
|
63
|
+
`.canon/memory/` carries the same arrangement, its own hook regenerating `index.md` from each entry's `title`, `description`, and `category`. A hand-maintained `priority.md` sits beside it carrying execution order and what each task is waiting on, which the alphabetical index cannot express.
|
|
64
|
+
|
|
65
|
+
`canon tasks validate` reads a row against its own table before it reads anything the row claims. A blank or prose line closes the table above it, so a row stranded there is checked against the line behind it rather than parsed as a continuation, and a row that clears that test still has its cell count checked against its header. A `## Needs a plan` row that states its own position, searched for `<ordinal> here` or the bare word `last` anywhere in the cell rather than at its start, is checked against where it actually sits, which is what catches a gap, a duplicate, and a sequence starting somewhere other than first alike.
|
|
66
|
+
|
|
67
|
+
Past that shape, it checks what a surviving row claims against what the tree holds: every plan pointer resolves, every task file is named by a board row or a backlog line and never by both, no task sits in two groups, and no two rows marked ready touch the same file. One check across both surfaces is what lets a task move between the board and the backlog without the move reading as a dropped file. The collision check is the half a reader cannot run by eye, and it is what keeps two workers from being handed colliding work. Blockers re-takes what a parked row waits on, reporting one whose cited task reached the trunk and one whose cited file nothing running still holds. A cited task settles the row by being archived, or by closing every outcome and naming a pull request the trunk carries, since the checkbox alone is marked while the branch is still in review. Both halves read a citation out of the blocker cell, so a row citing neither is reported as untested rather than counted clean, and so is a cited task the trunk could not answer for. It reports and never writes, because a row is the orchestrator's claim and a validator repairing one would assert the claim it exists to test. Nothing fires it automatically, since the board is gitignored per-machine scratch with no shared moment to hang a hook on, so the orchestrator's sweep calls it at the point the readiness claim is made and follows it with the parked re-test.
|
|
68
|
+
|
|
69
|
+
`canon:task-board` owns the two operations that bracket a task's life. It creates the file, holding the filename convention and the frontmatter contract so a malformed write cannot break the index for every sibling, and it moves a shipped task to `.canon/tasks/archive/`. Creation is where the origin invariant is enforced: every task names a plan, a groundwork folder, an intake folder, or an issue, since a task with no origin is either lost context or work nobody decided to do.
|
|
70
|
+
|
|
71
|
+
Archiving a task carries its plan with it, in the same act. The merge is what settles a plan, and the hook below reaches the archive with nobody watching, so a second call after it would be a second failure point leaving the task archived and the plan live. A task whose plan a sibling still cites archives on its own and leaves the plan live, since moving it on the first task to close strands every other pointer at a path that has gone.
|
|
72
|
+
|
|
73
|
+
Nothing chained that archive until the `post-merge` git hook landed. Every earlier step fires from `canon:auto-ship` or `canon:git-ship`, both of which finish while the pull request is still open, so a task archived there would close for work that may be abandoned. The board is gitignored, which rules out reading it from anywhere but the machine that pulled. The hook names the board's archive candidates and stays silent otherwise, including on a project with no board.
|
|
74
|
+
|
|
75
|
+
Candidates rather than closed tasks, because outcomes are marked on the branch. A task can read all `[x]` while its pull request is still open, so `canon:task-board` confirms the work reached `main` before it moves anything, and the hook's own output says that check is still owed. A companion `post-rewrite` hook carries the same announcement for anyone pulling with rebase, which fires that event instead of `post-merge`.
|
|
76
|
+
|
|
77
|
+
It announces and moves nothing, so `canon:task-board` stays the only writer. A shell-side archive would change a gitignored board with no diff to review and no session watching, and `index.md` regenerates from a session hook that a shell `mv` never fires. Both hooks ship with the `base` tooling stack, so a target project running this workflow gets the same trigger.
|