@erclx/canon 4.57.0 → 4.58.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/claude-autoship/SKILL.md +1 -1
- package/claude/skills/claude-groundwork/SKILL.md +1 -0
- package/claude/skills/claude-worker/SKILL.md +1 -0
- package/claude/skills/claude-worktree/SKILL.md +8 -6
- package/claude/skills/draft-and-pick/SKILL.md +8 -8
- package/governance/rules/core/005-behavior.md +1 -0
- package/package.json +1 -1
- package/src/tasks/validate.ts +22 -5
- package/standards/skill.md +1 -0
|
@@ -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.
|
|
@@ -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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
70
|
-
4. Report
|
|
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
|
|
@@ -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
|
|
package/package.json
CHANGED
package/src/tasks/validate.ts
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
880
|
-
|
|
881
|
-
|
|
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. */
|
package/standards/skill.md
CHANGED
|
@@ -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.
|