@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.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/auto-ship/SKILL.md +6 -5
- package/claude/skills/auto-ship/references/tier-failures.md +7 -4
- package/claude/skills/backlog-triage/REQUIREMENT.md +47 -0
- package/claude/skills/backlog-triage/SKILL.md +139 -0
- package/claude/skills/canon-rollout/SKILL.md +6 -6
- package/claude/skills/context-fold/REQUIREMENT.md +75 -0
- package/claude/skills/context-fold/SKILL.md +162 -0
- package/claude/skills/{docs-fold → context-fold}/references/anchor-sweep.md +1 -1
- package/claude/skills/{docs-fold → context-fold}/references/classify.md +4 -2
- package/claude/skills/context-fold/references/context-refresh.md +48 -0
- package/claude/skills/context-fold/references/promoted-pages.md +29 -0
- package/claude/skills/context-fold/references/receipt-sweep.md +43 -0
- package/claude/skills/context-fold/references/unusable-baseline.md +25 -0
- package/claude/skills/{docs-fold → context-fold}/references/wireframe-sweep.md +1 -1
- package/claude/skills/docs-fold/REQUIREMENT.md +8 -58
- package/claude/skills/docs-fold/SKILL.md +3 -278
- package/claude/skills/docs-sync/REQUIREMENT.md +1 -1
- package/claude/skills/docs-sync/SKILL.md +1 -1
- package/claude/skills/draft-context/REQUIREMENT.md +2 -2
- package/claude/skills/draft-context/SKILL.md +3 -3
- package/claude/skills/draft-docs/REQUIREMENT.md +1 -1
- package/claude/skills/draft-ready/SKILL.md +1 -1
- package/claude/skills/draft-wireframes/REQUIREMENT.md +1 -1
- package/claude/skills/draft-wireframes/SKILL.md +2 -2
- package/claude/skills/git-pr/SKILL.md +8 -146
- package/claude/skills/git-pr/references/evidence.md +114 -0
- package/claude/skills/git-pr/references/labels.md +20 -0
- package/claude/skills/git-pr/references/task-number.md +26 -0
- package/claude/skills/git-ship/SKILL.md +5 -4
- package/claude/skills/memory-capture/REQUIREMENT.md +2 -2
- package/claude/skills/memory-capture/SKILL.md +19 -9
- package/claude/skills/memory-review/REQUIREMENT.md +5 -3
- package/claude/skills/memory-review/SKILL.md +22 -107
- package/claude/skills/memory-review/references/later-phases.md +111 -0
- package/claude/skills/memory-review/references/receipt-format.md +19 -3
- package/claude/skills/plan-feature/REQUIREMENT.md +1 -1
- package/claude/skills/review-address/REQUIREMENT.md +1 -1
- package/claude/skills/review-address/SKILL.md +1 -1
- package/claude/skills/review-branch/SKILL.md +1 -1
- package/claude/skills/role-orchestrator/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +4 -2
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +2 -2
- package/claude/skills/session-resume/REQUIREMENT.md +1 -1
- package/claude/skills/session-resume/SKILL.md +4 -3
- package/claude/skills/session-worktree/SKILL.md +4 -10
- package/claude/skills/task-board/REQUIREMENT.md +1 -1
- package/claude/skills/task-board/SKILL.md +7 -81
- package/claude/skills/task-board/references/archive.md +54 -0
- package/claude/skills/task-board/references/decline.md +40 -0
- package/claude/skills/teach-workspace/SKILL.md +8 -105
- package/claude/skills/teach-workspace/references/lesson-craft.md +51 -1
- package/claude/skills/teach-workspace/references/promotion.md +41 -0
- package/docs/agents/architecture-record.md +26 -0
- package/docs/agents/commands.md +87 -154
- package/docs/agents/context-audit-checks.md +2 -20
- package/docs/agents/context-audit.md +1 -1
- package/docs/agents/context-classify.md +1 -1
- package/docs/agents/drift-surfaces.md +192 -0
- package/docs/agents/index.md +12 -4
- package/docs/agents/install-and-sync.md +2 -181
- package/docs/agents/markdown-audit-weight.md +73 -0
- package/docs/agents/markdown-audit.md +3 -90
- package/docs/agents/migrate.md +34 -0
- package/docs/agents/records-stale.md +62 -0
- package/docs/agents/records.md +5 -1
- package/docs/agents/sandbox.md +1 -1
- package/docs/agents/tasks-archive.md +81 -0
- package/docs/agents/tasks-plans.md +149 -0
- package/docs/agents/tasks-validate.md +131 -0
- package/docs/agents/tasks.md +7 -334
- package/docs/target-projects.md +3 -3
- package/docs/workflow/ai-workflow.md +34 -29
- package/docs/workflow/operating-model.md +1 -1
- package/governance/rules/claude/555-tasks.md +2 -1
- package/governance/rules/core/045-memory.md +1 -1
- package/package.json +1 -1
- package/src/claude/cases/setup.ts +1 -1
- package/src/claude/cases/workflow.ts +22 -1
- package/src/commands/records.ts +146 -0
- package/src/commands/sandbox.ts +6 -4
- package/src/commands/tasks.ts +39 -29
- package/src/context/classify/extract.ts +1 -1
- package/src/context/classify/ollama.ts +1 -1
- package/src/context/classify/run.ts +1 -1
- package/src/gate/measures.ts +1 -1
- package/src/gate/stages.ts +1 -0
- package/src/migrate/skill-names.ts +15 -2
- package/src/records/stale.ts +243 -0
- package/src/sandbox/expect.ts +1 -1
- package/src/tasks/archive.ts +223 -92
- package/src/tasks/reach.ts +1 -1
- package/src/tasks/record.ts +60 -17
- package/src/tasks/validate.ts +37 -4
- 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/memory.md +13 -1
- package/standards/plan.md +20 -34
- package/standards/readme.md +8 -77
- package/standards/requirements.md +2 -2
- package/standards/tasks.md +19 -126
- package/standards/versioning.md +1 -1
- package/tooling/astro/reference.md +1 -1
- package/tooling/base/configs/.husky/post-merge +1 -1
- package/tooling/base/configs/commitlint.config.js +27 -1
- package/tooling/base/reference.md +1 -1
- package/tooling/claude/reference.md +1 -1
- package/tooling/go/reference.md +1 -1
- package/tooling/nextjs/reference.md +1 -1
- package/tooling/php/reference.md +1 -1
- package/tooling/python/reference.md +1 -1
- package/tooling/vite-react/reference.md +1 -1
- package/tooling/web/reference.md +1 -1
|
@@ -47,21 +47,17 @@ Try each source in order. Stop at the first match.
|
|
|
47
47
|
4b. **Session context.** When on `main` or `master` with no matching plan, read the current conversation to infer a kebab slug from the topic being discussed. Propose it: `Infer: <slug>. Confirm or rename?` Do not enter the worktree until the user confirms or provides a corrected name.
|
|
48
48
|
5. **Ask.** None of the above applies. Ask the user for a name. Do not invent one.
|
|
49
49
|
|
|
50
|
-
Tier 0 sits ahead of the inference because every tier below it answers from state the caller cannot set
|
|
50
|
+
Tier 0 sits ahead of the inference because every tier below it answers from state the caller cannot set. A dispatched worker starts on `main` beside several plans, so without it the run lands on tier 3 and asks somebody who is not there.
|
|
51
51
|
|
|
52
52
|
Validate the result: letters, digits, dots, underscores, dashes only, max 64 chars (`/` separators are also allowed). If the derived name violates the rule, sanitize by replacing invalid chars with `-` and truncating. Show the sanitized name in the preview before invoking.
|
|
53
53
|
|
|
54
54
|
Resolve `<type>` here as well, since Step 3 previews it and Step 5 renames onto it, drawing the value from the type vocabulary in `${CLAUDE_SKILL_DIR}/../../standards/branch.md`. A type the caller spelled in tier 0 wins outright and no reading overrides it. A name that came from a plan, through tier 1 or tier 2, takes its type from `canon tasks plan-branch <plan> --json`, read off the record's `type` field. Every other case takes `feat`, which covers a name from a branch, a bare name from the user, and a plan the verb could not answer for.
|
|
55
55
|
|
|
56
|
-
The verb is the reading rather than this body, because a type read off a plan's
|
|
57
|
-
|
|
58
|
-
The caller's type still wins over the verb's, because tier 0 is the one source that knows something no file states. The ordinary caller is `auto-ship` Step 0, which ran the verb itself and is handing over the answer it got, so nothing is overridden in that case either.
|
|
56
|
+
The verb is the reading rather than this body, because a type read off a plan's sections is a judgment that two sessions reading one plan can grade differently, and the verb answers the same for both. The caller's type still wins over the verb's, because tier 0 is the one source that knows something no file states.
|
|
59
57
|
|
|
60
58
|
Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero. Take `feat` and say the verb did not answer where it refuses, where the record carries no `type` key, or where the installed binary carries no `plan-branch` subcommand.
|
|
61
59
|
|
|
62
|
-
A wrong type is
|
|
63
|
-
|
|
64
|
-
`git-branch` does not correct it, which three surfaces used to say it did. Its second guard reads `If branch name already follows conventions, stop`, and `${CLAUDE_SKILL_DIR}/../../standards/branch.md` makes the type vocabulary an axis without making the choice within that vocabulary one, so `feat/` over a fix conforms and the guard fires ahead of the Analysis line that would have re-derived the type.
|
|
60
|
+
A wrong type is cosmetic, since `git-stage` and `git-pr` read their types off the diff rather than the branch name. Nothing corrects it later either: `git-branch` stops on any name that already conforms, and `feat/` over a fix conforms.
|
|
65
61
|
|
|
66
62
|
Then test both names the entry is about to claim. Neither read needs a worktree, and a stop after Step 4 leaves one built with the session sitting inside it, so both belong here rather than beside the rename:
|
|
67
63
|
|
|
@@ -74,7 +70,7 @@ The two tests catch different collisions. The branch test misses the one `${CLAU
|
|
|
74
70
|
|
|
75
71
|
The branch test fires on the tier 1 and tier 4 sources whenever the branch the session started on is already conventional, since a name derived from that branch resolves back onto it. Stopping is the answer there. The concern already has a branch, git refuses a second under the same name, and the bare-name rename this replaces only carried the collision forward to the `git-branch` step. It fires on tier 0 as well, where a caller handed a name something already holds.
|
|
76
72
|
|
|
77
|
-
It reads both ref spaces rather than the local head alone,
|
|
73
|
+
It reads both ref spaces rather than the local head alone, the way `checkClaim` does, so a name this skill clears and a branch a dispatcher cleared are one answer, and a branch pushed from elsewhere is caught before the first push rather than at it.
|
|
78
74
|
|
|
79
75
|
## Step 3: preview
|
|
80
76
|
|
|
@@ -153,8 +149,6 @@ Then report the port this worktree derives, on a second line:
|
|
|
153
149
|
|
|
154
150
|
Branch on the exit rather than on the output, since the helper prints nothing to stdout when it refuses and reading that as a number reports an offset of zero, which is the main checkout's.
|
|
155
151
|
|
|
156
|
-
The helper refuses a folder left behind after its worktree was removed, which Step 4 cannot land on, since it registers whatever it creates. The branch is here so a refusal is never read back as an offset of zero, which is the main checkout's port and the collision the helper exists to prevent.
|
|
157
|
-
|
|
158
152
|
The offset is what `role-orchestrator` sends a reader here to read rather than assign, and what an operator overrides through `WORKTREE_PORT_OFFSET` when two worktrees derive the same value. Deriving it correctly and printing it nowhere leaves both instructions naming a number no surface emits.
|
|
159
153
|
|
|
160
154
|
Do not invoke `ExitWorktree` from this skill. Exit is the user's call.
|
|
@@ -42,6 +42,6 @@ Placing a row without checking for another writer collides the same way. Two ses
|
|
|
42
42
|
|
|
43
43
|
## Out of scope
|
|
44
44
|
|
|
45
|
-
- Editing the contents of a task that already exists, which `
|
|
45
|
+
- Editing the contents of a task that already exists, which `context-fold` owns along with marking outcomes and sweeping plans
|
|
46
46
|
- Deciding what the task should argue. This owns the file's existence and its shape, not its content.
|
|
47
47
|
- Relocating a plan, which one skill owns so two do not relocate it differently
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: task-board
|
|
3
|
-
description: Creates a task file in `.canon/tasks/` with the filename, phase label, and frontmatter the standard requires, and archives a shipped one out of the folder. Use when asked to "add a task", "create a task", "queue this", "put this on the board", "archive that task", or "close out a shipped task". Do NOT use to mark an outcome `[x]`. That is `
|
|
3
|
+
description: Creates a task file in `.canon/tasks/` with the filename, phase label, and frontmatter the standard requires, and archives a shipped one out of the folder. Use when asked to "add a task", "create a task", "queue this", "put this on the board", "archive that task", or "close out a shipped task". Do NOT use to mark an outcome `[x]`. That is `context-fold`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Task board
|
|
7
7
|
|
|
8
|
-
Owns the two operations that bring a task file into existence and take it out of the folder. `
|
|
8
|
+
Owns the two operations that bring a task file into existence and take it out of the folder. `context-fold` edits the contents of a task that already exists, marking outcomes `[x]`. Do not mark outcomes here, and do not move a plan by hand: the archive carries it.
|
|
9
9
|
|
|
10
10
|
Read `${CLAUDE_SKILL_DIR}/../../standards/tasks.md` before writing any file. It holds the filename convention, the frontmatter contract, and the file format. Do not work them from memory.
|
|
11
11
|
|
|
12
12
|
## Guards
|
|
13
13
|
|
|
14
14
|
- Resolve the board at the main worktree root, not `pwd`. Run `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd` outside a git repo. Every read and write below resolves against that root. The board is gitignored scratch shared across worktrees, so a linked worktree writing to its own `pwd` creates a second board nothing else reads.
|
|
15
|
-
- A new task file is a main-root write, so it goes out as a heredoc. Archiving already runs through `canon tasks archive`, which resolves the root in-process. Marking an outcome shipped is `
|
|
15
|
+
- A new task file is a main-root write, so it goes out as a heredoc. Archiving already runs through `canon tasks archive`, which resolves the root in-process. Marking an outcome shipped is `context-fold` and runs through `canon tasks outcome`. Resolve that root and route the write the way `session-worktree` states.
|
|
16
16
|
- If `.canon/tasks/` does not exist at that root, stop: `❌ No .canon/tasks/ board. Run canon claude init to set it up.`
|
|
17
17
|
- Route on the request rather than on a flag. Creating names work that does not exist yet, archiving names a task file already shipped, and declining names one decided against. If the request fits none of the three, stop: `❌ Ambiguous. Say whether to create a task, archive one, or decline one.`
|
|
18
18
|
- Never hand-edit `.canon/tasks/index.md`. A hook regenerates it from sibling frontmatter after a write. Do not run the regen command directly, except after a shell write from a linked worktree: the hook matches `Write|Edit|MultiEdit` and nothing fires on `Bash`, so that one case regenerates explicitly with `canon indexes regen --no-stage --root <main-root> <main-root>/.canon/tasks/index.md`.
|
|
@@ -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
|
|
|
@@ -84,85 +84,11 @@ Say the origins were read even when nothing comes back, which is the ordinary re
|
|
|
84
84
|
|
|
85
85
|
## Archive
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
Do not move the file, edit `priority.md`, or regenerate the index by hand. `canon tasks archive` owns all three as one unit and the hook calls the same command, so a hand-rolled move here drifts from the unattended path.
|
|
90
|
-
|
|
91
|
-
### Step 1: confirm the work reached main
|
|
92
|
-
|
|
93
|
-
`docs-fold` marks outcomes on the branch as step 1 of the ship chain, so an all-`[x]` task routinely describes a pull request that is still open. The command gates on the outcomes and cannot tell those two apart, which is what puts this check here:
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
git fetch origin main --quiet && git log origin/main --oneline -20
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Match the shipped outcomes against that log, widening to `gh pr list --state merged --limit 20` when a remote is configured and the log does not settle it. When the work is not on `main`, name the task and stop: `❌ Work not on main. Archiving now loses the task if the pull request is abandoned.`
|
|
100
|
-
|
|
101
|
-
The board is gitignored, so an archived task has no history behind it and nothing restores one archived early. Skip this check when the task carries a `Pull request:` line and the last number it lists is merged, since the number already proves what the log is being read for.
|
|
102
|
-
|
|
103
|
-
### Step 2: run the archive
|
|
104
|
-
|
|
105
|
-
Pass the task's filename stem, or the pull request number when the request names one:
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
canon tasks archive <stem> --json
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
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.
|
|
112
|
-
|
|
113
|
-
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 `plan` when the task was the last live citation of a live plan, holding the `from` and `to` of the plan moved alongside it. A `plan` of `null` means the task cited no live plan, or that a sibling still holds it.
|
|
114
|
-
|
|
115
|
-
### Step 3: route on a refusal
|
|
116
|
-
|
|
117
|
-
Each reason has one resolution and none of them is to archive around it:
|
|
118
|
-
|
|
119
|
-
- `open-outcomes`: the named outcomes are unmarked or genuinely open. Run `docs-fold` when the work shipped and nothing marked it. Leave the task on the board when the outcome is real. When the work is being abandoned, cut it by striking the body, `- ~~<outcome>~~ <why>`, whatever the checkbox holds, so the board records what was dropped rather than meeting this refusal a second time.
|
|
120
|
-
- `ambiguous`: two tasks name one pull request, which is the misfile `${CLAUDE_SKILL_DIR}/../../standards/tasks.md` rules out. Resolve the citation by hand, since no sweep repairs it.
|
|
121
|
-
- `earlier-slice`: the number is listed on the task but is not the last, so a later slice is still to merge. Leave the task on the board. Its last slice's merge archives it.
|
|
122
|
-
- `no-match`: the stem or number names nothing on the board. Check the name against the listed stems.
|
|
123
|
-
- `bad-input`: the command line was wrong rather than the board. Read the message, fix the arguments, and run it again. Nothing on the board needs repair, which is what separates this from the two above.
|
|
124
|
-
|
|
125
|
-
Do not move a plan by hand from this skill. The command carries the plan with the task when no other live task cites it, and retargets the archived task's `Plan:` line at the new path. A second mover drifts into relocating the same file differently.
|
|
126
|
-
|
|
127
|
-
Leave `TASK-ARCHIVE.md` alone when it is present in the archive folder. It records the single-file era in the shape that era used, and splitting it would fabricate per-task files nobody wrote.
|
|
128
|
-
|
|
129
|
-
### Step 4: clear prose naming the task
|
|
130
|
-
|
|
131
|
-
The command drops the task's row from `.canon/tasks/priority.md` and leaves prose alone. Remove any sentence that still names the archived task or counts the rows that changed, since a stale count reads as board state.
|
|
87
|
+
Read `${CLAUDE_SKILL_DIR}/references/archive.md` when the request names a shipped task to archive. It carries the check that the work reached `main`, the `canon tasks archive` call, the route for each refusal, and the prose to clear afterwards. Never move the file, edit `priority.md`, or regenerate the index by hand, since the command owns all three.
|
|
132
88
|
|
|
133
89
|
## Decline
|
|
134
90
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
Do not move the file, edit `priority.md` or `backlog.md`, or regenerate the index by hand. `canon tasks decline` owns all three as one unit.
|
|
138
|
-
|
|
139
|
-
### Step 1: gather the reason
|
|
140
|
-
|
|
141
|
-
Ask for the reason when the request does not carry one, and stop rather than guessing: `❌ No reason. Say why the task is being declined.` The command takes it as `--reason <text>` and refuses without it, so gathering it here saves a round trip through that refusal.
|
|
142
|
-
|
|
143
|
-
### Step 2: run the decline
|
|
144
|
-
|
|
145
|
-
Pass the task's filename stem:
|
|
146
|
-
|
|
147
|
-
```bash
|
|
148
|
-
canon tasks decline <stem> --reason "<text>" [--by <name>] --json
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
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`.
|
|
152
|
-
|
|
153
|
-
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 `plan` when the task was the last live citation of a live plan, holding the `from` and `to` of the plan moved alongside it, the same shape `canon tasks archive` uses for its own `plan` field.
|
|
154
|
-
|
|
155
|
-
### Step 3: route on a refusal
|
|
156
|
-
|
|
157
|
-
- `no-match`: the stem does not name exactly one task. Either none matches, or exactly one starts with it and the full name is needed. Check the name against the listed stems.
|
|
158
|
-
- `ambiguous`: the stem is a prefix more than one task starts with, unlike archive's own `ambiguous`, which fires on a shared pull request. Decline takes no pull-request selector, so this is the only route to it. Pass the full stem.
|
|
159
|
-
- `bad-input`: the command line was wrong rather than the board. Read the message, fix the arguments, and run it again. Nothing on the board needs repair, which is what separates this from the two above.
|
|
160
|
-
|
|
161
|
-
Do not move a plan by hand from this skill. The command carries the plan with the task when no other live task cites it, and retargets the declined task's `Plan:` line at the new path. A second mover drifts into relocating the same file differently.
|
|
162
|
-
|
|
163
|
-
### Step 4: clear prose naming the task
|
|
164
|
-
|
|
165
|
-
The command drops the task's row from `.canon/tasks/priority.md` or `.canon/tasks/backlog.md` and leaves prose alone. Remove any sentence that still names the declined task or counts the rows that changed, since a stale count reads as board state.
|
|
91
|
+
Read `${CLAUDE_SKILL_DIR}/references/decline.md` when the request names a task decided against. It carries gathering the reason, the `canon tasks decline` call, the route for each refusal, and the prose to clear afterwards. Never move the file, edit `priority.md` or `backlog.md`, or regenerate the index by hand, since the command owns all three.
|
|
166
92
|
|
|
167
93
|
## Output
|
|
168
94
|
|
|
@@ -198,4 +124,4 @@ Archive, reporting the paths the command returned:
|
|
|
198
124
|
<ordering and index disposition in one line>
|
|
199
125
|
```
|
|
200
126
|
|
|
201
|
-
A refusal reports the reason and the resolution
|
|
127
|
+
A refusal reports the reason and the resolution the mode's reference routes it to under its Step 3, on one line each.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Archive a shipped task
|
|
3
|
+
description: How task-board confirms a task's work reached main, runs canon tasks archive, routes each refusal, and clears prose naming the archived task
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Archive a shipped task
|
|
7
|
+
|
|
8
|
+
The Archive mode of `task-board`. The session reads this file when the request names a task file already shipped.
|
|
9
|
+
|
|
10
|
+
The `post-merge` git hook archives the task a merge closed, so a request arriving here is usually one the hook could not resolve on its own. Run the steps below against whatever the hook left in place.
|
|
11
|
+
|
|
12
|
+
Do not move the file, edit `priority.md`, or regenerate the index by hand. `canon tasks archive` owns all three as one unit and the hook calls the same command, so a hand-rolled move here drifts from the unattended path.
|
|
13
|
+
|
|
14
|
+
## Step 1: confirm the work reached main
|
|
15
|
+
|
|
16
|
+
`context-fold` marks outcomes on the branch as step 1 of the ship chain, so an all-`[x]` task routinely describes a pull request that is still open. The command gates on the outcomes and cannot tell those two apart, which is what puts this check here:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git fetch origin main --quiet && git log origin/main --oneline -20
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Match the shipped outcomes against that log, widening to `gh pr list --state merged --limit 20` when a remote is configured and the log does not settle it. When the work is not on `main`, name the task and stop: `❌ Work not on main. Archiving now loses the task if the pull request is abandoned.`
|
|
23
|
+
|
|
24
|
+
The board is gitignored, so an archived task has no history behind it and nothing restores one archived early. Skip this check when the task carries a `Pull request:` line and the last number it lists is merged, since the number already proves what the log is being read for.
|
|
25
|
+
|
|
26
|
+
## Step 2: run the archive
|
|
27
|
+
|
|
28
|
+
Pass the task's filename stem, or the pull request number when the request names one:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
canon tasks archive <stem> --json
|
|
32
|
+
```
|
|
33
|
+
|
|
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
|
+
|
|
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
|
+
|
|
38
|
+
## Step 3: route on a refusal
|
|
39
|
+
|
|
40
|
+
Each reason has one resolution and none of them is to archive around it:
|
|
41
|
+
|
|
42
|
+
- `open-outcomes`: the named outcomes are unmarked or genuinely open. Run `context-fold` when the work shipped and nothing marked it. Leave the task on the board when the outcome is real. When the work is being abandoned, cut it by striking the body, `- ~~<outcome>~~ <why>`, whatever the checkbox holds, so the board records what was dropped rather than meeting this refusal a second time.
|
|
43
|
+
- `ambiguous`: two tasks name one pull request, which is the misfile `${CLAUDE_SKILL_DIR}/../../standards/tasks.md` rules out. Resolve the citation by hand, since no sweep repairs it.
|
|
44
|
+
- `earlier-slice`: the number is listed on the task but is not the last, so a later slice is still to merge. Leave the task on the board. Its last slice's merge archives it.
|
|
45
|
+
- `no-match`: the stem or number names nothing on the board. Check the name against the listed stems.
|
|
46
|
+
- `bad-input`: the command line was wrong rather than the board. Read the message, fix the arguments, and run it again. Nothing on the board needs repair, which is what separates this from the two above.
|
|
47
|
+
|
|
48
|
+
Do not move a plan by hand from this skill. The command carries the plan with the task when no other live task cites it, and retargets the archived task's `Plan:` line at the new path. A second mover drifts into relocating the same file differently.
|
|
49
|
+
|
|
50
|
+
Leave `TASK-ARCHIVE.md` alone when it is present in the archive folder. It records the single-file era in the shape that era used, and splitting it would fabricate per-task files nobody wrote.
|
|
51
|
+
|
|
52
|
+
## Step 4: clear prose naming the task
|
|
53
|
+
|
|
54
|
+
The command drops the task's row from `.canon/tasks/priority.md` and leaves prose alone. Remove any sentence that still names the archived task or counts the rows that changed, since a stale count reads as board state.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Decline a task
|
|
3
|
+
description: How task-board gathers the reason a task is decided against, runs canon tasks decline, routes each refusal, and clears prose naming the declined task
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Decline a task
|
|
7
|
+
|
|
8
|
+
The Decline mode of `task-board`. The session reads this file when the request names a task decided against.
|
|
9
|
+
|
|
10
|
+
A task decided against carries no `post-merge` hook of its own, so every decline request arrives here directly rather than through work the hook already did.
|
|
11
|
+
|
|
12
|
+
Do not move the file, edit `priority.md` or `backlog.md`, or regenerate the index by hand. `canon tasks decline` owns all three as one unit.
|
|
13
|
+
|
|
14
|
+
## Step 1: gather the reason
|
|
15
|
+
|
|
16
|
+
Ask for the reason when the request does not carry one, and stop rather than guessing: `❌ No reason. Say why the task is being declined.` The command takes it as `--reason <text>` and refuses without it, so gathering it here saves a round trip through that refusal.
|
|
17
|
+
|
|
18
|
+
## Step 2: run the decline
|
|
19
|
+
|
|
20
|
+
Pass the task's filename stem:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
canon tasks decline <stem> --reason "<text>" [--by <name>] --json
|
|
24
|
+
```
|
|
25
|
+
|
|
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
|
+
|
|
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
|
+
|
|
30
|
+
## Step 3: route on a refusal
|
|
31
|
+
|
|
32
|
+
- `no-match`: the stem does not name exactly one task. Either none matches, or exactly one starts with it and the full name is needed. Check the name against the listed stems.
|
|
33
|
+
- `ambiguous`: the stem is a prefix more than one task starts with, unlike archive's own `ambiguous`, which fires on a shared pull request. Decline takes no pull-request selector, so this is the only route to it. Pass the full stem.
|
|
34
|
+
- `bad-input`: the command line was wrong rather than the board. Read the message, fix the arguments, and run it again. Nothing on the board needs repair, which is what separates this from the two above.
|
|
35
|
+
|
|
36
|
+
Do not move a plan by hand from this skill. The command carries the plan with the task when no other live task cites it, and retargets the declined task's `Plan:` line at the new path. A second mover drifts into relocating the same file differently.
|
|
37
|
+
|
|
38
|
+
## Step 4: clear prose naming the task
|
|
39
|
+
|
|
40
|
+
The command drops the task's row from `.canon/tasks/priority.md` or `.canon/tasks/backlog.md` and leaves prose alone. Remove any sentence that still names the declined task or counts the rows that changed, since a stale count reads as board state.
|
|
@@ -9,7 +9,7 @@ argument-hint: <subject to learn, or the topic of a workspace to resume or promo
|
|
|
9
9
|
|
|
10
10
|
Run a learning workspace on one subject across sessions. The workspace holds what the learner has been through, so a session weeks later resumes from the folder rather than from the conversation.
|
|
11
11
|
|
|
12
|
-
The shape of the workspace is fixed by `${CLAUDE_SKILL_DIR}/../../standards/teach.md`. Read it before writing anything into the folder. The glossary answers to `${CLAUDE_SKILL_DIR}/../../standards/glossary.md
|
|
12
|
+
The shape of the workspace is fixed by `${CLAUDE_SKILL_DIR}/../../standards/teach.md`. Read it before writing anything into the folder. The glossary answers to `${CLAUDE_SKILL_DIR}/../../standards/glossary.md` wherever it lands. The pedagogy sits in `${CLAUDE_SKILL_DIR}/references/pedagogy.md`, the lesson craft in `${CLAUDE_SKILL_DIR}/references/lesson-craft.md`, and the promotion routing in `${CLAUDE_SKILL_DIR}/references/promotion.md`.
|
|
13
13
|
|
|
14
14
|
## Guards
|
|
15
15
|
|
|
@@ -77,9 +77,7 @@ A claim nothing was read for is the failure this step exists against. Where no s
|
|
|
77
77
|
|
|
78
78
|
Pick the next lesson from the learning records rather than from the subject's own order. The target is the band immediately past what the learner can already do unaided, which `${CLAUDE_SKILL_DIR}/references/pedagogy.md` states in full.
|
|
79
79
|
|
|
80
|
-
Open on what `due` reports as overdue, oldest date first, before anything new. Where nothing is overdue, open on retrieval of what the last session got wrong. A learner who cannot retrieve the previous lesson is not ready for the next one, and moving on anyway buys fluency that decays.
|
|
81
|
-
|
|
82
|
-
Reading the last record alone is what the schedule replaces. A topic missed three sessions ago and never revisited is invisible to that read and sits in `due` with a date already past, which is the whole reason the field exists.
|
|
80
|
+
Open on what `due` reports as overdue, oldest date first, before anything new. Where nothing is overdue, open on retrieval of what the last session got wrong. A learner who cannot retrieve the previous lesson is not ready for the next one, and moving on anyway buys fluency that decays. Never place from the last record alone, since a topic missed three sessions ago is invisible to that read and sits in `due` with a date already past.
|
|
83
81
|
|
|
84
82
|
## Step 4: write the lesson and the reference
|
|
85
83
|
|
|
@@ -103,69 +101,11 @@ It writes nothing and reports three things:
|
|
|
103
101
|
- `success`, the mission's success lines, carried here so Step 5 needs no second read.
|
|
104
102
|
- `quiz`, one entry per question, carrying `order` and `answer`.
|
|
105
103
|
|
|
106
|
-
Write the correct option first, then present the options in the order `order` reports, reading it as authored indices where `0` is the correct one. Take the order as given
|
|
107
|
-
|
|
108
|
-
### The quiz and the teach-back block
|
|
109
|
-
|
|
110
|
-
The quiz markup is a contract rather than a convention. `canon teach nav` splices a stepper into the lesson that gates on exactly the class names and nesting `${CLAUDE_SKILL_DIR}/references/lesson-craft.md` states under `## Quiz construction`, so a quiz in any other shape shows every question at once and nothing reports it. That is the leak this shape closes: a later stem naming what an earlier question asked for answers it while both sit on screen.
|
|
111
|
-
|
|
112
|
-
Write each option as a `<label class="opt" data-k="<letter>">` holding a radio `<input>` and a `<span>`, every option in one question sharing a `name` and each question taking a different one, the correct option carrying `data-a="1"`, and one `.fb` block as the last child of the `.q`. Never write a `<button class="opt">` in a new lesson. That is the shape written before the stepper, kept working by an injected script the button itself still triggers, and a lesson mixing the two gets both mechanisms.
|
|
113
|
-
|
|
114
|
-
Then carry a teach-back block beside the quiz. A quiz is recognition and the pedagogy prefers production, so a lesson offering only a quiz tests the weak form. Ask for an explanation to a named audience, and carry a `<details>` listing what a complete explanation covers, closed by default, so a learner reading with no session in the room can grade themselves. The reference states both shapes in full.
|
|
115
|
-
|
|
116
|
-
Compose the quiz and the teach-back block as one string. Below, that string is the block list's last `raw` entry, followed only by `refs`, rather than markup written into the file by hand.
|
|
117
|
-
|
|
118
|
-
### Building the lesson body
|
|
119
|
-
|
|
120
|
-
Write the chrome as four empty marker pairs rather than composing it by hand: `<!-- canon:teach:style -->`/`<!-- /canon:teach:style -->` inside `<head>`, and `<!-- canon:teach:header -->`, `<!-- canon:teach:footnav -->`, and `<!-- canon:teach:scripts -->` each with its own close marker, in that order in `<body>`.
|
|
121
|
-
|
|
122
|
-
Build the authored `<h1>`, lede, body, and quiz as a JSON array of blocks rather than composing markup by hand, and render it through the verb rather than through a component import:
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
echo '[
|
|
126
|
-
{"type":"heading","level":1,"text":"<title>"},
|
|
127
|
-
{"type":"paragraph","lede":true,"text":"<the dek>"},
|
|
128
|
-
{"type":"paragraph","text":"<a body paragraph>"},
|
|
129
|
-
{"type":"paragraph","text":"<a claim resting on a source>","cites":[1]},
|
|
130
|
-
{"type":"list","ordered":true,"items":["<step one>","<step two>"]},
|
|
131
|
-
{"type":"raw","html":"<the quiz and teach-back block composed above>"},
|
|
132
|
-
{"type":"refs","items":[{"title":"<source name>","url":"<https URL>"}]}
|
|
133
|
-
]' | canon teach render --json
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
A `heading`, a `paragraph` (`lede: true` for the dek, `cites` for a claim resting on a source), a `list`, and the one `refs` block the cites number into cover the structural body. Reach for `raw` only where none of those can carry the content, never as a shortcut around composing one, and give the quiz and teach-back block the array's last `raw` entry every time, followed only by `refs`, since their fixed contract is not a components concern. Take the call's `html` field and write it between the header's close marker and the footnav's open marker, and nothing else anywhere in the file.
|
|
137
|
-
|
|
138
|
-
Report it rather than proceeding silently when the verb does not resolve, which is an installed CLI predating it, and never compose the lesson body by hand as a fallback. That is the state this section exists to end, and a target holds this skill body before it holds the verb, since a plugin skill reaches a target the moment it merges while the CLI reaches one only when a release publishes.
|
|
139
|
-
|
|
140
|
-
Then run:
|
|
141
|
-
|
|
142
|
-
```bash
|
|
143
|
-
canon teach nav <topic> --json
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
It fills every marker pair from what the workspace holds on disk: the embedded stylesheet, the header with its breadcrumb and jump menus, the prev/next footer nav, and the behavior scripts, and it rewrites the workspace's contents page and the teach-root listing in the same run. It refuses a lesson missing one of the four marker pairs by name rather than guessing at the boundary, so a marker dropped while writing the lesson is caught here rather than read back later as a lesson nothing links to. Report it rather than proceeding silently when the verb does not resolve, which is an installed CLI predating it, and never compose the chrome by hand as a fallback.
|
|
147
|
-
|
|
148
|
-
Seed the stylesheet through the verb rather than authoring a palette, on the first lesson in a workspace:
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
canon teach stylesheet <topic> --json
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
It writes the design tokens as custom properties and the components built on them, from the one source every other rendered surface reads. Add lesson rules under the seed and reach a value through its property rather than restating the hex, which is what let each workspace fork the palette from every other. It refuses to overwrite, so running it again on a workspace that has grown its own rules is safe and reports `written` as false.
|
|
104
|
+
Write the correct option first, then present the options in the order `order` reports, reading it as authored indices where `0` is the correct one. Take the order as given, since a lesson that reorders on its own judgment puts the answer back in the first slot.
|
|
155
105
|
|
|
156
|
-
|
|
106
|
+
### The quiz, the teach-back block, and the lesson body
|
|
157
107
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
```bash
|
|
161
|
-
canon teach glossary <topic> --json \
|
|
162
|
-
--term "<term>=<definition, written without using the term>" \
|
|
163
|
-
--first-seen <the lesson or reference page this batch comes from>
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
`--term` repeats and one call writes the file once, which is what keeps a batch of terms from racing on it. A term already defined is refused, since a definition the subject has moved under is a revision of the entry rather than a second one.
|
|
167
|
-
|
|
168
|
-
Follow `${CLAUDE_SKILL_DIR}/references/lesson-craft.md` for what makes a lesson worth returning to. Keep every quiz answer the same length, so formatting leaks no clue about which one is correct.
|
|
108
|
+
Read `${CLAUDE_SKILL_DIR}/references/lesson-craft.md` before writing the lesson. `## Quiz construction` and `## Teach back` there fix the markup `canon teach nav`'s stepper gates on, so a quiz in any other shape shows every question at once and nothing reports it. `## Building the lesson body` carries the four marker pairs, the render, nav, stylesheet, and glossary verbs, and the rule against composing any of them by hand when a verb does not resolve. Keep every quiz answer the same length, so formatting leaks no clue about which one is correct.
|
|
169
109
|
|
|
170
110
|
### Hand over a link, never a path
|
|
171
111
|
|
|
@@ -209,37 +149,7 @@ Then restate the mission's success lines with what is now met, reading them from
|
|
|
209
149
|
|
|
210
150
|
Run this when the invocation asks for it, or offer it in one line when a mission finishes, since that is when the workspace stops growing and its reference pages stop changing. Never run it unasked mid-course.
|
|
211
151
|
|
|
212
|
-
Read `${CLAUDE_SKILL_DIR}/references/promotion.md` first. It carries what may be promoted, the routing test, both spellings of the wiki folder, the refusal when a project has none,
|
|
213
|
-
|
|
214
|
-
Propose and wait. A promoted page is public prose that needs a line naming who owns its subject, which is a judgment about ownership rather than a move a session makes on its own reading. Present one block per candidate page:
|
|
215
|
-
|
|
216
|
-
```plaintext
|
|
217
|
-
reference/<slug>.md → <destination path>
|
|
218
|
-
Subject owner: <who owns it, in a few words>
|
|
219
|
-
Still owed: <what the destination expects that the page does not carry yet>
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
Then stop and let the operator strike, redirect, or confirm each block.
|
|
223
|
-
|
|
224
|
-
Write nothing to a destination here. One skill owns the durable writes, and two skills editing one file at one step is the failure that rule exists against. Record each confirmed block in `.canon/tmp/handoff/teach-promotion/<slug>.md` at the main worktree root instead, appending when the file exists, with one H2 per destination naming its path, the source page beneath it, and the page body fenced:
|
|
225
|
-
|
|
226
|
-
````markdown
|
|
227
|
-
## <destination path>
|
|
228
|
-
|
|
229
|
-
Source: .canon/teach/<nn>-<topic>/reference/<slug>.md
|
|
230
|
-
|
|
231
|
-
```markdown
|
|
232
|
-
<the page body as it should land, with the source line the destination expects>
|
|
233
|
-
```
|
|
234
|
-
````
|
|
235
|
-
|
|
236
|
-
The body is fenced rather than written bare because a reference page carries headings of its own, and the reader splits this file on its H2 lines. An unfenced body turns every section heading in the page into a destination naming no path. Open the body fence with four backticks so a page carrying a fenced code block of its own still closes where it should, and widen both fences together if it carries a four-backtick fence.
|
|
237
|
-
|
|
238
|
-
Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. Fall back to `latest` on an empty result.
|
|
239
|
-
|
|
240
|
-
The handoff is its own file rather than a shared one. The routed-facts file another skill writes is deleted by whichever pass folds it, so a second producer's unread work goes with it, and a sibling path costs the folding skill one more read and removes the interaction.
|
|
241
|
-
|
|
242
|
-
An append is a whole-file operation, so send it as a heredoc, per Step 0. Then tell the operator that `/docs-fold` folds the file in from a branch. The proposal costs nothing tracked and runs anywhere, while the page it describes is a tracked file, so the fold is a worktree operation and the workspace it came from is not.
|
|
152
|
+
Read `${CLAUDE_SKILL_DIR}/references/promotion.md` first. It carries what may be promoted, the routing test, both spellings of the wiki folder, the refusal when a project has none, what each destination expects a page to carry, and under `## Proposing and handing off` the proposal block, the handoff file this step writes in place of any destination, and how the operator is told to fold it. Propose and wait, and write nothing to a destination here.
|
|
243
153
|
|
|
244
154
|
## Output
|
|
245
155
|
|
|
@@ -254,15 +164,8 @@ Open: [<the url the serve verb reported>](<the same url>)
|
|
|
254
164
|
|
|
255
165
|
Omit the reference line where the lesson produced no durable page. The open line is the one line that is never omitted, since it is the only route the learner has into the page, and it carries what `canon serve` reported rather than a URL composed here. Where the verb refused, that line names the refusal instead of a link.
|
|
256
166
|
|
|
257
|
-
Write that line as a markdown link carrying the URL as both its text and its target, rather than as a bare URL and never inside backticks
|
|
167
|
+
Write that line as a markdown link carrying the URL as both its text and its target, rather than as a bare URL and never inside backticks, since a code span is text the reader has to copy.
|
|
258
168
|
|
|
259
169
|
Emit every path from the project root, in the form the project's instruction file sets.
|
|
260
170
|
|
|
261
|
-
A promotion pass reports
|
|
262
|
-
|
|
263
|
-
```plaintext
|
|
264
|
-
➡️ Promoting: .canon/teach/<nn>-<topic>/reference/<slug>.md → <destination path>
|
|
265
|
-
→ Confirmed pages wait at .canon/tmp/handoff/teach-promotion/<slug>.md. Run /docs-fold from a branch to fold them in.
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
A pass where the operator confirmed nothing writes no handoff file and reports that alone.
|
|
171
|
+
A promotion pass reports the shape `${CLAUDE_SKILL_DIR}/references/promotion.md` gives at the end of `## Proposing and handing off` instead.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Lesson craft reference
|
|
3
|
-
description: Typography, restraint, quiz construction,
|
|
3
|
+
description: Typography, restraint, quiz construction, what makes a lesson worth returning to, and the verbs that build the lesson body
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Lesson craft reference
|
|
@@ -121,3 +121,53 @@ Pick values the material needs rather than values the project happens to hold. I
|
|
|
121
121
|
- The lesson states what it assumes. A returning reader can tell in one line whether they are in the right place.
|
|
122
122
|
- The hard part is named as the hard part. Material that flattens everything to one difficulty gives a returning reader nothing to navigate by.
|
|
123
123
|
- Nothing depends on the session it was written in. A lesson referring to what was discussed is unreadable a week later.
|
|
124
|
+
|
|
125
|
+
## Building the lesson body
|
|
126
|
+
|
|
127
|
+
Step 4 of `teach-workspace` reads this section before writing a lesson, after the lesson verb has resolved its path and quiz order. Write the chrome as four empty marker pairs rather than composing it by hand: `<!-- canon:teach:style -->`/`<!-- /canon:teach:style -->` inside `<head>`, and `<!-- canon:teach:header -->`, `<!-- canon:teach:footnav -->`, and `<!-- canon:teach:scripts -->` each with its own close marker, in that order in `<body>`.
|
|
128
|
+
|
|
129
|
+
Build the authored `<h1>`, lede, body, and quiz as a JSON array of blocks rather than composing markup by hand, and render it through the verb rather than through a component import:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
echo '[
|
|
133
|
+
{"type":"heading","level":1,"text":"<title>"},
|
|
134
|
+
{"type":"paragraph","lede":true,"text":"<the dek>"},
|
|
135
|
+
{"type":"paragraph","text":"<a body paragraph>"},
|
|
136
|
+
{"type":"paragraph","text":"<a claim resting on a source>","cites":[1]},
|
|
137
|
+
{"type":"list","ordered":true,"items":["<step one>","<step two>"]},
|
|
138
|
+
{"type":"raw","html":"<the quiz and teach-back block composed above>"},
|
|
139
|
+
{"type":"refs","items":[{"title":"<source name>","url":"<https URL>"}]}
|
|
140
|
+
]' | canon teach render --json
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`## The block list` above states each type and where `raw` belongs. Take the call's `html` field and write it between the header's close marker and the footnav's open marker, and nothing else anywhere in the file.
|
|
144
|
+
|
|
145
|
+
Report it rather than proceeding silently when the verb does not resolve, which is an installed CLI predating it, and never compose the lesson body by hand as a fallback. That is the state this section exists to end, and a target holds this skill body before it holds the verb, since a plugin skill reaches a target the moment it merges while the CLI reaches one only when a release publishes.
|
|
146
|
+
|
|
147
|
+
Then run:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
canon teach nav <topic> --json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
It fills every marker pair from what the workspace holds on disk: the embedded stylesheet, the header with its breadcrumb and jump menus, the prev/next footer nav, and the behavior scripts, and it rewrites the workspace's contents page and the teach-root listing in the same run. It refuses a lesson missing one of the four marker pairs by name rather than guessing at the boundary, so a marker dropped while writing the lesson is caught here rather than read back later as a lesson nothing links to. Report it rather than proceeding silently when the verb does not resolve, which is an installed CLI predating it, and never compose the chrome by hand as a fallback.
|
|
154
|
+
|
|
155
|
+
Seed the stylesheet through the verb rather than authoring a palette, on the first lesson in a workspace:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
canon teach stylesheet <topic> --json
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
It writes the design tokens as custom properties and the components built on them, from the one source every other rendered surface reads. Add lesson rules under the seed and reach a value through its property rather than restating the hex, which is what let each workspace fork the palette from every other. It refuses to overwrite, so running it again on a workspace that has grown its own rules is safe and reports `written` as false.
|
|
162
|
+
|
|
163
|
+
Report it rather than proceeding silently when the verb does not resolve, which is an installed CLI predating it. Do not fall back to writing a palette by hand.
|
|
164
|
+
|
|
165
|
+
Add every term the lesson defines to `GLOSSARY.md` through the verb, which places the entries alphabetically in the shape the standard fixes:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
canon teach glossary <topic> --json \
|
|
169
|
+
--term "<term>=<definition, written without using the term>" \
|
|
170
|
+
--first-seen <the lesson or reference page this batch comes from>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`--term` repeats and one call writes the file once, which is what keeps a batch of terms from racing on it. A term already defined is refused, since a definition the subject has moved under is a revision of the entry rather than a second one.
|
|
@@ -50,3 +50,44 @@ Each destination gates prose the workspace does not, so name what the page still
|
|
|
50
50
|
- **The page promoted into a new context entry.** Entries are created deliberately, so a promotion proposing one is proposing a domain rather than moving a page.
|
|
51
51
|
- **The glossary split across destinations.** Terms drawn from one subject move together or not at all, since an entry defining a term the other half uses strands both.
|
|
52
52
|
- **The workspace emptied on promotion.** Promotion copies rather than moves. The workspace stays readable to the learner who built it, and the page's later life belongs to its destination.
|
|
53
|
+
|
|
54
|
+
## Proposing and handing off
|
|
55
|
+
|
|
56
|
+
Step 6 of `teach-workspace` reads this section once the routing above has placed each page. Propose and wait. A promoted page is public prose that needs a line naming who owns its subject, which is a judgment about ownership rather than a move a session makes on its own reading. Present one block per candidate page:
|
|
57
|
+
|
|
58
|
+
```plaintext
|
|
59
|
+
reference/<slug>.md → <destination path>
|
|
60
|
+
Subject owner: <who owns it, in a few words>
|
|
61
|
+
Still owed: <what the destination expects that the page does not carry yet>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Then stop and let the operator strike, redirect, or confirm each block.
|
|
65
|
+
|
|
66
|
+
Write nothing to a destination here. One skill owns the durable writes, and two skills editing one file at one step is the failure that rule exists against. Record each confirmed block in `.canon/tmp/handoff/teach-promotion/<slug>.md` at the main worktree root instead, appending when the file exists, with one H2 per destination naming its path, the source page beneath it, and the page body fenced:
|
|
67
|
+
|
|
68
|
+
````markdown
|
|
69
|
+
## <destination path>
|
|
70
|
+
|
|
71
|
+
Source: .canon/teach/<nn>-<topic>/reference/<slug>.md
|
|
72
|
+
|
|
73
|
+
```markdown
|
|
74
|
+
<the page body as it should land, with the source line the destination expects>
|
|
75
|
+
```
|
|
76
|
+
````
|
|
77
|
+
|
|
78
|
+
The body is fenced rather than written bare because a reference page carries headings of its own, and the reader splits this file on its H2 lines. An unfenced body turns every section heading in the page into a destination naming no path. Open the body fence with four backticks so a page carrying a fenced code block of its own still closes where it should, and widen both fences together if it carries a four-backtick fence.
|
|
79
|
+
|
|
80
|
+
Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. Fall back to `latest` on an empty result.
|
|
81
|
+
|
|
82
|
+
The handoff is its own file rather than a shared one. The routed-facts file another skill writes is deleted by whichever pass folds it, so a second producer's unread work goes with it, and a sibling path costs the folding skill one more read and removes the interaction.
|
|
83
|
+
|
|
84
|
+
An append is a whole-file operation, so send it as a heredoc, per the skill's Step 0. Then tell the operator that `/context-fold` folds the file in from a branch. The proposal costs nothing tracked and runs anywhere, while the page it describes is a tracked file, so the fold is a worktree operation and the workspace it came from is not.
|
|
85
|
+
|
|
86
|
+
A promotion pass reports this shape in place of the skill's Output block, one line per page the operator confirmed and one naming the handoff:
|
|
87
|
+
|
|
88
|
+
```plaintext
|
|
89
|
+
➡️ Promoting: .canon/teach/<nn>-<topic>/reference/<slug>.md → <destination path>
|
|
90
|
+
→ Confirmed pages wait at .canon/tmp/handoff/teach-promotion/<slug>.md. Run /context-fold from a branch to fold them in.
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
A pass where the operator confirmed nothing writes no handoff file and reports that alone.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Architecture record
|
|
3
|
+
description: The four findings canon context audit reads off the architecture record, being the entry cap and length gates the record states for itself, the claim coverage report, and the word counts
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Architecture record
|
|
7
|
+
|
|
8
|
+
Four findings from `canon context audit` read `canon/ARCHITECTURE.md` rather than a folder, and only the first two are facts. The other checks the audit runs are in `context-audit-checks.md`.
|
|
9
|
+
|
|
10
|
+
The entry cap check counts the record's decisions against the cap it states for itself, in a clause of the form `This record holds at most 12 decisions.` Like the length check, the cap belongs to the record rather than to the toolkit, so a record stating none is measured and never gated. A decision is a `###` heading outside a fenced block, so the template a standard shows does not count, and a heading carrying two decisions counts once. The JSON record carries what it read as `architecture.entryCap`, absent where the record states no cap, and the audit catalog counts a record past it as `recordOverCount`.
|
|
11
|
+
|
|
12
|
+
The length check compares the record against the ceiling it derives for itself, and only a record that states its own allowances has one. No standard sets a length rule for this document, so the numbers belong to whichever record declares them. The check reads a frame allowance and an allowance per decision out of the record's own prose and puts the ceiling at the frame plus the allowance times the decision count. The JSON record carries what it read as `architecture.allowances` and the reading as `architecture.lines` against `architecture.ceiling`.
|
|
13
|
+
|
|
14
|
+
A record stating no such rule is measured and reported and never gated, which is the ordinary shape of a project that wrote an architecture record and no convention about its length. Holding a pair of numbers in the toolkit and gating every project against them would fail a record on a ceiling that project never agreed to, and the framed line would credit the rule to a file that does not state it.
|
|
15
|
+
|
|
16
|
+
What the derivation costs where it does apply is that the ceiling rises when a decision is added and falls for nothing, so the check passes exactly when the file grew. It gates anyway, because a record that states a limit for itself and makes it computable from a count has turned it into a fact, which is what separates it from every judgment the audit reports. A rewrite of the declaring sentence past the clauses the check reads falls back to reporting rather than to a stale ceiling, so the failure shows up in the run's own output.
|
|
17
|
+
|
|
18
|
+
The coverage report classifies each decision as carrying a countable claim, a structural invariant, or neither, then reports each testable entry against whether it names a check that exists. A countable claim carries a figure a run could recompute, and an invariant quantifies over a named tree closely enough that a walk could falsify it. A check is a `scripts/**.sh` path the entry spells that is on disk, or a `canon` invocation matching a registered audit, so coverage reads the entry rather than the tree and a claim some check happens to cover without the entry saying so reads as unchecked.
|
|
19
|
+
|
|
20
|
+
Three limits are stated on every run rather than hidden. The countable signal reads digits alone, so a measured claim written in words reads as uncounted. Entries are counted by heading, and one heading holding several decisions counts once. Nothing is stored, so an entry rewritten tomorrow is classified afresh the next time the verb runs and no verdict goes stale.
|
|
21
|
+
|
|
22
|
+
The report gates nothing. Deciding whether a sentence states a claim is a judgment no parser settles, so the output names candidates for a reader. This answers a different question from the verification anchors `standards/architecture.md` describes, which record that one cited number was re-read. That mechanism says whether a marked figure held, and this one says how much of the record could be checked at all.
|
|
23
|
+
|
|
24
|
+
The third finding is a word count, measured in words rather than lines: one figure for the whole record and one per decision. `standards/architecture.md` asks a session to judge the file's weight by reading it rather than by counting it, and reads the word figure alongside that judgment when one is available. A paragraph written one source line to a paragraph passes the rendered-line measure other checks use while still reading heavy, which is the gap a word count closes without turning into a second cap.
|
|
25
|
+
|
|
26
|
+
The `## Risks / open questions` section is weighed the same way, reported apart from the whole-record figure since the standard singles it out for holding only what is still open. This finding gates nothing under any mode. The JSON record carries the whole-record figure as `architecture.words`, the section figure as `architecture.risksWords` where the record carries the heading, and the per-decision figure as `architecture.decisions[].words`.
|