@erclx/canon 4.121.0 → 4.123.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 (116) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/auto-ship/SKILL.md +6 -5
  3. package/claude/skills/auto-ship/references/tier-failures.md +7 -4
  4. package/claude/skills/backlog-triage/REQUIREMENT.md +47 -0
  5. package/claude/skills/backlog-triage/SKILL.md +139 -0
  6. package/claude/skills/canon-rollout/SKILL.md +6 -6
  7. package/claude/skills/context-fold/REQUIREMENT.md +75 -0
  8. package/claude/skills/context-fold/SKILL.md +162 -0
  9. package/claude/skills/{docs-fold → context-fold}/references/anchor-sweep.md +1 -1
  10. package/claude/skills/{docs-fold → context-fold}/references/classify.md +4 -2
  11. package/claude/skills/context-fold/references/context-refresh.md +48 -0
  12. package/claude/skills/context-fold/references/promoted-pages.md +29 -0
  13. package/claude/skills/context-fold/references/receipt-sweep.md +43 -0
  14. package/claude/skills/context-fold/references/unusable-baseline.md +25 -0
  15. package/claude/skills/{docs-fold → context-fold}/references/wireframe-sweep.md +1 -1
  16. package/claude/skills/docs-fold/REQUIREMENT.md +8 -58
  17. package/claude/skills/docs-fold/SKILL.md +3 -278
  18. package/claude/skills/docs-sync/REQUIREMENT.md +1 -1
  19. package/claude/skills/docs-sync/SKILL.md +1 -1
  20. package/claude/skills/draft-context/REQUIREMENT.md +2 -2
  21. package/claude/skills/draft-context/SKILL.md +3 -3
  22. package/claude/skills/draft-docs/REQUIREMENT.md +1 -1
  23. package/claude/skills/draft-ready/SKILL.md +1 -1
  24. package/claude/skills/draft-wireframes/REQUIREMENT.md +1 -1
  25. package/claude/skills/draft-wireframes/SKILL.md +2 -2
  26. package/claude/skills/git-pr/SKILL.md +8 -146
  27. package/claude/skills/git-pr/references/evidence.md +114 -0
  28. package/claude/skills/git-pr/references/labels.md +20 -0
  29. package/claude/skills/git-pr/references/task-number.md +26 -0
  30. package/claude/skills/git-ship/SKILL.md +5 -4
  31. package/claude/skills/memory-capture/REQUIREMENT.md +2 -2
  32. package/claude/skills/memory-capture/SKILL.md +19 -9
  33. package/claude/skills/memory-review/REQUIREMENT.md +5 -3
  34. package/claude/skills/memory-review/SKILL.md +22 -107
  35. package/claude/skills/memory-review/references/later-phases.md +111 -0
  36. package/claude/skills/memory-review/references/receipt-format.md +19 -3
  37. package/claude/skills/plan-feature/REQUIREMENT.md +1 -1
  38. package/claude/skills/review-address/REQUIREMENT.md +1 -1
  39. package/claude/skills/review-address/SKILL.md +1 -1
  40. package/claude/skills/review-branch/SKILL.md +1 -1
  41. package/claude/skills/role-orchestrator/SKILL.md +2 -2
  42. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -1
  43. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +4 -2
  44. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +2 -2
  45. package/claude/skills/session-resume/REQUIREMENT.md +1 -1
  46. package/claude/skills/session-resume/SKILL.md +4 -3
  47. package/claude/skills/session-worktree/SKILL.md +4 -10
  48. package/claude/skills/task-board/REQUIREMENT.md +1 -1
  49. package/claude/skills/task-board/SKILL.md +7 -81
  50. package/claude/skills/task-board/references/archive.md +54 -0
  51. package/claude/skills/task-board/references/decline.md +40 -0
  52. package/claude/skills/teach-workspace/SKILL.md +8 -105
  53. package/claude/skills/teach-workspace/references/lesson-craft.md +51 -1
  54. package/claude/skills/teach-workspace/references/promotion.md +41 -0
  55. package/docs/agents/architecture-record.md +26 -0
  56. package/docs/agents/commands.md +87 -154
  57. package/docs/agents/context-audit-checks.md +2 -20
  58. package/docs/agents/context-audit.md +1 -1
  59. package/docs/agents/context-classify.md +1 -1
  60. package/docs/agents/drift-surfaces.md +192 -0
  61. package/docs/agents/index.md +12 -4
  62. package/docs/agents/install-and-sync.md +2 -181
  63. package/docs/agents/markdown-audit-weight.md +73 -0
  64. package/docs/agents/markdown-audit.md +3 -90
  65. package/docs/agents/migrate.md +34 -0
  66. package/docs/agents/records-stale.md +62 -0
  67. package/docs/agents/records.md +5 -1
  68. package/docs/agents/sandbox.md +1 -1
  69. package/docs/agents/tasks-archive.md +81 -0
  70. package/docs/agents/tasks-plans.md +149 -0
  71. package/docs/agents/tasks-validate.md +131 -0
  72. package/docs/agents/tasks.md +7 -334
  73. package/docs/target-projects.md +3 -3
  74. package/docs/workflow/ai-workflow.md +34 -29
  75. package/docs/workflow/operating-model.md +1 -1
  76. package/governance/rules/claude/555-tasks.md +2 -1
  77. package/governance/rules/core/045-memory.md +1 -1
  78. package/package.json +1 -1
  79. package/src/claude/cases/setup.ts +1 -1
  80. package/src/claude/cases/workflow.ts +22 -1
  81. package/src/commands/records.ts +146 -0
  82. package/src/commands/sandbox.ts +6 -4
  83. package/src/commands/tasks.ts +39 -29
  84. package/src/context/classify/extract.ts +1 -1
  85. package/src/context/classify/ollama.ts +1 -1
  86. package/src/context/classify/run.ts +1 -1
  87. package/src/gate/measures.ts +1 -1
  88. package/src/gate/stages.ts +1 -0
  89. package/src/migrate/skill-names.ts +15 -2
  90. package/src/records/stale.ts +243 -0
  91. package/src/sandbox/expect.ts +1 -1
  92. package/src/tasks/archive.ts +223 -92
  93. package/src/tasks/reach.ts +1 -1
  94. package/src/tasks/record.ts +60 -17
  95. package/src/tasks/validate.ts +37 -4
  96. package/standards/board.md +136 -0
  97. package/standards/commit.md +5 -0
  98. package/standards/groundwork.md +25 -36
  99. package/standards/index.md +2 -1
  100. package/standards/memory.md +13 -1
  101. package/standards/plan.md +20 -34
  102. package/standards/readme.md +8 -77
  103. package/standards/requirements.md +2 -2
  104. package/standards/tasks.md +19 -126
  105. package/standards/versioning.md +1 -1
  106. package/tooling/astro/reference.md +1 -1
  107. package/tooling/base/configs/.husky/post-merge +1 -1
  108. package/tooling/base/configs/commitlint.config.js +27 -1
  109. package/tooling/base/reference.md +1 -1
  110. package/tooling/claude/reference.md +1 -1
  111. package/tooling/go/reference.md +1 -1
  112. package/tooling/nextjs/reference.md +1 -1
  113. package/tooling/php/reference.md +1 -1
  114. package/tooling/python/reference.md +1 -1
  115. package/tooling/vite-react/reference.md +1 -1
  116. package/tooling/web/reference.md +1 -1
package/standards/plan.md CHANGED
@@ -7,7 +7,7 @@ description: Filename and slug, required sections, the suggested-and-answer cont
7
7
 
8
8
  Applies to a feature plan at `.canon/plans/feature-<slug>.md`. One file holds one concern, written before implementation starts and read by whatever executes it, so it has to carry the scope without the conversation that produced it.
9
9
 
10
- The folder is gitignored, and backed wherever a records remote is configured: `canon records push` and `canon records pull` protect it against the machine being lost there, refuse with `no-remote` where it is not, and protect nothing against a plan deleted before anyone has pushed. That is why the archive step below is a move rather than a cleanup.
10
+ The folder is gitignored, and backed by `canon records push` wherever a records remote is configured. Nothing backs a plan deleted before a push, which is why the archive step below is a move rather than a cleanup.
11
11
 
12
12
  ## Scope
13
13
 
@@ -39,7 +39,7 @@ A plan failing these is non-conforming even when it satisfies every shape rule b
39
39
 
40
40
  - Name the file `feature-<slug>.md`, with `<slug>` two to four kebab-case words naming the concern.
41
41
  - Write one concern per file. A request spanning two independent concerns takes two plans rather than one bundling both, since a bundled plan cannot be executed by two sessions or abandoned by half.
42
- - A staged batch is a concern of its own and takes its own file rather than a `**Batch N**` sub-heading inside one plan's `**Files to touch:**`. `canon tasks plan-branch` derives one branch from one plan filename, so every batch sharing a file has nothing left to open a pull request against once an earlier batch merges under that name. State a batch's dependency on the ones before it in its own `**Constraints:**`.
42
+ - A staged batch is a concern of its own and takes its own file rather than a `**Batch N**` sub-heading inside one plan's `**Files to touch:**`. One plan filename derives one branch, so batches sharing a file have nothing to open a pull request against once the first merges. State a batch's dependency on the ones before it in its own `**Constraints:**`.
43
43
  - Derive the slug from the concern rather than from a branch, because the plan is written before the branch exists.
44
44
  - Give the branch that executes the plan the same slug. A later surface finds the plan from the branch name and finds nothing when the two spellings differ.
45
45
 
@@ -60,33 +60,29 @@ The document opens with `# Feature: <short title>` and one paragraph stating wha
60
60
  | `**Questions:**` | numbered open decisions, each with a suggestion and an answer slot | Always |
61
61
 
62
62
  - Write each marker as the table gives it. `Summary` opens the prose a reader scans, so it takes an H2, and the six that follow are labels over lists rather than sections of prose.
63
- - A section written in the other spelling is still that section. `## Risks` and `**Risks:**` name one thing, and a plan carrying either has stated its risks, so neither reads as an omission. Write the table's form in a new plan and leave an existing plan's spelling alone.
64
- - Both spellings break a depth run, so `canon markdown audit` reads either as the seam it is and reports a plan for a section that genuinely runs long rather than for its whole body. The markdown standard states the rule.
63
+ - A section written in the other spelling is still that section, since `## Risks` and `**Risks:**` name one thing. Write the table's form in a new plan and leave an existing plan's spelling alone. Either spelling breaks a depth run.
65
64
  - Write `None identified.` under a required section with nothing to report rather than dropping the marker. A dropped section and an unconsidered one read identically.
66
65
  - Aim `## Summary` at a person scanning the plan, not at the session executing it. The other sections carry what execution needs.
67
66
  - Give every `**Files to touch:**` entry a backticked path and something said about it. A bare path states scope and not intent, and the reason is what an executing session checks its edit against. Lead with the path or lead with a label carrying the path, whichever reads better for the entry.
68
- - Separate the two halves with a colon, and keep every path the entry declares ahead of it. What sits before the colon is the entry's subject and reads as a target, so both sides of a rename belong there and a file merely cited by the reason does not. A reason is free to name another path, and one entry that did was read as declaring a file its branch never wrote.
69
- - Read the list as what the branch sets out to write rather than as a bound on it. The ship chain writes past it on nearly every branch, since the sync skills refresh whichever context entry and public doc the change reaches, and no planner can name those before the change exists. `canon tasks plan-reach <plan>` reads the branch back against this list and every other live plan, ahead of the pull request.
67
+ - Separate the two halves with a colon, and keep every path the entry declares ahead of it. What sits before the colon reads as a target, so both sides of a rename belong there and a file merely cited by the reason does not.
68
+ - Read the list as what the branch sets out to write rather than as a bound on it, since the doc syncs at ship write past it on nearly every branch. `canon tasks plan-reach <plan>` reads the branch back against this list and every other live plan.
70
69
  - State every count and every claim about the tree as measured during the pass that wrote the plan. A figure carried in from a summary or an earlier session is the most common way a plan ships the wrong scope.
71
70
  - Prefer a short plan over a padded one. A section filled to look thorough costs the reader the same attention as one that matters.
72
71
 
73
72
  ### Constraints
74
73
 
75
- A constraint naming a surface to leave alone forbids two different acts, and it says which. A constraint carrying only the surface leaves the executing session to guess.
74
+ A constraint naming a surface to leave alone says which of two acts it forbids, rather than leaving the executing session to guess.
76
75
 
77
- - Forbid conforming that surface to whatever shape the change introduces. This is what a scope constraint means, and it keeps the branch from growing a second concern.
76
+ - Forbid conforming that surface to whatever shape the change introduces, which keeps the branch to one concern.
78
77
  - Never forbid retargeting a pointer the change breaks. A rename, a split, or a deletion that leaves a citation behind ships a dangling reference, so repairing it is required work rather than scope creep.
79
- - Decide both acts for every surface the constraint names. Carving the distinction out for one file and leaving its siblings under the bare wording ships one correct call beside one broken reference.
78
+ - Decide both acts for every surface the constraint names, not for one file among its siblings.
80
79
 
81
- A constraint measured against work in flight expires when that work merges, and a plan is read some time after it is written. Stamp the block with what it was measured against so a reader can test whether it still holds.
80
+ A constraint measured against work in flight expires when that work merges, so stamp the block with what it was measured against.
82
81
 
83
- - Give the stamp its own leading bullet, written as Measured against `<commit>` on <YYYY-MM-DD>. One stamp covers the whole block however many tracks the constraints below it name, since a plan is written against the tree once.
84
- - Stamp the commit and not the track names. The constraint already names each track by its work and its file set, so a second list of names is a second place to keep in step. A date alone does not separate two plans written the same afternoon on either side of three merges, which is why the commit is the part a reader tests against.
85
- - Re-test a stamped constraint before honoring it with `git fetch origin main --quiet && git log <commit>..origin/main --oneline -- <the paths the constraint names>`. Any commit it reports means the track landed and the constraint is dead. Fetch inside the same command, since a remote-tracking ref left behind reports fewer merges than have landed and reads a dead constraint as live, which is the failure the stamp exists to remove.
86
- - Scope the log to the paths rather than reading every subject. A squashed merge carries a pull request number while the constraint names its track by work and file set, so an unscoped log leaves the reader matching subjects.
87
- - Read an unstamped constraint as unverified rather than as live. There is no commit to anchor the log against, so confirming one costs the open pull request list against the named paths, which is the work a stamp removes. Every plan written before this rule carries an unstamped block, which is why the default sits here rather than in a sweep over the live folder.
88
-
89
- A dead constraint fails silently in the expensive direction. A session honoring one ships the dangling citation the change created and reports success, where a session crossing a live constraint collides visibly and is caught.
82
+ - Give the stamp its own leading bullet, written as Measured against `<commit>` on <YYYY-MM-DD>. One stamp covers the whole block.
83
+ - Stamp the commit rather than the track names or a date alone. The constraint already names each track, and a date does not separate two plans written on either side of a merge.
84
+ - Re-test a stamped constraint before honoring it with `git fetch origin main --quiet && git log <commit>..origin/main --oneline -- <the paths the constraint names>`. Any commit it reports means the track landed and the constraint is dead. Fetch in the same command and scope the log to the paths, since a stale remote-tracking ref reads a dead constraint as live and a squashed subject names no track.
85
+ - Read an unstamped constraint as unverified rather than as live, and confirm it against the open pull request list for the named paths.
90
86
 
91
87
  A constraint naming a `.canon/ready/` folder is a third shape beside the two above. It makes that folder's files the verbatim source for the paths this plan's `**Files to touch:**` lists, per `ready.md`, so the executing session copies those paths rather than authoring them.
92
88
 
@@ -104,8 +100,8 @@ A constraint naming a `.canon/ready/` folder is a third shape beside the two abo
104
100
  ### Review focus
105
101
 
106
102
  - Name each input or state that would break the change if the implementation got it wrong, one bullet each, with what the change must do there.
107
- - Write it for the reviewer rather than the builder. The session writing the plan understood the risk before any diff existed, and this section is where that understanding reaches the review pass.
108
- - Keep it apart from `**Risks:**`. A risk is something the work might collide with, and a focus item is something the finished diff must be shown to handle, so a reviewer confirms the second and the executing session plans around the first.
103
+ - Write it for the reviewer rather than the builder, since this section is where the planner's understanding of the risk reaches the review pass.
104
+ - Keep it apart from `**Risks:**`. A risk is something the work might collide with, and a focus item is something the finished diff must be shown to handle.
109
105
 
110
106
  ## The suggested-and-answer contract
111
107
 
@@ -131,32 +127,22 @@ An execution that deviates rewrites the suggestion into the form below, leaving
131
127
  - Write `- Suggested: needs your call, <why>` where the answer turns on preference rather than on a technical default. Fabricating a default there hides an open question behind an accepted answer.
132
128
  - Never fill an `- Answer:` slot on behalf of the person who owns it. Recording the pick as the suggestion is what the suggestion line is for.
133
129
  - Rewrite the `- Suggested:` line to the pick execution made when it deviates from an unanswered question, and leave the slot blank. The prohibition above reaches the answer line alone, so the suggestion line is where a deviation goes.
134
- - Open the rewritten text with `overridden at execution to <pick>,` and follow it with the measurement that moved the pick. The phrase is fixed, because a suggestion carrying a number is the ordinary shape of an authored one and says nothing about who wrote it.
130
+ - Open the rewritten text with `overridden at execution to <pick>,` and follow it with the measurement that moved the pick. The fixed phrase is what tells an override from an authored suggestion, which routinely carries a number too.
135
131
  - Take a deviation from an answered question back to whoever answered it rather than rewriting either line. A filled slot is a decision already made, and a suggestion rewritten under one leaves the plan holding two picks with no default resolving them.
136
132
  - Answer in place when a question is settled in conversation, so the file and the decision do not disagree.
137
133
 
138
- The fixed phrase is what a later reader tells an override by, and the measurement alone is not, since an authored suggestion routinely carries a number of its own and a reader meeting one learns nothing about who put it there. The plan is archived at ship, so the same deviation takes one line in the open task's `## Findings` naming what shipped. That is one fact in two registers, the plan holding why the pick moved and the task holding what the tree now has, rather than two records to keep in step.
134
+ The plan is archived at ship, so the same deviation takes one line in the open task's `## Findings` naming what shipped. The plan holds why the pick moved and the task holds what the tree now has.
139
135
 
140
- This contract inverts the one an intake folder keeps, where an empty slot means unread and acceptance is typed out. A plan is written and read in one sitting with every question already surfaced, so silence is a usable default here and is not one there. Both files state the inversion, since a contract read on only one side of a boundary is the one that gets applied to the wrong document.
136
+ This contract inverts the one an intake folder keeps, where an empty slot means unread and acceptance is typed out. A plan is written and read in one sitting with every question already surfaced, so silence is a usable default here and is not one there.
141
137
 
142
138
  ## Lifecycle
143
139
 
144
- - Write the plan before implementation starts, and treat it as the scope of the run that executes it.
140
+ - Write the plan before implementation starts, and treat it as the scope of the run that executes it. A subject that has to be measured before anyone can plan against it takes a measurement track first.
145
141
  - Keep every plan at one root. A plan copied into each parallel working tree forks, and the copies answer the same question differently.
146
142
  - Amend the plan in place when a decision changes mid-flight. Do not append a second passage narrating the change, which leaves a reader to work out which of two answers is current. An execution-time deviation from a suggestion is one such amendment, and the contract above fixes which line takes it.
147
- - Move the plan to `.canon/plans/archive/` when the work it describes ships or is declined. Never delete it, because the plan is where the rejected alternative is written down and nothing else records it. The archive nests inside `.canon/plans/` rather than sitting beside it as `.claude/plans-archive/`, so an older layout is a flat sibling by that name and a current one is not.
143
+ - Move the plan to `.canon/plans/archive/` when the work it describes ships or is declined. Never delete it, because the plan is where the rejected alternative is written down. A flat `.claude/plans-archive/` sibling is the older layout.
148
144
  - Write the plan in the same session that opens the task it serves. The session executing it later inherits reasoning it would otherwise re-derive.
149
145
 
150
- ## Anti-patterns
151
-
152
- - **The plan written before the measuring.** A subject that has to be measured before anyone can plan against it belongs in a measurement track, and every plan that skipped that step had to be superseded.
153
- - **The bundled plan.** Two concerns under one slug cannot be split later without rewriting both halves.
154
- - **The question with no suggestion.** It reads as thoroughness and lands as a blocked run.
155
- - **The answer filled in by the author.** A slot holding the writer's own pick destroys the only signal that anyone else agreed.
156
- - **The deviation recorded off the plan.** The reasoning lands in a pull request description no later reader of the plan opens, and the archived plan reads as though its suggestion held.
157
- - **The count carried in.** A figure quoted from an earlier session survives the change that invalidated it, and the scope built on it is quietly wrong.
158
- - **The plan deleted on ship.** The considered-and-dropped reasoning goes with it, and the next session re-proposes what this one rejected.
159
-
160
146
  ## Template
161
147
 
162
148
  ```markdown
@@ -147,85 +147,16 @@ See the [contributing guidelines](CONTRIBUTING.md).
147
147
 
148
148
  ### Correct
149
149
 
150
- ````markdown
151
- # Auth SDK
152
-
153
- Lightweight authentication library for Node.js with OAuth2 and JWT support.
154
-
155
- ## Features
156
-
157
- - OAuth2 provider integration (Google, GitHub, Azure)
158
- - JWT token generation and validation
159
- - Session management with Redis support
160
- - TypeScript support with full type definitions
161
-
162
- ## Installation
163
-
164
- ```bash
165
- npm install auth-sdk
150
+ ```plaintext
151
+ Lightweight authentication library for Node.js with OAuth2 and JWT support. # one line naming what it is
152
+ ## Quick start # sentence case, a runnable snippet follows
153
+ [MIT](LICENSE) # links the license file
166
154
  ```
167
155
 
168
- ## Quick start
169
-
170
- ```javascript
171
- import { AuthClient } from 'auth-sdk'
172
-
173
- const client = new AuthClient({
174
- provider: 'google',
175
- clientId: process.env.CLIENT_ID,
176
- })
177
-
178
- const user = await client.authenticate(code)
179
- ```
180
-
181
- ## Documentation
182
-
183
- Visit [docs.auth-sdk.dev](https://docs.auth-sdk.dev) for full API reference.
184
-
185
- ## Support
186
-
187
- - Report bugs via [GitHub Issues](../../issues)
188
- - Community support on [Discord](https://discord.gg/example)
189
-
190
- ## License
191
-
192
- [MIT](LICENSE)
193
- ````
194
-
195
156
  ### Incorrect
196
157
 
197
- ````markdown
198
- # Auth SDK
199
-
200
- This is a seamless and powerful authentication library that allows developers to easily integrate robust OAuth2 functionality.
201
-
202
- ## Why Use This?
203
-
204
- Basically, this library is just amazing and will revolutionize how you handle auth.
205
-
206
- ## Installation
207
-
208
- Simply run the following command to install:
209
-
210
- ```bash
211
- npm install auth-sdk
158
+ ```plaintext
159
+ ## Why Use This? # title case, and a pitch rather than a feature list
160
+ Simply run the following command to install: # filler qualifier before the command
161
+ Copyright (c) 2026 Example Corp # license text inline rather than linked
212
162
  ```
213
-
214
- ## API Documentation
215
-
216
- ### AuthClient Class
217
-
218
- #### Constructor
219
-
220
- constructor(options: AuthOptions)
221
-
222
- [...full API docs inline, should link to external docs...]
223
-
224
- ## License
225
-
226
- MIT License
227
-
228
- Copyright (c) 2026 Example Corp
229
-
230
- [...full license text, should reference LICENSE file...]
231
- ````
@@ -14,7 +14,7 @@ Governs the product-scope document at `canon/REQUIREMENTS.md`: problem, goals, n
14
14
  Does not govern:
15
15
 
16
16
  - Rationale for a technical choice: `architecture.md`
17
- - Execution order across the work the scope generates: `tasks.md`
17
+ - Execution order across the work the scope generates: `board.md`
18
18
  - Per-domain structure and narrative: `context.md`
19
19
 
20
20
  ## What goes in
@@ -40,7 +40,7 @@ Use `## Problem`, `## Goals`, `## Non-goals`, `## MVP features`, `## Tech stack`
40
40
 
41
41
  The MVP list is a historical record of the original scope. Once those features ship it stays as written. Do not renumber it, do not append to it, and do not annotate entries with status. A reader telling the original scope apart from what followed depends on the first one staying legible.
42
42
 
43
- Later scope arrives as a new section rather than as an extension of the MVP list. Name the section for what it delivers and state its entries as outcomes, the same way the goals are stated. Nothing sequences either list into versions. Work reaches the board as discrete tasks under `tasks.md`, which orders them by readiness, so a section here states what is wanted and never when it lands.
43
+ Later scope arrives as a new section rather than as an extension of the MVP list. Name the section for what it delivers and state its entries as outcomes, the same way the goals are stated. Nothing sequences either list into versions. Work reaches the board as discrete tasks under `tasks.md`, and `board.md` orders them by readiness, so a section here states what is wanted and never when it lands.
44
44
 
45
45
  ## Distribution
46
46
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Tasks reference
3
- description: Folder layout, filename convention, readiness groups, and content rules for .canon/tasks/
3
+ description: Folder layout, filename convention, file format, origin lines, and archiving for task files under .canon/tasks/
4
4
  ---
5
5
 
6
6
  # Tasks reference
@@ -13,10 +13,11 @@ The folder is gitignored. Board state changes when work ships rather than when a
13
13
 
14
14
  ## Scope
15
15
 
16
- Governs the task board under `.canon/tasks/`: folder layout, filenames, frontmatter, file format, origin lines, execution ordering, the backlog beside it, and archiving.
16
+ Governs the task files under `.canon/tasks/`: folder layout, filenames, frontmatter, file format, origin lines, archiving, and declining.
17
17
 
18
18
  Does not govern:
19
19
 
20
+ - Execution order, readiness groups, the backlog, and the generated index beside the tasks: `board.md`
20
21
  - The plan file a task cites, its sections, and its answer contract: `plan.md`
21
22
  - Phase-label format and which surfaces a label may appear on: `versioning.md`
22
23
  - Architectural reasoning that outlives a task: `architecture.md`
@@ -37,113 +38,11 @@ Does not govern:
37
38
  └── v13.0-toolkit-drift.md # canon-allow-reference: illustrates the vXX.Y-slug filename this section defines
38
39
  ```
39
40
 
40
- `canon tasks list` reports each file's readiness from those two surfaces, so a reader never infers it from the folder.
41
-
42
41
  One file per task is what keeps the board safe under parallel sessions. Two sessions working different tasks never write the same file, which matters because a gitignored board has no history to recover a clobbered write from.
43
42
 
44
- Siblings sit in the folder without being tasks, and each earns its place by being governed somewhere. `index.md`, `priority.md`, and `backlog.md` are governed here. Every `session-` file is a pre-compaction handoff governed by `session.md`, and each is optional: a project whose sessions never approach a compaction carries none. Anything filtering the folder to tasks skips all of them, so a name outside the set is a task whatever it holds.
45
-
46
- `backlog.md` is optional too. A project small enough that every task it holds would be planned soon carries none, and the ordering rules below then describe the whole board.
47
-
48
- The handoff takes one file per session for the reason a task does. A single shared path puts two sessions closing near each other on one file that neither can watch the other write, and the loser leaves no trace on a board with no history behind it.
49
-
50
- `index.md` is generated from sibling frontmatter. The folder is gitignored, so the whole-repo index walk skips it and a hook passing the changed path regenerates it instead. Never hand-edit it.
51
-
52
- The catalog is the one reader that filters nothing, so it carries a row per sibling alongside the tasks. That is what a folder catalog is for, and the handoffs are what make it worth stating: a board accumulates one row per session that ever wrote one, with nothing pruning them. Anything reading the catalog as the backlog therefore does its own filtering, and a reader that takes every row as a task reports the handoffs as queued work.
53
-
54
- The `task-board` skill creates and archives task files, and the archive carries the task's plan with it. `docs-fold` marks outcomes `[x]` in an existing file. Neither does the other's job.
55
-
56
- ## Ordering
57
-
58
- `priority.md` carries execution order and what each task is waiting on. The generated index sorts by filename and says nothing about order, so without this file board state gets reconstructed by hand every session. Why a row sits where it does inside its group is stated on the row itself, in the column that already carries what the task is waiting on, one line per row.
59
-
60
- That cell is the only home sequencing rationale has. Rationale spanning several rows, why one group of work runs before another, is carried by nothing and reaches a later session only through whoever remembers it. Naming the gap is deliberate: a second document holding it would be the version-sequencing surface this board replaced, and a row already states what it waits on, which is the part of the reasoning a reader acts on.
61
-
62
- Group tasks by readiness rather than by status, one row per task, under the columns each group fixes below. Keep it to links and blockers: tables, plus at most one sentence per section. A paragraph in `priority.md` is a defect whatever it says. Stating the shape this way is what lets a single diff fail, since a size cap only trips after the fact and every addition looks defensible on its own.
63
-
64
- Readiness is three groups under fixed headings, `## Run now`, `## Up next`, and `## Needs a plan`, in that order. The names are the contract rather than a suggestion, because a board grouped by readiness under names of its own satisfies every other rule here and still reads as empty to anything counting rows under a heading. Add no fourth group. A task belongs to exactly one, and the tests are read in order.
65
-
66
- - `## Run now`: a written plan covers every open outcome, and the task carries no reason it cannot start. A collision against the files something already running touches is one such reason, and the `Touches` column is what states it. A worker is handed a task from this group alone.
67
- - `## Up next`: a written plan exists, and the task carries a stated reason it cannot start. The `Waiting on` column names that reason.
68
- - `## Needs a plan`: everything else. The task has no plan, or the plan it carries no longer describes the work.
69
-
70
- Each group fixes its own columns, which follow from the test above it rather than from preference. Neither half of the `## Run now` test is checkable without the file set and the plan sitting beside the task.
71
-
72
- A `Touches` cell is copied from its plan's own list, so it states what the branch sets out to write rather than a bound on it, and a branch outgrows it while the row still reads as it did at dispatch. Correct the cell from the branch rather than from the plan once one is running, since the plan is the prediction that already went stale and only the diff says what was written. `canon tasks plan-reach <plan>` reads that diff against every live plan and every cell in this group, and the row's owner is the one who writes the correction: a worker never edits this board.
73
-
74
- Row position inside `## Needs a plan` is the order those tasks get planned in, top first. The three tests answer whether a task can start, which is mechanical, and none of them answers which task is worth starting, which is a judgment no column holds. Position is where that judgment is recorded, so the top row is the answer to what to plan next and a reader needs no other surface to get it. The other two groups take the same reading, and it costs them little, since a group holding what is already planned is short by construction.
75
-
76
- Position alone carries it, and no rank column exists. A number beside each row is a second thing to keep in step with the order it duplicates, and the file is edited by one session at a time, so the order the rows are written in is already unambiguous. State on each row why it sits where it does, in the same cell that carries what it is waiting on. A position with no stated reason is re-derived from memory by the next session, which is the failure the ordering replaces rather than moves.
77
-
78
- The `Waiting on` column under `## Up next` carries that reason in one of three forms. `## Needs a plan` states no file set at all, because a task with no plan has no bounded one to state. A group with no rows keeps its heading and its header row.
79
-
80
- Under `## Up next` a collision names the file held by the task already running, a sibling task names that task, and an external condition names both the condition and what would satisfy it. Naming what would satisfy it is what separates a blocked row from one nobody has examined, so a cell stating a condition with no way out of it fails the test. The header text is the contract the way the group names are, because anything reading the cell resolves the column by header rather than by position.
81
-
82
- Under `## Needs a plan` the cell carries two halves and each takes one clause: what the task needs before it can be planned, then why it sits at this position. The second clause is comparative, and it names what the row is ranked against in one of two forms: a sibling row, or the class of rows it sits in. The row `leads`, `heads`, `opens`, `closes`, `trails`, `precedes`, `follows`, or `outranks` whichever it names, or `sits under`, `above`, or `below` it. Either form puts something other than this row in the clause, which is what makes the position readable by anyone but its author.
83
-
84
- Why the task matters is not that claim, and it is what the cell drifts into. A reason naming no other row ranks against nothing, so every row reads defensible on its own and the order records when each was filed. The comparison replaces that reason rather than joining it, since the cell still takes two clauses and no third one is licensed by naming a rival in the second. A cell running past that is the paragraph this file already deletes, arriving one row at a time rather than all at once, and the group is where it costs the most, since it holds the rows nobody has read recently and is the longest group on any board that needs a backlog at all.
85
-
86
- `canon tasks validate` reads that clause back, so a cell phrased comparatively and unusually reports as unranked. The vocabulary above is the whole of what it recognizes, and widening it is a change to the check rather than something the cell may decide for itself.
87
-
88
- ```markdown
89
- ---
90
- title: Priority
91
- description: One line on what the board covers
92
- ---
93
-
94
- # Priority
95
-
96
- ## Run now
43
+ Siblings sit in the folder without being tasks, and each earns its place by being governed somewhere. `index.md`, `priority.md`, and `backlog.md` are governed by `board.md`. Every `session-` file is a pre-compaction handoff governed by `session.md`, and each is optional: a project whose sessions never approach a compaction carries none. Anything filtering the folder to tasks skips all of them, so a name outside the set is a task whatever it holds.
97
44
 
98
- | Task | Touches | Plan |
99
- | ------------------------------- | ----------------------- | ------------------------------------ |
100
- | [vXX.Y <slug>](vXX.Y-<slug>.md) | <what the task touches> | [<slug>](../plans/feature-<slug>.md) |
101
-
102
- ## Up next
103
-
104
- | Task | Touches | Waiting on |
105
- | ---- | ------- | ---------- |
106
-
107
- ## Needs a plan
108
-
109
- | Task | Waiting on |
110
- | ------------------------------- | --------------------------------------------------------------------------------- |
111
- | [vXX.Y <slug>](vXX.Y-<slug>.md) | <what it needs before it can be planned, and which row or class it ranks against> |
112
- ```
113
-
114
- The tests live here so the board does not carry them. Writing them as a sentence under each heading produces the paragraph the rule above deletes, and a criterion with no home gets restated from memory every time the board is touched.
115
-
116
- ## The backlog
117
-
118
- A task sits on the board when it would plausibly be planned within the next few waves, and on `backlog.md` otherwise. The call is a judgment, so restate it whenever the board is swept rather than making it once: a backlogged task rises when the work in front of it lands or the world changes under it, and a board row falls to the backlog when it stops being near-term.
119
-
120
- A mechanical test over age or origin was the alternative and neither predicts what gets picked next, which is the judgment the ordering exists to carry. Leaving everything on the board is the other alternative, and it is what produces a group too long to rank, where the ordering means nothing because nobody can hold the whole list in one reading.
121
-
122
- What the split buys is that the board is short enough for its order to be read, and what it costs is a second surface to keep. That trade only pays while the backlog stays honest about what it is, which is why it carries no order, no groups, and no readiness claim.
123
-
124
- Nothing is deleted. A backlogged task keeps its file, its findings, and its frontmatter, and the backlog row is a pointer at that file. The folder is gitignored and has no history behind it, so a row dropped without landing somewhere readable is gone with nothing to recover it from.
125
-
126
- The backlog is a flat list of links under one heading, sorted by filename. Sorting mechanically is what keeps it from reading as a queue: the order is the same order the index already sorts in, so no position on it means anything.
127
-
128
- ```markdown
129
- ---
130
- title: Backlog
131
- description: One line on what the backlog holds
132
- ---
133
-
134
- # Backlog
135
-
136
- Unordered. Nothing here is scheduled, and a task rises to `priority.md` when it becomes near-term.
137
-
138
- - [vXX.Y <slug>](vXX.Y-<slug>.md)
139
- - [vXX.Y <slug>](vXX.Y-<slug>.md)
140
- ```
141
-
142
- Add no fourth readiness group in place of this file. The three group names are the contract, and a backlog is a separate surface rather than a group because it makes no readiness claim at all: it says nobody has scheduled the task, which is a fact about attention rather than about whether the work can start.
143
-
144
- ## Validation
145
-
146
- `canon tasks validate` reads the board against the tree, and what it checks, what it refuses on, and what it reports are at `canon docs tasks`. Run it when the readiness claim is made rather than on a schedule, since the board is gitignored per-machine scratch and no shared moment exists to hang it on.
45
+ The `task-board` skill creates and archives task files, and the archive carries the task's plan with it. `context-fold` marks outcomes `[x]` in an existing file. Neither does the other's job.
147
46
 
148
47
  ## Filenames
149
48
 
@@ -171,7 +70,7 @@ Two headings, `## Outcomes` and `## Findings`. Outcomes are future and checkable
171
70
 
172
71
  Add no third heading. Status stays inline on an outcome rather than becoming an "In progress" section.
173
72
 
174
- Size the outcomes so one pull request closes all of them. A task whose outcomes span two pull requests ships the first half and leaves the rest open, with nothing recording which outcomes the merged work covered, so the board reads as in-progress work that no branch is carrying. Split the task before handing it off rather than after. This is what `## Archiving` below depends on, since a task closes whole or not at all.
73
+ Size the outcomes so one pull request closes all of them, and split the task before handing it off rather than after. A task closes whole or not at all, so outcomes spanning two pull requests leave the board showing in-progress work no branch carries.
175
74
 
176
75
  A sliced task is the one allowed exception: a task the operator decided to ship across several pull requests on purpose, one slice each. Its `Pull request:` line lists every slice, so the board records which merged work covered which outcomes, and it closes whole when the last slice's merge finds every outcome ticked.
177
76
 
@@ -215,29 +114,27 @@ Every task names where it came from, through a `Plan:`, `Groundwork:`, `Intake:`
215
114
 
216
115
  A task with no origin is either lost context or work nobody decided to do. The invariant runs both ways, and the second direction is the one that bites: a groundwork track, an intake folder, or an open issue that no task points at is work already decided and on its way to being forgotten.
217
116
 
218
- An intake folder answers that direction at folder scope rather than item scope, since one dump dispositions many items and most close without ever becoming a task. What names a folder is every item answered and no task citing it, on the board or in the archive. That is a dump nobody acted on. Counting the archive and declined folder beside the board is what separates it from one already promoted and settled, and a check reading the board alone calls every finished folder abandoned.
117
+ An intake folder answers that direction at folder scope rather than item scope. A folder with every item answered and no task citing it, live, archived, or declined, is a dump nobody acted on.
219
118
 
220
119
  `Plan:`, `Groundwork:`, and `Intake:` name their target as a markdown link whose text is the file or folder stem, so the line resolves on a ctrl-click the way `priority.md` rows already do. Write the path relative to `.canon/tasks/`, which makes it `../plans/`, `../groundwork/`, and `../intake/`. A path written from the project root renders as a link and resolves to nothing in an editor rooted at the project. `Ready:` names the ready folder a task's plan copies from, as a link to `../ready/<nn>-<slug>/` with the trailing slash a folder carries. Include it only when the folder exists. `canon tasks archive` reads it to move the folder, and falls back to a `.canon/ready/<nn>-<slug>/` path in the plan when a task carries no line.
221
120
 
222
121
  `Issue:` stays a bare `#NNN`, since an issue number is not a path and a full URL would write the remote into a gitignored file.
223
122
 
224
- Phase-label format and where labels may appear are governed by `standards/versioning.md`.
225
-
226
123
  `Plan:` points at `../plans/feature-<slug>.md` while the task is open. Once the task ships and the plan is archived, it points at `../plans/archive/feature-<slug>.md`, and at `../../plans/archive/feature-<slug>.md` once the task itself is archived a folder deeper. Retarget both halves of the link rather than dropping it, so a completed task still leads to the reasoning behind it.
227
124
 
228
- A project that archived plans before the folder nested under `.canon/plans/` holds closed tasks pointing at `../plans-archive/`, or at `../.tmp/plans-archive/` from before the durable records left the scratch tree. Each form resolves against the files it names, so leave those pointers where they are. A named route now moves the folder and retargets its pointers together, but no automation runs it, so an unmigrated project keeps holding the old spelling until someone does, and a task retargeted without its plan moving leads nowhere.
125
+ A project that archived plans before the folder nested under `.canon/plans/` holds closed tasks pointing at `../plans-archive/` or `../.tmp/plans-archive/`. Leave those pointers where they are, since each resolves against the files it names and a task retargeted without its plan moving leads nowhere.
229
126
 
230
- One plan per task. A plan cited by two tasks is a misfile rather than a shape to design for, which is why the sweep counts citations before archiving: the count is a guard against the misfile stranding a pointer, not support for the shape.
127
+ One plan per task. A plan cited by two tasks is a misfile rather than a shape to design for.
231
128
 
232
- `canon tasks plan-link <task> <plan>` writes or corrects the `Plan:` line. `plan-feature` calls it right after the plan file lands, when an existing task names the feature, so the line is a mechanical write rather than hand-edited markdown.
129
+ `canon tasks plan-link <task> <plan>` writes or corrects the `Plan:` line, so the line is a mechanical write rather than hand-edited markdown.
233
130
 
234
131
  `Groundwork:` points at `../groundwork/<slug>/`, the folder `plan-groundwork` fills. It names the surface it points at the way `Plan:` does. Use this key alone. `Research record` and `Decision record` are earlier spellings of the same thing and both convert to it.
235
132
 
236
- `Intake:` points at `../intake/<slug>/`, the folder an intake pass fills. Use it rather than `Groundwork:`, because a groundwork track measures one question in depth while an intake dispositions many across a tree, and one key covering both loses which kind of pass produced the task. The line names the folder rather than an item inside it. A task routinely promotes several items at once, so an anchored line would name one and drop the rest, and the item numbers belong in that task's `## Findings`.
133
+ `Intake:` points at `../intake/<slug>/`, the folder an intake pass fills. Use it rather than `Groundwork:`, so the line records which kind of pass produced the task. It names the folder rather than an item inside it, and the item numbers belong in that task's `## Findings`.
237
134
 
238
- `Pull request:` records which pull requests carry the task's work, each as a bare `#NNN` the way `Issue:` does. It lists every pull request that shipped part of the task, oldest first and separated by commas, as in `Pull request: #NNN, #NNN`, and a task shipped whole lists one. It is not an origin, so a task without one is well-formed. `git-pr` writes it when a pull request opens, which is the one step that always runs whether the chain drives it or a person does, and appends to the line rather than replacing it.
135
+ `Pull request:` records which pull requests carry the task's work, each as a bare `#NNN` the way `Issue:` does. It lists every pull request that shipped part of the task, oldest first and separated by commas, as in `Pull request: #NNN, #NNN`, and a task shipped whole lists one. It is not an origin, so a task without one is well-formed. `git-pr` appends to it when a pull request opens.
239
136
 
240
- The line is what lets a merge close its own task. Every merge on `main` is a squash carrying the number in its subject, so the number survives where a branch name does not, and `canon tasks archive --pull-request <n>` finds the task by any number the line lists and archives it only on the last, since outcomes are ticked at ship time and an earlier slice can merge after the last one shipped. Without the line the board can only be swept blind, and a blind sweep cannot tell a shipped task from an abandoned one. One pull request, one task: two tasks naming the same number refuse to archive rather than both moving.
137
+ The line is what lets a merge close its own task. `canon tasks archive --pull-request <n>` finds the task by any number the line lists and archives it only on the last. One pull request, one task: two tasks naming the same number refuse to archive rather than both moving.
241
138
 
242
139
  ## What goes in
243
140
 
@@ -257,26 +154,22 @@ The line is what lets a merge close its own task. Every merge on `main` is a squ
257
154
 
258
155
  ## Archiving
259
156
 
260
- Never delete a task file. A shipped task moves to `.canon/tasks/archive/` under its own name, and the live index regenerates without it. `canon tasks archive` owns that move, and what it does and what it refuses on are at `canon docs tasks`.
261
-
262
- The archive nests inside `.canon/tasks/` rather than sitting beside it as a flat `.claude/task-archive/`. Nesting is what lets a reader tell the two shapes apart on sight: the flat sibling is what a binary predating this convention still writes, so meeting one names an older checkout rather than a second archive to reconcile against this one.
263
-
264
- One destination rather than a per-project choice is what lets the move happen without asking. It mirrors the plans archive at `.canon/plans/archive/`, sitting inside the folder it archives the same way, and it inherits the board's own ignore entry rather than needing one of its own. The cost is that an archived task does not appear in diffs, which is the cost the live board already carries.
157
+ Never delete a task file. A shipped task moves to `.canon/tasks/archive/` under its own name, and the live index regenerates without it. `canon tasks archive` owns that move, and what it does and what it refuses on are at `canon docs tasks-archive`.
265
158
 
266
- Archiving a task archives its plan alongside it, when the closing task is that plan's last live citation. The archived task's `Plan:` line is retargeted at `../../plans/archive/feature-<slug>.md`, a folder deeper than the live task wrote it, so a completed task still leads to the reasoning behind it. The ready folder the task's `Ready:` line names moves with the plan to `.canon/ready/archive/<nn>-<slug>/`, and the `Ready:` line and the plan's path to the folder are retargeted, since the folder is the plan's verbatim source. A plan several tasks share stays live and the task archives anyway, since moving it on the first task to close strands every sibling's pointer at a path that has gone. Its ready folder stays with it.
159
+ The archive nests inside `.canon/tasks/` rather than sitting beside it as a flat `.claude/task-archive/`, which is what a binary predating this convention still writes. Meeting the flat sibling names an older checkout rather than a second archive to reconcile.
267
160
 
268
- One act rather than two is what makes the pair safe. The merge is the event that settles a plan, and a `post-merge` hook reaching the archive with nobody watching cannot act on a warning, so a second call after it would be a second failure point leaving the task archived and the plan live.
161
+ Archiving a task archives its plan alongside it, when the closing task is that plan's last live citation. The archived task's `Plan:` line is retargeted at `../../plans/archive/feature-<slug>.md`. The ready folder its `Ready:` line names moves with the plan to `.canon/ready/archive/<nn>-<slug>/`, and both pointers to it are retargeted. A plan several tasks share stays live with its ready folder, and the task archives anyway.
269
162
 
270
- A task with an open outcome stays on the board, and so does its plan. Close it, or cut it from the task when the work is being abandoned, so what was dropped is recorded rather than inferred from an archived file. Cutting means striking the outcome's body: `- ~~<outcome>~~ <why>`. `archiveTask` reads a struck body as cut whatever its checkbox holds, so a task carrying only cut outcomes still archives and a mixed task carries both counts on its success record.
163
+ A task with an open outcome stays on the board, and so does its plan. Close it, or cut it from the task when the work is being abandoned, so what was dropped is recorded rather than inferred from an archived file. Cutting means striking the outcome's body: `- ~~<outcome>~~ <why>`. A struck body reads as cut whatever its checkbox holds, so a task carrying only cut outcomes still archives.
271
164
 
272
165
  ## Declining
273
166
 
274
167
  A task decided against moves to `.canon/tasks/declined/` rather than `.canon/tasks/archive/`. The two folders answer different questions: archive means the work shipped, declined means somebody decided against doing it. Neither reading fits a task that is merely unscheduled, which stays on `backlog.md` rather than moving anywhere, since nobody has decided against it and it may still rise when the board has room.
275
168
 
276
- `canon tasks decline` carries no outcome-state gate. A task can be decided against at any outcome state, open outcomes included, which is what separates its refusal set from archive's: the two never share one, since a shared gate would let one archive a task that cannot yet ship or decline one that already has.
169
+ `canon tasks decline` carries no outcome-state gate, so a task can be decided against at any outcome state, open outcomes included.
277
170
 
278
171
  The decision is recorded on the task itself with a `Declined:` line, in the `Plan:`/`Pull request:` family: `Declined: <reason>, <who> on <YYYY-MM-DD>`. It anchors the same way `Pull request:` does, after the last origin line the task carries. The line is free prose after the colon, since it names no file to link.
279
172
 
280
173
  Declining a task moves its plan alongside it the same way archiving does, when the declining task is that plan's last live citation. A plan several tasks share stays where it is, and a declined task's plan lands in `.canon/plans/archive/` indistinguishable from a shipped one by folder alone. The task file under `.canon/tasks/declined/` is what records which it was.
281
174
 
282
- The move clears whichever of `priority.md` or `backlog.md` holds the task's row, since a decided-against task most often comes off the backlog before anyone plans it, but a row already promoted to the ordering file is cleared the same way archive clears it.
175
+ The move clears whichever of `priority.md` or `backlog.md` holds the task's row.
@@ -14,7 +14,7 @@ Governs the two version namespaces, phase labels and semver tags, and which surf
14
14
  Does not govern:
15
15
 
16
16
  - The format of a phase label, which is project-specific by the rule below
17
- - Task filenames and board layout: `tasks.md`
17
+ - Task filenames and folder layout: `tasks.md`
18
18
  - Commit subject, branch name, and pull request title format: `commit.md`, `branch.md`, and `pr.md`
19
19
  - Voice, rhythm, and sentence construction in any text carrying a label: the `write-human` skill
20
20
  - Punctuation, formatting, and word choice in any text carrying a label: `markdown.md`
@@ -58,7 +58,7 @@ Append to the `## Scripts` table:
58
58
 
59
59
  ## CI docs (extend)
60
60
 
61
- In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `astro check` passes ``. The Build row's assertion reads: `` `astro build` succeeds ``.
61
+ In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `astro check` passes ``. The Build row's assertion reads: `` `astro build` succeeds ``. <!-- audit-ignore-citations: canon/context/ci.md -->
62
62
 
63
63
  ## Social card route
64
64
 
@@ -7,7 +7,7 @@
7
7
  # to hand a worker. This is the only event that lands after a merge.
8
8
  #
9
9
  # Candidates rather than closed tasks, because an all-`[x]` board entry does not
10
- # mean the work merged. `docs-fold` marks outcomes on the branch as step 1 of
10
+ # mean the work merged. `context-fold` marks outcomes on the branch as step 1 of
11
11
  # the ship chain, so a task can read closed while its pull request is still open
12
12
  # or already abandoned. The board is gitignored and an archived task has nothing
13
13
  # to recover it, so `task-board` confirms the work is on `main` before moving
@@ -22,12 +22,38 @@ const subjectFirstWordCase = (parsed) => {
22
22
  ]
23
23
  }
24
24
 
25
+ const CO_AUTHOR_TRAILER = /^co-authored-by:/i
26
+ const CLAUDE_OR_ANTHROPIC = /\b(claude|anthropic)\b/i
27
+
28
+ // Reads raw rather than footer, since the parser files a lone trailer under
29
+ // body and leaves footer null.
30
+ const noClaudeCoAuthor = (parsed) => {
31
+ const offending = (parsed.raw ?? '')
32
+ .split('\n')
33
+ .find(
34
+ (line) => CO_AUTHOR_TRAILER.test(line) && CLAUDE_OR_ANTHROPIC.test(line),
35
+ )
36
+
37
+ return [
38
+ offending === undefined,
39
+ `remove the co-author trailer crediting Claude or Anthropic: ${offending}`,
40
+ ]
41
+ }
42
+
25
43
  const config = {
26
44
  extends: ['@commitlint/config-conventional'],
27
- plugins: [{ rules: { 'subject-first-word-case': subjectFirstWordCase } }],
45
+ plugins: [
46
+ {
47
+ rules: {
48
+ 'subject-first-word-case': subjectFirstWordCase,
49
+ 'no-claude-co-author': noClaudeCoAuthor,
50
+ },
51
+ },
52
+ ],
28
53
  rules: {
29
54
  'subject-case': [0],
30
55
  'subject-first-word-case': [2, 'always'],
56
+ 'no-claude-co-author': [2, 'always'],
31
57
  'header-max-length': [2, 'always', 72],
32
58
  'scope-case': [2, 'always', 'lower-case'],
33
59
  'subject-full-stop': [2, 'never', '.'],
@@ -13,7 +13,7 @@ Golden config files live in `tooling/base/configs/` and are copied into the targ
13
13
  - `.prettierrc`: `semi: false`, `singleQuote: true`, plus a parser override per non-standard extension (`.mdx` to `markdown`).
14
14
  - `.shellcheckrc`: `external-sources=true`. Required for shellcheck to follow `source` directives.
15
15
  - `.editorconfig`: `root = true`, with an `[*.sh]` block setting `indent_style = space` and `indent_size = 2`.
16
- - `commitlint.config.js`: ESM default export extending `@commitlint/config-conventional`. Rules are `header-max-length: 72`, `scope-case: lower-case`, `subject-full-stop: never`, and `subject-case` disabled.
16
+ - `commitlint.config.js`: ESM default export extending `@commitlint/config-conventional`. Rules are `header-max-length: 72`, `scope-case: lower-case`, `subject-full-stop: never`, `subject-case` disabled, and a local `no-claude-co-author` rule refusing a `Co-authored-by` trailer that names Claude or Anthropic.
17
17
  - `.husky/`: `pre-commit`, `commit-msg`, `pre-push`, `post-merge`, `post-rewrite`.
18
18
  - `.vscode/extensions.json` and `.vscode/settings.json`: editor wiring for Prettier, cspell, shfmt, and shellcheck.
19
19
  - `scripts/verify.sh`: the maintenance entry point behind `check`.
@@ -25,7 +25,7 @@ canon/
25
25
  .canon/
26
26
  ├── tasks/ ← seeded. One file per task plus a generated index.md, local scratch only. `archive/` holds the tasks that shipped.
27
27
  ├── diagrams/ ← seeded. Per-kind Mermaid views. `index.md` is the discovery anchor. `<kind>.md` files hold one diagram each, grouped by the `category` frontmatter field.
28
- ├── plans/ ← execution detail for multi-step tasks. `feature-*.md` entries swept by docs-fold into `archive/`.
28
+ ├── plans/ ← execution detail for multi-step tasks. `feature-*.md` entries swept by context-fold into `archive/`.
29
29
  ├── review/ ← one subfolder per producer. `branch/` for review-branch, `feedback/` for canon feedback, `memory/` for memory-review with its own `archive/`, `design/` for canon design render.
30
30
  ├── tmp/ ← ephemeral scratch space, safe to delete without loss
31
31
  └── memory/ ← session facts no context entry owns. `index.md` regenerated by a hook.
@@ -57,4 +57,4 @@ Append to the `## Scripts` table:
57
57
 
58
58
  ## CI docs (extend)
59
59
 
60
- In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `go vet ./...` passes ``. Add a Lint row whose assertion reads: `` `golangci-lint run` passes ``. Add a Tests row whose assertion reads: `` `go test -race ./...` exits 0 ``.
60
+ In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `go vet ./...` passes ``. Add a Lint row whose assertion reads: `` `golangci-lint run` passes ``. Add a Tests row whose assertion reads: `` `go test -race ./...` exits 0 ``. <!-- audit-ignore-citations: canon/context/ci.md -->
@@ -50,4 +50,4 @@ Append to the `## Scripts` table:
50
50
 
51
51
  ## CI docs (extend)
52
52
 
53
- In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `next typegen && tsc --noEmit` passes ``.
53
+ In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `next typegen && tsc --noEmit` passes ``. <!-- audit-ignore-citations: canon/context/ci.md -->
@@ -71,4 +71,4 @@ Append to the `## Scripts` table:
71
71
 
72
72
  ## CI docs (extend)
73
73
 
74
- In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `phpstan analyse` passes ``. Add a Lint row whose assertion reads: `` `php-cs-fixer check` passes ``. Add a Tests row whose assertion reads: `` `phpunit` exits 0 ``.
74
+ In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `phpstan analyse` passes ``. Add a Lint row whose assertion reads: `` `php-cs-fixer check` passes ``. Add a Tests row whose assertion reads: `` `phpunit` exits 0 ``. <!-- audit-ignore-citations: canon/context/ci.md -->
@@ -62,4 +62,4 @@ Append to the `## Scripts` table:
62
62
 
63
63
  ## CI docs (extend)
64
64
 
65
- In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `mypy .` passes ``. Add a Lint row whose assertion reads: `` `ruff check` and `ruff format --check` pass ``. Add a Tests row whose assertion reads: `` `pytest` exits 0 ``.
65
+ In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `mypy .` passes ``. Add a Lint row whose assertion reads: `` `ruff check` and `ruff format --check` pass ``. Add a Tests row whose assertion reads: `` `pytest` exits 0 ``. <!-- audit-ignore-citations: canon/context/ci.md -->
@@ -52,4 +52,4 @@ Append to the `## Scripts` table:
52
52
 
53
53
  ## CI docs (extend)
54
54
 
55
- In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `tsc --noEmit` passes ``.
55
+ In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `tsc --noEmit` passes ``. <!-- audit-ignore-citations: canon/context/ci.md -->