@erclx/canon 5.38.0 → 5.39.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 (149) hide show
  1. package/README.md +1 -1
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/api-design/SKILL.md +12 -9
  4. package/claude/skills/auto-ship/SKILL.md +43 -23
  5. package/claude/skills/backlog-triage/SKILL.md +62 -53
  6. package/claude/skills/backlog-triage/references/apply.md +69 -0
  7. package/claude/skills/build-in-slices/SKILL.md +8 -4
  8. package/claude/skills/canon-cli/SKILL.md +5 -1
  9. package/claude/skills/canon-feedback/SKILL.md +18 -8
  10. package/claude/skills/canon-feedback-triage/SKILL.md +10 -3
  11. package/claude/skills/canvas-board/SKILL.md +36 -17
  12. package/claude/skills/ci-workflow/SKILL.md +5 -4
  13. package/claude/skills/code-craft/SKILL.md +10 -6
  14. package/claude/skills/codebase-layout/SKILL.md +3 -5
  15. package/claude/skills/context-fold/REQUIREMENT.md +4 -4
  16. package/claude/skills/context-fold/SKILL.md +42 -23
  17. package/claude/skills/context-fold/references/context-refresh.md +4 -3
  18. package/claude/skills/context-fold/references/context.md +79 -64
  19. package/claude/skills/create-rule/SKILL.md +7 -3
  20. package/claude/skills/create-skill/SKILL.md +27 -7
  21. package/claude/skills/create-skill/references/skill-reference.md +41 -17
  22. package/claude/skills/decision-escalate/SKILL.md +8 -5
  23. package/claude/skills/deploy-app/SKILL.md +1 -1
  24. package/claude/skills/deprecation-migration/SKILL.md +12 -4
  25. package/claude/skills/design-taste/SKILL.md +17 -11
  26. package/claude/skills/docs-sync/REQUIREMENT.md +1 -0
  27. package/claude/skills/docs-sync/SKILL.md +17 -6
  28. package/claude/skills/docs-sync/references/docs.md +54 -21
  29. package/claude/skills/docs-sync/references/readme.md +32 -8
  30. package/claude/skills/document-health/SKILL.md +3 -4
  31. package/claude/skills/draft-and-pick/SKILL.md +73 -47
  32. package/claude/skills/draft-doc/SKILL.md +9 -4
  33. package/claude/skills/draft-doc/references/context.md +4 -4
  34. package/claude/skills/draft-doc/references/docs.md +1 -0
  35. package/claude/skills/draft-doc/references/wiki.md +3 -2
  36. package/claude/skills/draft-figure/SKILL.md +9 -7
  37. package/claude/skills/draft-identity/SKILL.md +24 -52
  38. package/claude/skills/draft-identity/references/card-route.md +46 -0
  39. package/claude/skills/draft-ready/SKILL.md +19 -7
  40. package/claude/skills/draft-screencast/SKILL.md +6 -112
  41. package/claude/skills/draft-screencast/references/beats-template.md +119 -0
  42. package/claude/skills/draft-slides/SKILL.md +7 -4
  43. package/claude/skills/git-branch/SKILL.md +3 -3
  44. package/claude/skills/git-commit/SKILL.md +5 -3
  45. package/claude/skills/git-followup/SKILL.md +16 -9
  46. package/claude/skills/git-issue/SKILL.md +6 -5
  47. package/claude/skills/git-pr/SKILL.md +56 -44
  48. package/claude/skills/git-ship/SKILL.md +43 -34
  49. package/claude/skills/git-ship/references/ci-watch.md +31 -0
  50. package/claude/skills/git-split/SKILL.md +28 -100
  51. package/claude/skills/git-split/references/independent.md +40 -0
  52. package/claude/skills/git-split/references/stacked.md +73 -0
  53. package/claude/skills/git-stage/SKILL.md +9 -7
  54. package/claude/skills/git-worktree/SKILL.md +27 -49
  55. package/claude/skills/git-worktree/references/cleanup.md +55 -0
  56. package/claude/skills/markdown-craft/SKILL.md +2 -2
  57. package/claude/skills/markdown-craft/references/markdown.md +82 -53
  58. package/claude/skills/plan-feature/SKILL.md +52 -46
  59. package/claude/skills/plan-feature/references/batches.md +46 -0
  60. package/claude/skills/plan-groundwork/SKILL.md +60 -74
  61. package/claude/skills/plan-groundwork/references/output.md +48 -0
  62. package/claude/skills/plan-intake/SKILL.md +28 -21
  63. package/claude/skills/plan-intake-answer/SKILL.md +14 -13
  64. package/claude/skills/read-frames/SKILL.md +2 -0
  65. package/claude/skills/record-screencast/SKILL.md +31 -11
  66. package/claude/skills/review-address/SKILL.md +76 -158
  67. package/claude/skills/review-address/references/rebase-only.md +34 -0
  68. package/claude/skills/review-craft/SKILL.md +27 -11
  69. package/claude/skills/review-pr/SKILL.md +91 -40
  70. package/claude/skills/review-ui/SKILL.md +62 -37
  71. package/claude/skills/review-ui/references/hosted-preview.md +30 -0
  72. package/claude/skills/role-orchestrator/SKILL.md +105 -74
  73. package/claude/skills/role-orchestrator/references/orchestrator-filing.md +26 -0
  74. package/claude/skills/role-planner/SKILL.md +66 -64
  75. package/claude/skills/role-reviewer/SKILL.md +57 -46
  76. package/claude/skills/role-worker/SKILL.md +68 -63
  77. package/claude/skills/search-craft/SKILL.md +7 -5
  78. package/claude/skills/session-compact/SKILL.md +16 -11
  79. package/claude/skills/session-map/SKILL.md +31 -10
  80. package/claude/skills/session-relay/SKILL.md +32 -65
  81. package/claude/skills/session-resume/SKILL.md +18 -13
  82. package/claude/skills/session-worktree/SKILL.md +70 -42
  83. package/claude/skills/systematic-debugging/SKILL.md +6 -1
  84. package/claude/skills/target-check/SKILL.md +13 -6
  85. package/claude/skills/target-setup/SKILL.md +39 -27
  86. package/claude/skills/task-board/SKILL.md +68 -60
  87. package/claude/skills/task-board/references/create-report.md +36 -0
  88. package/claude/skills/task-complete/SKILL.md +20 -18
  89. package/claude/skills/teach-workspace/SKILL.md +61 -33
  90. package/claude/skills/test-craft/SKILL.md +2 -1
  91. package/claude/skills/test-first/SKILL.md +15 -9
  92. package/claude/skills/ui-checklist/SKILL.md +18 -16
  93. package/claude/skills/ux-audit/SKILL.md +9 -10
  94. package/claude/skills/ux-measure/SKILL.md +22 -18
  95. package/claude/skills/video-craft/SKILL.md +15 -7
  96. package/claude/skills/write-human/SKILL.md +9 -1
  97. package/claude/skills/youtube-transcripts/SKILL.md +2 -1
  98. package/docs/agents/context-audit-checks.md +19 -1
  99. package/docs/agents/context-audit.md +2 -2
  100. package/docs/agents/driver.md +1 -1
  101. package/docs/agents/hooks.md +21 -1
  102. package/docs/agents/index.md +10 -14
  103. package/docs/agents/indexes.md +25 -29
  104. package/docs/agents/install-and-sync.md +124 -235
  105. package/docs/agents/markdown-audit-density.md +39 -0
  106. package/docs/agents/markdown-audit-weight.md +9 -7
  107. package/docs/agents/markdown-audit.md +18 -14
  108. package/docs/agents/migrate.md +41 -16
  109. package/docs/agents/output-shape.md +60 -45
  110. package/docs/agents/overview.md +36 -12
  111. package/docs/agents/routing.md +1 -1
  112. package/docs/agents/rule-citations.md +1 -1
  113. package/docs/agents/skills-reach.md +1 -1
  114. package/docs/agents/targets.md +25 -77
  115. package/docs/target/migrations.md +1 -1
  116. package/docs/target/projects.md +2 -2
  117. package/docs/workflow/ai-workflow.md +2 -2
  118. package/docs/workflow/parallel-features.md +2 -2
  119. package/docs/workflow/skill-map.md +1 -1
  120. package/docs/workflow/visual-design-workflow.md +1 -1
  121. package/package.json +1 -1
  122. package/src/commands/claude.ts +1 -1
  123. package/src/commands/context.ts +106 -7
  124. package/src/commands/hooks.ts +63 -0
  125. package/src/commands/markdown.ts +169 -32
  126. package/src/context/audit.ts +256 -5
  127. package/src/context/classify/prompts.ts +1 -1
  128. package/src/context/gate.ts +23 -7
  129. package/src/context/shape-gated.ts +28 -0
  130. package/src/design/document.ts +1 -1
  131. package/src/design/tokens.ts +1 -1
  132. package/src/gate/measures.ts +15 -190
  133. package/src/gate/stages.ts +7 -23
  134. package/src/hooks/reply-links.ts +110 -0
  135. package/src/markdown/density.ts +258 -0
  136. package/src/markdown/gate.ts +57 -18
  137. package/src/markdown/reference-shape.ts +103 -0
  138. package/src/markdown/scope.ts +174 -0
  139. package/src/markdown/structure.ts +9 -59
  140. package/src/shipped/references.ts +1 -1
  141. package/tooling/claude/seeds/.claude/hooks/reply-links.sh +72 -0
  142. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +22 -1
  143. package/tooling/claude/seeds/.claude/settings.json +4 -0
  144. package/docs/agents/commands.md +0 -154
  145. package/docs/agents/docs.md +0 -27
  146. package/docs/agents/drift-surfaces.md +0 -201
  147. package/docs/agents/gov-detect.md +0 -47
  148. package/docs/agents/scripting.md +0 -142
  149. package/src/markdown/growth.ts +0 -43
package/README.md CHANGED
@@ -164,7 +164,7 @@ Each domain carries an entry written for someone maintaining the toolkit rather
164
164
  - [Claude Code plugin](canon/context/claude-plugin/index.md)
165
165
  - [Governance rules](canon/context/governance/index.md)
166
166
  - [Tooling stacks](canon/context/tooling/index.md)
167
- - [Design system](canon/context/design/index.md)
167
+ - [Design system](canon/context/design.md)
168
168
  - [Slides](canon/context/features/slides.md)
169
169
  - [Transcripts](canon/context/features/transcripts.md)
170
170
  - [Sandbox](canon/context/sandbox/index.md)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
4
- "version": "5.38.0",
4
+ "version": "5.39.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -7,18 +7,16 @@ metadata:
7
7
 
8
8
  # API design
9
9
 
10
- A session changing a surface sees the one caller it is working for and none of the others, so it renames a field in place, returns whatever record it had in hand, and ships. Every caller it did not open breaks on the next release. This skill carries the judgment at the moment the surface is drawn: what a caller can observe, what the contract says before the body exists, and how a change lands without breaking anyone.
10
+ A session changing a surface sees the one caller it is working for, so it renames a field in place, returns whatever record it had in hand, and ships. Every caller it did not open breaks on the next release. This skill carries the judgment at the moment the surface is drawn.
11
11
 
12
- Load it before changing anything a caller outside the module reads, whether code, a script parsing output, or a stored file. A surface is cheap to shape before its first caller and expensive after.
13
-
14
- Skip it when the change stays behind an existing boundary and alters nothing a caller can observe.
12
+ Load it before changing anything a caller outside the module reads, whether code, a script parsing output, or a stored file, since a surface is cheap to shape before its first caller and expensive after. Skip it when the change alters nothing a caller can observe.
15
13
 
16
14
  ## Decide what a caller can observe
17
15
 
18
16
  Every observable behavior becomes a dependency once there are enough callers, whatever the contract promises, which is Hyrum's Law. Decide what is observable, and expose nothing by accident.
19
17
 
20
18
  - Return a shape built for the caller over the internal record you had in hand. A storage type, an internal identifier, or a field kept for bookkeeping becomes a contract the moment it ships.
21
- - Treat ordering, timing, default values, and the exact text of an error or a log line as observable. Sort output on purpose or say it is unordered, rather than leaving it in whatever order the data arrived.
19
+ - Treat ordering, timing, default values, and the exact text of an error or a log line as observable. Sort output on purpose or say it is unordered, never in whatever order the data arrived.
22
20
  - Keep the surface as small as the first caller needs. A field or flag added later is free, and one removed later breaks someone.
23
21
  - Keep one name for one concept across every surface the project exposes. A caller learning `id` in one output and `key` in the next has to guess which is which.
24
22
 
@@ -31,7 +29,8 @@ Every observable behavior becomes a dependency once there are enough callers, wh
31
29
 
32
30
  ## Keep one error shape per surface
33
31
 
34
- - Give every failure on one surface the same shape, carrying a stable code a caller can branch on and a message for the person reading it. Branching on message text breaks the first time the message is reworded. Keep internal implementation details out of the message, since a caller reads it and cannot act on a stack frame.
32
+ - Give every failure on one surface the same shape, a stable code a caller can branch on and a message for the person, since branching on message text breaks at the first rewording
33
+ - Keep internal implementation details out of the message, since a caller reads it and cannot act on a stack frame
35
34
  - Reuse the error type the project already throws or returns where one exists, rather than inventing a second error convention beside it.
36
35
 
37
36
  ## Validate at the boundary
@@ -44,10 +43,10 @@ Every observable behavior becomes a dependency once there are enough callers, wh
44
43
  ## Change additively
45
44
 
46
45
  - Add a field, a flag, or a parameter as optional, with a default that keeps the old behavior.
47
- - Never rename or remove anything in place. Add the new name beside the old one, keep both working, and remove the old one only once no caller reads it, proving that and moving the callers through `deprecation-migration`.
46
+ - Never rename or remove anything in place. Add the new name beside the old, keep both working, and remove the old only once no caller reads it, proven through `deprecation-migration`.
48
47
  - Never change what an existing field means or what type it carries. A new meaning takes a new name.
49
- - When a caller cannot pin a version, such as a module everything in one repository imports, keep one version of the surface live and migrate every caller in the change that alters it, the One-Version Rule. Two versions live side by side split the callers between them.
50
- - Record each deprecation where a caller looks, naming the replacement and what happens next, rather than in a commit message nobody reads again. Whether to deprecate, and when a deprecation turns compulsory, is `deprecation-migration`.
48
+ - When a caller cannot pin a version, such as a module one repository imports, keep one version live and migrate every caller in the change, the One-Version Rule. Two live versions split the callers.
49
+ - Record each deprecation where a caller looks, naming the replacement and what happens next, never only in a commit message. Whether and when to deprecate is `deprecation-migration`.
51
50
 
52
51
  ## Make a retried write safe
53
52
 
@@ -82,6 +81,8 @@ Read `${CLAUDE_SKILL_DIR}/references/adopted.md` only when extending this guidan
82
81
 
83
82
  ### Changes, retries, and order
84
83
 
84
+ Changes to an existing surface carry their own signs.
85
+
85
86
  - An existing field is changing type or meaning under the same name.
86
87
  - A write that a client could retry carries no key, or checks for one with a read before the insert.
87
88
  - Output order follows whatever the underlying data returned, and nothing states whether it is sorted.
@@ -97,6 +98,8 @@ Check each line against the surface the change produced. A line answering no is
97
98
 
98
99
  ### Input, writes, and contract
99
100
 
101
+ Check the input and the contract the same way.
102
+
100
103
  - Input is checked once, at the boundary, and nowhere deeper.
101
104
  - Every write a caller could retry takes a key, claims it atomically, and refuses a reused key with a different body.
102
105
  - The contract states what the surface returns and what it refuses.
@@ -12,45 +12,62 @@ Chain the post-plan pipeline in a single run. Every step but the UI checklist ha
12
12
  ## Guards
13
13
 
14
14
  - All `.canon/plans/` reads resolve at the main worktree root, not the current worktree. Resolve that root the way `session-worktree` does.
15
- - Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../git-branch/references/slug.md`. This skill takes the stop rather than the `latest` fallback, since it commits and opens a pull request. If empty, stop: `❌ Detached HEAD. Checkout the feature branch first.` This slug is provisional. It is superseded once `session-worktree` runs, whether at Step 0 or before this chain began. Every later step keys its output on the slug that run resolves, being the worktree and the branch, regardless of which plan Step 1 reads.
15
+ - Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../git-branch/references/slug.md`, taking the stop over the `latest` fallback, since this skill opens a pull request
16
+ - If the slug is empty, stop: `❌ Detached HEAD. Checkout the feature branch first.`
16
17
  - Resolve `<plan>` in Step 1, ahead of any other read.
17
18
  - If the working tree has uncommitted changes unrelated to the plan, stop: `❌ Uncommitted changes outside the plan. Commit or stash before autoshipping.`
18
19
 
20
+ Treat that slug as provisional. Once `session-worktree` runs, at Step 0 or before this chain began, every later step keys on the worktree and branch it resolved, whichever plan Step 1 reads.
21
+
19
22
  ## Step 0: take the role, then enter a worktree
20
23
 
21
24
  Invoke `canon:role-worker` first, whatever the worktree state. That skill states the boundaries, the lifetime, and the channel obligations of a session building one branch under one plan, and a dispatched worker reaches the role here and nowhere else. Report it rather than proceeding silently when `canon:role-worker` does not resolve.
22
25
 
23
- If `git rev-parse --git-dir` equals `git rev-parse --git-common-dir`, the session is in the main worktree. Invoke `canon:session-worktree` before continuing, carrying the argument the subsection below derives. The wrapper handles branch alignment. Do not call `EnterWorktree` directly.
24
-
25
- If neither command resolves, stop: `❌ Not a git repository. Autoship needs git or a WorktreeCreate hook.`
26
+ Then compare `git rev-parse --git-dir` with `git rev-parse --git-common-dir`. The `session-worktree` wrapper handles branch alignment, so never call `EnterWorktree` directly.
26
27
 
27
- If the two commands differ, the session is already in a linked worktree. Continue.
28
+ | Reading | Route |
29
+ | ---------------- | ---------------------------------------------------------------------------------------------- |
30
+ | Equal | The main worktree, so invoke `canon:session-worktree` with the argument the subsection derives |
31
+ | Different | Already in a linked worktree, so continue |
32
+ | Neither resolves | Stop: `❌ Not a git repository. Autoship needs git or a WorktreeCreate hook.` |
28
33
 
29
34
  ### Name the worktree from the plan rather than leaving it to be derived
30
35
 
31
- When the invocation argument is a plan path or a bare slug, name the worktree from `canon tasks plan-branch <argument> --json` rather than from a reading of the plan. Read `${CLAUDE_SKILL_DIR}/references/worktree-name.md` before invoking `canon:session-worktree` for how each record shape reaches its tier 0 argument. A task path or no argument at all invokes it bare.
36
+ When the invocation argument is a plan path or a bare slug, name the worktree from `canon tasks plan-branch <argument> --json` rather than from a reading of the plan. A task path or no argument at all invokes `canon:session-worktree` bare.
37
+
38
+ Read `${CLAUDE_SKILL_DIR}/references/worktree-name.md` before invoking `canon:session-worktree`, for how each record shape reaches its tier 0 argument.
32
39
 
33
40
  ## Step 1: read the plan
34
41
 
35
- Resolve `<plan>` in this order, stopping at the first match:
42
+ Resolve `<plan>` in this order, stopping at the first match. Only a path reaches tier 1, and a bare slug is read as a plan's throughout, since the two would collide on any similar name.
43
+
44
+ 1. **Caller-supplied task.** A path under `.canon/tasks/`. Take what its first `Plan:` line names, per `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md`, after reading `tier-failures.md`.
45
+ 2. **Caller-supplied plan.** Anything else, a plan path or a bare slug, as `session-worktree` tier 0 takes it. A slug resolves to `.canon/plans/feature-<slug>.md`, and a path is taken from the main root.
46
+ 3. **Derived.** `.canon/plans/feature-<slug>.md`, from the `<slug>` the Guards derived.
36
47
 
37
- 1. **Caller-supplied task.** The invocation carried a path under `.canon/tasks/`. If it does not resolve to a file, stop: `❌ No task at <path>. Path was supplied, not derived, so check it and re-run.` Read that task's first `Plan:` line and take what it names as `<plan>`, per `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md`, reading `${CLAUDE_SKILL_DIR}/references/tier-failures.md` first for how the pointer resolves and the four stops it can take.
38
- 2. **Caller-supplied plan.** The invocation carried something else. Accept it as a plan path or a bare slug, in the same position `session-worktree` tier 0 accepts its name. A bare slug resolves to `.canon/plans/feature-<slug>.md`, and a path is taken as given from the main worktree root. If it does not resolve to a file, stop: `❌ No plan at <path>. Path was supplied, not derived, so check it and re-run.`
39
- 3. **Derived.** `.canon/plans/feature-<slug>.md`, from the `<slug>` the Guards derived. If it does not exist, stop: `❌ No approved plan at .canon/plans/feature-<slug>.md, where <slug> was derived from the current branch, <branch>. The invocation carried no argument, so pass a plan or a task path, or run /plan-feature first.`
48
+ Each tier stops with its own message on a file that does not resolve.
40
49
 
41
- Only a path reaches tier 1, and a bare slug is read as a plan's throughout, since the two would collide on any similar name.
50
+ | Tier | Stop |
51
+ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
52
+ | Task | `❌ No task at <path>. Path was supplied, not derived, so check it and re-run.` |
53
+ | Plan | `❌ No plan at <path>. Path was supplied, not derived, so check it and re-run.` |
54
+ | Derived | `❌ No approved plan at .canon/plans/feature-<slug>.md, where <slug> was derived from the current branch, <branch>. The invocation carried no argument, so pass a plan or a task path, or run /plan-feature first.` |
42
55
 
43
56
  ### When a tier fails
44
57
 
45
58
  Tier 1 stops on a `Plan:` line linking several plans, on a task carrying none, and on a pointer into a plans archive or to no file. `${CLAUDE_SKILL_DIR}/references/tier-failures.md` carries the stop each one takes and the order the archive and existence tests run in.
46
59
 
47
- Test the shape of whatever `<plan>` resolved to before reading it as one, after all three tiers, since a file resolving under any tier can still be the wrong document.
60
+ ### Test the plan's shape
48
61
 
49
- Check for a `**Files to touch:**` or `## Files to touch` marker per `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`, the one section every plan carries structurally and a task never does, where `## Outcomes` and `## Findings` are the task's own. If neither form is present, stop: `❌ <path> carries no plan sections. A plan lives at .canon/plans/feature-<slug>.md, and a task reaches one through its Plan: line only from .canon/tasks/. Point autoship at either and re-run.`
62
+ Test the shape of whatever `<plan>` resolved to before reading it, after all three tiers, since a file resolving under any tier can be the wrong document. Look for a `**Files to touch:**` or `## Files to touch` marker per `${CLAUDE_SKILL_DIR}/../plan-feature/references/plan.md`, which a plan always carries and a task never does.
50
63
 
51
- Read `<plan>` at the main worktree root. This file is the scope for this run.
64
+ If neither form is present, stop: `❌ <path> carries no plan sections. A plan lives at .canon/plans/feature-<slug>.md, and a task reaches one through its Plan: line only from .canon/tasks/. Point autoship at either and re-run.` Otherwise read `<plan>` at the main worktree root, which is the scope for this run.
52
65
 
53
- Its sections and its answer contract are fixed by `${CLAUDE_SKILL_DIR}/../plan-feature/references/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}/../plan-feature/references/plan.md`, opening with the bold source-and-date label that `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md` fixes.
66
+ ### Unanswered questions
67
+
68
+ The plan's sections and answer contract are fixed by `${CLAUDE_SKILL_DIR}/../plan-feature/references/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.
69
+
70
+ When this run decides against a suggestion, rewrite its line as `overridden at execution to <pick>,` plus the measurement, leaving `- Answer:` blank. Put the same deviation in one line under the open task's `## Findings`, opening with the bold source-and-date label `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md` fixes.
54
71
 
55
72
  ## Step 2: implement
56
73
 
@@ -73,7 +90,7 @@ Run `canon gov test-order --json` from the worktree this chain is building in, n
73
90
  The verb reads git history rather than the working tree, so it catches a violation that already reached this branch's history in an earlier session or round. It reports and never gates, so branch on the record's `kind` and its `findings` array rather than on the exit, which a shell function wrapping `canon` can flatten to zero.
74
91
 
75
92
  - `kind: 'measured'`, `findings` empty. Nothing on the branch reached history ahead of its test. Continue to Step 5.
76
- - `kind: 'measured'`, `findings` non-empty. Report each pair's `subject` and `reason` to the user and continue to Step 5. Do not stop the chain and do not attempt a fix, since rewriting it here rewrites what an earlier run shipped.
93
+ - `kind: 'measured'`, `findings` non-empty. Report each pair's `subject` and `reason` and continue to Step 5 without a fix, since a rewrite here rewrites what an earlier run shipped.
77
94
  - `kind: 'unreadable'`. Report the `message` and continue to Step 5, since a shallow clone or a repository with no trunk is no reason to stop shipping.
78
95
 
79
96
  ### When the verb is absent
@@ -82,17 +99,22 @@ Never read a missing subcommand as clean. Read `${CLAUDE_SKILL_DIR}/references/v
82
99
 
83
100
  ## Step 5: UI checklist (conditional)
84
101
 
85
- If the diff touches UI files (JSX, TSX, Vue, Svelte, HTML, or CSS under `src/`), invoke `canon:ui-checklist`, then continue to Step 6 whatever it returns, in every run. The Step 6 draft mark holds the merge, so a stop here protects nothing. When it produces a checklist, read `${CLAUDE_SKILL_DIR}/references/ui-checklist.md` for the counts to hold before `git-pr` removes the file.
102
+ If the diff touches UI files (JSX, TSX, Vue, Svelte, HTML, or CSS under `src/`), invoke `canon:ui-checklist`, then continue to Step 6 whatever it returns, in every run. The Step 6 draft mark holds the merge, so a stop here protects nothing.
103
+
104
+ When it produces a checklist, read `${CLAUDE_SKILL_DIR}/references/ui-checklist.md` for the counts to hold before `git-pr` removes the file.
86
105
 
87
106
  ## Step 6: ship
88
107
 
89
108
  Invoke `canon:git-ship`. That body owns the sequence, being the verify gate, both doc syncs, staging, the commit grouping, the branch rename, the pull request, and the CI watch, along with the reason each step sits where it does. Read the order there and never here.
90
109
 
91
- One thing this chain adds. Mark the pull request as a draft as soon as `git-ship`'s pull request step returns, ahead of its CI watch, naming the number that step returned rather than one resolved by branch. Read `${CLAUDE_SKILL_DIR}/references/draft-mark.md` on reaching that point for the one command that proves the target, marks it, and reads the flag back. A `false` read stops the chain, and never re-issue the mark on it.
110
+ ### The draft mark
92
111
 
93
- Emit the Output block on the wake after `git-ship`'s background CI watch exits on `passing`, not when the turn ends on its watch line.
112
+ This chain adds one thing. Mark the pull request as a draft as soon as `git-ship`'s pull request step returns, ahead of its CI watch, naming the number that step returned rather than one resolved by branch.
94
113
 
95
- `git-ship` verifies again at its own gate, repeating Step 3 on a clean run. Keep that repeat, since a resumed run enters at that gate.
114
+ - Read `${CLAUDE_SKILL_DIR}/references/draft-mark.md` on reaching that point for the one command that proves the target, marks it, and reads the flag back
115
+ - A `false` read stops the chain, and never re-issue the mark on it
116
+ - Emit the Output block on the wake after `git-ship`'s background CI watch exits on `passing`, not when the turn ends on its watch line
117
+ - Keep `git-ship`'s own verify gate, which repeats Step 3 on a clean run, since a resumed run enters at that gate
96
118
 
97
119
  ## Output
98
120
 
@@ -103,9 +125,7 @@ Respond with up to two lines:
103
125
  🖼️ <N> visual boxes unchecked (<M> taste), owed on the evidence comment of <PR url> to the operator, or to the UI reviewer for the boxes a driver can run
104
126
  ```
105
127
 
106
- `<state>` is whatever the Step 6 read returned, being `draft` or `ready, unsupervised`, rather than the state the mark asked for.
107
-
108
- Fill the second line from the counts Step 5 held, and omit it when no checklist was produced.
128
+ `<state>` is whatever the Step 6 read returned, being `draft` or `ready, unsupervised`, rather than the state the mark asked for. Fill the second line from the counts Step 5 held, and omit it when no checklist was produced.
109
129
 
110
130
  This block replaces the one `git-ship` closes on rather than following it, since emitting both reports one run twice and buries the state under a `✅ Shipped` that does not name it.
111
131
 
@@ -9,25 +9,37 @@ metadata:
9
9
 
10
10
  A backlog only grows while nothing can decline a row. This pass reads every row against the current tree, files one verdict per row for the operator, and later applies what they approved.
11
11
 
12
- The record is an ordinary intake folder at `.canon/intake/<nn>-backlog-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. Read `${CLAUDE_SKILL_DIR}/../task-board/references/board.md` and `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md` before applying, since the first holds the backlog and the ordering file and the second holds the `Declined:` line.
12
+ The record is an ordinary intake folder at `.canon/intake/<nn>-backlog-triage/`, so three sibling references bind it.
13
+
14
+ - `${CLAUDE_SKILL_DIR}/../plan-intake/references/intake.md`, read before writing any file in the folder, holds the item format, the frontmatter, the index shape, and the answer contract
15
+ - `${CLAUDE_SKILL_DIR}/../task-board/references/board.md`, read before applying, holds the backlog and the ordering file
16
+ - `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md`, read before applying, holds the `Declined:` line
13
17
 
14
18
  ## Guards
15
19
 
16
- - Resolve `.canon/tasks/` and `.canon/intake/` at the main worktree root, not `pwd`. Run `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd` outside a git repo.
20
+ - 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
17
21
  - If `.canon/tasks/backlog.md` does not exist at that root, stop: `❌ No backlog at .canon/tasks/backlog.md. Nothing to triage.`
18
- - 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, which is what keeps silence from deciding a row.
22
+ - Never fill a `You:` slot, and never read an empty one as agreement, so silence never decides a row. Answering runs through `plan-intake-answer` or the operator's own edit.
19
23
  - 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.
20
24
 
21
25
  ## Step 1: detect the phase
22
26
 
23
- Run `canon intake list --json` and take the folders whose slug ends in `-backlog-triage`. Read the newest by ordinal. Where the installed binary carries no such subcommand, stop and report it, since opening a folder beside one awaiting apply leaves the first stranded and applying needs the same verb.
27
+ Run `canon intake list --json` and read the newest folder by ordinal whose slug ends in `-backlog-triage`. Where the binary carries no such subcommand, stop and report it, since a second folder would strand the first and apply needs the same verb.
28
+
29
+ | State of the newest folder | Route |
30
+ | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
31
+ | None, or fully applied | Run the file phase |
32
+ | Unread above zero | Stop: `❌ <n> items in <slug> are unread. Run /canon:plan-intake-answer, then re-run this skill to apply.` |
33
+ | Malformed above zero | Stop: `❌ <n> items in <slug> carry no answer slot. Repair them in the cluster files before applying.` |
34
+ | Every item answered, some not yet applied | Run the apply phase |
24
35
 
25
- - **No folder, or the newest is fully applied.** Run the file phase.
26
- - **Unread above zero.** Stop: `❌ <n> items in <slug> are unread. Run /canon:plan-intake-answer, then re-run this skill to apply.`
27
- - **Malformed above zero.** Stop: `❌ <n> items in <slug> carry no answer slot. Repair them in the cluster files before applying.`
28
- - **Every item answered, some not yet applied.** Run the apply phase.
36
+ ### When an item counts as applied
29
37
 
30
- An item is applied once its task has moved under `.canon/tasks/declined/` or `.canon/tasks/archive/`, or once its live task file carries a `## Findings` line naming the folder slug. A task whose file sits in none of the three folders counts as applied only once `backlog.md` no longer names it, since its dangling line is the one thing apply has left to remove. Test that per item rather than trusting a count, since a run can stop partway. An item apply skipped for an unreadable answer stays unapplied, so the folder stays in the apply phase until the operator rewrites that answer.
38
+ Test each item rather than trusting a count, since a run can stop partway. An item apply skipped for an unreadable answer stays unapplied until the operator rewrites it.
39
+
40
+ - Its task has moved under `.canon/tasks/declined/` or `.canon/tasks/archive/`
41
+ - Its live task file carries a `## Findings` line naming the folder slug
42
+ - Its task file sits in none of the three folders and `backlog.md` no longer names it, since the dangling line is all apply has left to remove
31
43
 
32
44
  ## File phase
33
45
 
@@ -35,7 +47,7 @@ Writes only inside `.canon/intake/<nn>-backlog-triage/`, so it runs under any ro
35
47
 
36
48
  ### Step 2: claim the folder
37
49
 
38
- Run `canon records ordinal intake backlog-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.
50
+ Run `canon records ordinal intake backlog-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 binary carries no such subcommand, report it and stop rather than picking an ordinal by hand.
39
51
 
40
52
  ### Step 3: read every row
41
53
 
@@ -45,33 +57,51 @@ A row whose link resolves to no file gets an item anyway, carrying `decline` and
45
57
 
46
58
  ### Step 4: measure each row
47
59
 
48
- Measure every claim the task makes against the tree and `git log` during this pass. Grep for the construct its defect names and count the sites, check whether each open outcome's behavior exists, and search the log for a commit or pull request that shipped it under another task. Never carry a figure from the task file forward as current, since the task states what was true when it was filed.
60
+ Measure every claim the task makes against the tree and `git log` during this pass, never carrying a figure from the task file forward, since it states what was true when filed. Name the commit the pass measured against in the overview body.
61
+
62
+ - Grep for the construct its defect names and count the sites
63
+ - Check whether each open outcome's behavior exists
64
+ - Search the log for a commit or pull request that shipped it under another task
49
65
 
50
- Then gather the evidence Step 5 reads for each row that reproduces. Check whether its origin line or `## Findings` names an issue, a run, or a person it bit. Search the log for a commit or pull request that worked around it rather than fixing it. For a defect in something the project ships, whether a route or a page its users call or a rule, a seed, a scaffold, or a verb other repositories copy or run, name the shipped path it sits on. Record each one that is absent as absent rather than inferring it.
66
+ For each row that reproduces, gather the evidence Step 5 reads, and record each piece that is absent as absent rather than inferring it.
51
67
 
52
- Name the commit the pass measured against in the overview body.
68
+ - An issue, a run, or a person it bit, named in its origin line or `## Findings`
69
+ - A commit or pull request that worked around it rather than fixing it
70
+ - The shipped path a defect sits on, being a route or page users call, or a rule, seed, scaffold, or verb other repositories copy or run
53
71
 
54
72
  ### Step 5: pick the verdict
55
73
 
56
- Keep and promote carry the burden of proof, and a defect that still exists does not meet it. A row filed against the tree nearly always still reproduces, so a test of reproduction alone keeps almost every row.
74
+ Keep and promote carry the burden of proof, and a defect that still exists does not meet it. A row filed against the tree nearly always reproduces, so a reproduction test alone keeps almost every row. A row earns keep or promote only by naming a cost or a reach from Step 4's evidence.
75
+
76
+ | Ground | What it takes |
77
+ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
78
+ | Cost | A wrong result this pass observed by running the verb or reading committed output, or an issue, run, person, or workaround commit it hit, not measured fixed |
79
+ | Reach | The defect sits on a shipped path, so everyone reaching it receives a wrong instruction, a broken or unbounded behavior, or a wrong output, hit or not |
80
+
81
+ A plausible cost names nothing. A row arguing what could go wrong off a shipped path, with no observed output and no hit, is declined however likely it sounds.
82
+
83
+ ### What is not a reach
57
84
 
58
- A row earns keep or promote only by naming one of two things from the evidence Step 4 gathered:
85
+ Three things are not a reach, and a row resting on one needs a cost instead.
59
86
 
60
- - **A cost**: the defect gives a wrong result that this pass observed, by running the verb that carries it or reading the committed output that shows it, or that the row's evidence shows an issue, a run, or a person hit, a workaround commit included. A hit counts only while the pass does not measure it as fixed or as no longer recurring.
61
- - **A reach**: the defect sits on a path the project ships, and everyone who reaches that path, whether a user calling it or a target copying or running it, receives the defect as a wrong instruction, a broken, failing, or unbounded behavior, or a wrong output. Reach needs no hit, since the path's users meet the defect whether or not anyone reported it.
87
+ - A missing feature on a shipped surface, which leaves the path working and adds a capability, a document kind, or guidance
88
+ - A defect that changes nothing a user or a target receives, such as dead code on a shipped path
89
+ - A defect that shows only in this repository's own config, workflows, scripts, or fixtures
62
90
 
63
- A plausible cost names nothing. A row arguing what could go wrong off a shipped path, with no observed output and no hit, is declined however likely the argument sounds. Three things are not a reach: a missing feature on a shipped surface, a defect that changes nothing a user or a target receives, such as dead code on a shipped path, and a defect that shows only in this repository's own config, workflows, scripts, or fixtures.
91
+ A missing protection is a defect, not a missing feature, and is a reach. Read a row that way when the shipped path lacks a run-time bound, check, or guard, such as a limit, a timeout, or input validation, so it can fail, run unbounded, or return a wrong output.
64
92
 
65
- A missing protection is a defect, not a missing feature. Read a row as a missing protection when the shipped path lacks a bound, a check, or a guard on what it does at run time, such as a limit, a timeout, or input validation, so that the path can fail, run unbounded, or return a wrong output for every user who reaches it. That is a reach. A missing feature leaves the path working as it does today and adds a capability, a document kind, or a sentence of guidance, and it is not a reach.
93
+ A missing feature earns keep through the cost arm when a user asked for it in an issue or a report, or worked around its absence. A feature nobody asked for is declined, and neither a passing mention nor a request the project's own planning filed with no user behind it counts.
66
94
 
67
- A missing feature can still earn keep or promote through the cost arm. A user asking for it in an issue or a report, or working around its absence on their side, is a case someone hit, so that row is never declined for being a feature. A feature nobody asked for is declined. A passing mention is not a request, and neither is one the project's own planning records filed with no user behind it.
95
+ ### The four verdicts
68
96
 
69
- - **keep**: the defect reproduces this pass, stated with a count or a path, and the row names a cost or a reach, stated with the observation, the hit, or the shipped path.
70
- - **promote**: it qualifies for keep, and the thing it was waiting on has cleared, naming what cleared it.
71
- - **archive**: every open outcome is measured shipped, naming the commit or pull request per outcome position.
72
- - **decline**: anything else, including a defect that reproduces but names no cost and no reach, a defect the tree no longer holds, work superseded by another task, and a row whose argument rests only on its original filing.
97
+ Age is evidence in neither direction. An old hit reads the same as a new one, by whether the pass measures it as still able to recur.
73
98
 
74
- Age is evidence in neither direction. A row untouched for months is not more declinable than one filed yesterday, and a row filed yesterday is not more worth keeping. An old hit is read the same way as a new one, by whether the pass measures it as still able to recur.
99
+ | Verdict | When |
100
+ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
101
+ | keep | The defect reproduces this pass, with a count or a path, and the row names a cost or a reach with its observation, hit, or shipped path |
102
+ | promote | It qualifies for keep, and what it was waiting on has cleared, naming what cleared it |
103
+ | archive | Every open outcome is measured shipped, naming the commit or pull request per outcome position |
104
+ | decline | Anything else, including a reproduction naming no cost or reach, a defect the tree no longer holds, superseded work, or the filing alone |
75
105
 
76
106
  ### Step 6: write the items
77
107
 
@@ -79,9 +109,9 @@ Write one item per row in the intake item format, one cluster file per domain th
79
109
 
80
110
  - **`Problem:`** states what this pass measured, with the count, path, or commit behind it
81
111
  - **`Fix:`** states what applying the verdict does to the board
82
- - **`Worth it:`** names the cost or the reach the verdict rests on in one clause, or says the row names neither. A `yes` or a `later` carries no verdict on its own, so a decline can follow either word
112
+ - **`Worth it:`** names the cost or reach the verdict rests on in one clause, or says the row names neither. A `yes` or `later` alone carries no verdict.
83
113
  - **`Open:`** reads `decline, archive, keep, or promote?` on every item
84
- - **`Suggested:`** opens with exactly one of the four verdict tokens, then the reason. An archive suggestion lists every outcome position it closes, as `archive 1, 3: shipped in #NNN`
114
+ - **`Suggested:`** opens with exactly one verdict token, then the reason. An archive lists every outcome position it closes, as `archive 1, 3: shipped in #NNN`.
85
115
  - **`Overlaps:`** names a live board task that already carries the work, where one does
86
116
  - **`You:`** ships empty
87
117
 
@@ -89,42 +119,21 @@ Write `00-overview.md` last, per the intake standard, with the verdict counts by
89
119
 
90
120
  ## Apply phase
91
121
 
92
- Writes the board and task files. Refuses when this session's name starts with `planner-` or `worker-`: read it from `canon sessions list --self --json` before writing anything, and stop: `❌ Apply writes the board, which the <role> role bans. Run it from an operator or orchestrator session.` A refused or failed read proceeds, since it is indistinguishable from a solo project running no named sessions.
122
+ Writes the board and task files, so it refuses when this session's name starts with `planner-` or `worker-`. Read the name from `canon sessions list --self --json` before writing anything, and stop: `❌ Apply writes the board, which the <role> role bans. Run it from an operator or orchestrator session.`
93
123
 
94
- Every write here is a main-root write. Route a change inside an existing file the way `session-worktree` states, and report it rather than proceeding silently when that skill does not resolve.
124
+ A refused or failed read proceeds, since it is indistinguishable from a solo project running no named sessions. Every write here is a main-root write, so route a change inside an existing file the way `session-worktree` states, and report it rather than proceeding silently when that skill does not resolve.
95
125
 
96
126
  ### Step 7: read the answers
97
127
 
98
- Run `canon intake list <slug> --json` and read each item's `answer` and `suggested`.
99
-
100
- - An answer of `ok` takes the suggested verdict. A `suggested` that opens with no verdict token is malformed: report the item and skip it, never treating it as keep.
101
- - An answer opening with a verdict token overrides the suggestion. An override to archive names its own outcome positions, and one that names none is reported and skipped.
102
- - Any other answer, such as a free-text note, is reported and skipped. Never guess a verdict from prose.
103
- - An item already applied, by the test in Step 1, is skipped silently, which is what makes a re-run write nothing twice.
128
+ Read `${CLAUDE_SKILL_DIR}/references/apply.md` on reaching this step, for how each answer reads, what each verdict runs, and how a refusal routes. The file phase skips it.
104
129
 
105
130
  ### Step 8: run each verdict
106
131
 
107
- Take the task stem from the item heading. Every verdict leaves the folder slug on its task, which is how Step 1 and `task-board`'s unlinked-origin scan both see the folder as acted on.
108
-
109
- - **decline**: `canon tasks decline <stem> --reason "<reason>, backlog triage <slug> item <cluster>#<n>" --json`. The verb clears the backlog line.
110
- - **decline on a missing file**: when the stem resolves to no file in `.canon/tasks/`, `declined/`, or `archive/`, the verb would refuse as `no-match`, so skip it. Drop the dangling line from `backlog.md` directly and report it as removed with no task behind it. Any other verdict on such an item is reported and skipped, since there is no file to keep, archive, or promote.
111
- - **archive**: `canon tasks outcome <stem> --close <p> ... --json` for the named positions, then `canon tasks archive <stem> --json`. Outcomes already closed report as closed rather than refusing, so archive still runs. Archive leaves `backlog.md` alone, so drop the task's backlog line once it succeeds, then append the `## Findings` line naming the evidence to the archived file.
112
- - **keep**: append the `## Findings` line naming what reproduced. The backlog line stays.
113
- - **promote**: move the backlog line to the bottom of `## Needs a plan` in `.canon/tasks/priority.md`, with a `Waiting on` cell stating what cleared and ending in `last`, then append the `## Findings` line.
114
-
115
- Write the `## Findings` line as `- **<YYYY-MM-DD>, backlog triage <slug>:** <verdict>, <evidence>`, the bold label form `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md` fixes. It is the last write on every verdict with a task file behind it, since Step 1 reads it as applied and a line written ahead of a refused move would mark a row done that never moved.
116
-
117
- Before any promote, check the roster the way `task-board` step 4 does: a live session in this repository whose name starts with `orchestrator-` and whose `sessionId` is not this one. When one is found, write the `## Findings` line but not `priority.md` or `backlog.md`, and message that session with the row and its cell so it places the row itself.
132
+ Run each verdict through the task verbs the reference names, writing the `## Findings` line last on every verdict with a task file behind it.
118
133
 
119
134
  ### Step 9: route on a refusal
120
135
 
121
- Report every refusal on its item and continue with the rest, since one row's refusal says nothing about another's.
122
-
123
- - `decline` refusing with `no-match`: the task already moved elsewhere. Report and skip. A missing-file item never reaches the verb, per Step 8.
124
- - `outcome` refusing, or `archive` refusing with `open-outcomes`: the verdict named too few positions. Report the positions still open and leave the row on the backlog. Never retry with guessed positions.
125
- - A promote whose row already sits in `priority.md`: report it, skip the move, and still write the `## Findings` line.
126
-
127
- Close with `canon tasks validate --json` and report every finding it raises against a row this run touched. After a shell write under `.canon/tasks/`, regenerate the index with `canon indexes regen --no-stage --root <main-root> <main-root>/.canon/tasks/index.md`, since no hook fires on `Bash`.
136
+ Report every refusal on its item and continue, then close with `canon tasks validate --json`, per the reference.
128
137
 
129
138
  ## Output
130
139
 
@@ -0,0 +1,69 @@
1
+ ---
2
+ title: Backlog triage apply reference
3
+ description: How the apply phase of a backlog triage reads the operator's answers, runs each verdict through the task verbs, writes the findings line, and routes a refusal
4
+ ---
5
+
6
+ # Backlog triage apply reference
7
+
8
+ Applies to Steps 7 through 9 of `backlog-triage`, the phase that writes the board and task files once every item in a `<nn>-backlog-triage` intake folder carries an answer. The role guard and the main-root write route stay in the skill body.
9
+
10
+ ## Step 7: read the answers
11
+
12
+ Run `canon intake list <slug> --json` and read each item's `answer` and `suggested`. Never guess a verdict from prose.
13
+
14
+ | Answer | Reading |
15
+ | ------------------------------------------- | -------------------------------------------------------------------- |
16
+ | `ok` | Takes the suggested verdict |
17
+ | `ok` on a `suggested` opening with no token | Malformed, so report the item and skip it, never treating it as keep |
18
+ | Opens with a verdict token | Overrides the suggestion |
19
+ | An archive override naming no positions | Reported and skipped |
20
+ | Any other answer, such as a free-text note | Reported and skipped |
21
+
22
+ An item already applied, by the test in the skill's Step 1, is skipped silently, which is what makes a re-run write nothing twice.
23
+
24
+ ## Step 8: run each verdict
25
+
26
+ Take the task stem from the item heading. Every verdict leaves the folder slug on its task, which is how Step 1 and `task-board`'s unlinked-origin scan both see the folder as acted on.
27
+
28
+ | Verdict | Command and board write |
29
+ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
30
+ | decline | `canon tasks decline <stem> --reason "<reason>, backlog triage <slug> item <cluster>#<n>" --json`, which clears the backlog line |
31
+ | archive | `canon tasks outcome <stem> --close <p> ... --json` for the named positions, then `canon tasks archive <stem> --json` |
32
+ | keep | Append the `## Findings` line naming what reproduced, leaving the backlog line |
33
+ | promote | Move the backlog line to the bottom of `## Needs a plan` in `.canon/tasks/priority.md`, its `Waiting on` cell stating what cleared and ending in `last` |
34
+
35
+ ### Archive and missing files
36
+
37
+ An archive treats outcomes already closed as closed rather than refusing, so it still runs. Archive leaves `backlog.md` alone, so drop the task's backlog line once it succeeds, then append the `## Findings` line naming the evidence to the archived file.
38
+
39
+ A decline whose stem resolves to no file in `.canon/tasks/`, `declined/`, or `archive/` would refuse as `no-match`, so skip the verb. Drop the dangling line from `backlog.md` directly and report it as removed with no task behind it. Any other verdict on such an item is reported and skipped, since there is no file to act on.
40
+
41
+ ### The findings line
42
+
43
+ Write it as `- **<YYYY-MM-DD>, backlog triage <slug>:** <verdict>, <evidence>`, the bold label form `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md` fixes. It is the last write on every verdict with a task file behind it.
44
+
45
+ Step 1 reads the line as applied, so a line written ahead of a refused move would mark a row done that never moved.
46
+
47
+ ### Promote and a live orchestrator
48
+
49
+ Before any promote, check the roster the way `task-board` step 4 does, for a live session in this repository whose name starts with `orchestrator-` and whose `sessionId` is not this one.
50
+
51
+ When one is found, write the `## Findings` line but not `priority.md` or `backlog.md`, and message that session with the row and its cell so it places the row itself.
52
+
53
+ ## Step 9: route on a refusal
54
+
55
+ Report every refusal on its item and continue with the rest, since one row's refusal says nothing about another's.
56
+
57
+ | Refusal | Route |
58
+ | ----------------------------------------------------- | ------------------------------------------------------------------------------------- |
59
+ | `decline` with `no-match` | The task already moved elsewhere, so report and skip |
60
+ | `outcome` refusing, or `archive` with `open-outcomes` | Too few positions named, so report those still open and leave the row, never guessing |
61
+ | A promote whose row already sits in `priority.md` | Report it, skip the move, and still write the `## Findings` line |
62
+
63
+ A missing-file item never reaches the verb, per Step 8.
64
+
65
+ ### Closing the run
66
+
67
+ Close with `canon tasks validate --json` and report every finding it raises against a row this run touched.
68
+
69
+ After a shell write under `.canon/tasks/`, regenerate the index with `canon indexes regen --no-stage --root <main-root> <main-root>/.canon/tasks/index.md`, since no hook fires on `Bash`.
@@ -7,9 +7,9 @@ metadata:
7
7
 
8
8
  # Build in slices
9
9
 
10
- A session handed a plan tends to build all of it in one pass and test at the end. The diff grows past what anyone can read, a failure late in the run could have come from any of the changes, and nothing in history marks a point that worked. A defect it notices on the way gets fixed in the same diff, so the review reads two changes as one. This skill carries the discipline that keeps each step small enough to verify and revert on its own.
10
+ A session handed a plan tends to build all of it in one pass and test at the end. The diff grows past what anyone can read, a late failure could have come from any change, and nothing in history marks a point that worked.
11
11
 
12
- Load it before implementing a plan with more than one behavior or more than one file to change. Skip it for a change that is already one behavior in one place.
12
+ A defect it notices on the way gets fixed in the same diff, so the review reads two changes as one. This skill keeps each step small enough to verify and revert on its own. Load it before implementing a plan with more than one behavior or file, and skip it for one behavior in one place.
13
13
 
14
14
  ## Run the cycle per slice
15
15
 
@@ -26,7 +26,7 @@ Leave the build passing at every commit. A slice that cannot pass on its own is
26
26
 
27
27
  - Prefer one path through every layer it needs, end to end, over one whole layer at a time. A finished path proves the layers fit, and a finished layer proves nothing until the next one meets it.
28
28
  - Take the riskiest or least certain piece first. When it fails, the slices that would have rested on it are not built yet.
29
- - Build the contract first when two sides are written against it, such as a command and the skill that calls it, or a format and its reader. Commit the contract as its own slice, then each side against it.
29
+ - Build the contract first when two sides are written against it, such as a command and the skill calling it, or a format and its reader. Commit it as its own slice, then each side.
30
30
  - Read the plan's file list as the scope of the whole branch, not as the order. Cut the order from the three rules above.
31
31
 
32
32
  ## Keep each commit safe to revert on its own
@@ -40,7 +40,7 @@ Leave the build passing at every commit. A slice that cannot pass on its own is
40
40
 
41
41
  A defect outside the plan that turns up mid-build is a finding, not a slice. Fixing it on this branch widens a diff the plan scoped and hides the fix inside an unrelated review.
42
42
 
43
- - Write it in one line under the open task's `## Findings`, naming the file and what is wrong, when the build is closing a task. Open the line with the bold source-and-date label from `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md`.
43
+ - Write it as one line under the task's `## Findings`, naming the file and the fault when the build closes a task, opening with the label `${CLAUDE_SKILL_DIR}/../task-board/references/tasks.md` fixes
44
44
  - Carry it in the handover message instead when no task exists, and never create a file to hold it.
45
45
  - Leave the file as it is, even when the fix is one line.
46
46
  - Fix it here only when the plan's own change cannot pass without it, and then say so in the commit that does.
@@ -74,6 +74,8 @@ Read `${CLAUDE_SKILL_DIR}/references/adopted.md` only when extending this guidan
74
74
 
75
75
  ### Scope and ordering
76
76
 
77
+ Scope and order carry their own signs.
78
+
77
79
  - You opened a file the plan does not name and are about to edit it.
78
80
  - A commit deletes code and adds its replacement in the same change.
79
81
  - You are about to stage a test in a commit after the one that added its code.
@@ -89,6 +91,8 @@ Check each line against the branch the build produced. A line answering no is fi
89
91
 
90
92
  ### Tree and scope
91
93
 
94
+ Check the tree and the scope the same way.
95
+
92
96
  - The working tree holds no uncommitted implementation work.
93
97
  - Every file changed is in the plan's scope, or its commit says why the plan needed it.
94
98
  - Every defect noticed outside the plan is written in the task's findings or the handover, and its file is unchanged.
@@ -88,6 +88,8 @@ Read the doc each line names before running one of these. `canon docs install-an
88
88
 
89
89
  ### Design, migrate, and indexes
90
90
 
91
+ Three more verbs rewrite files in place.
92
+
91
93
  - `canon design sync` replaces the shipped design files and leaves `.claude/design/project/` alone.
92
94
  - `canon migrate --write` rewrites files that cite a retired name or layout. It reports and writes nothing without the flag. See `canon docs migrate`.
93
95
  - `canon indexes regen` rewrites each generated `index.md` from the folder's current state, so a hand edit there is lost. See `canon docs indexes`.
@@ -96,4 +98,6 @@ Read the doc each line names before running one of these. `canon docs install-an
96
98
 
97
99
  ## Source of truth
98
100
 
99
- - `canon docs install-and-sync` states what each install and sync verb writes, refuses, or leaves alone. `canon docs indexes` and `canon docs migrate` state the same for their verbs. When a doc and this skill disagree, the doc wins.
101
+ - `canon docs install-and-sync` states what each install and sync verb writes, refuses, or leaves alone
102
+ - `canon docs indexes` and `canon docs migrate` state the same for their verbs
103
+ - When a doc and this skill disagree, the doc wins