@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.
Files changed (125) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +33 -159
  3. package/assets/brand/mark.svg +17 -9
  4. package/claude/.claude-plugin/plugin.json +1 -1
  5. package/claude/skills/app-drive/REQUIREMENT.md +5 -5
  6. package/claude/skills/app-drive/SKILL.md +17 -6
  7. package/claude/skills/app-drive/references/drive-record.md +61 -8
  8. package/claude/skills/app-drive/references/isolate.md +1 -1
  9. package/claude/skills/canon-cli/SKILL.md +2 -2
  10. package/claude/skills/canon-feedback/REQUIREMENT.md +1 -1
  11. package/claude/skills/canon-feedback/SKILL.md +1 -1
  12. package/claude/skills/design-identity/REQUIREMENT.md +4 -4
  13. package/claude/skills/design-identity/SKILL.md +7 -7
  14. package/claude/skills/{pick-by-eye → design-pick}/REQUIREMENT.md +6 -6
  15. package/claude/skills/{pick-by-eye → design-pick}/SKILL.md +19 -10
  16. package/claude/skills/{pick-by-eye → design-pick}/references/board.md +17 -11
  17. package/claude/skills/design-pick/references/design-config.md +33 -0
  18. package/claude/skills/design-record/REQUIREMENT.md +2 -2
  19. package/claude/skills/design-record/SKILL.md +1 -1
  20. package/claude/skills/design-taste/REQUIREMENT.md +2 -2
  21. package/claude/skills/design-taste/SKILL.md +3 -3
  22. package/claude/skills/design-taste/references/preflight.md +1 -1
  23. package/claude/skills/git-issue/REQUIREMENT.md +1 -1
  24. package/claude/skills/git-issue/references/issue.md +1 -1
  25. package/claude/skills/plan-feature/SKILL.md +1 -1
  26. package/claude/skills/plan-feature/references/plan.md +21 -6
  27. package/claude/skills/plan-groundwork/SKILL.md +1 -1
  28. package/claude/skills/pr-evidence/references/evidence.md +2 -2
  29. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +3 -3
  30. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +1 -1
  31. package/claude/skills/role-orchestrator/references/orchestrator-planning-wait.md +24 -0
  32. package/claude/skills/role-planner/SKILL.md +4 -2
  33. package/claude/skills/role-worker/SKILL.md +5 -6
  34. package/claude/skills/task-board/references/tasks.md +1 -1
  35. package/claude/skills/task-complete/references/receipt-sweep.md +1 -1
  36. package/claude/skills/{ux-audit → ui-audit}/REQUIREMENT.md +3 -3
  37. package/claude/skills/{ux-audit → ui-audit}/SKILL.md +7 -7
  38. package/claude/skills/ui-checklist/REQUIREMENT.md +1 -1
  39. package/claude/skills/ui-craft/SKILL.md +1 -1
  40. package/claude/skills/ui-craft/references/copy.md +4 -1
  41. package/claude/skills/{ui-record → ui-film}/REQUIREMENT.md +6 -4
  42. package/claude/skills/{ui-record → ui-film}/SKILL.md +26 -13
  43. package/claude/skills/ui-film/references/compose-craft.md +81 -0
  44. package/claude/skills/{ui-record → ui-film}/references/config.md +9 -4
  45. package/claude/skills/{ui-record → ui-film}/references/handoff.md +17 -7
  46. package/claude/skills/{ui-record → ui-film}/references/pr-clip.md +5 -5
  47. package/claude/skills/{ui-record → ui-film}/references/take-craft.md +28 -2
  48. package/claude/skills/{ux-measure → ui-measure}/REQUIREMENT.md +3 -3
  49. package/claude/skills/{ux-measure → ui-measure}/SKILL.md +6 -6
  50. package/docs/agents/audits.md +1 -1
  51. package/docs/agents/migrate.md +13 -13
  52. package/docs/index.md +1 -0
  53. package/docs/migrations/by-hand.md +107 -0
  54. package/docs/migrations/by-verb.md +118 -0
  55. package/docs/migrations/index.md +14 -0
  56. package/docs/target/index.md +1 -2
  57. package/docs/target/projects.md +3 -3
  58. package/docs/target/sync.md +23 -3
  59. package/docs/workflow/ai-workflow.md +1 -1
  60. package/docs/workflow/skill-map-on-request.md +1 -2
  61. package/docs/workflow/skill-map.md +9 -10
  62. package/docs/workflow/visual-design-workflow.md +6 -1
  63. package/governance/ledger/claude/background.md +26 -0
  64. package/governance/ledger/skills/ui-craft/copy.md +8 -4
  65. package/governance/rules/claude/background.md +19 -0
  66. package/governance/stacks/base.toml +1 -0
  67. package/package.json +6 -4
  68. package/scripts/ci/install-playwright-deps.sh +52 -0
  69. package/scripts/core/design-sync.ts +204 -0
  70. package/scripts/core/regen-web-favicon.ts +27 -12
  71. package/scripts/core/regen-web-previews.ts +8 -1
  72. package/scripts/core/render-film.ts +179 -0
  73. package/src/capture/frame-values.ts +12 -12
  74. package/src/claude/cases/authoring.ts +2 -2
  75. package/src/claude/cases/misc.ts +1 -1
  76. package/src/claude/cases/setup.ts +0 -5
  77. package/src/claude/cases/workflow.ts +6 -6
  78. package/src/commands/fixtures/gh.sh +64 -0
  79. package/src/commands/gov.ts +33 -3
  80. package/src/commands/migrate.ts +12 -2
  81. package/src/commands/records.ts +3 -2
  82. package/src/commands/sandbox.ts +9 -2
  83. package/src/commands/take.ts +159 -11
  84. package/src/commands/tasks.ts +183 -0
  85. package/src/design/favicon.ts +85 -13
  86. package/src/design/reference.ts +98 -0
  87. package/src/design/render.ts +16 -14
  88. package/src/design/tokens.ts +4 -3
  89. package/src/gate/stages.ts +8 -2
  90. package/src/gov/audit.ts +48 -11
  91. package/src/gov/detect.ts +1 -1
  92. package/src/init/steps.ts +2 -2
  93. package/src/markdown/scope.ts +9 -14
  94. package/src/migrate/record-layout.ts +5 -3
  95. package/src/migrate/skill-names.ts +21 -4
  96. package/src/migrate/status.ts +3 -3
  97. package/src/records/validate.ts +15 -2
  98. package/src/sandbox/expect.ts +32 -0
  99. package/src/sandbox/headless/dispatch-shim.sh +15 -0
  100. package/src/sandbox/headless/dispatch.ts +12 -14
  101. package/src/sandbox/headless/run.ts +6 -3
  102. package/src/sandbox/stub-gh.sh +26 -0
  103. package/src/sandbox/stub-remote.ts +8 -29
  104. package/src/sync/engine.ts +1 -1
  105. package/src/take/camera.ts +100 -11
  106. package/src/take/film.ts +55 -4
  107. package/src/take/fixtures/input.html +138 -0
  108. package/src/take/keys.ts +193 -0
  109. package/src/take/log.ts +94 -1
  110. package/src/take/perform.ts +165 -10
  111. package/src/take/plan.ts +101 -6
  112. package/src/take/pointer.ts +2 -1
  113. package/src/take/screencast.ts +28 -3
  114. package/src/take/stepper.ts +141 -23
  115. package/src/take/zoom/NOTICE.md +1 -1
  116. package/src/tasks/launch-wait.ts +160 -0
  117. package/tooling/claude/reference.md +1 -1
  118. package/claude/skills/canon-feedback-triage/REQUIREMENT.md +0 -34
  119. package/claude/skills/canon-feedback-triage/SKILL.md +0 -98
  120. package/claude/skills/plan-ready/REQUIREMENT.md +0 -35
  121. package/claude/skills/plan-ready/SKILL.md +0 -136
  122. package/claude/skills/plan-ready/references/assembly.md +0 -75
  123. package/claude/skills/plan-ready/references/ready.md +0 -128
  124. package/docs/target/migrations.md +0 -203
  125. /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 `ux-measure-<slug>.md`
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.