@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.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/auto-ship/SKILL.md +7 -8
- package/claude/skills/auto-ship/references/tier-failures.md +7 -4
- package/claude/skills/backlog-triage/SKILL.md +1 -1
- 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/draft-ready/SKILL.md +1 -1
- package/claude/skills/git-ship/SKILL.md +4 -3
- package/claude/skills/memory-capture/SKILL.md +14 -4
- package/claude/skills/memory-review/REQUIREMENT.md +4 -2
- package/claude/skills/memory-review/SKILL.md +18 -7
- package/claude/skills/memory-review/references/later-phases.md +7 -4
- package/claude/skills/memory-review/references/receipt-format.md +19 -3
- package/claude/skills/role-orchestrator/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/session-resume/SKILL.md +4 -3
- package/claude/skills/task-board/SKILL.md +1 -1
- package/claude/skills/task-board/references/archive.md +1 -1
- package/claude/skills/task-board/references/decline.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/tasks-archive.md +3 -3
- package/docs/agents/tasks-plans.md +5 -3
- package/docs/agents/tasks-validate.md +1 -1
- package/docs/agents/tasks.md +1 -1
- 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 +4 -203
- package/docs/workflow/index.md +2 -0
- package/docs/workflow/operating-model.md +18 -47
- package/docs/workflow/parallel-features.md +77 -0
- package/docs/workflow/skill-map.md +148 -0
- package/governance/renames.toml +34 -0
- package/governance/rules/{claude/555-tasks.md → canon/655-tasks.md} +2 -1
- 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/routing.ts +1 -1
- package/src/commands/gov.ts +1 -1
- package/src/commands/tasks.ts +39 -29
- package/src/gate/measures.ts +1 -6
- package/src/gate/stages.ts +1 -0
- 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/src/tasks/archive.ts +212 -105
- package/src/tasks/reach.ts +1 -1
- package/src/tasks/record.ts +59 -16
- package/src/tasks/validate.ts +1 -1
- package/standards/board.md +136 -0
- package/standards/commit.md +5 -0
- package/standards/groundwork.md +25 -36
- package/standards/index.md +2 -1
- package/standards/plan.md +20 -34
- package/standards/readme.md +8 -77
- package/standards/requirements.md +2 -2
- package/standards/rule.md +4 -6
- package/standards/tasks.md +18 -125
- package/standards/versioning.md +1 -1
- package/tooling/base/configs/commitlint.config.js +27 -1
- package/tooling/base/reference.md +2 -2
- 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/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/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
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
203
|
-
|
|
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.
|