@erclx/canon 4.57.0 → 4.59.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "4.57.0",
4
+ "version": "4.59.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -87,7 +87,7 @@ Check for a `**Files to touch:**` or `## Files to touch` marker per `${CLAUDE_SK
87
87
 
88
88
  Read `<plan>` at the main worktree root. This file is the scope for this run.
89
89
 
90
- Its sections and its answer contract are fixed by `${CLAUDE_SKILL_DIR}/../../standards/plan.md`. A blank `- Answer:` accepts the `- Suggested:` line above it, so an unanswered question is a decision this run executes rather than a reason to stop.
90
+ Its sections and its answer contract are fixed by `${CLAUDE_SKILL_DIR}/../../standards/plan.md`. A blank `- Answer:` accepts the `- Suggested:` line above it, so an unanswered question is a decision this run executes rather than a reason to stop. When this run decides against that suggestion, rewrite the `- Suggested:` line as `overridden at execution to <pick>,` plus the measurement, leaving the `- Answer:` slot blank, and put the same deviation in one line under the open task's `## Findings`, per `${CLAUDE_SKILL_DIR}/../../standards/plan.md`.
91
91
 
92
92
  ## Step 2: implement
93
93
 
@@ -23,6 +23,7 @@ Read `${CLAUDE_SKILL_DIR}/../../standards/groundwork.md` before writing any file
23
23
  - One exception, at close only: write one task file recording what the track concluded.
24
24
  - A second exception, for what a spike reads: write an input under `.canon/tmp/groundwork-fixtures/<slug>/`. Keep it out of `.canon/groundwork/` so mode detection never matches a fixture as a track. A fixture a headless run is pointed at goes outside the repository instead, per the rule in `## Running a spike`.
25
25
  - What a spike produces stays inside the track rather than joining the two exceptions above: write evidence a spike file cites under `.canon/groundwork/<nn>-<slug>/evidence/`, which the first rule already permits. Mode detection matches entries at the top level of the tracks directory, so the sibling the fixtures rule guards against cannot be a folder nested inside a track, and the scratch path holds only what can be deleted without loss, which a recording a finding rests on is not.
26
+ - A `draft-and-pick` run invoked from inside a live track follows its own branch for this, writing to `evidence/` here rather than `.canon/tmp/<slug>/`.
26
27
  - Reading is not restricted. External research is in scope, so read documentation, comparable projects, and papers whenever a live question needs them.
27
28
  - Every claim about a source outside the project carries a link to it. A source found and not read is listed as a lead and is never cited.
28
29
  - Treat the folder as gitignored and unbacked. It dies with the machine, so `07-next-session.md` repeats what it needs instead of pointing at its siblings.
@@ -141,7 +141,7 @@ Use severity: `critical` (blocks merge), `should-fix` (fix before merge), `minor
141
141
 
142
142
  ## Step 4: post to the PR
143
143
 
144
- Write the comment to `.canon/tmp/pr-review/body-<number>-<short-sha>.md`, which the rest of this step calls `<body-file>`. The PR number stops two sessions reviewing different pull requests from overwriting each other between the write and the post. The head commit stops a second pass overwriting the first one's body, and leaves the folder a record of which commit each review covered.
144
+ Write the comment to `.canon/tmp/pr-review/body-<number>-<short-sha>.md` at the main worktree root, not the current worktree, which the rest of this step calls `<body-file>`. Resolve that root the way `claude-worktree` does, and send the write as a plain single `Bash` command carrying a heredoc from a linked worktree, since `Edit` and `Write` refuse a main-root path there. The PR number stops two sessions reviewing different pull requests from overwriting each other between the write and the post, and the head commit stops a second pass overwriting the first one's body, leaving the folder a record of which commit each review covered.
145
145
 
146
146
  Derive both segments from Step 1. Never pick a suffix by hand, and never reuse a name the folder already holds.
147
147
 
@@ -26,6 +26,7 @@ running and re-invoking it would restart the build.
26
26
  - Report a plan that fails to resolve as unreadable from here, naming the main-root path. Reporting the task as having no plan is true where this session stands and wrong about the world, and a reader with no second tree to check cannot separate the two.
27
27
  - Build the plan the launch named. Do not write a second one when the path fails to resolve, since a row that cites a plan already has one and drafting another produces two plans for one row.
28
28
  - Report a draft flag reading ready once, and leave it cleared. The ship chain marks the pull request a draft and no step anywhere un-marks, so a flag reading ready afterwards was lifted by a person readying it to merge, which GitHub requires and is theirs to take. Re-drafting fights them, which four sessions did on 2026-08-31 on a belief no surface in the tree states.
29
+ - Rewrite a plan question's `- Suggested:` line as `overridden at execution to <pick>,` plus the measurement when this build decides against an unanswered one, leaving the `- Answer:` slot blank, and put the same deviation in one line under the open task's `## Findings`, per `${CLAUDE_SKILL_DIR}/../../standards/plan.md`.
29
30
 
30
31
  ## The board is read-only
31
32
 
@@ -118,14 +118,16 @@ A linked worktree is a second working directory over one repository, and every e
118
118
 
119
119
  Report the state on one line. Do not install. Entering a worktree to read is as common as entering one to run, and an install is slow, needs a network, and picks an ecosystem on the session's behalf.
120
120
 
121
- Read the worktree root and emit the first line that matches:
121
+ Read the worktree root and evaluate node and python independently, each emitting its own line regardless of the other's state:
122
122
 
123
- - `package.json` present, `node_modules/` missing: `Dependencies are not installed. Run <install> before any build, test, or server command.` Take `<install>` from the lockfile beside the manifest, and use `bun install` when no lockfile names one.
124
- - A `pyproject.toml` or `requirements.txt` present, `.venv/` missing: `No virtual environment. Create and populate one before running anything.`
125
- - A manifest present with its folder alongside it: `Dependencies are installed.`
126
- - No package manifest of either kind: `No package manifest, so there is nothing to install.`
123
+ - Node. `package.json` present, `node_modules/` missing: `Dependencies are not installed. Run <install> before any build, test, or server command.` Take `<install>` from the lockfile beside the manifest, and use `bun install` when no lockfile names one. `package.json` present with `node_modules/` alongside it: `Node dependencies are installed.`
124
+ - Python. A `pyproject.toml` or `requirements.txt` present, `.venv/` missing: `No virtual environment. Create and populate one before running anything.` Either manifest present with `.venv/` alongside it: `Python dependencies are installed.`
127
125
 
128
- The last line is what keeps the step honest on a stack this skill cannot read. Entry is not stack-aware, and silence is indistinguishable from a check that passed.
126
+ Name the ecosystem in both installed lines rather than leaving `Dependencies are installed.` unqualified. Both checks can fire on one project, so an unqualified line reported the same sentence twice for a dual-root project with both folders present, and a reader could not tell which half each line answered.
127
+
128
+ Emit the closing line only when neither manifest is present, tested directly rather than reached by falling through the two checks above unmatched: `No package manifest, so there is nothing to install.` A dual-root project matches both checks above, and a fallthrough test would route it here by accident.
129
+
130
+ The closing line is what keeps the step honest on a stack this skill cannot read. Entry is not stack-aware, and silence is indistinguishable from a check that passed.
129
131
 
130
132
  Then report the port this worktree derives, on a second line:
131
133
 
@@ -16,14 +16,14 @@ Some decisions are settled by looking rather than by reasoning, and no draft is
16
16
  ## Step 1: name the decision and the arms
17
17
 
18
18
  1. State the decision in one sentence, naming what changes between arms and what stays fixed.
19
- 2. Derive a kebab slug from that sentence. Every file this run writes goes to `.canon/tmp/<slug>/`, per `.claude/rules/canon/core/055-scratch.md`.
19
+ 2. Derive a kebab slug from that sentence. Call the folder every file this run writes to `<dest>` below. `<dest>` is `.canon/tmp/<slug>/`, per `.claude/rules/canon/core/055-scratch.md`. Running inside a live `claude-groundwork` track is the one exception: `<dest>` is the track's own `evidence/<slug>/` instead, since a candidate render is evidence the track's decision file cites rather than spike input.
20
20
  3. Write one arm per candidate, each carrying an id, a label, and what the arm costs. An arm with no stated cost is not an option.
21
21
  4. Make the current state arm `0`, so the baseline is a candidate rather than an absence. A decision with nothing shipped yet says so and starts at arm `1`.
22
22
  5. Stop at three to five arms. Two is a comparison the operator can hold in prose, and past five the pick stops being a look and becomes a sort.
23
23
 
24
24
  ## Step 2: author the candidate set as one page
25
25
 
26
- Write every arm side by side on one self-contained HTML page at `.canon/tmp/<slug>/candidates.html`.
26
+ Write every arm side by side on one self-contained HTML page at `<dest>/candidates.html`.
27
27
 
28
28
  - One page, never one file per arm. The comparison is the artifact, and several images handed over separately ask the operator to hold the differences in memory.
29
29
  - Label each arm on the page with its id and its cost, so the render carries what the question will ask about.
@@ -36,12 +36,12 @@ Write every arm side by side on one self-contained HTML page at `.canon/tmp/<slu
36
36
  Render the page, then look at what came back:
37
37
 
38
38
  ```bash
39
- canon capture .canon/tmp/<slug>/candidates.html --selector <element>
39
+ canon capture <dest>/candidates.html --selector <element>
40
40
  ```
41
41
 
42
42
  - `--selector` has no default and the command refuses without it. Name the element wrapping the arms rather than `body`, which crops to whatever the page's own margins leave.
43
43
  - `canon capture` and `canon drive` both need a browser binary the toolkit does not install. When either refuses for that reason, report the refusal and name `bunx playwright install chromium` as the repair, then stop rather than describing an arm nobody has seen.
44
- - Serve the page instead of capturing it when the operator has to drive the decision, such as a hover response, a scroll-linked position, or a pace. Start `canon serve .canon/tmp/<slug> --entry candidates.html` in the background and read the link off its record, since the printed link opens `index.html` without that flag and the scratch folder holds no such file. A still answers how a thing looks and answers none of those.
44
+ - Serve the page instead of capturing it when the operator has to drive the decision, such as a hover response, a scroll-linked position, or a pace. Start `canon serve <dest> --entry candidates.html` in the background and read the link off its record, since the printed link opens `index.html` without that flag and `<dest>` holds no such file. A still answers how a thing looks and answers none of those.
45
45
  - Hand over the address rather than a description. Emit the PNG path on its own line, and the link beside it where the page is served.
46
46
  - Never report a visual result you have not looked at. A claim about appearance with no render behind it is a guess.
47
47
 
@@ -60,14 +60,14 @@ Put the choice to the operator through the structured question surface, per `.cl
60
60
  2. Write fresh arms off the pick and return to Step 2 where the correction opens a new question. Revise the one arm where it does not.
61
61
  3. Re-render, hand off again, and take the next answer.
62
62
  4. Repeat until the operator says it is right. The loop stops on their word and on nothing else, so a run stopping because the arms stopped differing has stopped early.
63
- 5. Hold the real surface untouched across every iteration. Nothing outside `.canon/tmp/<slug>/` changes until the pick is final.
63
+ 5. Hold the real surface untouched across every iteration. Nothing outside `<dest>` changes until the pick is final.
64
64
 
65
65
  ## Step 6: close
66
66
 
67
67
  1. Apply the winning arm to the real surface, in one change.
68
- 2. Close out whatever document stated the decision as open, in the same change, naming the arm that won and the ones that stayed defensible. A pick that changes a surface and records nothing about why leaves the next reader to re-derive it from a diff, and the losing arms are gone by the next step. Skip this where nothing stated the decision.
69
- 3. Delete `.canon/tmp/<slug>/` and every losing arm with it. A variant left behind is a second design nobody maintains.
70
- 4. Report the scratch folder as still standing when that delete is refused, naming the path for the operator to remove, rather than closing on a report the tree contradicts. The pick is applied either way, so the run has done its work and the folder is what outlives it.
68
+ 2. Close out whatever document stated the decision as open, in the same change, naming the arm that won and the ones that stayed defensible. A pick that changes a surface and records nothing about why leaves the next reader to re-derive it from a diff, and the losing arms are gone by the next step where `<dest>` is deleted. Skip this where nothing stated the decision.
69
+ 3. Delete `<dest>` and every losing arm with it, when `<dest>` is the scratch path. A variant left behind there is a second design nobody maintains. Leave `<dest>` in place when it is a live track's `evidence/<slug>/`: `claude-groundwork`'s write scope treats evidence as durable rather than as scratch a session may delete, and the render a decision file cites has to stay where that file points.
70
+ 4. Report `<dest>` as still standing when the scratch-path delete is refused, naming the path for the operator to remove, rather than closing on a report the tree contradicts. The pick is applied either way, so the run has done its work and the folder is what outlives it.
71
71
  5. Report every surface that changed, each on its own line, and name the arm that won by its id and its cost.
72
72
 
73
73
  ## Reading a measurement
@@ -9,7 +9,7 @@ description: Scope boundary for the one-shot onboarding chain and the setup step
9
9
 
10
10
  Without this skill, a session installs a stack whose name it recalled rather than read, hand-writes configs the tooling sync already ships as golden files, and re-runs the chain over a project that owns its configs already.
11
11
 
12
- Two failures are the expensive ones because both return success. A monorepo run drops husky into every subtree, git honors one `core.hooksPath`, and the extra hook directories go quiet with nothing reporting it. A caller reads the chain as onboarding complete when index bootstrap and plugin provisioning never ran, since no step states whether they belong to the chain.
12
+ Two failures are the expensive ones because both return success. A monorepo run drops husky into every subtree, git honors one `core.hooksPath`, and the extra hook directories go quiet with nothing reporting it. A caller reads the chain as onboarding complete when plugin provisioning never runs, since no step states that it sits outside the chain.
13
13
 
14
14
  Two more start at the resolve step. A project whose language the toolkit carries no stack for resolves to `base`, and the preview reports the resolved name without marking it as a fallback, so `base` development dependencies, scripts, and hooks land on a project that will not use them and nobody had the moment to decline.
15
15
 
@@ -20,7 +20,7 @@ The other is the refusal that ends rather than routes. An existing project, an i
20
20
  - Resolve every domain argument against a catalog read at run time, so a stack, rule, snippet, or tooling name the catalog does not carry cannot reach the command line
21
21
  - Preview the resolved chain before the first command runs, naming the evidence file behind each detected technology
22
22
  - Pass `--skip base` on every subtree past the first in a monorepo, so the shared hook layer lands once
23
- - State which onboarding steps the chain does not run, so a caller knows what is left rather than inferring completeness from a clean report
23
+ - State which onboarding steps the chain does not run and why, so a caller knows what is left and where the boundary sits rather than inferring completeness from a clean report
24
24
  - Mark a stack reached by fallback as a fallback in the preview, and name what `base` carries, so the write can be declined at the one point it is still declinable
25
25
  - Name a destination for every state the chain does not serve, in this body rather than only in a reference doc the person would have to already know to open, and say for each whether the chain stops or runs on a default
26
26
 
@@ -37,9 +37,10 @@ The other is the refusal that ends rather than routes. An existing project, an i
37
37
  ## Out of scope
38
38
 
39
39
  - Installing governance rules alone: `setup-gov`. This skill installs governance beside tooling and the seeds.
40
- - Bootstrapping the `index.md` system: `setup-indexes`. Neither `canon init` nor this chain performs it.
41
40
  - Provisioning Claude Code plugins: `setup-plugins`. Those install once per machine rather than into a project, so no project-scoped chain can carry them.
42
- - Running the verification scripts, which `setup-verify` owns and this chain invokes as its last step
41
+ - Running the verification scripts, which `setup-verify` owns and this chain invokes as step 4
42
+ - Publishing repository metadata: `repo-metadata`. It reaches a remote, which is past the project edge this chain stops at.
43
+ - Making the first commit: `git-commit`. It reaches the project's history, which is past the project edge this chain stops at.
43
44
  - Re-installing into a project that already has the files, which the per-domain `canon <domain> install` and `canon sync` commands own
44
45
  - Deciding which of those per-domain commands an existing project needs: `canon-operator`. That skill reads the target before naming one, which this chain never does, so it is the destination rather than a guess made here.
45
46
  - Seeding the `.claude/` folder without the tooling chain, which `canon claude init` does on its own and `setup-indexes` finishes
@@ -10,7 +10,7 @@ Orchestrates the onboarding chain. Detects project type, resolves per-domain arg
10
10
  ## Scope
11
11
 
12
12
  - This skill and `canon init` run once on a fresh scaffold, never on an existing project. They do not guard against clobbering existing configs. When tempted to add guards, mode switches, or an existing-project branch, stop. Extend the per-domain `canon <domain> install` or `canon sync` paths instead.
13
- - The chain does not bootstrap the `index.md` system and does not provision Claude Code plugins. `setup-indexes` owns the first. `setup-plugins` owns the second, which installs once per machine rather than into a project, so no project-scoped chain can carry it. Name both in the report so a clean result does not read as onboarding complete.
13
+ - The chain folds `setup-indexes` in as its own final step, once the project-scoped work is written. It still does not provision Claude Code plugins: `setup-plugins` owns that, installing once per machine rather than into a project, so no project-scoped chain can carry it. Name it in the report so a clean result does not read as onboarding complete.
14
14
 
15
15
  ## Declined states
16
16
 
@@ -75,7 +75,7 @@ A resolved name and a fallback read alike once written, so mark the fallback her
75
75
 
76
76
  ## Execute
77
77
 
78
- Run the chain in order, starting immediately after the preview. Each step's permission dialog is the confirmation gate. Do not pause for additional confirmation. Run from the target project's current directory.
78
+ Run the chain in order, starting immediately after the preview. Each step's permission dialog is the confirmation gate, except step 5, which hands off to a skill that confirms its own folder list with the operator. Do not pause for additional confirmation elsewhere. Run from the target project's current directory.
79
79
 
80
80
  Step 1: `canon init` installs base tooling, claude seeds, governance rules, and wiki.
81
81
 
@@ -115,6 +115,8 @@ Do not generate ESLint, Vitest, or Playwright configs. They ship as golden files
115
115
 
116
116
  Step 4: invoke `setup-verify`. Runs the `package.json` scripts and reports pass/fail.
117
117
 
118
+ Step 5: hand off to `setup-indexes` to bootstrap the `index.md` system over the project's own documentation folders. The skill confirms candidate folders with the operator rather than running unattended, which is the one step in this chain that pauses for a conversation. A fresh scaffold usually has no markdown-heavy folder yet, so this step frequently hands off with nothing for `setup-indexes` to present, which is that skill's own outcome to define rather than a claim this chain makes for it.
119
+
118
120
  ## Report
119
121
 
120
122
  After the chain, report:
@@ -123,6 +125,9 @@ After the chain, report:
123
125
  - Tooling stack synced (or skipped). Name the layers pulled via the extends chain.
124
126
  - Any post-sync fixups applied (ESLint pin, filename renames)
125
127
  - `setup-verify` outcome
128
+ - `setup-indexes` outcome (folders bootstrapped, or none found)
126
129
  - Any domains or scripts that failed
127
130
  - Any detection gaps surfaced during resolve
128
- - Onboarding steps left to the caller: `setup-indexes` for the `index.md` system, `setup-plugins` for Claude Code plugins
131
+ - Onboarding steps left to the caller: `setup-plugins` for Claude Code plugins
132
+
133
+ The chain stops at the project edge. `repo-metadata` and `git-commit` also ship, reaching a remote and the project's history respectively, and neither runs here.
@@ -59,8 +59,9 @@ The chain is:
59
59
  - `canon tooling sync <stack> --write` adds stack-specific deps, scripts, and gitignore entries
60
60
  - The agent reads `canon tooling reference <stack>` (plus parents) as its audit context, follows it to generate eslint, vitest, playwright configs and the stack's setup script, and extends `.claude/context/ci.md` and `.claude/context/development.md` per the reference's extend sections <!-- audit-ignore-citations: .claude/context/development.md -->
61
61
  - `setup-verify` runs the installed `package.json` scripts (lint, typecheck, check, test, build) and reports pass or fail
62
+ - `setup-indexes` bootstraps the `index.md` system over the project's own documentation folders, confirming candidate folders with the operator rather than running unattended
62
63
 
63
- The chain serves a fresh scaffold and names a destination for the three states it does not. An existing project goes to `canon:canon-operator`, which reads what the project already carries before naming a per-domain command. An install wanting the Claude layer without the tooling chain runs `canon claude init` for the seed docs and then `canon:setup-indexes` for the index system. A language the toolkit ships no stack for is the one of the three the chain still runs for, on `base`, with the fallback marked in the preview so it can be declined there.
64
+ The chain stops at the project edge. `repo-metadata` and `git-commit` also ship, reaching a remote and the project's history respectively, and neither runs as part of it. The chain serves a fresh scaffold and names a destination for the three states it does not. An existing project goes to `canon:canon-operator`, which reads what the project already carries before naming a per-domain command. An install wanting the Claude layer without the tooling chain runs `canon claude init` for the seed docs and then `canon:setup-indexes` for the index system. A language the toolkit ships no stack for is the one of the three the chain still runs for, on `base`, with the fallback marked in the preview so it can be declined there.
64
65
 
65
66
  Keep the `## Scripts` table in `.claude/context/development.md` current as scripts are added. Base tooling seeds that entry with the commands it installs, and each stack reference extends the table. `project-commands` reads it to start the app or run a check on request, so a command missing from the table cannot be run that way. A project whose entry outgrew one file and split into `.claude/context/development/` keeps the table in `overview.md`, which is where the skill looks next. <!-- audit-ignore-citations: .claude/context/development.md -->
66
67
 
@@ -14,6 +14,7 @@ description: Handle judgment calls, scope discipline, and file-editing mechanics
14
14
  - Put a call the user's preference decides through the structured question surface, such as `AskUserQuestion` in Claude Code, and fall back to a numbered list in one message where none exists. Rank the recommendation first and mark it, order the rest behind it, and give each option its cost, since an option with no stated cost is picked blind.
15
15
  - Author the real choices only. A structured surface appends its own trailing escapes for a free-text answer and for reopening the question as conversation, so never write either as an option. On the numbered-list fallback, say that answering outside the list is fine.
16
16
  - Answer from the artifact when one already put the question in writing with a suggestion, rather than re-asking it. A blank `- Answer:` slot in a plan accepts the `- Suggested:` line above it, per the plan standard, which your toolkit resolves by name.
17
+ - Rewrite the `- Suggested:` line rather than the blank `- Answer:` slot when execution decides against an unanswered plan question's suggestion, per the plan standard, which your toolkit resolves by name.
17
18
 
18
19
  ## Scope discipline
19
20
 
@@ -8,3 +8,4 @@ description: Write temporary files to the scratch folder structure
8
8
 
9
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
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, but the ignore file is the source: `.gitignore` and its manifest counterpart are what keep the fallback spelling out of `git status`, and the hook and this rule both defer to it. `canon migrate records` is what moves a project from this second spelling to the first. <!-- canon-keep-record-root -->
11
+ - "The project root" above means the current worktree by default. `085-worktrees.md` states the same mixed default for `.canon/tmp/` and defers to each skill's own body for which write resolves at the main root instead.
@@ -11,6 +11,7 @@ description: Route tracked-file writes and shared session scratch correctly from
11
11
  ## Shared session scratch
12
12
 
13
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
+ - `.canon/tmp/` is mixed rather than main-root by default: it stays worktree-local per `055-scratch.md`, and a skill that resolves a write there at the main root instead states so in its own body.
14
15
  - From a linked worktree, every `Edit` or `Write` to a tracked file (source, docs) must use a path starting with `pwd`.
15
16
  - 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
17
  - `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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/canon",
3
3
  "type": "module",
4
- "version": "4.57.0",
4
+ "version": "4.59.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -134,14 +134,24 @@ export function backlogPath(root: string): string {
134
134
  return join(tasksDir(root), BACKLOG_FILE)
135
135
  }
136
136
 
137
+ /**
138
+ * Every link target in a cell, in order. `linkTarget` reads only the first,
139
+ * which is what the single-link `Task` and `Plan` columns need; `citedStem`
140
+ * walks the rest to find a blocker cell's bare sibling pointer.
141
+ */
142
+ function linkTargets(cell: string): string[] {
143
+ return [...cell.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)].map((match) =>
144
+ match[1].trim(),
145
+ )
146
+ }
147
+
137
148
  /**
138
149
  * Pulls the target out of a markdown link, which is how both the `Task` and the
139
150
  * `Plan` column spell their pointer. A cell carrying prose instead of a link
140
151
  * yields nothing, and that absence is the finding rather than a parse failure.
141
152
  */
142
153
  function linkTarget(cell: string): string | undefined {
143
- const match = /\[[^\]]*\]\(([^)]+)\)/.exec(cell)
144
- return match ? match[1].trim() : undefined
154
+ return linkTargets(cell)[0]
145
155
  }
146
156
 
147
157
  function linkText(cell: string): string {
@@ -874,11 +884,18 @@ function checkFolderClaims(
874
884
  * directory names something else and yields nothing. A row waiting on a plan
875
885
  * links that plan, and reading its stem as a task would report the row settled
876
886
  * against a folder the plan does not sit in.
887
+ *
888
+ * A cell can carry more than one link, such as a `Waiting on` cell naming the
889
+ * record answering the blocker before naming the sibling task it waits on, so
890
+ * this walks every target rather than reading only the first. It stops at the
891
+ * first bare one rather than trying a later link when that one fails to
892
+ * resolve to a stem.
877
893
  */
878
894
  function citedStem(cell: string): string | undefined {
879
- const target = linkTarget(cell)?.split('#')[0]
880
- if (!target || target.includes('/')) return undefined
881
- return stemOf(target)
895
+ const target = linkTargets(cell)
896
+ .map((t) => t.split('#')[0])
897
+ .find((t) => t && !t.includes('/'))
898
+ return target ? stemOf(target) : undefined
882
899
  }
883
900
 
884
901
  /** What one blocker citation produced, since a row can be neither settled nor open. */
@@ -35,4 +35,5 @@ Reference docs for consistent authoring across the toolkit and target projects.
35
35
  - [Tasks reference](tasks.md): Folder layout, filename convention, readiness groups, and content rules for .canon/tasks/
36
36
  - [Teach reference](teach.md): Workspace layout, ordinal naming, frontmatter, and the mission and learning-record formats for a learning workspace
37
37
  - [Versioning reference](versioning.md): Phase label vs semver discipline across tasks, PRs, reviews, issues, commits, and tags
38
+ - [Wiki reference](wiki.md): Folder split, frontmatter, naming convention, and sourcing for a wiki reference page
38
39
  - [Wireframe reference](wireframes.md): Shape and content rules for .claude/wireframes/<surface>.md files
@@ -210,6 +210,7 @@ Without this skill, a session <observed failure>, <observed failure>.
210
210
  ### Output and tuning
211
211
 
212
212
  - Skill success lines emit the full relative path from the project root (`<dir>/<file>`) for any file written, updated, or deleted. A bare filename names a file the reader cannot open. The `## Output` section of the project's instruction file sets the form that path takes, so a skill body states which path is emitted and leaves the form to that section.
213
+ - Before a skill writes anything, decide whether the output is a deliverable the project keeps or a toolkit session record. A deliverable lands among the project's own tracked files. A session record lands under `.canon/`, in the named subfolder for its kind (`tasks/`, `plans/`, `review/`, `memory/`, `groundwork/`, `intake/`, `proposals/`, `diagrams/`, or `teach/`, with `tmp/` for scratch nothing else claims), never in a folder the body invents. `.claude/ARCHITECTURE.md`'s per-folder decisions are the precedent for which kind takes which folder.
213
214
  - Codify a skill's posted or generated output as a fenced template, and keep the body consistent with every capability the frontmatter description names.
214
215
  - When a skill gathers user input or pre-seeds a template, attach a concrete proposed default to every question, derived from project context. Accept "use defaults" as a bulk-confirm.
215
216
  - Separate correctness axes (routing, sourcing, escalation, decline) from shape axes (line count, formatting, variant sprawl) when tuning a skill. Tighten only on correctness regressions. Do not convert soft caps to hard caps for aesthetic drift when correctness passes.
@@ -0,0 +1,65 @@
1
+ ---
2
+ title: Wiki reference
3
+ description: Folder split, frontmatter, naming convention, and sourcing for a wiki reference page
4
+ ---
5
+
6
+ # Wiki reference
7
+
8
+ Applies to each authored page under `wiki/`. Skip for `index.md` at any depth, which is a hand-maintained catalog rather than a reference page, and carries `subtitle` rather than `description`.
9
+
10
+ ## Scope
11
+
12
+ Governs each authored page under `wiki/`: which folder it belongs in, its frontmatter, its filename, and how it cites the subject it documents.
13
+
14
+ Does not govern:
15
+
16
+ - Voice, rhythm, sentence construction, and information density: the `write-human` skill
17
+ - Headings, punctuation, word choice, and file references: `markdown.md`
18
+
19
+ ## What a working wiki page looks like
20
+
21
+ A page works when a reader who has never opened it settles two things without asking anyone:
22
+
23
+ - Which folder holds it, decided from the subject alone rather than from where it happened to get written
24
+ - Where the content came from, so a claim can be checked against its owner rather than against this repository
25
+
26
+ A page failing either is non-conforming even when it satisfies every shape rule below.
27
+
28
+ ## Placement
29
+
30
+ - Write a page here only when its subject is owned outside this repository. Route anything about how this repository works to `docs/`, `.claude/context/`, or a skill body instead.
31
+ - File the page by owner: `wiki/claude/` for Anthropic, `wiki/tools/` for another vendor, `wiki/concepts/` where no single vendor owns the subject.
32
+ - Treat `wiki/concepts/rule-writing-vocabulary.md` as the one recorded exception to the owner test, stated in the page itself.
33
+
34
+ ## Frontmatter
35
+
36
+ - `title` (required): sentence case, naming the subject
37
+ - `description` (required): one line naming what the page covers
38
+
39
+ ## Naming
40
+
41
+ - Name a `wiki/claude/` page with a `claude-` prefix before the kebab subject.
42
+ - Name a `wiki/tools/` or `wiki/concepts/` page as a bare kebab subject, with no prefix.
43
+
44
+ ## Sourcing
45
+
46
+ - Close the intro paragraph with a `Source:` sentence naming the owner. Link the canonical page where one exists, and name the owner alone where the subject has no single URL.
47
+ - Fetch current information through the `claude-code-guide` agent when the subject is Claude Code. Do not work from training knowledge.
48
+ - Propose an addition or correction and wait for confirmation. Do not write to a wiki file unasked.
49
+
50
+ ## Template
51
+
52
+ ```markdown
53
+ ---
54
+ title: <Subject>
55
+ description: <one line naming what this page covers>
56
+ ---
57
+
58
+ # <Subject>
59
+
60
+ <What the subject is and why it matters.> Source: <owner, with a link to the canonical page where one exists>.
61
+
62
+ ## <Section>
63
+
64
+ <Reference content.>
65
+ ```