@erclx/canon 4.6.1 → 4.7.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/canon-feedback-file/SKILL.md +3 -3
- package/claude/skills/canon-feedback-triage/SKILL.md +1 -1
- package/claude/skills/canon-rollout/SKILL.md +1 -1
- package/claude/skills/canon-screencast/SKILL.md +5 -5
- package/claude/skills/canon-slides-draft/SKILL.md +1 -1
- package/claude/skills/claude-address-review/SKILL.md +4 -4
- package/claude/skills/claude-autoship/SKILL.md +17 -17
- package/claude/skills/claude-design-extract/SKILL.md +2 -2
- package/claude/skills/claude-diagram/SKILL.md +13 -13
- package/claude/skills/claude-docs/SKILL.md +23 -23
- package/claude/skills/claude-feature/SKILL.md +6 -6
- package/claude/skills/claude-groundwork/SKILL.md +16 -16
- package/claude/skills/claude-intake/SKILL.md +8 -8
- package/claude/skills/claude-intake-answer/SKILL.md +1 -1
- package/claude/skills/claude-markdown-propose/REQUIREMENT.md +2 -2
- package/claude/skills/claude-markdown-propose/SKILL.md +8 -8
- package/claude/skills/claude-markdown-propose/references/format.md +3 -3
- package/claude/skills/claude-memory-capture/SKILL.md +13 -13
- package/claude/skills/claude-memory-review/SKILL.md +28 -28
- package/claude/skills/claude-memory-review/references/receipt-format.md +1 -1
- package/claude/skills/claude-orchestrate/SKILL.md +9 -9
- package/claude/skills/claude-orchestrate/references/orchestrator-dispatch.md +1 -1
- package/claude/skills/claude-orchestrate/references/orchestrator-parked.md +1 -1
- package/claude/skills/claude-orchestrate/references/orchestrator-poll.md +1 -1
- package/claude/skills/claude-orchestrate/references/orchestrator-resume.md +1 -1
- package/claude/skills/claude-orchestrate/scripts/poll.sh +1 -1
- package/claude/skills/claude-planner/REQUIREMENT.md +1 -1
- package/claude/skills/claude-planner/SKILL.md +6 -6
- package/claude/skills/claude-pr-review/SKILL.md +5 -5
- package/claude/skills/claude-review/SKILL.md +4 -4
- package/claude/skills/claude-seed-sync/SKILL.md +2 -2
- package/claude/skills/claude-tasks/SKILL.md +16 -16
- package/claude/skills/claude-teach/SKILL.md +9 -9
- package/claude/skills/claude-ui-test/SKILL.md +3 -3
- package/claude/skills/claude-ux-audit/SKILL.md +5 -5
- package/claude/skills/claude-ux-measure/SKILL.md +3 -3
- package/claude/skills/claude-worker/SKILL.md +2 -2
- package/claude/skills/claude-worktree/SKILL.md +3 -3
- package/claude/skills/git-issue/SKILL.md +4 -4
- package/claude/skills/git-pr/SKILL.md +6 -6
- package/claude/skills/git-ship/SKILL.md +3 -3
- package/claude/skills/git-split/REQUIREMENT.md +1 -1
- package/claude/skills/git-split/SKILL.md +20 -20
- package/claude/skills/migration-superseded/SKILL.md +4 -4
- package/claude/skills/session-map/SKILL.md +2 -2
- package/claude/skills/session-resume/SKILL.md +8 -8
- package/docs/agents/capture.md +1 -1
- package/docs/agents/commands.md +5 -2
- package/docs/agents/demo.md +1 -1
- package/docs/agents/indexes.md +1 -1
- package/docs/agents/install-and-sync.md +1 -1
- package/docs/agents/intake.md +1 -1
- package/docs/agents/key-changes.md +1 -1
- package/docs/agents/records.md +12 -9
- package/docs/agents/restated.md +2 -2
- package/docs/agents/rule-citations.md +1 -1
- package/docs/agents/sandbox.md +1 -1
- package/docs/agents/tasks.md +6 -6
- package/docs/agents/teach.md +4 -4
- package/docs/ai-workflow.md +26 -19
- package/docs/operating-model.md +3 -3
- package/docs/target-projects.md +22 -2
- package/docs/visual-design-workflow.md +1 -1
- package/governance/rules/claude/555-tasks.md +3 -3
- package/governance/rules/claude/556-groundwork.md +2 -2
- package/governance/rules/claude/557-intake.md +2 -2
- package/governance/rules/claude/558-plan.md +3 -3
- package/governance/rules/claude/559-memory.md +2 -2
- package/governance/rules/claude/560-diagrams.md +3 -3
- package/governance/rules/claude/561-teach.md +2 -2
- package/governance/rules/claude/562-session.md +2 -2
- package/governance/rules/core/035-tasks.md +2 -2
- package/governance/rules/core/045-memory.md +3 -3
- package/governance/rules/core/055-scratch.md +2 -1
- package/governance/rules/core/085-worktrees.md +1 -1
- package/package.json +1 -1
- package/scripts/core/check-ignore-parity.sh +24 -51
- package/scripts/core/install-check.sh +4 -4
- package/scripts/core/snapshot.sh +1 -1
- package/scripts/manage-sandbox.sh +1 -1
- package/scripts/tooling/verify.sh +2 -2
- package/snippets/claude/decision-memo.md +1 -1
- package/snippets/claude/feature-recap.md +1 -1
- package/src/audits/catalog.ts +1 -1
- package/src/claude/cases/all.ts +1 -1
- package/src/claude/seeds.ts +13 -2
- package/src/claude/skills-rank.ts +1 -1
- package/src/cli.ts +4 -4
- package/src/commands/demo.ts +1 -1
- package/src/commands/design.ts +1 -1
- package/src/commands/feedback.ts +5 -5
- package/src/commands/gov.ts +17 -6
- package/src/commands/intake.ts +1 -1
- package/src/commands/migrate.ts +173 -2
- package/src/commands/pr.ts +1 -1
- package/src/commands/records.ts +1 -1
- package/src/commands/serve.ts +2 -2
- package/src/commands/slides.ts +1 -1
- package/src/commands/tasks.ts +5 -5
- package/src/commands/teach.ts +1 -1
- package/src/context/audit.ts +1 -1
- package/src/demo/compile.ts +1 -1
- package/src/gov/citations.ts +1 -1
- package/src/gov/restated.ts +3 -3
- package/src/migrate/apply.ts +67 -1
- package/src/migrate/records.ts +276 -0
- package/src/paths.ts +1 -1
- package/src/pr/paths.ts +1 -1
- package/src/record-root.ts +77 -27
- package/src/records/backup.ts +71 -17
- package/src/records/validate.ts +1 -1
- package/src/sync/layout.ts +11 -2
- package/src/tasks/answers.ts +1 -1
- package/src/tasks/archive.ts +20 -3
- package/standards/diagrams.md +4 -4
- package/standards/glossary.md +1 -1
- package/standards/groundwork.md +3 -3
- package/standards/index.md +2 -2
- package/standards/intake.md +3 -3
- package/standards/memory.md +2 -2
- package/standards/plan.md +3 -3
- package/standards/session.md +2 -2
- package/standards/standard.md +1 -1
- package/standards/tasks.md +9 -9
- package/standards/teach.md +2 -2
- package/standards/versioning.md +1 -1
- package/tooling/base/configs/.husky/post-merge +1 -1
- package/tooling/base/reference.md +1 -1
- package/tooling/claude/manifest.toml +13 -3
- package/tooling/claude/reference.md +28 -15
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +8 -8
package/docs/ai-workflow.md
CHANGED
|
@@ -12,7 +12,7 @@ A concise reference for when to reach for which tool, organized by what you're t
|
|
|
12
12
|
|
|
13
13
|
## Documents
|
|
14
14
|
|
|
15
|
-
Project docs
|
|
15
|
+
Project docs split across two roots at the project root, on one mechanical line: what is committed lives in `.claude/`, and every gitignored session record lives in `.canon/`, which a single ignore entry covers.
|
|
16
16
|
|
|
17
17
|
```plaintext
|
|
18
18
|
.claude/
|
|
@@ -20,12 +20,19 @@ Project docs live in `.claude/` at the project root.
|
|
|
20
20
|
├── ARCHITECTURE.md ← technical design decisions
|
|
21
21
|
├── DESIGN.md ← visual intent and token decisions (UI projects)
|
|
22
22
|
├── WIREFRAMES.md ← ASCII wireframes: layout, UI copy, and interaction rules (UI projects)
|
|
23
|
-
├── diagrams/ ← one Mermaid entry per diagram kind with a generated index.md, redrawn on demand
|
|
24
|
-
├── tasks/ ← one file per task with a generated index.md, gitignored local scratch
|
|
25
23
|
├── context/ ← per-domain narrative loaded on demand via index.md
|
|
26
24
|
└── rules/ ← path-scoped governance rules, written by canon gov install
|
|
25
|
+
|
|
26
|
+
.canon/
|
|
27
|
+
├── diagrams/ ← one Mermaid entry per diagram kind with a generated index.md, redrawn on demand
|
|
28
|
+
├── tasks/ ← one file per task with a generated index.md, local scratch
|
|
29
|
+
├── plans/ ← one plan per feature, archived inside itself once it ships
|
|
30
|
+
├── memory/ ← durable session facts no context entry owns
|
|
31
|
+
└── tmp/ ← deletable scratch, safe to remove without loss
|
|
27
32
|
```
|
|
28
33
|
|
|
34
|
+
A project scaffolded before the move keeps its records under `.claude/`, and every command reads either root. `canon migrate records` moves one project across and repoints what cites it.
|
|
35
|
+
|
|
29
36
|
Three tiers of context load with different cost: always-loaded (root `CLAUDE.md`, `.claude/REQUIREMENTS.md`, `.claude/ARCHITECTURE.md`), path-scoped lazy (`.claude/rules/<scope>.md` with `paths:` glob), and on-demand lookup (`.claude/context/<domain>.md`, or `.claude/context/<domain>/` once a domain outgrows one file, discovered via `.claude/context/index.md`). See [the context model](../.claude/context/context-model.md) for the full picture.
|
|
30
37
|
|
|
31
38
|
Run `canon init` to seed the `.claude/` directory, a root `CLAUDE.md` file, and `.claude/rules/` in one pass. `canon init` chains claude init and governance install. Claude Code auto-loads every file in `.claude/rules/` at session start, applying always-on rules unconditionally and path-scoped rules to files matching their `paths:` glob.
|
|
@@ -44,8 +51,8 @@ One session works for most features. Prefer splitting across two sessions only w
|
|
|
44
51
|
|
|
45
52
|
Work in Claude Code directly. It reads `CLAUDE.md` automatically and has full file access, no pasting needed.
|
|
46
53
|
|
|
47
|
-
- When the input is a pile of findings rather than one feature, invoke `canon:claude-intake` first. It files the dump into `.
|
|
48
|
-
- When the current state is unmeasured and more than one approach is live, invoke `canon:claude-groundwork` first. It opens a track folder under `.
|
|
54
|
+
- When the input is a pile of findings rather than one feature, invoke `canon:claude-intake` first. It files the dump into `.canon/intake/<nn>-<slug>/`, one item per finding carrying a problem measured against the tree, a proposed fix, and a verdict, then names which items are plan-ready, which need measuring, and which are already settled.
|
|
55
|
+
- When the current state is unmeasured and more than one approach is live, invoke `canon:claude-groundwork` first. It opens a track folder under `.canon/groundwork/<nn>-<slug>/` and ends in a decision, which may be to do nothing. Skip it when the approach is already settled.
|
|
49
56
|
- Invoke `canon:claude-feature` to scan for code-level conflicts and ambiguities, confirm approach before proceeding
|
|
50
57
|
- Implement the feature, then Claude Code runs the commands defined in `CLAUDE.md`, fixes failures, and iterates until all pass
|
|
51
58
|
- For UI changes, invoke `canon:claude-ui-test` to generate and run Playwright e2e tests
|
|
@@ -53,7 +60,7 @@ Work in Claude Code directly. It reads `CLAUDE.md` automatically and has full fi
|
|
|
53
60
|
|
|
54
61
|
The routing test is whether the repository can answer an item today. A session grepping handles the yes, and a groundwork track handles the no.
|
|
55
62
|
|
|
56
|
-
A groundwork track may run experiments to settle a question, writing a fixture it reads itself under `.
|
|
63
|
+
A groundwork track may run experiments to settle a question, writing a fixture it reads itself under `.canon/tmp/groundwork-fixtures/<slug>/` and spawning up to three billed headless runs before it asks. A fixture a headless run is pointed at sits outside the repository, since a session started under the project root inherits that project's `CLAUDE.md` and rules and would measure them instead of the arm.
|
|
57
64
|
|
|
58
65
|
What a spike produces goes somewhere else again. An input the run reads is re-runnable and cited by nothing, so the scratch path above is the right lifetime for it, while a recording or a render the track cites as evidence for a finding is what a later reader opens to check the claim. Evidence therefore lives in `evidence/` inside the track beside the file citing it, since the scratch tree holds only what can be deleted without loss.
|
|
59
66
|
|
|
@@ -70,14 +77,14 @@ Start a fresh Claude Code session. The diff is sufficient context for both revie
|
|
|
70
77
|
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.
|
|
71
78
|
|
|
72
79
|
- Create a worktree per feature, then start a Claude Code session in each
|
|
73
|
-
- Invoke `canon:claude-feature` in each session. Plans land at the main worktree root as `.
|
|
80
|
+
- Invoke `canon:claude-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.
|
|
74
81
|
- Implement, verify, and review each feature independently. `claude-review` and `claude-ui-test` write per-branch files at the main worktree root (`review/branch/review-<slug>.md`, `review/ui-checklist-<slug>.md`), 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
|
|
75
82
|
- Ship each worktree separately with `canon:git-ship`
|
|
76
83
|
- For full autonomy per worktree, invoke `canon:claude-autoship` instead of the manual chain. Approve the plan, walk away, come back to draft PRs.
|
|
77
84
|
|
|
78
85
|
To run several worktrees as a coordinated flow rather than ad hoc, assert the orchestrator role in one warm session with `canon:claude-orchestrate`. It holds the cross-feature call, plans each feature itself or dispatches a cold planner under `canon:claude-planner` to write the plan, refills the ready queue so a free worker never waits, and reviews each worker's PR with `canon:claude-pr-review`, then tells the session holding that branch to run `canon:claude-address-review` whenever the pass posted a finding at any severity, which is the same threshold `canon:claude-pr-review` states and posts its open heading under. The human launches workers and merges. See [operating model](operating-model.md) for the full loop.
|
|
79
86
|
|
|
80
|
-
Execution order comes off `.
|
|
87
|
+
Execution order comes off `.canon/tasks/priority.md` and nothing sequences work into versions. Scope stays in `.claude/REQUIREMENTS.md` as a statement of what is wanted, and it reaches the board as discrete tasks the orchestrator orders by readiness.
|
|
81
88
|
|
|
82
89
|
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.
|
|
83
90
|
|
|
@@ -87,7 +94,7 @@ Before a handoff, the orchestrator checks the plan against the tree rather than
|
|
|
87
94
|
|
|
88
95
|
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.
|
|
89
96
|
|
|
90
|
-
`.
|
|
97
|
+
`.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.
|
|
91
98
|
|
|
92
99
|
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.
|
|
93
100
|
|
|
@@ -95,29 +102,29 @@ An execution that picks other than the suggestion rewrites the `- Suggested:` li
|
|
|
95
102
|
|
|
96
103
|
`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.
|
|
97
104
|
|
|
98
|
-
`canon records push` carries these folders off the disk they live on, and `canon records pull` brings them back. Nine of them are backed: `diagrams`, `groundwork`, `intake`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`, each carrying whatever it has archived inside it. The history lives in a second git directory at `.
|
|
105
|
+
`canon records push` carries these folders off the disk they live on, and `canon records pull` brings them back. Nine of them are backed: `diagrams`, `groundwork`, `intake`, `memory`, `plans`, `proposals`, `review`, `tasks`, and `teach`, each carrying whatever it has archived inside it. 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.
|
|
99
106
|
|
|
100
107
|
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](agents/records.md) for the refusal table.
|
|
101
108
|
|
|
102
|
-
`canon records size` reports what each of these folders holds, heaviest first, along with `.
|
|
109
|
+
`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.
|
|
103
110
|
|
|
104
|
-
A plan that ships is archived, never deleted. `canon:claude-docs` moves it to `.
|
|
111
|
+
A plan that ships is archived, never deleted. `canon:claude-docs` moves it to `.canon/plans/archive/` and retargets the task file'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.
|
|
105
112
|
|
|
106
|
-
A branch review report takes the other route and is swept rather than archived. `claude-review` writes it to `.
|
|
113
|
+
A branch review report takes the other route and is swept rather than archived. `claude-review` writes it to `.canon/review/branch/`, the session addressing it reads it once, and the durable record of what a review found is the comment `claude-pr-review` posts on the pull request, so `claude-docs` 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.
|
|
107
114
|
|
|
108
115
|
The plans sweep reads the whole board rather than the tasks the session touched. It is the one place the skill reaches past its own rule against editing a task file the session did not change, because a task that closed while an earlier run missed its archive is exactly what the sweep exists to clear. Reaching it is safe: the archive moves the plan and retargets the pointer in the same pass, so an untouched task ends up with a working link rather than a broken one.
|
|
109
116
|
|
|
110
117
|
`canon:claude-docs` 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.
|
|
111
118
|
|
|
112
|
-
`.
|
|
119
|
+
`.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.
|
|
113
120
|
|
|
114
|
-
`.
|
|
121
|
+
`.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.
|
|
115
122
|
|
|
116
123
|
`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.
|
|
117
124
|
|
|
118
125
|
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.
|
|
119
126
|
|
|
120
|
-
`canon:claude-tasks` 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 `.
|
|
127
|
+
`canon:claude-tasks` 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.
|
|
121
128
|
|
|
122
129
|
Archiving a task leaves its plan alone, because `canon:claude-docs` owns the plans sweep and already holds the last-live-citation rule. That makes the order load-bearing, so the archive verb refuses the last task pointing at a live plan. The sweep only reaches tasks still in the live folder, and archiving that task first would strand the plan there with nothing citing it. A task whose plan a sibling still cites archives freely, because the sweep is correct to leave that plan where it is and a gate reading the folder instead would park every task sharing one plan behind a sweep that will never move it.
|
|
123
130
|
|
|
@@ -152,13 +159,13 @@ The list stays written in the skill body as the fallback for a target whose inst
|
|
|
152
159
|
|
|
153
160
|
#### Memory in the chain
|
|
154
161
|
|
|
155
|
-
`git-ship` runs its verify gate and then opens on `claude-memory-capture`, which sends what the session learned to the surface that owns it. `autoship` reaches the same step by invoking that skill at its Step 7 rather than restating the order. A fact about a domain carrying an entry in `.claude/context/index.md` is routed to that entry, and `claude-docs` folds it in on the next step, so it ships in the same pull request. Anything no entry owns stays a file in `.
|
|
162
|
+
`git-ship` runs its verify gate and then opens on `claude-memory-capture`, which sends what the session learned to the surface that owns it. `autoship` reaches the same step by invoking that skill at its Step 7 rather than restating the order. A fact about a domain carrying an entry in `.claude/context/index.md` is routed to that entry, and `claude-docs` folds it in on the next step, so it ships in the same pull request. Anything no entry owns stays a file in `.canon/memory/`.
|
|
156
163
|
|
|
157
164
|
Capture leads rather than trails because a routed fact edits a tracked file, which has to reach the branch before the commit steps run.
|
|
158
165
|
|
|
159
166
|
If capture wrote at least one memory file, `claude-memory-review` then proposes a decision-ready fix scoped to those entries while context is fresh, otherwise it is skipped. It stops at Propose. Review the receipt and run Apply yourself, on its own commit separate from the feature.
|
|
160
167
|
|
|
161
|
-
Run `claude-memory-review` standalone to curate the whole pen. An entry it retires moves to `.
|
|
168
|
+
Run `claude-memory-review` standalone to curate the whole pen. An entry it retires moves to `.canon/tmp/memory-archive/` rather than being deleted, since the folder is gitignored and a bulk pass has no undo.
|
|
162
169
|
|
|
163
170
|
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 `claude-docs` 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.
|
|
164
171
|
|
|
@@ -197,7 +204,7 @@ This section is the corpus the coverage claim is measured against: every name `c
|
|
|
197
204
|
| `canon:setup-plugins` | On a new machine, to install the community and official plugins user-scoped |
|
|
198
205
|
| `canon:setup-verify` | After the agent generates configs, to run the installed scripts and report pass or fail |
|
|
199
206
|
| `canon:claude-design-extract` | Before the first UI feature, to draft `.claude/DESIGN.md` |
|
|
200
|
-
| `canon:claude-diagram` | Once the architecture is written, to draft per-kind entries under `.
|
|
207
|
+
| `canon:claude-diagram` | Once the architecture is written, to draft per-kind entries under `.canon/diagrams/` |
|
|
201
208
|
|
|
202
209
|
### Decide what to build
|
|
203
210
|
|
package/docs/operating-model.md
CHANGED
|
@@ -53,7 +53,7 @@ halts on a plan question it may not answer, or argues back against an
|
|
|
53
53
|
instruction the tree contradicts, is working correctly. The four measured halts
|
|
54
54
|
to date each cost the dispatcher one reply and each was right.
|
|
55
55
|
|
|
56
|
-
What the worker may not do is write `.
|
|
56
|
+
What the worker may not do is write `.canon/tasks/priority.md` or the backlog
|
|
57
57
|
beside it. Those are gitignored, so an overwrite drops a row with no history to
|
|
58
58
|
recover it from, and a worker cannot pick a free task label without reading
|
|
59
59
|
every task file and every archive entry. It reports the row it needs and lets
|
|
@@ -69,7 +69,7 @@ and no later session recovers that vantage.
|
|
|
69
69
|
|
|
70
70
|
One feature travels this path end to end.
|
|
71
71
|
|
|
72
|
-
1. The next feature is planned with `claude-feature`, writing a plan to `.
|
|
72
|
+
1. The next feature is planned with `claude-feature`, writing a plan to `.canon/plans/`. The orchestrator runs it warm when the row turns on a contract other features consume or a shared wiring seam, and dispatches a planner under `claude-planner` otherwise. A cold planner measures the row against the tree rather than trusting what the row claims, and it reads what is in flight from open pull requests rather than from branches and worktrees, which this repository leaves behind after a squash merge.
|
|
73
73
|
2. Orchestrator checks the plan waits on nobody, checks the branch is unclaimed, and checks the plan's file set is disjoint from every track in flight, then dispatches a background worker with `claude --bg` against the plan, naming the branch and the model on the launch rather than leaving the worker to derive either. No count caps how many run at once. The branch travels as the argument to the worker's own worktree call, which is the one place the name is read rather than inferred. It falls back to naming the invocation for a human to run through `claude-worktree` and `claude-autoship` when the plan still waits on an answer only the operator can give, the check refuses, the sets overlap, or a stated reason serializes the plan behind a track already in flight. Either way, the worker enters its own worktree, builds, self-checks, opens a PR, and stops at the PR boundary.
|
|
74
74
|
3. Orchestrator reviews the PR with `claude-pr-review` and posts findings to it.
|
|
75
75
|
4. Orchestrator tells the session holding that branch to run `claude-address-review` once the pass posted a finding at any severity, resolving the target then with `canon sessions list --branch` and reporting the invocation for the human when no live session holds it. The worker addresses the findings, rebases onto `origin/main` when a sibling landed first and left the branch unable to merge, then pushes a follow-up. A pass carrying only minor findings dispatches too, since the grade runs low often enough that a floor at should-fix loses fixes a worker would have made. `claude-pr-review` states that threshold and the heading follows it, so an open heading is itself the signal to send.
|
|
@@ -203,7 +203,7 @@ coordination costs more than the change it was too small.
|
|
|
203
203
|
|
|
204
204
|
Two tiers hold work at different altitudes.
|
|
205
205
|
|
|
206
|
-
- Tasks (`.
|
|
206
|
+
- 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.
|
|
207
207
|
- Edits: a few lines, done immediately with no ceremony.
|
|
208
208
|
|
|
209
209
|
Nothing above these sequences work into versions. Scope is stated in
|
package/docs/target-projects.md
CHANGED
|
@@ -70,7 +70,7 @@ Scaffold installs tooling and seeds. It does not fill the planning docs or the d
|
|
|
70
70
|
|
|
71
71
|
1. Fill `.claude/REQUIREMENTS.md` and `.claude/ARCHITECTURE.md`. The seed provides the files, the scope and decisions are yours to write.
|
|
72
72
|
2. For a UI project, invoke `canon:claude-design-extract` to draft `.claude/DESIGN.md`. With no UI code yet it takes the greenfield path and proposes tokens from the requirements and a `## Personality` section. Skip for non-UI projects.
|
|
73
|
-
3. Optionally invoke `canon:claude-diagram` to draft entries under `.
|
|
73
|
+
3. Optionally invoke `canon:claude-diagram` to draft entries under `.canon/diagrams/` from the architecture and the requirements. One file per diagram kind, so a later refresh of one kind leaves the others untouched. It renders each diagram it writes to verify the layout, which downloads the Mermaid CLI on first use and takes about 15 seconds.
|
|
74
74
|
4. Start the feature loop. See [AI workflow](ai-workflow.md) for the per-feature sequence.
|
|
75
75
|
|
|
76
76
|
A machine without a renderer still gets the diagrams and is told which check was skipped.
|
|
@@ -124,6 +124,26 @@ Per-domain mechanics live in the corresponding `docs/<domain>.md`. The skill bod
|
|
|
124
124
|
|
|
125
125
|
When the toolkit updates, target projects pull changes per domain. There is one catch-all and several targeted entry points.
|
|
126
126
|
|
|
127
|
+
### Move the records first, once
|
|
128
|
+
|
|
129
|
+
Session records moved out of `.claude/` and into a root of their own. What is committed stays where it is, and everything gitignored, being the task board, the plans, the memory pen, the review reports, and the scratch folder, now lives under `.canon/`, which a single ignore entry covers.
|
|
130
|
+
|
|
131
|
+
**`canon migrate records` is not in a published release yet.** It ships with the change that split the roots, so an installed binary answers `unknown command` until the next release carries it. Until then, run it out of a canon checkout against the project by path:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
canon tooling sync claude . --write
|
|
135
|
+
bun src/cli.ts migrate records --root /path/to/project --json
|
|
136
|
+
bun src/cli.ts migrate records --root /path/to/project --write --json
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Once the release lands, the same three lines read `canon migrate records` from inside the project. Either way the first line takes the `.canon/` ignore entry, and the verb refuses until the project has it, since every folder it relocates is ignored where it stands and landing one under a tracked root commits the memory pen. The second reports the plan and the third applies it, moving the folders and repointing every tracked file that cites one.
|
|
140
|
+
|
|
141
|
+
Read the `ok` field out of the `--json` record rather than the exit code. A shell profile that wraps `canon` in a function takes its status from whatever the function runs last, so an absent subcommand and a clean run can both exit 0, and a reader watching the exit alone concludes the move happened.
|
|
142
|
+
|
|
143
|
+
A tracked file that names an old record path on purpose, such as prose dating a decision, keeps it by carrying `canon-keep-record-root` on that line or the one above. The report pass prints every file it would rewrite, which is where to catch one before `--write` runs.
|
|
144
|
+
|
|
145
|
+
Until the move runs, the project is exposed. The shipped ignore set no longer names the old record paths, so a project holding records at `.claude/` stops ignoring them on its next `canon tooling sync`, and the first sign is a memory file or a task board appearing in a commit. Every command reads either root, so nothing else breaks in the meantime, and running the move is what closes it.
|
|
146
|
+
|
|
127
147
|
### Check first
|
|
128
148
|
|
|
129
149
|
`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.
|
|
@@ -142,7 +162,7 @@ Each domain holds its own toolkit commit, so syncing governance today does not m
|
|
|
142
162
|
|
|
143
163
|
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.
|
|
144
164
|
|
|
145
|
-
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:claude-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 `.
|
|
165
|
+
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:claude-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.
|
|
146
166
|
|
|
147
167
|
`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.
|
|
148
168
|
|
|
@@ -22,7 +22,7 @@ Claude Code reads both and writes the implementation. Works for CLI tools, inter
|
|
|
22
22
|
|
|
23
23
|
The toolkit seed in `tooling/claude/seeds/.claude/DESIGN.md` ships a token-table template with a starting set of roles, and `standards/design.md` carries the same tables under `## Template` with placeholder rows. The column headers are what the renderer parses, so they stay verbatim in either, while the rows and values are the project's own.
|
|
24
24
|
|
|
25
|
-
The `canon:claude-design-extract` skill drafts the file, sourcing tokens from a project's existing prose and CLI UI surfaces, or proposing them from `.claude/REQUIREMENTS.md` and a `## Personality` paragraph when no UI code exists yet. `canon design render` writes an HTML plus CSS preview to `.
|
|
25
|
+
The `canon:claude-design-extract` skill drafts the file, sourcing tokens from a project's existing prose and CLI UI surfaces, or proposing them from `.claude/REQUIREMENTS.md` and a `## Personality` paragraph when no UI code exists yet. `canon design render` writes an HTML plus CSS preview to `.canon/review/design/` for eyeballing the current system without leaving Claude Code. See `.claude/context/design.md`.
|
|
26
26
|
|
|
27
27
|
A cell no source anchors ends in `? verify`, and the preview shows that marker beside the value rather than folding it in, so a swatch and a font sample stay built from the value alone. A confidence line above the sections names how many cells are anchored against how many are tagged, which is what tells a reader whether they are looking at a record of the code or a proposal about it. It reads the columns a source could anchor and leaves out the row names, so the ratio is not diluted by cells no tag could ever reach. The proposal path tags nearly all of them, so that count reads low on day one by design.
|
|
28
28
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .
|
|
2
|
+
description: Route .canon/tasks/ edits to the tasks standard for filenames, frontmatter, and task file format
|
|
3
3
|
paths:
|
|
4
|
-
- '.
|
|
4
|
+
- '.canon/tasks/**'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Tasks standards
|
|
@@ -9,4 +9,4 @@ paths:
|
|
|
9
9
|
## Authority
|
|
10
10
|
|
|
11
11
|
- Follow the tasks standard for filenames, frontmatter, what belongs, and the task file format. It is the single source. Read it with `canon standards tasks`.
|
|
12
|
-
- Never hand-edit `.
|
|
12
|
+
- Never hand-edit `.canon/tasks/index.md`. A hook regenerates it from sibling frontmatter.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .
|
|
2
|
+
description: Route .canon/groundwork/ edits to the groundwork standard for ordinal naming, numbering, frontmatter, and required file contents
|
|
3
3
|
paths:
|
|
4
|
-
- '.
|
|
4
|
+
- '.canon/groundwork/**'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Groundwork standards
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .
|
|
2
|
+
description: Route .canon/intake/ edits to the intake standard for the item format, the answer contract, and ordinal naming
|
|
3
3
|
paths:
|
|
4
|
-
- '.
|
|
4
|
+
- '.canon/intake/**'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Intake standards
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .
|
|
2
|
+
description: Route .canon/plans/ edits to the plan standard for sections, the answer contract, and the archive move
|
|
3
3
|
paths:
|
|
4
|
-
- '.
|
|
4
|
+
- '.canon/plans/**'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Plan standards
|
|
@@ -14,7 +14,7 @@ paths:
|
|
|
14
14
|
|
|
15
15
|
## Archiving
|
|
16
16
|
|
|
17
|
-
- Move a shipped plan to `.
|
|
17
|
+
- Move a shipped plan to `.canon/plans/archive/`. Never delete one.
|
|
18
18
|
- Amend a plan in place when a decision changes. Do not append a second passage narrating the change.
|
|
19
19
|
|
|
20
20
|
## Authority
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .
|
|
2
|
+
description: Route .canon/memory/ edits to the memory standard for the filename, frontmatter, body shape, and lifecycle
|
|
3
3
|
paths:
|
|
4
|
-
- '.
|
|
4
|
+
- '.canon/memory/**'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Memory standards
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .
|
|
2
|
+
description: Route .canon/diagrams edits to the diagrams standard for Mermaid conventions
|
|
3
3
|
paths:
|
|
4
|
-
- '.
|
|
4
|
+
- '.canon/diagrams/**'
|
|
5
5
|
- '.claude/DIAGRAMS.md'
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -14,5 +14,5 @@ paths:
|
|
|
14
14
|
|
|
15
15
|
## Scope
|
|
16
16
|
|
|
17
|
-
- Write a new diagram to `.
|
|
17
|
+
- Write a new diagram to `.canon/diagrams/<kind>.md`, never to `.claude/DIAGRAMS.md`
|
|
18
18
|
- Convert a `.claude/DIAGRAMS.md` left by an older install into per-kind entries before editing it
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .
|
|
2
|
+
description: Route .canon/teach/ edits to the teach standard for layout, ordinal naming, and the mission and record formats, in the projects that open a learning workspace
|
|
3
3
|
paths:
|
|
4
|
-
- '.
|
|
4
|
+
- '.canon/teach/**'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Teach standards
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .
|
|
2
|
+
description: Route .canon/tasks/ session map edits to the session standard for the filename, the core sections, and what a handoff carries
|
|
3
3
|
paths:
|
|
4
|
-
- '.
|
|
4
|
+
- '.canon/tasks/session-*.md'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Session map standards
|
|
@@ -6,8 +6,8 @@ description: Scope, size, and link a task file correctly
|
|
|
6
6
|
|
|
7
7
|
## Task files
|
|
8
8
|
|
|
9
|
-
- `.
|
|
9
|
+
- `.canon/tasks/` is gitignored local session scratch, one file per task. Edit freely. No staging or revert before commits.
|
|
10
10
|
- Only create a task for work that spans multiple sessions or has real dependencies. Handle small edits immediately without a task entry.
|
|
11
11
|
- Do not add tasks retroactively for work already completed. Completed work is visible in git.
|
|
12
|
-
- When a task needs execution detail beyond its own file, create a plan in `.
|
|
12
|
+
- When a task needs execution detail beyond its own file, create a plan in `.canon/plans/` and link to it from the task's intro paragraph. When that task ships, move its plan file to `.canon/plans/archive/`. Never delete it.
|
|
13
13
|
- Write the plan in the same session as the task file. The session that executes the plan later inherits reasoning context it would otherwise have to re-derive.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Keep memory writes scoped to .
|
|
2
|
+
description: Keep memory writes scoped to .canon/memory/ and out of context-owned domains
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Memory standards
|
|
6
6
|
|
|
7
7
|
## Writing memory
|
|
8
8
|
|
|
9
|
-
- Write all memory files to `.
|
|
9
|
+
- Write all memory files to `.canon/memory/`, not `~/.claude/projects/`
|
|
10
10
|
- A fact about a domain goes to that domain's `.claude/context/` entry, not to memory. `canon:claude-memory-capture` routes it there and `canon:claude-docs` folds it in. Memory keeps only what no context entry owns. Report it rather than proceeding silently when either skill does not resolve. Both ship with the plugin and this rule ships with the CLI, so a project that installed governance alone does not have them.
|
|
11
|
-
- Never delete a memory entry. Retire one by moving it to `.
|
|
11
|
+
- Never delete a memory entry. Retire one by moving it to `.canon/tmp/memory-archive/`. A bulk retire runs through the shell, where no file edit fires a path-scoped rule, and the folder is gitignored with nothing to recover from.
|
|
12
12
|
- Follow the memory standard for the filename and type prefix, the frontmatter, the body shape each type carries, and the lifecycle. Read it with `canon standards memory`. Check every entry in the pen against that standard and fix what breaks it, since nothing keeps the folder conforming on its own.
|
|
@@ -6,4 +6,5 @@ description: Write temporary files to the scratch folder structure
|
|
|
6
6
|
|
|
7
7
|
## Temporary files
|
|
8
8
|
|
|
9
|
-
- Write temporary files to `.
|
|
9
|
+
- Write temporary files to `.canon/tmp/<slug>/<file>.md` in the project root, a nested `<slug>/` folder with a kebab-slug tied to the topic, not a flat `<slug>-<file>.md`. The scratch-guard hook enforces the location.
|
|
10
|
+
- Write to `.claude/.tmp/<slug>/` instead in a project that carries no `.canon/` root, which is one the record move has not reached. The hook accepts either, and `canon migrate records` is what moves a project from the first spelling to the second. <!-- canon-keep-record-root -->
|
|
@@ -10,7 +10,7 @@ description: Route tracked-file writes and shared session scratch correctly from
|
|
|
10
10
|
|
|
11
11
|
## Shared session scratch
|
|
12
12
|
|
|
13
|
-
- Shared session scratch (`.
|
|
13
|
+
- Shared session scratch (`.canon/plans/`, `.canon/review/`, `.canon/memory/`, `.canon/tasks/`) lives at the main worktree root, not inside a linked worktree. From a linked worktree, resolve these paths against the main root via `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`. Fall back to `pwd` if not a git repo.
|
|
14
14
|
- From a linked worktree, every `Edit` or `Write` to a tracked file (source, docs) must use a path starting with `pwd`.
|
|
15
15
|
- From a linked worktree, `Edit` and `Write` are refused for every main-root path, session scratch included. The refusal names session isolation and points at the worktree copy, which is a second gitignored file no later session reads, so never take that redirect.
|
|
16
16
|
- `Read` resolves against the main root normally from a linked worktree. A main-root write reaches it only through `Bash`, as one plain command rather than a compound one, which is refused for complexity.
|
package/package.json
CHANGED
|
@@ -9,22 +9,22 @@ GITIGNORE="$PROJECT_ROOT/.gitignore"
|
|
|
9
9
|
MANIFEST="$PROJECT_ROOT/tooling/claude/manifest.toml"
|
|
10
10
|
SECTION="# Claude"
|
|
11
11
|
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
12
|
+
# Parity is exact, with no exception list behind it, and the move to `.canon/`
|
|
13
|
+
# is what removed the one there was. Its two members each named a path inside
|
|
14
|
+
# `.claude/` that this repository ignored and the manifest withheld, so a target
|
|
15
|
+
# kept tracking it. A single root entry can withhold nothing inside itself: git
|
|
16
|
+
# does not descend into an excluded directory, so a re-inclusion like
|
|
17
|
+
# `!.canon/diagrams/` matches nothing. Retiring both is what buys the one line.
|
|
16
18
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
19
|
+
# The diagrams divergence is the one that cost something. A target used to track
|
|
20
|
+
# its diagrams and keep the context audit's default coverage of them, and it now
|
|
21
|
+
# ignores them with the rest of its records, so that coverage is gone unless the
|
|
22
|
+
# project names the folder itself. `.claude/context/tooling.md` carries the
|
|
23
|
+
# narrative.
|
|
20
24
|
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
SANCTIONED=(
|
|
25
|
-
".claude/diagrams|a target that tracks its diagrams keeps the context audit's default coverage of them, which ignoring the folder for every target would take away. This repository backs the folder through its records remote instead."
|
|
26
|
-
".claude/README.md|the landing page a records pull writes back into a repository that set up a records remote. A target has not, so it never receives the file."
|
|
27
|
-
)
|
|
25
|
+
# An exception mechanism is not kept against a future divergence, because an
|
|
26
|
+
# empty one cannot be exercised and a check nobody can test is a check nobody
|
|
27
|
+
# should trust. It comes back with its first real member or not at all.
|
|
28
28
|
|
|
29
29
|
if [ ! -f "$GITIGNORE" ]; then
|
|
30
30
|
echo "No .gitignore at ${GITIGNORE#"$PROJECT_ROOT/"}, ignore parity unverifiable." >&2
|
|
@@ -38,15 +38,15 @@ fi
|
|
|
38
38
|
|
|
39
39
|
# A gitignore pattern and a manifest entry describe the same folder whether or
|
|
40
40
|
# not either spells the trailing slash, so presence is compared with it dropped.
|
|
41
|
-
#
|
|
41
|
+
# No live pair differs that way now that both lists carry the same two entries,
|
|
42
|
+
# and the normalization stays because either side may be written without one.
|
|
42
43
|
normalize() {
|
|
43
44
|
printf '%s\n' "${1%/}"
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
# Every pattern the file carries, comments and blank lines dropped. The whole
|
|
47
|
-
# file rather than one header,
|
|
48
|
-
# its own
|
|
49
|
-
# list that carries it.
|
|
48
|
+
# file rather than one header, since a claude-scoped entry filed under a header
|
|
49
|
+
# of its own would otherwise read as missing from a list that carries it.
|
|
50
50
|
gitignore_patterns() {
|
|
51
51
|
awk '{ sub(/[[:space:]]+$/, "") } $0 ~ /^[[:space:]]*#/ || $0 == "" { next } { print }' "$GITIGNORE"
|
|
52
52
|
}
|
|
@@ -106,7 +106,6 @@ if [ ${#shipped[@]} -eq 0 ]; then
|
|
|
106
106
|
fi
|
|
107
107
|
|
|
108
108
|
failures=""
|
|
109
|
-
sanction_notes=""
|
|
110
109
|
|
|
111
110
|
# Entries the manifest ships that this repository does not ignore. A target is
|
|
112
111
|
# told to ignore a folder the toolkit itself tracks, which no decision sanctions,
|
|
@@ -121,48 +120,22 @@ done
|
|
|
121
120
|
# and says nothing about `node_modules/` or `.env`.
|
|
122
121
|
#
|
|
123
122
|
# `.canon` is read as a bare root as well as a prefix, since it is one line
|
|
124
|
-
# covering a whole tree where `.claude/`
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
123
|
+
# covering a whole tree where `.claude/` names a folder inside a root that also
|
|
124
|
+
# holds tracked content. Leaving it out is what would let a new entry sit outside
|
|
125
|
+
# the only stage comparing the two lists, which is the direction that goes
|
|
126
|
+
# silently blind.
|
|
128
127
|
for pattern in "${ignored[@]}"; do
|
|
129
128
|
case "$pattern" in
|
|
130
129
|
.claude/* | .canon | .canon/*) ;;
|
|
131
130
|
*) continue ;;
|
|
132
131
|
esac
|
|
133
132
|
contains "$pattern" "${shipped[@]}" && continue
|
|
134
|
-
|
|
135
|
-
matched=false
|
|
136
|
-
for sanction in "${SANCTIONED[@]}"; do
|
|
137
|
-
[ "${sanction%%|*}" = "$pattern" ] || continue
|
|
138
|
-
matched=true
|
|
139
|
-
sanction_notes="$sanction_notes $pattern stays out of the manifest: ${sanction#*|}"$'\n'
|
|
140
|
-
break
|
|
141
|
-
done
|
|
142
|
-
|
|
143
|
-
if [ "$matched" = false ]; then
|
|
144
|
-
failures="$failures $pattern is ignored here and absent from the manifest"$'\n'
|
|
145
|
-
fi
|
|
146
|
-
done
|
|
147
|
-
|
|
148
|
-
# A sanction naming a path that is no longer divergent, either because the
|
|
149
|
-
# manifest took it or because .gitignore dropped it.
|
|
150
|
-
for sanction in "${SANCTIONED[@]}"; do
|
|
151
|
-
entry="${sanction%%|*}"
|
|
152
|
-
if contains "$entry" "${ignored[@]}" && ! contains "$entry" "${shipped[@]}"; then
|
|
153
|
-
continue
|
|
154
|
-
fi
|
|
155
|
-
failures="$failures $entry is sanctioned as a divergence and is no longer one"$'\n'
|
|
133
|
+
failures="$failures $pattern is ignored here and absent from the manifest"$'\n'
|
|
156
134
|
done
|
|
157
135
|
|
|
158
136
|
if [ -n "$failures" ]; then
|
|
159
137
|
echo "The ignore set a target receives disagrees with this repository's own:" >&2
|
|
160
138
|
printf '%s' "$failures" >&2
|
|
161
|
-
echo "Add the entry to the \"$SECTION\" array in ${MANIFEST#"$PROJECT_ROOT/"} and to .gitignore
|
|
139
|
+
echo "Add the entry to the \"$SECTION\" array in ${MANIFEST#"$PROJECT_ROOT/"} and to .gitignore. The two lists are compared exactly, and nothing here records an exception." >&2
|
|
162
140
|
exit 1
|
|
163
141
|
fi
|
|
164
|
-
|
|
165
|
-
if [ -n "$sanction_notes" ]; then
|
|
166
|
-
echo "Sanctioned divergences from the ignore set a target receives:"
|
|
167
|
-
printf '%s' "$sanction_notes"
|
|
168
|
-
fi
|
|
@@ -7,7 +7,7 @@ PROJECT_ROOT="${PROJECT_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
|
7
7
|
|
|
8
8
|
source "$PROJECT_ROOT/scripts/lib/ui.sh"
|
|
9
9
|
|
|
10
|
-
TMP_ROOT="$PROJECT_ROOT/.
|
|
10
|
+
TMP_ROOT="$PROJECT_ROOT/.canon/tmp/install-check"
|
|
11
11
|
CLONE_DIR="$TMP_ROOT/clone"
|
|
12
12
|
TARGET_DIR="$TMP_ROOT/target"
|
|
13
13
|
KEEP=0
|
|
@@ -20,10 +20,10 @@ for arg in "$@"; do
|
|
|
20
20
|
Usage: scripts/core/install-check.sh [--keep]
|
|
21
21
|
|
|
22
22
|
Verifies the README install path end to end:
|
|
23
|
-
1. Clones this repo into .
|
|
23
|
+
1. Clones this repo into .canon/tmp/install-check/clone
|
|
24
24
|
2. Runs bun install in the clone
|
|
25
25
|
3. Runs the CLI with --help to confirm it executes
|
|
26
|
-
4. Scaffolds a fresh project in .
|
|
26
|
+
4. Scaffolds a fresh project in .canon/tmp/install-check/target
|
|
27
27
|
5. Runs canon init and asserts a scaffold landed
|
|
28
28
|
|
|
29
29
|
Flags:
|
|
@@ -83,7 +83,7 @@ log_step "Assert scaffold"
|
|
|
83
83
|
# `@`-reference convention rule is the one snippets-domain file that still
|
|
84
84
|
# installs, since `base` carries `governance/rules/snippets/` as a
|
|
85
85
|
# folder-whole entry the same way it does `core` and `claude`.
|
|
86
|
-
for path in "CLAUDE.md" ".claude/wiki/index.md" ".claude" ".claude/context/index.md" ".claude/wireframes/index.md" ".
|
|
86
|
+
for path in "CLAUDE.md" ".claude/wiki/index.md" ".claude" ".claude/context/index.md" ".claude/wireframes/index.md" ".canon/diagrams/index.md" \
|
|
87
87
|
".prettierrc" ".editorconfig" ".lintstagedrc" ".husky/pre-commit" ".github/workflows/verify.yml" "scripts/verify.sh" \
|
|
88
88
|
".claude/rules/core/000-constitution.md" ".claude/rules/snippets/505-at-references.md"; do
|
|
89
89
|
if [ ! -e "$TARGET_DIR/$path" ]; then
|
package/scripts/core/snapshot.sh
CHANGED
|
@@ -10,7 +10,7 @@ source "$PROJECT_ROOT/scripts/lib/ui.sh"
|
|
|
10
10
|
|
|
11
11
|
NESTED="${VERIFY_NESTED:-false}"
|
|
12
12
|
|
|
13
|
-
OUTPUT_FILE=".
|
|
13
|
+
OUTPUT_FILE=".canon/tmp/project/PROJECT-SNAPSHOT.md"
|
|
14
14
|
|
|
15
15
|
check_dependencies() {
|
|
16
16
|
command -v find >/dev/null 2>&1 || log_error "find not installed"
|