@erclx/canon 5.68.0 → 5.70.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/CHANGELOG.md +36 -0
- package/README.md +33 -159
- package/assets/brand/mark.svg +17 -9
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/app-drive/REQUIREMENT.md +5 -5
- package/claude/skills/app-drive/SKILL.md +17 -6
- package/claude/skills/app-drive/references/drive-record.md +61 -8
- package/claude/skills/app-drive/references/isolate.md +1 -1
- package/claude/skills/canon-cli/SKILL.md +2 -2
- package/claude/skills/canon-feedback/REQUIREMENT.md +1 -1
- package/claude/skills/canon-feedback/SKILL.md +1 -1
- package/claude/skills/design-identity/REQUIREMENT.md +4 -4
- package/claude/skills/design-identity/SKILL.md +7 -7
- package/claude/skills/{pick-by-eye → design-pick}/REQUIREMENT.md +6 -6
- package/claude/skills/{pick-by-eye → design-pick}/SKILL.md +19 -10
- package/claude/skills/{pick-by-eye → design-pick}/references/board.md +17 -11
- package/claude/skills/design-pick/references/design-config.md +33 -0
- package/claude/skills/design-record/REQUIREMENT.md +2 -2
- package/claude/skills/design-record/SKILL.md +1 -1
- package/claude/skills/design-taste/REQUIREMENT.md +2 -2
- package/claude/skills/design-taste/SKILL.md +3 -3
- package/claude/skills/design-taste/references/preflight.md +1 -1
- package/claude/skills/git-issue/REQUIREMENT.md +1 -1
- package/claude/skills/git-issue/references/issue.md +1 -1
- package/claude/skills/plan-feature/SKILL.md +1 -1
- package/claude/skills/plan-feature/references/plan.md +21 -6
- package/claude/skills/plan-groundwork/SKILL.md +1 -1
- package/claude/skills/pr-evidence/references/evidence.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +3 -3
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-planning-wait.md +24 -0
- package/claude/skills/role-planner/SKILL.md +4 -2
- package/claude/skills/role-worker/SKILL.md +5 -6
- package/claude/skills/task-board/references/tasks.md +1 -1
- package/claude/skills/task-complete/references/receipt-sweep.md +1 -1
- package/claude/skills/{ux-audit → ui-audit}/REQUIREMENT.md +3 -3
- package/claude/skills/{ux-audit → ui-audit}/SKILL.md +7 -7
- package/claude/skills/ui-checklist/REQUIREMENT.md +1 -1
- package/claude/skills/ui-craft/SKILL.md +1 -1
- package/claude/skills/ui-craft/references/copy.md +4 -1
- package/claude/skills/{ui-record → ui-film}/REQUIREMENT.md +6 -4
- package/claude/skills/{ui-record → ui-film}/SKILL.md +26 -13
- package/claude/skills/ui-film/references/compose-craft.md +81 -0
- package/claude/skills/{ui-record → ui-film}/references/config.md +9 -4
- package/claude/skills/{ui-record → ui-film}/references/handoff.md +17 -7
- package/claude/skills/{ui-record → ui-film}/references/pr-clip.md +5 -5
- package/claude/skills/{ui-record → ui-film}/references/take-craft.md +28 -2
- package/claude/skills/{ux-measure → ui-measure}/REQUIREMENT.md +3 -3
- package/claude/skills/{ux-measure → ui-measure}/SKILL.md +6 -6
- package/docs/agents/audits.md +1 -1
- package/docs/agents/migrate.md +13 -13
- package/docs/index.md +1 -0
- package/docs/migrations/by-hand.md +107 -0
- package/docs/migrations/by-verb.md +118 -0
- package/docs/migrations/index.md +14 -0
- package/docs/target/index.md +1 -2
- package/docs/target/projects.md +3 -3
- package/docs/target/sync.md +23 -3
- package/docs/workflow/ai-workflow.md +1 -1
- package/docs/workflow/skill-map-on-request.md +1 -2
- package/docs/workflow/skill-map.md +9 -10
- package/docs/workflow/visual-design-workflow.md +6 -1
- package/governance/ledger/claude/background.md +26 -0
- package/governance/ledger/skills/ui-craft/copy.md +8 -4
- package/governance/rules/claude/background.md +19 -0
- package/governance/stacks/base.toml +1 -0
- package/package.json +6 -4
- package/scripts/ci/install-playwright-deps.sh +52 -0
- package/scripts/core/design-sync.ts +204 -0
- package/scripts/core/regen-web-favicon.ts +27 -12
- package/scripts/core/regen-web-previews.ts +8 -1
- package/scripts/core/render-film.ts +179 -0
- package/src/capture/frame-values.ts +12 -12
- package/src/claude/cases/authoring.ts +2 -2
- package/src/claude/cases/misc.ts +1 -1
- package/src/claude/cases/setup.ts +0 -5
- package/src/claude/cases/workflow.ts +6 -6
- package/src/commands/fixtures/gh.sh +64 -0
- package/src/commands/gov.ts +33 -3
- package/src/commands/migrate.ts +12 -2
- package/src/commands/records.ts +3 -2
- package/src/commands/sandbox.ts +9 -2
- package/src/commands/take.ts +159 -11
- package/src/commands/tasks.ts +183 -0
- package/src/design/favicon.ts +85 -13
- package/src/design/reference.ts +98 -0
- package/src/design/render.ts +16 -14
- package/src/design/tokens.ts +4 -3
- package/src/gate/stages.ts +8 -2
- package/src/gov/audit.ts +48 -11
- package/src/gov/detect.ts +1 -1
- package/src/init/steps.ts +2 -2
- package/src/markdown/scope.ts +9 -14
- package/src/migrate/record-layout.ts +5 -3
- package/src/migrate/skill-names.ts +21 -4
- package/src/migrate/status.ts +3 -3
- package/src/records/validate.ts +15 -2
- package/src/sandbox/expect.ts +32 -0
- package/src/sandbox/headless/dispatch-shim.sh +15 -0
- package/src/sandbox/headless/dispatch.ts +12 -14
- package/src/sandbox/headless/run.ts +6 -3
- package/src/sandbox/stub-gh.sh +26 -0
- package/src/sandbox/stub-remote.ts +8 -29
- package/src/sync/engine.ts +1 -1
- package/src/take/camera.ts +100 -11
- package/src/take/film.ts +55 -4
- package/src/take/fixtures/input.html +138 -0
- package/src/take/keys.ts +193 -0
- package/src/take/log.ts +94 -1
- package/src/take/perform.ts +165 -10
- package/src/take/plan.ts +101 -6
- package/src/take/pointer.ts +2 -1
- package/src/take/screencast.ts +28 -3
- package/src/take/stepper.ts +141 -23
- package/src/take/zoom/NOTICE.md +1 -1
- package/src/tasks/launch-wait.ts +160 -0
- package/tooling/claude/reference.md +1 -1
- package/claude/skills/canon-feedback-triage/REQUIREMENT.md +0 -34
- package/claude/skills/canon-feedback-triage/SKILL.md +0 -98
- package/claude/skills/plan-ready/REQUIREMENT.md +0 -35
- package/claude/skills/plan-ready/SKILL.md +0 -136
- package/claude/skills/plan-ready/references/assembly.md +0 -75
- package/claude/skills/plan-ready/references/ready.md +0 -128
- package/docs/target/migrations.md +0 -203
- /package/claude/skills/{pick-by-eye → design-pick}/references/live-arms.md +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import { join, relative } from 'node:path'
|
|
4
|
+
import {
|
|
5
|
+
type AnswerRefusal,
|
|
6
|
+
type OpenQuestion,
|
|
7
|
+
planAnswers,
|
|
8
|
+
} from '@/tasks/answers'
|
|
9
|
+
import {
|
|
10
|
+
describeUnmatchedStem,
|
|
11
|
+
listTaskStems,
|
|
12
|
+
readPlanTargets,
|
|
13
|
+
tasksDir,
|
|
14
|
+
} from '@/tasks/archive'
|
|
15
|
+
|
|
16
|
+
export const DEFAULT_LAUNCH_WAIT_SECONDS = 1800
|
|
17
|
+
export const LAUNCH_POLL_MS = 30_000
|
|
18
|
+
|
|
19
|
+
export type LaunchRefusal =
|
|
20
|
+
| AnswerRefusal
|
|
21
|
+
| 'no-board'
|
|
22
|
+
| 'no-match'
|
|
23
|
+
| 'ambiguous'
|
|
24
|
+
| 'several-plans'
|
|
25
|
+
|
|
26
|
+
export interface LaunchRefused {
|
|
27
|
+
readonly ok: false
|
|
28
|
+
readonly reason: LaunchRefusal
|
|
29
|
+
readonly message: string
|
|
30
|
+
readonly detail: readonly string[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* One reading of a row. `reason` is `unlinked` while the task carries no
|
|
35
|
+
* `Plan:` line, `open` while its plan holds a question for the operator, and
|
|
36
|
+
* `launchable` once it holds none. `plan` and `open` keep the names
|
|
37
|
+
* `plan-answers` gives them, so the dispatch reads one concept by one name.
|
|
38
|
+
*/
|
|
39
|
+
export interface LaunchReading {
|
|
40
|
+
readonly ok: true
|
|
41
|
+
readonly task: string
|
|
42
|
+
readonly plan?: string
|
|
43
|
+
readonly reason: 'unlinked' | 'open' | 'launchable'
|
|
44
|
+
readonly launchable: boolean
|
|
45
|
+
readonly open: readonly OpenQuestion[]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type LaunchOutcome = LaunchReading | LaunchRefused
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The record a wait ends on. A reading ends `launchable` or, at the deadline,
|
|
52
|
+
* `timeout` carrying the last reading, so the caller still sees which question
|
|
53
|
+
* holds the row. A refusal ends the wait at once, since nothing a planner
|
|
54
|
+
* writes next turns an archived plan or an unknown task launchable.
|
|
55
|
+
*/
|
|
56
|
+
export type LaunchWait =
|
|
57
|
+
| (Omit<LaunchReading, 'reason'> & {
|
|
58
|
+
readonly reason: 'launchable' | 'timeout'
|
|
59
|
+
readonly waitedSeconds: number
|
|
60
|
+
})
|
|
61
|
+
| (LaunchRefused & { readonly waitedSeconds: number })
|
|
62
|
+
|
|
63
|
+
export interface LaunchClock {
|
|
64
|
+
now(): number
|
|
65
|
+
sleep(ms: number): Promise<void>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface LaunchWaitOptions {
|
|
69
|
+
readonly waitSeconds: number
|
|
70
|
+
readonly clock: LaunchClock
|
|
71
|
+
readonly pollMs?: number
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Reads a row by its task stem: resolves the task's `Plan:` line, then runs the
|
|
76
|
+
* `plan-answers` gate on the plan it names. Keying on the line rather than on
|
|
77
|
+
* the plan file is what keeps a plan still being written from reading as
|
|
78
|
+
* landed, since a planner links it only after it validates.
|
|
79
|
+
*
|
|
80
|
+
* It reports and never writes, and answers planning alone. The branch and
|
|
81
|
+
* claim checks stay with the dispatch runbook, which owns them.
|
|
82
|
+
*/
|
|
83
|
+
export async function readLaunchable(
|
|
84
|
+
root: string,
|
|
85
|
+
task: string,
|
|
86
|
+
): Promise<LaunchOutcome> {
|
|
87
|
+
const dir = tasksDir(root)
|
|
88
|
+
if (!existsSync(dir)) {
|
|
89
|
+
return refuse('no-board', `No task board at ${relative(root, dir)}.`)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const stems = await listTaskStems(dir)
|
|
93
|
+
if (!stems.includes(task)) {
|
|
94
|
+
const { reason, message, detail } = describeUnmatchedStem(stems, task)
|
|
95
|
+
return refuse(reason, message, detail)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const targets = readPlanTargets(
|
|
99
|
+
await readFile(join(dir, `${task}.md`), 'utf8'),
|
|
100
|
+
)
|
|
101
|
+
const [target] = targets
|
|
102
|
+
if (target === undefined) {
|
|
103
|
+
return { ok: true, task, reason: 'unlinked', launchable: false, open: [] }
|
|
104
|
+
}
|
|
105
|
+
if (targets.length > 1) {
|
|
106
|
+
return refuse(
|
|
107
|
+
'several-plans',
|
|
108
|
+
`${task} links ${targets.length} plans, so no single plan is the one to dispatch.`,
|
|
109
|
+
targets,
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const answers = await planAnswers(root, target)
|
|
114
|
+
if (!answers.ok) return answers
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
ok: true,
|
|
118
|
+
task,
|
|
119
|
+
plan: answers.plan,
|
|
120
|
+
reason: answers.launchable ? 'launchable' : 'open',
|
|
121
|
+
launchable: answers.launchable,
|
|
122
|
+
open: answers.open,
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Re-reads the row every `pollMs` until it is launchable, a read refuses, or
|
|
128
|
+
* the next reading would land past the deadline.
|
|
129
|
+
*/
|
|
130
|
+
export async function waitForLaunchable(
|
|
131
|
+
root: string,
|
|
132
|
+
task: string,
|
|
133
|
+
opts: LaunchWaitOptions,
|
|
134
|
+
): Promise<LaunchWait> {
|
|
135
|
+
const { clock } = opts
|
|
136
|
+
const pollMs = opts.pollMs ?? LAUNCH_POLL_MS
|
|
137
|
+
const started = clock.now()
|
|
138
|
+
const deadline = started + opts.waitSeconds * 1000
|
|
139
|
+
const waited = () => Math.round((clock.now() - started) / 1000)
|
|
140
|
+
|
|
141
|
+
for (;;) {
|
|
142
|
+
const reading = await readLaunchable(root, task)
|
|
143
|
+
if (!reading.ok) return { ...reading, waitedSeconds: waited() }
|
|
144
|
+
if (reading.launchable) {
|
|
145
|
+
return { ...reading, reason: 'launchable', waitedSeconds: waited() }
|
|
146
|
+
}
|
|
147
|
+
if (clock.now() + pollMs > deadline) {
|
|
148
|
+
return { ...reading, reason: 'timeout', waitedSeconds: waited() }
|
|
149
|
+
}
|
|
150
|
+
await clock.sleep(pollMs)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function refuse(
|
|
155
|
+
reason: LaunchRefusal,
|
|
156
|
+
message: string,
|
|
157
|
+
detail: readonly string[] = [],
|
|
158
|
+
): LaunchRefused {
|
|
159
|
+
return { ok: false, reason, message, detail }
|
|
160
|
+
}
|
|
@@ -16,7 +16,7 @@ The claude stack installs the Claude Code workflow into a project through `canon
|
|
|
16
16
|
|
|
17
17
|
- `.canon/tasks/` holds one file per task plus a generated `index.md`, local scratch only, with `archive/` holding the tasks that shipped
|
|
18
18
|
- `.canon/plans/` holds execution detail for multi-step tasks, and `canon tasks archive` moves a `feature-*.md` plan into `archive/` when the `post-merge` hook archives its task
|
|
19
|
-
- `.canon/review/` holds the reports a review or measurement skill writes, such as `
|
|
19
|
+
- `.canon/review/` holds the reports a review or measurement skill writes, such as `ui-measure-<slug>.md`
|
|
20
20
|
- `.canon/tmp/` is ephemeral scratch, safe to delete without loss
|
|
21
21
|
- `.claude/worktrees/` is where `EnterWorktree` creates a linked worktree, which is why it stays under `.claude/`
|
|
22
22
|
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: canon-feedback-triage
|
|
3
|
-
description: Why the feedback queue is read from GitHub rather than local scratch, and why triage files measured items instead of routing or implementing
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Canon feedback triage requirement
|
|
7
|
-
|
|
8
|
-
## Gap
|
|
9
|
-
|
|
10
|
-
Without this skill, the feedback queue fills and nothing drains it. A local folder looks like the place to read and is the wrong one, since that scratch is per-machine and any cleanup removes it.
|
|
11
|
-
|
|
12
|
-
Triage then fails three ways. An issue routes to a fix from a paragraph, which skips the scope argument. A self-contradicting report gets a guess instead of a question. The same issue gets filed twice because two passes cannot see the board carries it.
|
|
13
|
-
|
|
14
|
-
## Must
|
|
15
|
-
|
|
16
|
-
- Read the durable queue through the `gh` CLI rather than local session scratch, and stop when it is absent or unauthenticated
|
|
17
|
-
- File one measured item per open issue into an intake folder, headed so an answer finds its issue, and name each skipped issue with its carrier
|
|
18
|
-
- Measure each claim against the tree rather than carrying the issue's figures forward
|
|
19
|
-
- Claim the folder through the CLI rather than picking an ordinal by hand
|
|
20
|
-
- Report an empty queue as nothing open rather than widening the label to find work
|
|
21
|
-
|
|
22
|
-
## Must not
|
|
23
|
-
|
|
24
|
-
- Post to the remote, whether a comment, a close, or a label
|
|
25
|
-
- Implement a fix or write a plan
|
|
26
|
-
- Fill an answer slot, or read an empty one as agreement
|
|
27
|
-
- Write outside the folder it claimed
|
|
28
|
-
|
|
29
|
-
## Out of scope
|
|
30
|
-
|
|
31
|
-
- Filing new feedback, which `canon-feedback` owns
|
|
32
|
-
- Answering the filed items, which `plan-intake-answer` owns
|
|
33
|
-
- Writing the plan a plan-worthy item needs, which `plan-feature` owns
|
|
34
|
-
- Issues carrying any other label, which no skill here owns
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: canon-feedback-triage
|
|
3
|
-
description: Files every open GitHub issue labeled `feedback` on the toolkit repo into an intake folder as one measured item each, skipping any issue the board already carries. Use when asked to "triage toolkit feedback", "work through the feedback issues", "process feedback issues", or "what feedback is open". Do NOT use to file new feedback (that is `canon-feedback`), to answer the filed items (that is `plan-intake-answer`), or for general GitHub issue triage unrelated to toolkit feedback.
|
|
4
|
-
metadata:
|
|
5
|
-
family: upkeep
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Canon feedback triage
|
|
9
|
-
|
|
10
|
-
Consume the feedback queue that `canon feedback` fills. Each open `feedback` issue is measured against the tree and filed as one item for the operator, who answers it before anything is built.
|
|
11
|
-
|
|
12
|
-
The record is an ordinary intake folder at `.canon/intake/<nn>-feedback-triage/`. Read `${CLAUDE_SKILL_DIR}/../plan-intake/references/intake.md` before writing any file in it, since it holds the item format, the frontmatter, the index shape, and the answer contract this skill is bound by.
|
|
13
|
-
|
|
14
|
-
- Run from the toolkit repo root
|
|
15
|
-
- Read GitHub issues and write only inside the claimed folder, posting no comment, close, or label to GitHub
|
|
16
|
-
|
|
17
|
-
## Guards
|
|
18
|
-
|
|
19
|
-
- Resolve `.canon/tasks/` and `.canon/intake/` at the main worktree root, from `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd` outside git
|
|
20
|
-
- If `gh` is not on PATH, stop: `❌ gh CLI not found. Install it to read feedback issues.`
|
|
21
|
-
- If `gh auth status` fails, stop: `❌ gh is not authenticated. Run gh auth login.`
|
|
22
|
-
- If no open `feedback` issues exist, stop: `✅ No open feedback issues.`
|
|
23
|
-
|
|
24
|
-
### Slots and records
|
|
25
|
-
|
|
26
|
-
Two more guards hold through every step.
|
|
27
|
-
|
|
28
|
-
- Never fill a `You:` slot, and never read an empty one as agreement. Answering runs through `plan-intake-answer` or the operator's own edit.
|
|
29
|
-
- Branch on each `canon` record's `ok` and `reason` rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
|
|
30
|
-
|
|
31
|
-
## Step 1: list the queue
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
gh issue list --label feedback --state open --limit 1000 --json number,title,body,labels
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Take every issue. An issue without the `feedback` label does not surface here by design.
|
|
38
|
-
|
|
39
|
-
## Step 2: drop what the board already carries
|
|
40
|
-
|
|
41
|
-
An issue is carried when its number appears in a live or archived task's `Issue:` line under `.canon/tasks/`, or in an item heading of an existing `.canon/intake/` folder, written `(#NNN)`. Search both at the main root.
|
|
42
|
-
|
|
43
|
-
Set each carried issue aside with its carrier, a task path or a folder and item. Name every one in the overview, so a re-run while an earlier folder sits unanswered files nothing twice. When every issue is carried, stop: `✅ Every open feedback issue is already on the board.`
|
|
44
|
-
|
|
45
|
-
## Step 3: claim the folder
|
|
46
|
-
|
|
47
|
-
Run `canon records ordinal intake feedback-triage --claim --json` and take its `name`. The verb creates the folder in the same act, so two sessions opening at once never share one. Where the installed binary carries no such subcommand, report it and stop rather than picking an ordinal by hand.
|
|
48
|
-
|
|
49
|
-
## Step 4: measure each issue
|
|
50
|
-
|
|
51
|
-
For each remaining issue, measure its claim against the tree during this pass, never carrying a figure from the issue forward, since it states what was true when filed.
|
|
52
|
-
|
|
53
|
-
- Grep for the construct its defect names and count the sites
|
|
54
|
-
- Check whether the behavior it asks for exists
|
|
55
|
-
- Search the log for a commit or pull request that shipped it under another task
|
|
56
|
-
- Run the issue's Repro section in a form that writes nothing when it is one command that works here, and say on `Problem:` what it showed
|
|
57
|
-
- Mark the item `not reproduced here` when the repro names another project's paths or needs more, per `## Repro versus groundwork` in the intake standard
|
|
58
|
-
|
|
59
|
-
Classify each against the toolkit's own surfaces:
|
|
60
|
-
|
|
61
|
-
- **Direct fix.** A size, not an answer, so it suggests `plan` and `Fix:` says a short plan covers it. One surface, one file, no architectural choice. A typo, a stale reference, a one-line correction.
|
|
62
|
-
- **Plan-worthy.** Multiple files, a new skill or rule, a behavior change, or a cross-surface move.
|
|
63
|
-
- **Needs clarification.** The observed and expected behavior conflict or the surface is unnamed. The item asks the operator for the missing detail.
|
|
64
|
-
|
|
65
|
-
Strip the reporting project's specifics from the fix, being its filenames, frameworks, deploy targets, and label values. The issue describes one project, and the fix serves every project that installs the toolkit.
|
|
66
|
-
|
|
67
|
-
Name the commit the pass measured against in the overview.
|
|
68
|
-
|
|
69
|
-
## Step 5: write the items
|
|
70
|
-
|
|
71
|
-
Write one item per issue in the intake item format, one cluster file per domain the fix touches, numbered in read order. Head each item `### N. <defect> (#NNN)` so an answer finds its issue.
|
|
72
|
-
|
|
73
|
-
- `Problem:` states what this pass measured, with the count, path, or commit behind it
|
|
74
|
-
- `Fix:` states the class from Step 4 and the change it names
|
|
75
|
-
- `Open:` reads `plan, ask, or decline?` on every item
|
|
76
|
-
- `Suggested:` opens with exactly one of those tokens, then the reason
|
|
77
|
-
- `You:` ships empty
|
|
78
|
-
|
|
79
|
-
Write `00-overview.md` last, per the intake standard, with the counts by token, an open-questions list linking each item, the commit measured against, and every issue skipped in Step 2 with its carrier. Where the pass runs out of context before the index, write `99-next-session.md` naming the last issue filed.
|
|
80
|
-
|
|
81
|
-
## Output
|
|
82
|
-
|
|
83
|
-
```plaintext
|
|
84
|
-
📂 Opened .canon/intake/<nn>-feedback-triage/
|
|
85
|
-
|
|
86
|
-
**Filed:** <N> issues across <N> clusters, measured against <sha>
|
|
87
|
-
**Skipped:** <n> already carried
|
|
88
|
-
**Suggested:** <n> plan, <n> ask, <n> decline
|
|
89
|
-
|
|
90
|
-
Next: /canon:plan-intake-answer to answer the `You:` slots
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
An answered plan item promotes through `task-board` with an `Issue: #NNN` line, and the shipping pull request's `Closes #NNN` drains the queue. Closing a declined issue on GitHub stays the operator's act.
|
|
94
|
-
|
|
95
|
-
## Notes
|
|
96
|
-
|
|
97
|
-
- The `feedback` label is what `canon feedback` and the `toolkit-feedback.yml` issue form both apply.
|
|
98
|
-
- This skill files and never reimplements. `plan-intake-answer` owns the answers and `task-board` owns the task a plan item becomes.
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: plan-ready
|
|
3
|
-
description: Why writing a ready folder, its overview, the thin plan, and the task row is one invocation rather than four hand writes, and where the boundary against the planning and shipping skills falls
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Plan ready requirement
|
|
7
|
-
|
|
8
|
-
## Gap
|
|
9
|
-
|
|
10
|
-
A session handing finished files to a worker writes the folder, the overview, the plan, and the task row by hand, and the four drift. A destination missing from the plan's `## Files to touch` is invisible to `plan-reach`, so a second track writes the same file.
|
|
11
|
-
|
|
12
|
-
Numbering scans one place, so an ordinal can reuse a number an archive already spent, and a bare phase label reserves nothing. A draft beside the real files, or no verbatim-source line, sends the worker off to write its own version. A main-root write from a worktree lands in a copy nobody reads, and the archive move goes unreported.
|
|
13
|
-
|
|
14
|
-
## Must
|
|
15
|
-
|
|
16
|
-
- Refuse before writing when the operator named no finished file or a named file does not resolve
|
|
17
|
-
- Read the ordinal from the live folder and the archive together, and take the label from `canon tasks next-label --claim`, stopping on a binary that predates the flag
|
|
18
|
-
- Derive the overview's `destinations` and the plan's `## Files to touch` from one list and compare them before reporting
|
|
19
|
-
- Copy each finished file whole into the folder with nothing else, and write the plan's `## Constraints` line naming the folder as the verbatim source
|
|
20
|
-
- Place the task's row by the branches `task-board` states, route main-root writes through `Bash`, and name the archive move in the closing report
|
|
21
|
-
|
|
22
|
-
## Must not
|
|
23
|
-
|
|
24
|
-
- Edit a finished file, since the worker copies verbatim
|
|
25
|
-
- Hardcode a branch type or restate the folder shape
|
|
26
|
-
- Write `priority.md` or `backlog.md` where an orchestrator is on the roster or the session is a worker or planner
|
|
27
|
-
- Automate the archive move or start the worker
|
|
28
|
-
|
|
29
|
-
## Out of scope
|
|
30
|
-
|
|
31
|
-
- Writing a plan for a change nobody has written, which `plan-feature` owns
|
|
32
|
-
- Copying the folder into a worktree and shipping it, which `role-worker` owns
|
|
33
|
-
- A task file for a row with no ready folder, which `task-board` owns
|
|
34
|
-
- Shipping a branch, which `git-ship` owns
|
|
35
|
-
- The folder's shape and lifecycle, which `references/ready.md` owns
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: plan-ready
|
|
3
|
-
description: Writes a ready folder for finished files, its `00-overview.md`, the thin plan that points at it, and the task with a board row placed, reported to an orchestrator, or left to the operator, in one invocation. Use when asked to "write a ready folder", "hand these files to a worker", "package these finished files for a worker", "make a ready handoff", or when a session already holds the exact text of a skill, rule, or doc and a worker should copy it rather than author it. Do NOT use to plan a change nobody has written yet, which is `plan-feature`, to copy a folder into a worktree and ship it, which is `role-worker`, or to file a task with no finished files behind it, which is `task-board`.
|
|
4
|
-
metadata:
|
|
5
|
-
family: generate
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Plan ready
|
|
9
|
-
|
|
10
|
-
A warm session that has already written a change hands the exact text to the worker that ships it. This skill writes the three artifacts that handoff needs and stops before anyone is dispatched.
|
|
11
|
-
|
|
12
|
-
Read these in parallel before writing:
|
|
13
|
-
|
|
14
|
-
- `${CLAUDE_SKILL_DIR}/references/ready.md`: the folder layout, the overview frontmatter, the mirrored tree, and the thin-plan contract
|
|
15
|
-
- `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`: the plan's sections and its answer contract
|
|
16
|
-
- `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md`: the task file's shape and origin lines
|
|
17
|
-
- `${CLAUDE_SKILL_DIR}/references/assembly.md`: the overview and thin-plan templates and the destination match check
|
|
18
|
-
|
|
19
|
-
## Guards
|
|
20
|
-
|
|
21
|
-
- If the operator named no finished file, stop: `❌ No finished file named. A ready folder carries text that already exists, so write the files first or use /canon:plan-feature.`
|
|
22
|
-
- If a named file does not resolve, stop: `❌ <path> does not exist. Name a file that is already written.`
|
|
23
|
-
- Resolve `<main-root>` as `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`. The folder, the plan, and the task all live there.
|
|
24
|
-
|
|
25
|
-
## Step 1: read the finished files
|
|
26
|
-
|
|
27
|
-
1. Take each finished file with the destination path the operator gave it. A file arriving with no destination is asked for once, since the mirrored tree cannot place it.
|
|
28
|
-
2. Read every file whole. The overview states what the worker still owns beyond copying, and that comes off the files, not off the operator's description of them.
|
|
29
|
-
3. Take the branch type from `${CLAUDE_SKILL_DIR}/../git-branch/references/branch.md`, never from a list carried here.
|
|
30
|
-
4. Derive one kebab slug for the change. The folder, the plan, and the task all carry it.
|
|
31
|
-
|
|
32
|
-
## Step 2: allocate the ordinal and the label
|
|
33
|
-
|
|
34
|
-
Read `<main-root>/.canon/ready/` and `<main-root>/.canon/ready/archive/` together and take the highest `<nn>` across both, plus one. A folder set with no entry takes `01`. Keep this skill-local. No verb allocates a ready ordinal, and two writers running at once can claim one number, so re-read the folder immediately before Step 3 writes.
|
|
35
|
-
|
|
36
|
-
Claim the label through the verb rather than reading it bare or scanning for it:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
canon tasks next-label --claim --json
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
The claim reserves the label under `.canon/ordinal-locks/`, so a second session allocating at the same moment gets the next one. A bare read reserves nothing and can return a label a live claim already holds.
|
|
43
|
-
|
|
44
|
-
Branch on the record's `reason` rather than on the exit code, taking `label` from a record that carries one.
|
|
45
|
-
|
|
46
|
-
- `label-contended` means re-run the claim
|
|
47
|
-
- Report the verb as unavailable and stop when the binary carries no `next-label` subcommand, or prints no record for an unknown `--claim`, since it predates the flag
|
|
48
|
-
- Never fall back to a bare read or a label picked by hand, which reopens the collision the claim exists to prevent
|
|
49
|
-
|
|
50
|
-
## Step 3: write the overview
|
|
51
|
-
|
|
52
|
-
Write `<main-root>/.canon/ready/<nn>-<slug>/00-overview.md` from the template in `${CLAUDE_SKILL_DIR}/references/assembly.md`. Set `destinations` from the Step 1 list and nothing else, and state in prose what the worker still owns, or `nothing further` where the files are the whole change.
|
|
53
|
-
|
|
54
|
-
## Step 4: mirror the tree
|
|
55
|
-
|
|
56
|
-
Copy each finished file to `<main-root>/.canon/ready/<nn>-<slug>/<destination path>`, one command per file, so the tree mirrors the destinations exactly.
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
install -D -m 0644 <source> <main-root>/.canon/ready/<nn>-<slug>/<destination path>
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
- Copy whole and edit nothing. A change wanted now is made in the source file first and copied after.
|
|
63
|
-
- Carry no notes, alternates, or drafts into the folder. Anything else belongs in the plan or the pull request body.
|
|
64
|
-
- Write each of these at the main root, routed the way `session-worktree` states. Create a whole new file with a heredoc.
|
|
65
|
-
|
|
66
|
-
## Step 5: write the thin plan
|
|
67
|
-
|
|
68
|
-
Write `<main-root>/.canon/plans/feature-<slug>.md` from the template, by heredoc, per `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`.
|
|
69
|
-
|
|
70
|
-
- List every Step 1 destination under `## Files to touch`, each with one clause on why it changes, pointing at the folder's copy rather than restating it.
|
|
71
|
-
- Add a `## Constraints` line naming the ready folder as the verbatim source for those paths.
|
|
72
|
-
- Name the branch type from Step 1 in `## Summary`. Write `None identified.` under `## Questions` unless a call is genuinely the operator's.
|
|
73
|
-
|
|
74
|
-
Run the match check in `${CLAUDE_SKILL_DIR}/references/assembly.md` before going further. The two lists are one list, and a path in either without the other stops the run.
|
|
75
|
-
|
|
76
|
-
## Step 6: write the task
|
|
77
|
-
|
|
78
|
-
Write `<main-root>/.canon/tasks/<label>-<slug>.md` by heredoc from the label Step 2 claimed, never calling the verb again, which would claim another. Its frontmatter, H1, and `## Outcomes` follow `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md`.
|
|
79
|
-
|
|
80
|
-
Write a `Ready:` line beside the `Plan:` line pointing at the folder, then point the task at the plan through the verb:
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
canon tasks plan-link <task-stem> .canon/plans/feature-<slug>.md --json
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Branch on the record rather than on the exit. The `Ready:` line orients a worker reading the task, and `canon tasks archive` reads it to move the folder on ship.
|
|
87
|
-
|
|
88
|
-
### Place the row
|
|
89
|
-
|
|
90
|
-
A task file with no row is a dropped task, so place the row in the same pass. The plan exists and no worker holds it yet, so the row takes `## Up next`, per the tests in `${CLAUDE_SKILL_DIR}/../task-board/references/board.md`.
|
|
91
|
-
|
|
92
|
-
A path `canon tasks plan-reach` reports as claimed is a shared file rather than a hold. Name each one and its holder wherever the row is reported, so whoever controls the board decides whether a contract, a relocation, or a sweep holds the row.
|
|
93
|
-
|
|
94
|
-
### Check the roster
|
|
95
|
-
|
|
96
|
-
Check the roster the way `canon:task-board` Step 4 does, reading `canon sessions list --self --json` for this session and `canon sessions list --json` for a same-repository row under another `sessionId` whose `name` starts with `orchestrator-`. A refused read counts as failed.
|
|
97
|
-
|
|
98
|
-
- **Orchestrator found.** Write neither file. Message that session with the row's title, the plan link, and the group with its reason, so two sessions never write the board at once.
|
|
99
|
-
- **Not found, and this session's name starts with `worker-` or `planner-`.** Write neither file, since both role bodies ban a board write. Report the row and its group in Step 8.
|
|
100
|
-
- **Not found otherwise, or the roster read fails.** Run `canon tasks place <stem> --group up-next --waiting "Planned, not dispatched yet"`, since stopping on a failed read strands the task.
|
|
101
|
-
|
|
102
|
-
Say which branch fired in Step 8.
|
|
103
|
-
|
|
104
|
-
Regenerate the task index after writing, since a hook on `Write|Edit` never fires on `Bash`:
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
canon indexes regen .canon/tasks
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Step 7: validate
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
canon records validate plans --json
|
|
114
|
-
canon markdown audit <main-root>/.canon/ready/<nn>-<slug>/00-overview.md
|
|
115
|
-
canon markdown audit <main-root>/.canon/plans/feature-<slug>.md
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
Fix what the records name in the files this run wrote and re-run once. Report a finding on a file it did not write, such as a neighbor folder missing its overview, without repairing it.
|
|
119
|
-
|
|
120
|
-
## Step 8: report
|
|
121
|
-
|
|
122
|
-
```plaintext
|
|
123
|
-
✅ Ready folder written: .canon/ready/<nn>-<slug>/
|
|
124
|
-
Plan: .canon/plans/feature-<slug>.md
|
|
125
|
-
Task: .canon/tasks/<label>-<slug>.md
|
|
126
|
-
Row: <label> <title>, <written under ## Up next | reported to <orchestrator name> | left for the operator to place>, <reason>
|
|
127
|
-
Archive on ship: canon tasks archive moves the folder to .canon/ready/archive/<nn>-<slug>/
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Name the archive move every time, so whoever ships knows the verb carries the folder and needs no step of their own.
|
|
131
|
-
|
|
132
|
-
## Rules
|
|
133
|
-
|
|
134
|
-
- Edit no finished file. The worker copies verbatim, so a change belongs in the source before this runs.
|
|
135
|
-
- Restate nothing the standards own. A shape question is answered by `ready.md` and a plan question by `plan.md`.
|
|
136
|
-
- Dispatch nothing. Starting the worker belongs to whoever runs the board.
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: assembly
|
|
3
|
-
description: The overview and thin-plan templates for a ready folder, and the match check between the two lists of destinations
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Assembly
|
|
7
|
-
|
|
8
|
-
Loaded from Step 3 and Step 5 of `plan-ready`. The shape rules live in `${CLAUDE_SKILL_DIR}/references/ready.md` and `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`, so this file holds the fill-in forms and the one check that compares them.
|
|
9
|
-
|
|
10
|
-
## The overview
|
|
11
|
-
|
|
12
|
-
```markdown
|
|
13
|
-
---
|
|
14
|
-
title: <Change in sentence case>
|
|
15
|
-
description: <one line naming what the handoff carries>
|
|
16
|
-
type: <branch type from branch.md>
|
|
17
|
-
destinations:
|
|
18
|
-
- <path/to/file>
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
<What the files carry, one bullet each where a file's purpose is not its name.>
|
|
22
|
-
|
|
23
|
-
<What the worker still owns beyond copying, or "nothing further".>
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
- Write `destinations` from the Step 1 list in the order the plan will list them.
|
|
27
|
-
- Keep the prose to what the files cannot say themselves, such as a docs sync, a sandbox scenario, a test the folder does not carry, or the order a change lands in.
|
|
28
|
-
|
|
29
|
-
## The thin plan
|
|
30
|
-
|
|
31
|
-
```markdown
|
|
32
|
-
# Feature: <short title>
|
|
33
|
-
|
|
34
|
-
<Two or three sentences on why this ships as a copy rather than as an authoring job.>
|
|
35
|
-
|
|
36
|
-
## Summary
|
|
37
|
-
|
|
38
|
-
- <what the change does, one bullet per file group>
|
|
39
|
-
- <branch type and what the worker still owns>
|
|
40
|
-
|
|
41
|
-
## Constraints
|
|
42
|
-
|
|
43
|
-
- `.canon/ready/<nn>-<slug>/` holds the verbatim source for every path below. Copy each file whole rather than reading it as a description and writing a version.
|
|
44
|
-
- <a live plan or run row holding a path, when one exists>
|
|
45
|
-
|
|
46
|
-
## Files to touch
|
|
47
|
-
|
|
48
|
-
- `<path/to/file>`: copy from the ready folder, <one clause on what it changes>
|
|
49
|
-
|
|
50
|
-
## Verification
|
|
51
|
-
|
|
52
|
-
- <the command that proves the copy landed, or "None identified.">
|
|
53
|
-
|
|
54
|
-
## Risks
|
|
55
|
-
|
|
56
|
-
- <what could go wrong, or "None identified.">
|
|
57
|
-
|
|
58
|
-
## Review focus
|
|
59
|
-
|
|
60
|
-
- <an input the copied files must handle, or "None identified.">
|
|
61
|
-
|
|
62
|
-
## Questions
|
|
63
|
-
|
|
64
|
-
None identified.
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## The destination match check
|
|
68
|
-
|
|
69
|
-
Read the overview's `destinations` and the plan's `## Files to touch` paths, then compare the two sets:
|
|
70
|
-
|
|
71
|
-
- A path in `destinations` and not in the plan is invisible to `plan-reach`, so the plan gains the line.
|
|
72
|
-
- A path in the plan and not in `destinations` names a file the folder does not carry, so either the folder gains the file or the plan loses the line.
|
|
73
|
-
- A path in either list with no file at that relative path inside the folder is a broken mirror. Report it and stop rather than writing a plan around a hole.
|
|
74
|
-
|
|
75
|
-
Report the sets as equal, or name each path that differs and on which side. Do not report the check as passed without having read both lists.
|