@erclx/canon 4.122.0 → 4.124.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.
Files changed (124) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/auto-ship/SKILL.md +7 -8
  3. package/claude/skills/auto-ship/references/tier-failures.md +7 -4
  4. package/claude/skills/backlog-triage/SKILL.md +1 -1
  5. package/claude/skills/codebase-layout/REQUIREMENT.md +1 -1
  6. package/claude/skills/codebase-layout/SKILL.md +1 -1
  7. package/claude/skills/create-rule/SKILL.md +5 -2
  8. package/claude/skills/design-taste/SKILL.md +1 -1
  9. package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
  10. package/claude/skills/draft-ready/SKILL.md +1 -1
  11. package/claude/skills/git-ship/SKILL.md +4 -3
  12. package/claude/skills/memory-capture/SKILL.md +14 -4
  13. package/claude/skills/memory-review/REQUIREMENT.md +4 -2
  14. package/claude/skills/memory-review/SKILL.md +18 -7
  15. package/claude/skills/memory-review/references/later-phases.md +7 -4
  16. package/claude/skills/memory-review/references/receipt-format.md +19 -3
  17. package/claude/skills/role-orchestrator/SKILL.md +2 -2
  18. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
  19. package/claude/skills/session-resume/SKILL.md +4 -3
  20. package/claude/skills/task-board/SKILL.md +1 -1
  21. package/claude/skills/task-board/references/archive.md +1 -1
  22. package/claude/skills/task-board/references/decline.md +1 -1
  23. package/claude/skills/test-first/REQUIREMENT.md +1 -1
  24. package/docs/agents/counts.md +3 -3
  25. package/docs/agents/drift-surfaces.md +2 -2
  26. package/docs/agents/rule-citations.md +4 -4
  27. package/docs/agents/tasks-archive.md +3 -3
  28. package/docs/agents/tasks-plans.md +5 -3
  29. package/docs/agents/tasks-validate.md +1 -1
  30. package/docs/agents/tasks.md +1 -1
  31. package/docs/agents/test-order.md +1 -1
  32. package/docs/index.md +3 -0
  33. package/docs/target-migrations.md +93 -0
  34. package/docs/target-projects.md +4 -216
  35. package/docs/target-scenarios.md +56 -0
  36. package/docs/target-sync.md +93 -0
  37. package/docs/workflow/ai-workflow.md +4 -203
  38. package/docs/workflow/index.md +2 -0
  39. package/docs/workflow/operating-model.md +18 -47
  40. package/docs/workflow/parallel-features.md +77 -0
  41. package/docs/workflow/skill-map.md +148 -0
  42. package/governance/renames.toml +34 -0
  43. package/governance/rules/{claude/555-tasks.md → canon/655-tasks.md} +2 -1
  44. package/governance/rules/{claude/560-diagrams.md → canon/660-diagrams.md} +1 -1
  45. package/governance/rules/{claude/562-session.md → canon/662-session.md} +1 -1
  46. package/governance/rules/code/000-code.md +21 -0
  47. package/governance/rules/lib/300-testing-ts.md +0 -4
  48. package/governance/rules/lib/330-testing-py.md +0 -4
  49. package/governance/rules/{claude/500-prose.md → writing/800-prose.md} +1 -1
  50. package/governance/rules/{claude/501-markdown.md → writing/801-markdown.md} +1 -1
  51. package/governance/rules/{claude/502-mermaid.md → writing/802-mermaid.md} +1 -1
  52. package/governance/stacks/base.toml +7 -6
  53. package/governance/stacks/node.toml +1 -1
  54. package/governance/stacks/python.toml +1 -1
  55. package/package.json +1 -1
  56. package/scripts/core/install-check.sh +3 -5
  57. package/src/claude/routing.ts +1 -1
  58. package/src/commands/gov.ts +1 -1
  59. package/src/commands/tasks.ts +39 -29
  60. package/src/gate/measures.ts +1 -6
  61. package/src/gate/stages.ts +1 -0
  62. package/src/gov/citations.ts +1 -1
  63. package/src/gov/list.ts +1 -1
  64. package/src/gov/restated.ts +2 -2
  65. package/src/init/steps.ts +1 -1
  66. package/src/labels/spelling.ts +1 -1
  67. package/src/records/prune.ts +1 -1
  68. package/src/shipped/references.ts +1 -1
  69. package/src/tasks/archive.ts +212 -105
  70. package/src/tasks/reach.ts +1 -1
  71. package/src/tasks/record.ts +59 -16
  72. package/src/tasks/validate.ts +1 -1
  73. package/standards/board.md +136 -0
  74. package/standards/commit.md +5 -0
  75. package/standards/groundwork.md +25 -36
  76. package/standards/index.md +2 -1
  77. package/standards/plan.md +20 -34
  78. package/standards/readme.md +8 -77
  79. package/standards/requirements.md +2 -2
  80. package/standards/rule.md +4 -6
  81. package/standards/tasks.md +18 -125
  82. package/standards/versioning.md +1 -1
  83. package/tooling/base/configs/commitlint.config.js +27 -1
  84. package/tooling/base/reference.md +2 -2
  85. package/tooling/claude/seeds/.claude/hooks/path-form.sh +1 -1
  86. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +1 -1
  87. package/tooling/web/reference.md +1 -1
  88. package/governance/rules/core/000-constitution.md +0 -30
  89. package/governance/rules/core/020-concurrency.md +0 -22
  90. package/governance/rules/core/030-error-handling.md +0 -31
  91. package/governance/rules/core/040-performance.md +0 -20
  92. package/governance/rules/core/050-logging.md +0 -20
  93. package/governance/rules/core/060-naming.md +0 -19
  94. package/governance/rules/snippets/600-at-references.md +0 -9
  95. /package/governance/rules/{core/025-indexes.md → canon/601-indexes.md} +0 -0
  96. /package/governance/rules/{core/035-tasks.md → canon/602-tasks.md} +0 -0
  97. /package/governance/rules/{core/045-memory.md → canon/603-memory.md} +0 -0
  98. /package/governance/rules/{core/055-scratch.md → canon/604-scratch.md} +0 -0
  99. /package/governance/rules/{core/085-worktrees.md → canon/605-worktrees.md} +0 -0
  100. /package/governance/rules/{core/087-git.md → canon/606-git.md} +0 -0
  101. /package/governance/rules/{claude/510-context.md → canon/610-context.md} +0 -0
  102. /package/governance/rules/{claude/511-indexes.md → canon/611-indexes.md} +0 -0
  103. /package/governance/rules/{claude/520-wireframes.md → canon/620-wireframes.md} +0 -0
  104. /package/governance/rules/{claude/530-requirements.md → canon/630-requirements.md} +0 -0
  105. /package/governance/rules/{claude/540-architecture.md → canon/640-architecture.md} +0 -0
  106. /package/governance/rules/{claude/545-decisions.md → canon/645-decisions.md} +0 -0
  107. /package/governance/rules/{claude/550-design.md → canon/650-design.md} +0 -0
  108. /package/governance/rules/{claude/556-groundwork.md → canon/656-groundwork.md} +0 -0
  109. /package/governance/rules/{claude/557-intake.md → canon/657-intake.md} +0 -0
  110. /package/governance/rules/{claude/558-plan.md → canon/658-plan.md} +0 -0
  111. /package/governance/rules/{claude/559-memory.md → canon/659-memory.md} +0 -0
  112. /package/governance/rules/{claude/561-teach.md → canon/661-teach.md} +0 -0
  113. /package/governance/rules/{claude/563-ready.md → canon/663-ready.md} +0 -0
  114. /package/governance/rules/{core/005-behavior.md → claude/565-behavior.md} +0 -0
  115. /package/governance/rules/{core/015-output.md → claude/566-output.md} +0 -0
  116. /package/governance/rules/{core/070-planning.md → claude/567-planning.md} +0 -0
  117. /package/governance/rules/{core → code}/010-testing.md +0 -0
  118. /package/governance/rules/{ci → tooling}/700-ci-workflow.md +0 -0
  119. /package/governance/rules/{core/065-spelling.md → tooling/765-spelling.md} +0 -0
  120. /package/governance/rules/{core/075-dependencies.md → tooling/775-dependencies.md} +0 -0
  121. /package/governance/rules/{core/080-config-comments.md → tooling/780-config-comments.md} +0 -0
  122. /package/governance/rules/{core/090-code-comments.md → tooling/790-code-comments.md} +0 -0
  123. /package/governance/rules/{claude/580-readme.md → writing/880-readme.md} +0 -0
  124. /package/governance/rules/{claude/591-standard-authoring.md → writing/891-standard-authoring.md} +0 -0
@@ -0,0 +1,93 @@
1
+ ---
2
+ title: Target sync
3
+ description: Check what drifted in a toolkit-managed project and pull it in, per domain or all at once
4
+ category: Agent surface
5
+ ---
6
+
7
+ # Target sync
8
+
9
+ When the toolkit updates, a target project pulls changes per domain. There is one catch-all and several targeted entry points. A project scaffolded before a layout move runs the one-time moves in [target migrations](target-migrations.md) first.
10
+
11
+ ## Check first
12
+
13
+ `canon sync --check <path>` reports what has drifted without writing anything. It splits each difference by cause, which is the question that decides what to do next.
14
+
15
+ `canon:target-check` is the same reading grouped by domain rather than by cause, run from inside the project. It reports the toolkit version, the `canon/` folder, governance rules, tooling, and seeds from the verbs that own each comparison, and reports the Claude harness as unchecked because no verb reads it. It repairs nothing, so it is what to run before deciding rather than instead of deciding.
16
+
17
+ ### The binary first
18
+
19
+ The report opens by naming the binary running it. The installed version reads against the newest published one, and a version behind that points you at `canon upgrade`, since every section under it is a reading taken by whichever toolkit you happen to have. An unreachable registry reports unknown with its reason rather than failing, so `--exit-code` still gates on the drift the check measured locally and an offline machine stays green.
20
+
21
+ ### Then the causes
22
+
23
+ A `stale` file still matches what the toolkit installed, so the update is mechanical. A `customized` file carries local edits, so taking the upstream version is a decision and `canon:seed-sync` is the tool for it. A `stranded` file sits where an older toolkit installed it and the toolkit has since moved, which is a relocation the report names but no command runs. A `retired` rule is one under `.claude/rules/canon/` the toolkit no longer ships, and the next `canon gov sync` deletes it. A `renamed` rule is one the toolkit now ships under a new name, and the next sync installs the new name and deletes the old file.
24
+
25
+ That attribution comes from `canon/config/config.json`, which every install and sync now writes, falling back to `.claude/canon/config.json` for a target stamped before that move. A target stamped before that path shipped is read from the retired `.claude/canon.json` instead, reported rather than migrated. Governance records a hash per installed file, plus the stack `canon gov install` was given, and tooling records the stack chain it resolved instead of any file hash, since its install runs no per-file walk to attribute.
26
+
27
+ Each domain holds its own toolkit commit, so syncing governance today does not move the revision tooling measures against, and each domain reports the upstream commits touching its own source path. Running any sync stamps that domain, and the report names the ones still unstamped.
28
+
29
+ A project that has never synced under a toolkit new enough to write a stamp falls back to the toolkit's own git history. Installed content matching any version that history published proves the file untouched, so it reports `stale` naming the commit it came from, and content matching no published version stays `drifted`. That fallback needs the toolkit as a git checkout. Installed from the registry it ships source without history, and the report says attribution was unavailable rather than reading every file as a local edit.
30
+
31
+ Further causes sit outside the per-domain scan, each naming something that walk cannot see. A seed the project edited is reported under `seeds` and reconciled with `canon:seed-sync`, since no sync command touches a seed. A file a newer seed folder replaced is reported under `superseded`, such as `.claude/TASKS.md` against the `.canon/tasks/` that now ships, and nothing moves it because the content is the project's own. A domain sitting at the root layout with nothing under `.claude/` is reported under `unmigrated`, and no command moves it either. The project moves the content itself.
32
+
33
+ `unmigrated` currently names no domain, since standards and snippets are the two the toolkit ever installed at the project root and both closed their install channel. A target still holding a root `standards/` or `snippets/` folder from an older toolkit is carrying its own authoring surface now, not an unfinished install, and nothing proposes moving either.
34
+
35
+ ### Rules you never received
36
+
37
+ A sync refreshes the files you already hold and adds none, so your rule set is frozen at the date you installed governance while every file in it reports as current. `newRules` names a rule you could receive that your tree does not hold, which is the one section that reads your absence rather than your contents. `canon gov sync` reports the same rules per file, marked `missing`, so you see them either way you check.
38
+
39
+ Take a clean section as reporting rather than as delivering. Nothing here installs, and it counts toward nothing, so pick the rules up with `canon gov install <stack>` or take one with `--add <rule>`. That separation is deliberate: adopting a rule changes what your project is governed by, which is a choice a sync has no business making for you.
40
+
41
+ Since `canon gov install` records the stack you gave it, the list is read by comparing that stack's current rules against what you hold right now, with no date involved at all. That is what lets it name a rule the toolkit shipped before you last synced: nothing here depends on when you installed. A target stamped before this recording shipped falls back to the older read below.
42
+
43
+ The fallback filters to what your stack can receive, read off the rule folders you already carry plus the folders the base stack takes whole. A rule under `lang/` or `ui/` belongs to some stacks and not others, so an unfiltered list would name rules you can never install. A target with neither a recorded stack nor a governance anchor reports nothing at all, since there is no date to measure against either.
44
+
45
+ In the fallback, an empty section is not proof either way when the toolkit running the check is not a full git clone. The read needs history to reach your anchor, and an install from the registry ships none, so the section goes quiet rather than saying it could not measure. Run the check from a clone before reading a clean result as a complete one. The stack-based read above is untouched by this, since it consults no history.
46
+
47
+ This also closes the case where a rule arrives citing a sibling you do not have. A sync refreshing a rule can land a version pointing at a file it never installs, and the section names the missing sibling rather than the broken citation, which is enough to act on.
48
+
49
+ ### What the toolkit stopped shipping
50
+
51
+ The last cause runs backwards. Every one above starts from what the toolkit ships and asks whether the target matches, so a folder the toolkit dropped appears in none of them. `reverse` walks the target instead and reports a folder sitting at a top-level path the toolkit once shipped and has since deleted.
52
+
53
+ Each entry carries a verdict, since a dropped folder and one the project wrote are the same bytes at the same path.
54
+
55
+ - `dropped` names the commit that published the content
56
+ - `unattributed` means the toolkit shipped that path and the content matches no version it published
57
+ - `project` means the folder only shares a retired name
58
+
59
+ Nothing acts on any of them, and the verdict is what makes the list safe to read.
60
+
61
+ Add `--json` for the machine-readable report, and `--exit-code` to fail a CI job when a target falls behind. Files the project authored itself never count toward that exit code, and neither do superseded artifacts, seed drift, tooling, or anything the reverse walk reports, since each names content the project is expected to edit or place itself. An unmigrated domain does count, because running the relocation closes it.
62
+
63
+ Tooling reports under a section of its own, and `measured` there says whether the target ever recorded a chain. One that never ran a tooling sync reports unmeasured rather than clean, which is what separates tooling nobody has looked at from tooling that is current. A workspace root records nothing either way, since each package resolves its own chain.
64
+
65
+ Reconcile the configs with `canon tooling diff <stack> <path>` to read which files differ, then run `canon tooling sync <stack> <path> --write` to apply them. The drift report counts categories and the diff names paths, which is the difference worth knowing before a golden config the project edited is replaced.
66
+
67
+ ## Catch-all
68
+
69
+ `canon sync <path>` runs every installed domain's sync in sequence. Safe to run on a cadence.
70
+
71
+ It never touches user-owned seed files. Governance rules in `.claude/rules/` and tooling configs refresh in place. Stale `.claude/GOV.md` from earlier installs is removed.
72
+
73
+ Standards take no part in that run. Nothing installed them, so there is no copy to reconcile and no `canon standards sync` to reach for.
74
+
75
+ ## Targeted
76
+
77
+ - Claude seed docs such as `CLAUDE.md` and `canon/REQUIREMENTS.md`: invoke `canon:seed-sync`. The skill splits each file into a preamble (between the H1 and the first H2) plus one part per `##` section, then diffs part by part and proposes per-part edits. User customizations are preserved.
78
+ - Governance rules already installed: `canon gov sync <path>` diffs and applies, and never adds new rules. A rule your recorded stack lists reports as `missing` instead.
79
+ - Tooling configs and seeds: `canon tooling <stack> <path>` overwrites golden configs and merges seeds
80
+ - Reference docs for a stack: `canon tooling reference <stack>` reads and never writes, so there is nothing to sync
81
+ - Index regeneration after markdown edits: `canon indexes regen`
82
+
83
+ Use a targeted entry point when only one surface moved upstream. Use the catch-all when the toolkit lands a bundled release.
84
+
85
+ ## Verify a sync
86
+
87
+ Before running a sync against a real project, run the relevant sandbox scenario. The sandbox provisions a representative project state and routes `SANDBOX_SCENARIO=sync` through the domain flow. See [sandbox](../canon/context/sandbox/index.md) for the scenario catalog and routing patterns.
88
+
89
+ ## Running sync from an agent session
90
+
91
+ `canon sync .` applies every installed domain sync, then offers to commit the result and open a pull request. That last step needs a terminal. Under `CANON_NON_INTERACTIVE=1`, which is how an agent runs it, the domain syncs still apply and the git workflow is refused: the command reports the branch and commit it would have created, writes nothing to git, and exits 0. Review the working tree and commit it yourself, or rerun interactively to reach the commit and pull request options.
92
+
93
+ Sync also refuses a target whose working tree is dirty, so commit or stash before running it. `canon sync --check .` has neither restriction, since it writes nothing.
@@ -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 instead of sequentially. Use one git worktree per feature so each session has its own working tree and branch.
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
 
@@ -171,9 +111,9 @@ The list stays written in the skill body as the fallback for a target whose inst
171
111
 
172
112
  Capture leads rather than trails because a routed fact edits a tracked file, which has to reach the branch before the commit steps run.
173
113
 
174
- The chain ends at capture, and review is run on its own.
114
+ The chain ends at capture, and review is run on its own. Capture closes with a line naming `/canon:memory-review` once `canon records stale memory` counts 25 or more entries due, and says nothing below that.
175
115
 
176
- Run `memory-review` standalone to curate the whole pen. An entry it retires moves to `.canon/memory/archive/` rather than being deleted, since a bulk pass has no undo.
116
+ Run `memory-review` standalone to curate the pen a batch at a time. Each pass takes the first 25 due entries in the order the stale verb reports, opening on those citing a path the tree no longer holds, and a named list overrides that order. An entry it retires moves to `.canon/memory/archive/` rather than being deleted, since a bulk pass has no undo. An entry it keeps is stamped with a `reviewed` date, which takes it out of the queue for 30 days.
177
117
 
178
118
  The receipt is collected once every item on it has been decided, and it survives untouched while any item is still pending. Whichever runs first takes it: Apply collects the receipt it has resolved, and `context-fold` scans the folder on every shipped branch for one an earlier session left behind. Before the file goes, each declined item is folded into the entry it was about, since a promotion survives in its target and in git while a decline is recorded nowhere else. `canon standards memory` states what a fold writes and which entry types take one.
179
119
 
@@ -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
- Groups run in the order a project meets them, so a reader at a known point scans to that group and reads across. The set reconciles the scenarios above with the lifecycle [target projects](../target-projects.md) describes, rather than inventing a third vocabulary beside those two, so a group name matches neither source exactly and every moment either one names has a group. Each row says when to reach for the skill. What it does is the skill's own description.
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
 
@@ -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
@@ -36,11 +36,9 @@ runs warm inside the orchestrator's own session or cold in a dispatched one, and
36
36
  the boundary between them is the cross-feature call: which rows collide, what
37
37
  merges before what, and whether a row should run at all stay with the
38
38
  orchestrator, because a session reading the board sees blockers and file sets
39
- and can write a confident merge order off a partial picture. Two trials on
40
- 2026-08-31 measured a cold planner against four rows and it reported ten things
41
- the task files got wrong, which is why the per-row measurement is free to go
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.
39
+ and can write a confident merge order off a partial picture. The per-row
40
+ measurement is free to go cold, since a cold planner reads the tree rather than
41
+ trusting what the task file claims.
44
42
 
45
43
  A dispatched planner owes two messages: the plan's path as the file lands, with
46
44
  what the task file got wrong beside it, and a block before that block becomes an
@@ -48,16 +46,10 @@ interactive prompt. The plan file is its only write. A stale count, a moved line
48
46
  or a path that no longer resolves is reported rather than repaired, since the
49
47
  task file and the board stay the orchestrator's to write.
50
48
 
51
- Refusing is part of the worker's job rather than a failure of it. A worker that
52
- halts on a plan question it may not answer, or argues back against an
53
- instruction the tree contradicts, is working correctly. The four measured halts
54
- to date each cost the dispatcher one reply and each was right.
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.
49
+ A worker that halts on a plan question it may not answer, or argues back against
50
+ an instruction the tree contradicts, is working correctly. It never writes
51
+ `.canon/tasks/priority.md` or the backlog beside it, and reports the row it
52
+ needs for the orchestrator to write. `role-worker` carries the reason for each.
61
53
 
62
54
  The orchestrator's cell reads every tracked file rather than every feature, and
63
55
  it offers no exception for a small one. A correction found while orchestrating
@@ -123,43 +115,24 @@ delta against, and `canon pr checks` reports the runs belonging to that tip, so
123
115
  follow-up push cannot be read as green off the predecessor's completed run. See
124
116
  [Head-sensitive pull request reads](../agents/pr-reads.md).
125
117
 
118
+ Three messages travel from the worker to its controller, and `role-worker`
119
+ states each with its reason: the worker's pull request as it opens, the end of
120
+ each address-review pass with the new CI state, and a block before it becomes an
121
+ interactive prompt. Nothing is sent on progress. An open pull request is what
122
+ starts the controller's review poll, with a dispatch still silent after thirty
123
+ minutes kept as a fallback.
124
+
126
125
  What the session channel carries is the handback instruction and the worker's
127
126
  reply to it, which is a notification layer over a record that stays on the PR. A
128
127
  reply that changes an outcome, such as a worker naming the plan question that
129
128
  already declined a finding, still belongs back on the PR, since the session
130
129
  holding it ends and the thread is what a later reader opens.
131
130
 
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
131
  Two rules put it there rather than leaving that to whoever remembers. A finding
158
132
  the worker declines carries the fact that settled it in the same reply body that
159
133
  already maps every finding, and a pass accepting that argument states the
160
134
  withdrawal or the regrade with what produced it instead of dropping the finding
161
- from its next comment. Both fire on a line the body already writes, so neither
162
- asks a session to judge mid-reply whether its own message mattered.
135
+ from its next comment.
163
136
 
164
137
  A reply that corrects the reviewing session rather than a finding stays off the
165
138
  thread. Which session holds which branch, or what gate a worker's edits pass
@@ -199,10 +172,8 @@ may hold a reason for, and it still opens the heading and sends the dispatch,
199
172
  because the author is the only party who can answer it.
200
173
 
201
174
  A minor the worker declines goes to the findings of the task the
202
- branch closes, since a thread does not survive the merge. The feedback
203
- becomes a durable artifact both sessions read, survives a session ending, and
204
- anchors to the change. That removes the copy-paste that otherwise routes review
205
- through the human between two sessions.
175
+ branch closes, since a thread does not survive the merge. Feedback kept on the
176
+ PR outlives both sessions, so no human copies review between them.
206
177
 
207
178
  ## Feature sizing
208
179
 
@@ -218,7 +189,7 @@ coordination costs more than the change it was too small.
218
189
 
219
190
  Two tiers hold work at different altitudes.
220
191
 
221
- - Tasks (`.canon/tasks/`): the active few pulled into the current turn, one file each. Gitignored, high churn. Shape governed by `standards/tasks.md`. `priority.md` beside them carries execution order, and `backlog.md` carries what nobody is scheduling.
192
+ - Tasks (`.canon/tasks/`): the active few pulled into the current turn, one file each. Gitignored, high churn. Shape governed by `standards/tasks.md`. `priority.md` beside them carries execution order, and `backlog.md` carries what nobody is scheduling, both shaped by `standards/board.md`.
222
193
  - Edits: a few lines, done immediately with no ceremony.
223
194
 
224
195
  Nothing above these sequences work into versions. Scope is stated in
@@ -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`, 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.