@erclx/canon 4.122.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.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/auto-ship/SKILL.md +5 -4
- package/claude/skills/auto-ship/references/tier-failures.md +7 -4
- package/claude/skills/backlog-triage/SKILL.md +1 -1
- package/claude/skills/draft-ready/SKILL.md +1 -1
- package/claude/skills/git-ship/SKILL.md +4 -3
- package/claude/skills/memory-capture/SKILL.md +14 -4
- package/claude/skills/memory-review/REQUIREMENT.md +4 -2
- package/claude/skills/memory-review/SKILL.md +17 -6
- package/claude/skills/memory-review/references/later-phases.md +6 -3
- package/claude/skills/memory-review/references/receipt-format.md +19 -3
- package/claude/skills/role-orchestrator/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/session-resume/SKILL.md +4 -3
- package/claude/skills/task-board/SKILL.md +1 -1
- package/claude/skills/task-board/references/archive.md +1 -1
- package/claude/skills/task-board/references/decline.md +1 -1
- package/docs/agents/tasks-archive.md +3 -3
- package/docs/agents/tasks-plans.md +5 -3
- package/docs/agents/tasks-validate.md +1 -1
- package/docs/agents/tasks.md +1 -1
- package/docs/workflow/ai-workflow.md +2 -2
- package/docs/workflow/operating-model.md +1 -1
- package/governance/rules/claude/555-tasks.md +2 -1
- package/package.json +1 -1
- package/src/commands/tasks.ts +39 -29
- package/src/gate/stages.ts +1 -0
- package/src/tasks/archive.ts +212 -105
- package/src/tasks/reach.ts +1 -1
- package/src/tasks/record.ts +59 -16
- package/src/tasks/validate.ts +1 -1
- package/standards/board.md +136 -0
- package/standards/commit.md +5 -0
- package/standards/groundwork.md +25 -36
- package/standards/index.md +2 -1
- package/standards/plan.md +20 -34
- package/standards/readme.md +8 -77
- package/standards/requirements.md +2 -2
- package/standards/tasks.md +18 -125
- package/standards/versioning.md +1 -1
- package/tooling/base/configs/commitlint.config.js +27 -1
- package/tooling/base/reference.md +1 -1
|
@@ -47,7 +47,7 @@ When the invocation argument is a plan path or a bare slug, name the worktree fr
|
|
|
47
47
|
|
|
48
48
|
Resolve `<plan>` in this order, stopping at the first match:
|
|
49
49
|
|
|
50
|
-
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}/../../standards/tasks.md`, reading `${CLAUDE_SKILL_DIR}/references/tier-failures.md` first for how the pointer resolves and the
|
|
50
|
+
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}/../../standards/tasks.md`, reading `${CLAUDE_SKILL_DIR}/references/tier-failures.md` first for how the pointer resolves and the four stops it can take.
|
|
51
51
|
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.`
|
|
52
52
|
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.`
|
|
53
53
|
|
|
@@ -55,7 +55,7 @@ Only a path reaches tier 1, and a bare slug is read as a plan's throughout, sinc
|
|
|
55
55
|
|
|
56
56
|
### When a tier fails
|
|
57
57
|
|
|
58
|
-
Tier 1 stops
|
|
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.
|
|
59
59
|
|
|
60
60
|
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.
|
|
61
61
|
|
|
@@ -149,18 +149,19 @@ One thing this chain adds. Mark the pull request as a draft as soon as `git-ship
|
|
|
149
149
|
|
|
150
150
|
## Output
|
|
151
151
|
|
|
152
|
-
Respond with up to
|
|
152
|
+
Respond with up to five lines:
|
|
153
153
|
|
|
154
154
|
```plaintext
|
|
155
155
|
✅ Autoshipped (<state>): <PR url>
|
|
156
156
|
<N minor findings kept in .canon/review/branch-<slug>.md>
|
|
157
157
|
<N facts routed to context entries>
|
|
158
158
|
<N memories captured in .canon/memory/>
|
|
159
|
+
<the review line memory-capture returned>
|
|
159
160
|
```
|
|
160
161
|
|
|
161
162
|
`<state>` is whatever the Step 8 read returned, being `draft` or `ready, unsupervised`, rather than the state the undo asked for.
|
|
162
163
|
|
|
163
|
-
Omit the second line if there were no minor findings, and the third if nothing routed. Omit the fourth if `memory-capture` wrote no memory file this session.
|
|
164
|
+
Omit the second line if there were no minor findings, and the third if nothing routed. Omit the fourth if `memory-capture` wrote no memory file this session, and the fifth if it returned no review line. Pass that line through verbatim, since a dispatched worker's controller relays it to the operator.
|
|
164
165
|
|
|
165
166
|
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.
|
|
166
167
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Tier 1 task reads
|
|
3
|
-
description: How auto-ship Step 1 resolves a caller-supplied task's Plan line to a plan, and the
|
|
3
|
+
description: How auto-ship Step 1 resolves a caller-supplied task's Plan line to a plan, and the four stops it takes when that line links several plans or its pointer is missing, archived, or resolves to no file
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Reading a task's plan pointer
|
|
@@ -9,14 +9,17 @@ Step 1 of `auto-ship`, tier 1. The session reads this file when the invocation c
|
|
|
9
9
|
|
|
10
10
|
## Reading the pointer
|
|
11
11
|
|
|
12
|
-
One plan per task is what makes the tier 1 read unambiguous, so it takes the first `Plan:` line and never scans for a second. A caller who means the task holds its path already, having read it off the board, which is why a bare slug never reaches this tier.
|
|
12
|
+
One plan per task is what makes the tier 1 read unambiguous, so it takes the first `Plan:` line outside a fenced sample and never scans for a second line or reads past a second link on that one. A caller who means the task holds its path already, having read it off the board, which is why a bare slug never reaches this tier.
|
|
13
13
|
|
|
14
|
-
Read the target out of the link's parentheses, and take the rest of the line when the line carries no link, since an older task writes the target as a plain path with nothing around it.
|
|
14
|
+
Read the target out of the link's parentheses, and take the rest of the line when the line carries no link, since an older task writes the target as a plain path with nothing around it. A bare line counts its leading run of tokens carrying `/` or ending in `.md` as targets and stops at the first word that is neither, so trailing prose after one path still names a single plan.
|
|
15
|
+
|
|
16
|
+
Resolve a relative target against the directory holding the task file rather than against `.canon/tasks/`, and take a project-root target from the root. The archived task is what makes that base matter, since the standard points its line at `../../plans/archive/feature-<slug>.md` once the task sits a folder deeper, and reading that from `.canon/tasks/` lands on a repository-root `plans/archive/` that never exists.
|
|
15
17
|
|
|
16
18
|
## When a tier fails
|
|
17
19
|
|
|
18
|
-
Tier 1 stops on
|
|
20
|
+
Tier 1 stops on four failures, and each names a different repair:
|
|
19
21
|
|
|
22
|
+
- The line links more than one plan. Stop: `❌ <path> links <N> plans on its Plan: line (<every target>), so the task does not say which one this run builds. Pass one plan path directly.` Test this ahead of the three below, since taking the first link runs whichever plan leads the line and reports success.
|
|
20
23
|
- No `Plan:` line at all. Stop: `❌ <path> carries no Plan: line, so nothing there names a plan to run. Write the plan and point the task at it, or pass the plan path directly.` A row still awaiting a plan is the ordinary case, so the message names the missing pointer rather than the missing plan sections a reader would then go hunting for.
|
|
21
24
|
- The pointer resolves into a plans archive. Stop: `❌ <path> points at an archived plan, which describes work that already shipped. Reopen the task against a live plan, or pass that plan directly.` Test the resolved path rather than the task's outcomes or its `Pull request:` line, since a stale board gets its ticks wrong and the standard fixes where a shipped pointer lands.
|
|
22
25
|
- The pointer resolves to no file. Stop: `❌ <path> points at <target>, which does not exist. The citation is stale, so repoint the task or pass the plan path directly.`
|
|
@@ -7,7 +7,7 @@ description: Files a measured verdict for every backlog row into an intake folde
|
|
|
7
7
|
|
|
8
8
|
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.
|
|
9
9
|
|
|
10
|
-
The record is an ordinary intake folder at `.canon/intake/<nn>-backlog-triage/`. Read `${CLAUDE_SKILL_DIR}/../../standards/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}/../../standards/tasks.md` before applying, since
|
|
10
|
+
The record is an ordinary intake folder at `.canon/intake/<nn>-backlog-triage/`. Read `${CLAUDE_SKILL_DIR}/../../standards/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}/../../standards/board.md` and `${CLAUDE_SKILL_DIR}/../../standards/tasks.md` before applying, since the first holds the backlog and the ordering file and the second holds the `Declined:` line.
|
|
11
11
|
|
|
12
12
|
## Guards
|
|
13
13
|
|
|
@@ -75,7 +75,7 @@ canon tasks plan-link <task-stem> .canon/plans/feature-<slug>.md --json
|
|
|
75
75
|
|
|
76
76
|
Branch on the record rather than on the exit. The `Ready:` line orients a worker reading the task, and `canon tasks archive` reads it to move the folder on ship.
|
|
77
77
|
|
|
78
|
-
A task file with no row is a dropped task, so place the row in the same pass. The plan exists, so the row takes `## Run now` when `canon tasks plan-reach` reports nothing claimed and `## Up next` otherwise, with the held path in its `Waiting on` cell, per the tests in `${CLAUDE_SKILL_DIR}/../../standards/
|
|
78
|
+
A task file with no row is a dropped task, so place the row in the same pass. The plan exists, so the row takes `## Run now` when `canon tasks plan-reach` reports nothing claimed and `## Up next` otherwise, with the held path in its `Waiting on` cell, per the tests in `${CLAUDE_SKILL_DIR}/../../standards/board.md`. Check the roster the way `canon:task-board` Step 4 does, reading `canon sessions list --self --json` for this session and `canon sessions list --json` for a row from the same repository, under another `sessionId`, whose `name` starts with `orchestrator-`. Treat a refused read as a roster read that failed.
|
|
79
79
|
|
|
80
80
|
- **Orchestrator found.** Write neither file. Message that session with the row's title, the plan link, and the group with its reason, so it places the row itself rather than two sessions writing the board at once.
|
|
81
81
|
- **Not found, and this session's name starts with `worker-` or `planner-`.** Write neither file, since both role bodies ban a board write with no exception. Report the row and its group in Step 8.
|
|
@@ -49,14 +49,15 @@ Capture leads the sequence because a routed fact lands in a context entry, which
|
|
|
49
49
|
|
|
50
50
|
## After completion
|
|
51
51
|
|
|
52
|
-
Output up to
|
|
52
|
+
Output up to four lines:
|
|
53
53
|
|
|
54
54
|
```plaintext
|
|
55
55
|
✅ Shipped
|
|
56
56
|
<N facts routed to context entries>
|
|
57
57
|
<N memories captured in .canon/memory/>
|
|
58
|
+
<the review line memory-capture returned>
|
|
58
59
|
```
|
|
59
60
|
|
|
60
|
-
Omit the second line if nothing routed
|
|
61
|
+
Omit the second line if nothing routed, the third if `memory-capture` wrote no memory file this session, and the fourth if `memory-capture` returned no review line. Pass that line through verbatim rather than restating it.
|
|
61
62
|
|
|
62
|
-
Emit nothing here when a wrapping caller states it closes on its own block. `auto-ship` is that caller and its block carries these same
|
|
63
|
+
Emit nothing here when a wrapping caller states it closes on its own block. `auto-ship` is that caller and its block carries these same three trailing lines below a first line naming the draft state, so emitting both reports one run twice. A caller that states no such thing gets this block, which is every direct invocation.
|
|
@@ -16,7 +16,7 @@ The filename and its type prefix, the frontmatter, the body shape each type carr
|
|
|
16
16
|
- All `.canon/memory/` reads and writes resolve at the main worktree root, not the current worktree. Resolve that root the way `session-worktree` does.
|
|
17
17
|
- Every path below sits at the main root, so each write in this skill goes out as a heredoc, routed the way `session-worktree` states. A memory entry holds one fact and this session has read it, so an update rewrites the whole file with a heredoc rather than editing a line inside it.
|
|
18
18
|
- If `.canon/memory/` does not exist at the main worktree root, create it, along with an `index.md` carrying `title` and `subtitle` frontmatter. `canon claude init` seeds both, and a project predating that seed has neither. Regeneration errors without the index, so the first write into a bare folder would report a frontmatter failure against a file that is fine.
|
|
19
|
-
- If the session produced no user corrections, confirmations, or context disclosures worth persisting, stop: `✅ Nothing worth capturing
|
|
19
|
+
- If the session produced no user corrections, confirmations, or context disclosures worth persisting, run Step 6 and stop: `✅ Nothing worth capturing.`, followed by the review line when Step 6 counted one batch due.
|
|
20
20
|
- Routing edits a tracked file, so it runs only where the caller commits. When the session is in the main worktree, or the caller states it does not commit, skip Step 3 and write every candidate as a memory file. `role-orchestrator` is the caller this covers.
|
|
21
21
|
|
|
22
22
|
## Step 1: read context
|
|
@@ -68,7 +68,7 @@ The handoff is a file rather than a spoken result so the routed fact survives a
|
|
|
68
68
|
|
|
69
69
|
## Step 4: dedupe
|
|
70
70
|
|
|
71
|
-
For each remaining candidate, grep the top-level `*.md` files in `.canon/memory/` for an existing file on the same topic, never its `review/` or `archive/` subfolders, since a hit there is a receipt or a retired entry rather than a live one to update. If one exists, update it in place rather than create a new file. Read it first and write the whole file back, since the guard above rules out editing a line inside it.
|
|
71
|
+
For each remaining candidate, grep the top-level `*.md` files in `.canon/memory/` for an existing file on the same topic, never its `review/` or `archive/` subfolders, since a hit there is a receipt or a retired entry rather than a live one to update. If one exists, update it in place rather than create a new file. Read it first and write the whole file back, since the guard above rules out editing a line inside it. Carry a `reviewed` field through the rewrite untouched, since dropping it sends a kept entry back to the front of the review queue.
|
|
72
72
|
|
|
73
73
|
## Step 5: write the residue
|
|
74
74
|
|
|
@@ -86,6 +86,14 @@ canon indexes regen --no-stage --root <main-root> <main-root>/.canon/memory/inde
|
|
|
86
86
|
|
|
87
87
|
Run `canon records validate memory` when the writes are done and fix what it names. It reads the whole pen rather than this session's writes, so treat a finding on a carried entry as one to fix in place rather than as a reason to stop.
|
|
88
88
|
|
|
89
|
+
## Step 6: count what review owes
|
|
90
|
+
|
|
91
|
+
Run `canon records stale memory --json` and count the entries whose `due` is true, and among them the ones carrying a non-empty `unresolved` list. Branch on the record rather than on the exit, which a shell function wrapping `canon` can flatten to zero. When the count reaches 25, one `memory-review` batch, the Output below adds the review line. Below that, add nothing.
|
|
92
|
+
|
|
93
|
+
Stay silent when the record refuses or the installed binary carries no `records stale` subcommand. This step reports and never stops the capture.
|
|
94
|
+
|
|
95
|
+
The line fires on a count rather than on every run. An unconditional prompt after each ship was tried and removed, since a review per ship costs more than it drains, and a hook would reach every session including workers that cannot act on it.
|
|
96
|
+
|
|
89
97
|
## Output
|
|
90
98
|
|
|
91
99
|
Respond with one line per fact routed, written, or updated:
|
|
@@ -100,6 +108,8 @@ When anything routed, add a line naming the handoff so the caller knows a `conte
|
|
|
100
108
|
|
|
101
109
|
Omit that line when the caller runs `context-fold` itself later in its own chain.
|
|
102
110
|
|
|
103
|
-
|
|
111
|
+
When Step 6 counted at least one batch due, add the review line last:
|
|
112
|
+
|
|
113
|
+
`→ <n> memory entries due for review, <m> citing a moved path. Run /canon:memory-review.`
|
|
104
114
|
|
|
105
|
-
`✅ Nothing worth capturing
|
|
115
|
+
If nothing was captured, output `✅ Nothing worth capturing.`, followed by the review line when Step 6 counted one batch due.
|
|
@@ -9,11 +9,13 @@ description: What memory review is for, the gaps it closes, and why every action
|
|
|
9
9
|
|
|
10
10
|
Without this skill, the memory folder grows and never drains. Entries pile up restating rules a durable surface already carries, nothing decides which memory has earned a place in one, and a rule that does get promoted arrives verbatim in a file whose voice it does not match. The folder then reads as a second source of truth that no surface points at.
|
|
11
11
|
|
|
12
|
-
A pen
|
|
12
|
+
A pen too large to review whole is the same gap wearing a different shape. Routing at capture takes the domain facts and whatever the ownership test leaves stays, in whatever mix of types that is, so the folder reaches a size no single pass can read: every entry plus every promotion target runs to hundreds of thousands of tokens before one entry is classified. A review that cannot run drains nothing however often someone asks for it, so the pass takes a bounded batch in the order the stale verb reports and reads targets per entry. An entry leaving the pen is archived rather than deleted, because a folder git does not hold gives a wrong bulk call no undo.
|
|
13
13
|
|
|
14
14
|
## Must
|
|
15
15
|
|
|
16
|
-
- Treat the folder as a holding pen, so every entry in scope leaves it as a promotion, a handoff, or an archive
|
|
16
|
+
- Treat the folder as a holding pen, so every entry in scope leaves it as a promotion, a handoff, or an archive, or stays only as a kept entry stamped with the date it was reviewed
|
|
17
|
+
- Take one bounded batch per pass in the order `canon records stale memory` reports, and stop rather than falling back to a full sweep when that verb is absent
|
|
18
|
+
- Stamp `reviewed` on every entry a pass keeps or skips, so the next batch moves on to entries nobody has read
|
|
17
19
|
- Archive an entry out of the pen rather than deleting it, since nothing recovers a file from a gitignored folder
|
|
18
20
|
- Hand a fact a context entry owns to `context-fold` through the routing file, rather than editing the entry here
|
|
19
21
|
- Verify the rule is not already stated or implied in the target before proposing a promotion, by reading the target rather than trusting the memory's claim about it
|
|
@@ -34,18 +34,23 @@ Propose is the entry point for a standalone run. The ship skills stop at capture
|
|
|
34
34
|
|
|
35
35
|
### Scope
|
|
36
36
|
|
|
37
|
-
- **
|
|
37
|
+
- **Batch:** run `canon records stale memory --json` and take the first 25 entries whose `due` is true, in the order the record lists them. When fewer than 25 are due, take all of them and say so. Never pad a batch with an entry that is not due.
|
|
38
|
+
- **Named set:** when the user names entries, a list, or a slug, that set is the scope and overrides the verb's order. Propose nothing outside it.
|
|
39
|
+
|
|
40
|
+
A whole pen does not fit one pass. Every entry plus every promotion target reads as several hundred thousand tokens on a grown project, so a full sweep fails before it classifies anything. The batch is bounded by the receipt the operator approves, which stays short enough for one pass at 25 items.
|
|
41
|
+
|
|
42
|
+
Branch on the record rather than on the exit, which a shell function wrapping `canon` can flatten to zero. When the installed binary carries no `records stale` subcommand, or the record comes back with `ok: false`, stop: `❌ canon records stale is missing or refused. Update the canon CLI, since a review without it falls back to a full sweep that cannot run.` Never fall back to reading the whole pen.
|
|
38
43
|
|
|
39
44
|
### Step 1: read the memory folder
|
|
40
45
|
|
|
41
46
|
Read in parallel from the project root:
|
|
42
47
|
|
|
43
|
-
- `.canon/memory/index.md`: the generated index
|
|
44
|
-
-
|
|
48
|
+
- `.canon/memory/index.md`: the generated index, which is what lets a batch entry find a duplicate outside the batch
|
|
49
|
+
- each entry in the batch, by name, never its `review/` or `archive/` subfolders: individual entries with frontmatter (`title`, `description`, `category`, and `reviewed` when a past pass kept it)
|
|
45
50
|
|
|
46
|
-
### Step 2:
|
|
51
|
+
### Step 2: search promotion targets per entry
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
Do not read every target up front. For each entry in the batch, grep the target set below for the entry's subject, taking two or three keywords from its title and rule, and read the files that hit. Read `canon/context/index.md` once for the **Promote to a context entry** route. Skip any file or folder that does not exist.
|
|
49
54
|
|
|
50
55
|
- `CLAUDE.md`: project behavior rules and Content ownership section, still a read target for the absorbed-already check even though it takes no new promotion
|
|
51
56
|
- every `SKILL.md` under `.claude/skills/`: domain-scoped internal skill bodies
|
|
@@ -54,9 +59,13 @@ Read in parallel from the project root. Skip any file or folder that does not ex
|
|
|
54
59
|
- every `*.md` under `governance/rules/` in the toolkit repo, or `.claude/rules/` in a target project: coding-standards rules
|
|
55
60
|
- every `*.md` under `internal/rules/` in the toolkit repo, or `.claude/rules/project/` in a target project: always-loaded rules a promote lands in
|
|
56
61
|
|
|
62
|
+
A target the grep missed turns a promote into a retire, and only the operator's pass catches that. Name the targets each item searched in its receipt entry so the miss is visible there.
|
|
63
|
+
|
|
57
64
|
### Step 3: classify each entry
|
|
58
65
|
|
|
59
|
-
`.canon/memory/` is a holding pen. Default every entry to promote or retire on review.
|
|
66
|
+
`.canon/memory/` is a holding pen. Default every entry to promote or retire on review. Keep is the exception, reserved for an entry still true that no surface owns, such as active task overlap or a user-type memory with no in-repo target.
|
|
67
|
+
|
|
68
|
+
An entry's `unresolved` list in the stale record names paths it cites that the tree no longer holds. Weigh it as evidence for **Retire**, or for a rewrite when the rule survives the move, and never read it as the verdict alone. A rule can outlive the file it happened to name.
|
|
60
69
|
|
|
61
70
|
`memory-capture` routes a project fact naming a domain with a context entry to that entry, so a pen filled since routing shipped is mostly feedback: rules about how to work, which no context entry owns. Propose against what the pen holds rather than expecting the older mix. An entry carried from before routing may still name a domain that has a context entry, and that entry's action is **Promote to a context entry**, which hands it to `context-fold` the same way capture does rather than editing the entry here.
|
|
62
71
|
|
|
@@ -72,6 +81,7 @@ For each in-scope entry (see Scope), pick one action:
|
|
|
72
81
|
- In the toolkit repo, point the user at `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md`, which own the source-of-truth rules under `governance/rules/`.
|
|
73
82
|
- In a target project, point the user at the `create-rule` skill, which scaffolds a project-local rule under `.claude/rules/`.
|
|
74
83
|
- **Retire**: the rule is stale, already absorbed into a durable surface, too vague to phrase as a rule, or a one-time incident narrative. Apply moves the file to `.canon/memory/archive/` rather than deleting it.
|
|
84
|
+
- **Keep**: the rule is still true and no durable surface owns it. The entry stays in the pen, and Apply stamps `reviewed` so the verb does not queue it again until the window lapses.
|
|
75
85
|
|
|
76
86
|
Retire is an archive, not a deletion, which `${CLAUDE_SKILL_DIR}/../../standards/memory.md` states as the rule and this skill executes. The archive is worth less than a plan's, since a promoted entry survives in its destination and a stale one is discarded on purpose, which is why the move is cheap rather than free.
|
|
77
87
|
|
|
@@ -111,6 +121,7 @@ Output one line per action taken in the most recent phase:
|
|
|
111
121
|
- `✅ Handed off: .canon/memory/<memory-file> → governance`
|
|
112
122
|
- `📦 Retired: .canon/memory/<memory-file> → .canon/memory/archive/`
|
|
113
123
|
- `🗑 Swept: .canon/review/<review-file>, folded <n> skips`
|
|
124
|
+
- `📌 Kept: .canon/memory/<memory-file>, reviewed <date>`
|
|
114
125
|
- `⏭ Kept: .canon/review/<review-file>, <n> items pending`
|
|
115
126
|
|
|
116
127
|
If the user accepted nothing, output: `✅ No changes applied.`
|
|
@@ -51,7 +51,7 @@ Promotions are a separate concern from any feature in flight. Keep the promoted
|
|
|
51
51
|
For each item, parse the `Decision:` line:
|
|
52
52
|
|
|
53
53
|
- `apply` (or affirmative): run the proposed action, flip emoji to ✅.
|
|
54
|
-
- `skip`: leave the memory in place, flip emoji to ⏭.
|
|
54
|
+
- `skip`: leave the memory in place, stamp it reviewed as below, flip emoji to ⏭.
|
|
55
55
|
- `defer` or empty: leave 📝 pending, take no action.
|
|
56
56
|
- Contains `?` or unrecognized verb: leave 📝 pending, take no action. Do not respond. Discussion is the Discuss phase's job.
|
|
57
57
|
|
|
@@ -63,10 +63,13 @@ Action by action type:
|
|
|
63
63
|
- **Promote to a context entry**: append the fact to `.canon/tmp/handoff/memory-routing/<slug>.md` at the main worktree root, then archive the memory file. `context-fold` folds it in on its next run from a branch, which is what keeps one skill writing context entries.
|
|
64
64
|
- **Hand off**: do not edit governance. Archive the memory file only if the user confirmed the handoff explicitly. Otherwise leave it in place.
|
|
65
65
|
- **Retire**: archive the memory file.
|
|
66
|
+
- **Keep**: stamp the memory file reviewed as below, and flip the emoji to 📌.
|
|
67
|
+
|
|
68
|
+
Stamping means writing `reviewed: <today>` as a `YYYY-MM-DD` date into the entry's frontmatter, replacing any earlier value, and changing nothing else in the file. The entry is main-root scratch, so the stamp goes out as the same whole-file heredoc rewrite the Guards state. Stamp only an entry that stays in the pen, being a keep or a skip. A promote or a retire takes no stamp, since the entry leaves for the archive. The stamp is what takes a kept entry off the front of `canon records stale memory`'s queue, so an entry kept without one returns in the next batch.
|
|
66
69
|
|
|
67
70
|
Archiving means creating `.canon/memory/archive/` at the main worktree root and moving the file there under its original name, overwriting any file already at that name. Send the `mkdir -p` and a plain `mv`, routed the way `session-worktree` states for a main-root move. Never delete a memory entry. Nothing recovers one from a gitignored folder.
|
|
68
71
|
|
|
69
|
-
Do not hand-edit `.canon/memory/index.md`. Once every archive move is done, regenerate it instead:
|
|
72
|
+
Do not hand-edit `.canon/memory/index.md`. Once every archive move and stamp is done, regenerate it instead:
|
|
70
73
|
|
|
71
74
|
```bash
|
|
72
75
|
canon indexes regen --no-stage --root <main-root> <main-root>/.canon/memory/index.md
|
|
@@ -78,7 +81,7 @@ Apply promotion edits one at a time via `Edit`. Claude Code's tool permission di
|
|
|
78
81
|
|
|
79
82
|
This governs the tracked surfaces a promote lands in, which sit at `pwd` and take `Edit` from anywhere. The receipt and the memory entries are main-root scratch and follow the skill's heredoc guard instead.
|
|
80
83
|
|
|
81
|
-
As each item resolves, update its status in the review file: flip the H2 emoji from 📝 to ✅ for applied, ⏭ for skipped, 📦 for retired, or 🤝 for handed off. Refresh the summary block counts at the top. Do not delete the file here. The sweep below decides whether it goes.
|
|
84
|
+
As each item resolves, update its status in the review file: flip the H2 emoji from 📝 to ✅ for applied, ⏭ for skipped, 📦 for retired, 📌 for kept, or 🤝 for handed off. Refresh the summary block counts at the top. Do not delete the file here. The sweep below decides whether it goes.
|
|
82
85
|
|
|
83
86
|
**Chat shortcut:** the user replies with `all`, `none`, a comma-separated list of numbers, or `skip <nums>`. Write the matching verb into the `Decision:` slot of every item the reply names, `apply` for `all` or a bare list and `skip` for a `skip` reply, then run the parse above against the file. A reply of `none` writes nothing. A slot the reply does not name keeps its own value, so the receipt stays the source of truth and an empty slot still means take no action.
|
|
84
87
|
|
|
@@ -16,9 +16,11 @@ Do not include a `Take:` slot in the template. Discuss inserts one directly unde
|
|
|
16
16
|
````plaintext
|
|
17
17
|
# Memory review: <slug>
|
|
18
18
|
|
|
19
|
+
**Batch:** <n> of <due> due entries, in `canon records stale memory` order. <due minus n> stay due after this batch.
|
|
20
|
+
|
|
19
21
|
**Pending:** <all numbers>
|
|
20
22
|
|
|
21
|
-
Legend: ✅ applied · ⏭ skipped · 📦 retired · 🤝 handed off · 📝 pending
|
|
23
|
+
Legend: ✅ applied · ⏭ skipped · 📦 retired · 📌 kept · 🤝 handed off · 📝 pending
|
|
22
24
|
|
|
23
25
|
How to respond: fill in `Decision:` per item (`apply`, `skip`, `defer`, or a question with `?`), then re-ping the skill. Say "discuss" for question rounds, "apply" to commit. Chat shortcut: `all`, `none`, or a list of numbers.
|
|
24
26
|
|
|
@@ -28,6 +30,8 @@ How to respond: fill in `Decision:` per item (`apply`, `skip`, `defer`, or a que
|
|
|
28
30
|
|
|
29
31
|
Why: <one-line pulled from the memory's Why>
|
|
30
32
|
|
|
33
|
+
Searched: <the targets the grep hit and read>
|
|
34
|
+
|
|
31
35
|
```diff
|
|
32
36
|
+ <rewritten rule text>
|
|
33
37
|
```
|
|
@@ -38,11 +42,23 @@ Decision:
|
|
|
38
42
|
|
|
39
43
|
`<memory-file>`
|
|
40
44
|
|
|
41
|
-
Reason: <one-line reason>
|
|
45
|
+
Reason: <one-line reason, naming any unresolved path the stale record listed>
|
|
46
|
+
|
|
47
|
+
Searched: <the targets the grep hit and read>
|
|
48
|
+
|
|
49
|
+
Decision:
|
|
50
|
+
|
|
51
|
+
## 3. 📝 Keep
|
|
52
|
+
|
|
53
|
+
`<memory-file>`
|
|
54
|
+
|
|
55
|
+
Reason: <one-line reason no surface owns the rule>
|
|
56
|
+
|
|
57
|
+
Searched: <the targets the grep hit and read>
|
|
42
58
|
|
|
43
59
|
Decision:
|
|
44
60
|
````
|
|
45
61
|
|
|
46
62
|
## Variation by action
|
|
47
63
|
|
|
48
|
-
For Hand off items, the body is a pointer to the governance target instead of a rewritten rule: `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md` in the toolkit repo, or the `create-rule` skill in a target project. For a Promote to an always-loaded rule item, the H2 target names the rule file path the rule lands in, `<target>` in the template above, being an existing path under `internal/rules/core/` or `governance/rules/core/` in the toolkit repo, or under `.claude/rules/project/` in a target project, or the `create-rule` pointer when no existing file fits. For Retire items, skip the rewrite block. Every item gets a `Decision:` slot regardless of action. `Take:` is added only when a question response is needed.
|
|
64
|
+
For Hand off items, the body is a pointer to the governance target instead of a rewritten rule: `internal-governance` and `${CLAUDE_SKILL_DIR}/../../standards/rule.md` in the toolkit repo, or the `create-rule` skill in a target project. For a Promote to an always-loaded rule item, the H2 target names the rule file path the rule lands in, `<target>` in the template above, being an existing path under `internal/rules/core/` or `governance/rules/core/` in the toolkit repo, or under `.claude/rules/project/` in a target project, or the `create-rule` pointer when no existing file fits. For Retire and Keep items, skip the rewrite block. A batch holding fewer than 25 entries says so in the `**Batch:**` line, and a scope the user named replaces that line with `**Batch:** named by the user, <n> entries`. Every item gets a `Decision:` slot regardless of action. `Take:` is added only when a question response is needed.
|
|
@@ -35,7 +35,7 @@ Then output the state of play so the human knows what to launch, review, and mer
|
|
|
35
35
|
|
|
36
36
|
`priority.md` is the ordering source. `index.md` sorts by filename and says nothing about order, so read the sequence from the first and never infer it from the second. When `priority.md` is absent, report the queue and say the order is unrecorded.
|
|
37
37
|
|
|
38
|
-
Row position under `## Needs a plan` is what gets planned next, top first, per `${CLAUDE_SKILL_DIR}/../../standards/
|
|
38
|
+
Row position under `## Needs a plan` is what gets planned next, top first, per `${CLAUDE_SKILL_DIR}/../../standards/board.md`. `backlog.md` carries what nobody is scheduling and is explicitly unordered, so read it for what exists and never as a queue. Report a backlog count rather than its rows, since listing them puts the length back in front of the reader the cut took it away from. This read reports how many rows exist and nothing about whether each still belongs there. `references/orchestrator-parked.md` is what re-tests a backlog row's currency, on its own trigger below.
|
|
39
39
|
|
|
40
40
|
No surface carries cross-version sequencing, so report none. A row's `Waiting on` cell states why that row sits where it does, and reasoning spanning several rows reaches this session only through whoever remembers it. Say nothing about an active version, since nothing in the tree states one and a version asserted from the board is the unsourced claim this omission exists against.
|
|
41
41
|
|
|
@@ -85,7 +85,7 @@ The tracked-file boundary collides with `CLAUDE.md`, which says to handle a smal
|
|
|
85
85
|
|
|
86
86
|
Record a change identified while orchestrating against the task that owns it, fold one no task owns into the next task touching the same surface, and file a task only when no such task exists or is expected. Run `review-branch` when the boundary is crossed anyway, since a branch-diff pass is not independent and is the only check a self-authored change can get.
|
|
87
87
|
|
|
88
|
-
Filing a row states the defect and the surface it was seen on, and stops there. The extent and the cause belong to whoever plans it, so a row names neither a count of how far the defect reaches nor the mechanism behind it. Say in a plain sentence in the task file that the count was not taken, where the row's shape invites one, rather than in a marker of any prescribed form. The board's own cell has no room for it, since `${CLAUDE_SKILL_DIR}/../../standards/
|
|
88
|
+
Filing a row states the defect and the surface it was seen on, and stops there. The extent and the cause belong to whoever plans it, so a row names neither a count of how far the defect reaches nor the mechanism behind it. Say in a plain sentence in the task file that the count was not taken, where the row's shape invites one, rather than in a marker of any prescribed form. The board's own cell has no room for it, since `${CLAUDE_SKILL_DIR}/../../standards/board.md` caps a `Waiting on` cell at two clauses.
|
|
89
89
|
|
|
90
90
|
A finding placed into another task's `## Findings` is filing under a second name and takes the same rule. An unverified cause costs more than an unverified count, since a count fails loudly at the first re-measurement while a mechanism nobody opened the module to check sends real work in the wrong direction.
|
|
91
91
|
|
|
@@ -17,7 +17,7 @@ The sweep's own question stays distinct from this one. It asks which parked row
|
|
|
17
17
|
|
|
18
18
|
Every row under `## Up next` and `## Needs a plan` in `.canon/tasks/priority.md`. A `## Run now` row carries no blocker by definition, so the pass skips it. On the idle trigger, also every row in `.canon/tasks/backlog.md` when the file exists, per the trigger split above. Resolve the board, the backlog, and each task file at the main worktree root, the way `session-worktree` does.
|
|
19
19
|
|
|
20
|
-
Take the board rows in board order and finish one before opening the next. Clearing a row changes what the next row collides with, so a pass that measures every row first and writes afterwards writes against a board it has already invalidated. On the idle trigger, walk the backlog after the board, in the file's own filename order, since `${CLAUDE_SKILL_DIR}/../../standards/
|
|
20
|
+
Take the board rows in board order and finish one before opening the next. Clearing a row changes what the next row collides with, so a pass that measures every row first and writes afterwards writes against a board it has already invalidated. On the idle trigger, walk the backlog after the board, in the file's own filename order, since `${CLAUDE_SKILL_DIR}/../../standards/board.md` fixes that file as unordered and nothing about a row's position there means anything to preserve mid-pass.
|
|
21
21
|
|
|
22
22
|
## Re-testing a row
|
|
23
23
|
|
|
@@ -35,7 +35,7 @@ Write the result into the row. A re-test reported in chat is lost at the next co
|
|
|
35
35
|
|
|
36
36
|
## Re-testing a backlog row
|
|
37
37
|
|
|
38
|
-
A backlog row carries no blocker cell, since `${CLAUDE_SKILL_DIR}/../../standards/
|
|
38
|
+
A backlog row carries no blocker cell, since `${CLAUDE_SKILL_DIR}/../../standards/board.md` fixes `backlog.md` as a flat list of links with no state of its own. The five kinds above test what a blocker names, and a backlog row names none, so this pass asks the board-or-backlog question the standard already states instead: has the work in front of the row landed, or has the world changed under it. Read the answer from the task file's own `## Findings` and its origin line (`Plan:`, `Groundwork:`, `Intake:`, or `Issue:`), which name what the row was filed against and whether the fact behind it still holds.
|
|
39
39
|
|
|
40
40
|
Clearing a row answers yes to that question. It is not a count of how long the row has waited: `## Two results that are not a re-test` below refuses age as evidence for a board row, and it refuses it here on the same ground, since a row untouched for weeks is not more clearable than one added yesterday.
|
|
41
41
|
|
|
@@ -14,13 +14,14 @@ Read these in parallel, skipping any that do not exist:
|
|
|
14
14
|
- the newest `.canon/compact/*.md`, then the newest `.canon/tasks/session-*.md`: the handoff a previous session wrote before a compaction. The first is the note `session-compact` writes, and the second is the board-side map per `${CLAUDE_SKILL_DIR}/../../standards/session.md`, which stays readable. Whichever exists leads the report rather than the reads, and both lead it when both exist.
|
|
15
15
|
- `.canon/tasks/index.md`: the folder catalog. Read this before any individual task file, and take the task list from it by dropping the `index`, `priority`, `backlog`, and `session-` rows, which are siblings rather than tasks.
|
|
16
16
|
- `.canon/plans/*.md`: execution detail for in-progress tasks
|
|
17
|
-
- `.canon/memory/index.md` and any memory files relevant to the top backlog item
|
|
18
17
|
|
|
19
18
|
Then read only the task files the summary needs, typically the top one. Do not read the whole folder.
|
|
20
19
|
|
|
20
|
+
Once the top task is known, grep `.canon/memory/index.md` for two or three terms from its title and read at most three of the entries whose rows match. Never read the index whole, since a grown pen's index runs to hundreds of rows and the report shows three at most. With no top task, or no row matching, read no memory entry and say nothing about it.
|
|
21
|
+
|
|
21
22
|
Most projects carry no handoff. Say nothing about its absence, since a line reporting it every run trains a reader to skip the line on the run where a handoff exists.
|
|
22
23
|
|
|
23
|
-
If
|
|
24
|
+
If the handoff, task, and plan surfaces are all absent or empty, stop: `✅ No tracked work found. Start a new task.`
|
|
24
25
|
|
|
25
26
|
## Step 2: summarize
|
|
26
27
|
|
|
@@ -32,7 +33,7 @@ Output these sections, omitting the first when no handoff was found:
|
|
|
32
33
|
|
|
33
34
|
**Active plans:** one line per file in `.canon/plans/`, linking each to its task file in `.canon/tasks/`. Say "None" if empty.
|
|
34
35
|
|
|
35
|
-
**Relevant context:**
|
|
36
|
+
**Relevant context:** the memory entries Step 1 matched to the top backlog item, three at most. Skip if none matched.
|
|
36
37
|
|
|
37
38
|
## Step 3: recommend
|
|
38
39
|
|
|
@@ -47,7 +47,7 @@ Write it immediately. Claude Code's tool permission dialog is the confirmation g
|
|
|
47
47
|
|
|
48
48
|
A task file with no row is a dropped task, so name the surface it lands on in the same pass that creates it. A task that would plausibly be planned within the next few waves takes a row under `## Needs a plan` in `.canon/tasks/priority.md`, positioned by where it sits against the rows already there, with the reason for that position in its `Waiting on` cell. Anything else takes a line in `.canon/tasks/backlog.md`, which is unordered and where in the file it goes means nothing.
|
|
49
49
|
|
|
50
|
-
The test and both file shapes are in `${CLAUDE_SKILL_DIR}/../../standards/
|
|
50
|
+
The test and both file shapes are in `${CLAUDE_SKILL_DIR}/../../standards/board.md`.
|
|
51
51
|
|
|
52
52
|
Check the roster for a live orchestrator before writing either file. Read `canon sessions list --self --json` for this session's own `sessionId` and `name`, then read `canon sessions list --json` and match a row whose `repository` matches this session's own, whose `sessionId` differs from it, and whose `name` starts with `orchestrator-`. That is the convention every hand-launched controller on this machine currently uses, and it excludes every `worker-` and `planner-` session cleanly. Treat a refusal from either call the same as a roster read that failed.
|
|
53
53
|
|
|
@@ -33,7 +33,7 @@ canon tasks archive <stem> --json
|
|
|
33
33
|
|
|
34
34
|
The command refuses rather than reports, and the refusal reaches this skill through the record rather than through the exit. Branch on `ok`, then on `reason`. An operator's shell profile may wrap `canon` in a function that runs the binary and then a second command and takes the second status, which masks every non-zero exit rather than only an absent verb. The binary exits 1 for an unknown subcommand and 1 for an ordinary refusal alike, so the record is the only signal that survives the wrapper.
|
|
35
35
|
|
|
36
|
-
On success the record carries `from`, `to`, `priorityRowRemoved`, and `indexRegenerated`, which is what moved, what row it cleared, and whether the index changed. It also carries `
|
|
36
|
+
On success the record carries `from`, `to`, `priorityRowRemoved`, and `indexRegenerated`, which is what moved, what row it cleared, and whether the index changed. It also carries `plans`, one `from` and `to` for each live plan the task was the last live citation of, in the order its `Plan:` line links them. An empty `plans` means the task cited no live plan, or that a sibling still holds every one it cited.
|
|
37
37
|
|
|
38
38
|
## Step 3: route on a refusal
|
|
39
39
|
|
|
@@ -25,7 +25,7 @@ canon tasks decline <stem> --reason "<text>" [--by <name>] --json
|
|
|
25
25
|
|
|
26
26
|
The command refuses rather than reports, and the refusal reaches this skill through the record rather than through the exit, the same wrapper hazard `canon tasks archive` carries. Branch on `ok`, then on `reason`.
|
|
27
27
|
|
|
28
|
-
On success the record carries `from`, `to`, `priorityRowRemoved`, `backlogRowRemoved`, and `indexRegenerated`, where `backlogRowRemoved` is decline's own field since a task can be declined straight off `backlog.md` and archive never checks that file. It also carries `
|
|
28
|
+
On success the record carries `from`, `to`, `priorityRowRemoved`, `backlogRowRemoved`, and `indexRegenerated`, where `backlogRowRemoved` is decline's own field since a task can be declined straight off `backlog.md` and archive never checks that file. It also carries `plans`, one `from` and `to` for each live plan the task was the last live citation of, empty when nothing moved, the same shape `canon tasks archive` uses for its own `plans` field.
|
|
29
29
|
|
|
30
30
|
## Step 3: route on a refusal
|
|
31
31
|
|
|
@@ -26,9 +26,9 @@ Exit codes: `0` archived, `1` refused. Every gate is a refusal rather than a war
|
|
|
26
26
|
|
|
27
27
|
An outcome whose body is struck reads as cut rather than open or closed, whatever its checkbox holds: `- ~~<outcome>~~ <why>`. A task carrying only cut outcomes archives, since the gate refuses `no-outcomes` only when both the closed and the cut counts are zero. The success record carries `closed` and `cut` as counts, so a reader tells a shipped task from an abandoned one without opening the file.
|
|
28
28
|
|
|
29
|
-
The task carries its plan with it. When the closing task is the last live one whose `Plan:` line resolves onto that file, the plan moves to `.canon/plans/archive/` under its own name and the archived task's
|
|
29
|
+
The task carries its plan with it. When the closing task is the last live one whose `Plan:` line resolves onto that file, the plan moves to `.canon/plans/archive/` under its own name and the archived task's link is rewritten as `[feature-<slug>](../../plans/archive/feature-<slug>.md)`, a folder deeper than the live task wrote it. A line linking several plans moves each one no other live task cites and rewrites only those links. The `plans` array on the success record carries a `from` and `to` per moved plan in line order, and is empty when nothing moved.
|
|
30
30
|
|
|
31
|
-
Every other relative link in the task is re-resolved for the deeper folder too, whether its target moved or not: `Groundwork:`, `Intake:`, a `Plan:` or `Ready:` line whose target stayed put, and a link in the body. Fenced blocks, inline code spans, absolute URLs, `#` anchors, and rooted paths are left alone. `canon tasks decline` applies the same rebase, since `declined/` sits at the same depth. The
|
|
31
|
+
Every other relative link in the task is re-resolved for the deeper folder too, whether its target moved or not: `Groundwork:`, `Intake:`, a `Plan:` or `Ready:` line whose target stayed put, and a link in the body. Fenced blocks, inline code spans, absolute URLs, `#` anchors, and rooted paths are left alone. `canon tasks decline` applies the same rebase, since `declined/` sits at the same depth. The plan retarget runs before the rebase and writes each moved link relative to the live board, so the rebase carries it into the deeper folder like every other link, while the ready retarget runs after it.
|
|
32
32
|
|
|
33
33
|
The ready folder the task's `Ready:` line names moves with the plan to `.canon/ready/archive/`, falling back to a `.canon/ready/<nn>-<slug>/` path in the plan when the line is absent. The `Ready:` line and the plan's literal path to the folder are retargeted, and the `ready` field on the success record carries `from` and `to`, or `null` when nothing moved. A folder that resolves to nothing, sits already archived, or meets a taken destination moves nothing and refuses nothing.
|
|
34
34
|
|
|
@@ -72,7 +72,7 @@ Exit codes: `0` declined, `1` refused. The `reason` field carries which gate fir
|
|
|
72
72
|
|
|
73
73
|
The decision is written onto the task as a `Declined: <reason>, <who> on <YYYY-MM-DD>` line, anchored the same way `Pull request:` is, after the last origin line the task carries.
|
|
74
74
|
|
|
75
|
-
The task carries its
|
|
75
|
+
The task carries its plans with it the same way archive does, each one the declining task is the last live citation of, and the success record carries the same `plans` array. 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.
|
|
76
76
|
|
|
77
77
|
Skills branch on the reason rather than on the exit code, the same rule `canon tasks archive` states:
|
|
78
78
|
|
|
@@ -14,7 +14,7 @@ description: The five verbs that read or write a task's plan, being where it sit
|
|
|
14
14
|
| `--json` | Emit a machine-readable record on stdout |
|
|
15
15
|
| `--root <path>` | Board root, defaulting to the main worktree |
|
|
16
16
|
|
|
17
|
-
The record carries `location`, one of `unstated`, `live`, `archived`, or `outside`, and `citedBy`, the other live tasks whose `Plan:` line lands on the same file. Exit codes: `0` read, `1` refused with `no-board` or `no-match`.
|
|
17
|
+
The record carries `location`, one of `unstated`, `several`, `live`, `archived`, or `outside`, `targets`, every target the line carries in line order, and `citedBy`, the other live tasks whose `Plan:` line lands on the same file through any of its links. `several` is a line linking more than one plan, which answers no single location, so `target` is absent there and `targets` names each link. Exit codes: `0` read, `1` refused with `no-board` or `no-match`.
|
|
18
18
|
|
|
19
19
|
The target resolves against the board folder and against the project root both, so `../plans/x.md` and `.canon/plans/x.md` land on the same file and one plan two tasks spelled differently counts once. Containment is tested at both record roots rather than at the one this tree resolves at, since a line somebody wrote against a root the tree has since left is still a path into the plans folder, and reading it as outside would report a shipped plan as still live. `docs/agents/records.md` states the read order.
|
|
20
20
|
|
|
@@ -142,6 +142,8 @@ canon tasks plan-link v28.1-trigger-escalation .canon/plans/feature-dispatch-ans
|
|
|
142
142
|
|
|
143
143
|
The plan resolves the same way `canon tasks plan-answers` resolves one, against the project root first and `.canon/tasks/` second, so a bare slug and a board-relative path both work. A reference resolving to no file refuses as `no-plan`, naming every base it looked under.
|
|
144
144
|
|
|
145
|
-
The write adds the line when it is absent and corrects the target in place when it exists, anchored on the H1 rather than on the last origin line, since `Plan:` is the first origin line a task carries rather than the last. The `action` field reports `added`, `corrected`, or `unchanged`, which makes a rerun against the same plan safe.
|
|
145
|
+
The write adds the line when it is absent and corrects the target in place when it exists, anchored on the H1 rather than on the last origin line, since `Plan:` is the first origin line a task carries rather than the last. A `Plan:` line inside a fenced sample is skipped. The `action` field reports `added`, `corrected`, or `unchanged`, which makes a rerun against the same plan safe, and `replaced` names the target the line carried before when it differed.
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
A line already linking several plans is refused as `several-plans` and left unchanged, with `detail` listing each link, even when one of them is the plan named. The verb cannot tell which link to drop, and reporting `unchanged` would hide a line that breaks the one-plan rule.
|
|
148
|
+
|
|
149
|
+
Exit codes: `0` recorded, `1` refused. The `reason` field carries `no-board`, `no-match`, `no-plan`, `several-plans`, or `bad-input`.
|
|
@@ -128,4 +128,4 @@ Skills branch on the findings rather than on the exit code:
|
|
|
128
128
|
canon tasks validate --json | jq -r '.findings[] | "\(.kind): \(.subject)"'
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
For the board format, the `Pull request:` line
|
|
131
|
+
For the board format, see `standards/board.md`. For the `Pull request:` line and the archive rules, see `standards/tasks.md`.
|
package/docs/agents/tasks.md
CHANGED
|
@@ -116,4 +116,4 @@ canon tasks list --json | jq -r '.tasks[] | select(.readiness == "backlog") | .s
|
|
|
116
116
|
|
|
117
117
|
`canon tasks validate` reports what each row of `priority.md` and each line of `backlog.md` claims against what the tree holds. Its seven checks, the three arrays that move no exit code, and the refusals are in `tasks-validate.md`.
|
|
118
118
|
|
|
119
|
-
For the board format, the `Pull request:` line
|
|
119
|
+
For the board format, see `standards/board.md`. For the `Pull request:` line and the archive rules, see `standards/tasks.md`.
|
|
@@ -171,9 +171,9 @@ The list stays written in the skill body as the fallback for a target whose inst
|
|
|
171
171
|
|
|
172
172
|
Capture leads rather than trails because a routed fact edits a tracked file, which has to reach the branch before the commit steps run.
|
|
173
173
|
|
|
174
|
-
The chain ends at capture, and review is run on its own.
|
|
174
|
+
The chain ends at capture, and review is run on its own. Capture closes with a line naming `/canon:memory-review` once `canon records stale memory` counts 25 or more entries due, and says nothing below that.
|
|
175
175
|
|
|
176
|
-
Run `memory-review` standalone to curate the
|
|
176
|
+
Run `memory-review` standalone to curate the pen a batch at a time. Each pass takes the first 25 due entries in the order the stale verb reports, opening on those citing a path the tree no longer holds, and a named list overrides that order. An entry it retires moves to `.canon/memory/archive/` rather than being deleted, since a bulk pass has no undo. An entry it keeps is stamped with a `reviewed` date, which takes it out of the queue for 30 days.
|
|
177
177
|
|
|
178
178
|
The receipt is collected once every item on it has been decided, and it survives untouched while any item is still pending. Whichever runs first takes it: Apply collects the receipt it has resolved, and `context-fold` scans the folder on every shipped branch for one an earlier session left behind. Before the file goes, each declined item is folded into the entry it was about, since a promotion survives in its target and in git while a decline is recorded nowhere else. `canon standards memory` states what a fold writes and which entry types take one.
|
|
179
179
|
|
|
@@ -218,7 +218,7 @@ coordination costs more than the change it was too small.
|
|
|
218
218
|
|
|
219
219
|
Two tiers hold work at different altitudes.
|
|
220
220
|
|
|
221
|
-
- Tasks (`.canon/tasks/`): the active few pulled into the current turn, one file each. Gitignored, high churn. Shape governed by `standards/tasks.md`. `priority.md` beside them carries execution order, and `backlog.md` carries what nobody is scheduling.
|
|
221
|
+
- Tasks (`.canon/tasks/`): the active few pulled into the current turn, one file each. Gitignored, high churn. Shape governed by `standards/tasks.md`. `priority.md` beside them carries execution order, and `backlog.md` carries what nobody is scheduling, both shaped by `standards/board.md`.
|
|
222
222
|
- Edits: a few lines, done immediately with no ceremony.
|
|
223
223
|
|
|
224
224
|
Nothing above these sequences work into versions. Scope is stated in
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Route .canon/tasks/ edits to the tasks standard for
|
|
2
|
+
description: Route .canon/tasks/ edits to the tasks standard for task files and the board standard for priority.md and backlog.md
|
|
3
3
|
paths:
|
|
4
4
|
- '.canon/tasks/**'
|
|
5
5
|
---
|
|
@@ -9,4 +9,5 @@ paths:
|
|
|
9
9
|
## Authority
|
|
10
10
|
|
|
11
11
|
- Follow the tasks standard for filenames, frontmatter, what belongs, and the task file format. It is the single source. Read it with `canon standards tasks`.
|
|
12
|
+
- Follow the board standard for `priority.md` and `backlog.md`, their readiness groups, and row order. Read it with `canon standards board`.
|
|
12
13
|
- Never hand-edit `.canon/tasks/index.md`. A hook regenerates it from sibling frontmatter.
|