@erclx/canon 5.39.0 → 5.40.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/README.md +5 -5
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/canvas-board/SKILL.md +1 -1
- package/claude/skills/draft-slides/SKILL.md +2 -3
- package/claude/skills/draft-slides/references/deck.md +183 -0
- package/claude/skills/git-pr/references/evidence.md +2 -2
- package/claude/skills/git-pr/references/final-command.md +1 -1
- package/claude/skills/git-pr/references/labels.md +1 -1
- package/claude/skills/git-pr/references/publish.md +1 -1
- package/claude/skills/review-ui/SKILL.md +1 -0
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
- package/claude/skills/task-board/references/board.md +1 -1
- package/claude/skills/task-board/references/tasks.md +1 -1
- package/claude/skills/ux-measure/SKILL.md +1 -1
- package/docs/agents/audits.md +61 -60
- package/docs/agents/context-audit.md +40 -58
- package/docs/agents/index.md +5 -51
- package/docs/agents/install-and-sync.md +1 -0
- package/docs/agents/markdown-audit.md +53 -99
- package/docs/agents/records.md +37 -79
- package/docs/agents/sessions-transfer.md +26 -70
- package/docs/target/index.md +0 -1
- package/docs/target/migrations.md +57 -66
- package/docs/target/projects.md +36 -78
- package/docs/target/scenarios.md +9 -19
- package/docs/target/sync.md +27 -68
- package/docs/workflow/ai-workflow.md +22 -68
- package/docs/workflow/operating-model.md +32 -186
- package/docs/workflow/parallel-features.md +29 -66
- package/docs/workflow/skill-map-on-request.md +22 -24
- package/docs/workflow/skill-map.md +72 -76
- package/docs/workflow/visual-design-workflow.md +25 -118
- package/docs/workflow/zshrc-aliases.md +18 -37
- package/package.json +1 -1
- package/src/canvas/client/inspector/fields.css +4 -0
- package/src/canvas/index.html +6 -0
- package/src/claude/skills-reach.ts +2 -2
- package/src/cli/ui.ts +3 -1
- package/src/commands/canvas.ts +23 -0
- package/src/commands/capture.ts +14 -0
- package/src/commands/claude.ts +13 -0
- package/src/commands/demo.ts +22 -0
- package/src/commands/deps.ts +6 -0
- package/src/commands/driver.ts +28 -2
- package/src/commands/gov.ts +52 -18
- package/src/commands/intake.ts +17 -0
- package/src/commands/labels.ts +38 -5
- package/src/commands/pr.ts +160 -43
- package/src/commands/records.ts +8 -0
- package/src/commands/secrets.ts +6 -0
- package/src/commands/sessions.ts +8 -0
- package/src/commands/tasks.ts +52 -0
- package/src/commands/teach.ts +13 -0
- package/src/commands/upstream.ts +25 -1
- package/src/commands/worktrees.ts +5 -0
- package/src/context/audit.ts +22 -4
- package/src/context/folders.ts +1 -1
- package/src/context/shape-gated.ts +4 -2
- package/src/counts/scan.ts +1 -1
- package/src/design/components.ts +1 -1
- package/src/design/document.ts +1 -1
- package/src/design/tokens.ts +1 -1
- package/src/gate/boundaries.ts +4 -1
- package/src/gate/measures.ts +1 -1
- package/src/gate/stages.ts +7 -7
- package/src/git/env.ts +6 -0
- package/src/markdown/scope.ts +5 -17
- package/src/pr/lift.ts +153 -19
- package/src/pr/painted.ts +87 -0
- package/src/roots/record.ts +18 -1
- package/src/sandbox/census.ts +1 -1
- package/src/sandbox/headless/run.ts +3 -2
- package/src/sandbox/headless/watch.ts +3 -4
- package/src/slides/convert/master.ts +3 -0
- package/src/sync/format.ts +46 -0
- package/src/sync/stamp.ts +6 -0
- package/tooling/astro/reference.md +2 -2
- package/tooling/base/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
- package/docs/agents/canvas.md +0 -86
- package/docs/agents/capture.md +0 -65
- package/docs/agents/census.md +0 -23
- package/docs/agents/comments.md +0 -34
- package/docs/agents/context-audit-checks.md +0 -108
- package/docs/agents/context-classify.md +0 -101
- package/docs/agents/counts.md +0 -74
- package/docs/agents/demo.md +0 -140
- package/docs/agents/driver.md +0 -103
- package/docs/agents/gate.md +0 -90
- package/docs/agents/hooks.md +0 -78
- package/docs/agents/intake.md +0 -81
- package/docs/agents/key-changes.md +0 -116
- package/docs/agents/label-coverage.md +0 -86
- package/docs/agents/markdown-audit-density.md +0 -39
- package/docs/agents/markdown-audit-weight.md +0 -83
- package/docs/agents/pr-evidence-layout.md +0 -70
- package/docs/agents/pr-evidence.md +0 -273
- package/docs/agents/pr-frames.md +0 -131
- package/docs/agents/pr-lift.md +0 -132
- package/docs/agents/pr-local.md +0 -111
- package/docs/agents/pr-preview.md +0 -155
- package/docs/agents/pr-reads.md +0 -111
- package/docs/agents/pr-tick.md +0 -72
- package/docs/agents/records-push.md +0 -61
- package/docs/agents/records-stale.md +0 -40
- package/docs/agents/records-validate.md +0 -57
- package/docs/agents/restated.md +0 -83
- package/docs/agents/routing.md +0 -61
- package/docs/agents/rule-citations.md +0 -90
- package/docs/agents/sandbox.md +0 -126
- package/docs/agents/sessions.md +0 -128
- package/docs/agents/skills-audit.md +0 -120
- package/docs/agents/skills-reach.md +0 -64
- package/docs/agents/slides.md +0 -176
- package/docs/agents/state-scoped-risk.md +0 -105
- package/docs/agents/superseded.md +0 -85
- package/docs/agents/tasks-archive.md +0 -83
- package/docs/agents/tasks-plans.md +0 -152
- package/docs/agents/tasks-validate.md +0 -133
- package/docs/agents/tasks.md +0 -123
- package/docs/agents/teach-authoring.md +0 -118
- package/docs/agents/teach.md +0 -157
- package/docs/agents/test-order.md +0 -58
- package/docs/agents/upstream.md +0 -94
- package/docs/agents/worktrees.md +0 -118
- package/docs/target/standards-moved.md +0 -34
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Tasks validate
|
|
3
|
-
description: Validating the board against the tree, the seven checks, the five report arrays, the exit codes, and the refusal reasons
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Tasks validate
|
|
7
|
-
|
|
8
|
-
`canon tasks validate` reports what each row of `priority.md` claims against what the tree holds. It reads and never writes, because a row is a session's claim about readiness and a validator that repaired one would be asserting the claim it exists to test.
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
canon tasks validate
|
|
12
|
-
canon tasks validate --json
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Seven checks run. Touches reaches only the `## Run now` rows, asking each to name its files, and Plan reaches every group, reading the Plan column under `## Run now` and the task file's own line under the other two. Mapping and Grouping test the folder contract and hold for every group, and Shape holds for every group too, ahead of the four. Ordering reaches only the `## Needs a plan` rows, and Blockers reaches every row outside `## Run now`:
|
|
16
|
-
|
|
17
|
-
| Check | What it reports |
|
|
18
|
-
| -------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
-
| Shape | A row whose cell count disagrees with its table's header, or one stranded behind a table a blank line already closed |
|
|
20
|
-
| Plan | A row whose plan citation disagrees with its group or its task file, or a task whose `Plan:` line lists one plan twice |
|
|
21
|
-
| Mapping | A row or backlog line naming no task file |
|
|
22
|
-
| Grouping | A task carrying a row in more than one readiness group, or on both surfaces |
|
|
23
|
-
| Ordering | A `## Needs a plan` row whose stated position disagrees with where it actually sits, or which states no position at all |
|
|
24
|
-
| Touches | A `## Run now` row whose Touches column names no file |
|
|
25
|
-
| Blockers | A parked row whose blocker has stopped holding, whose cited task resolves nowhere, or whose cited task was declined |
|
|
26
|
-
|
|
27
|
-
Shape runs before any other check reads a row, since a row failing it carries no dependable fields for the rest to check. A blank or prose line closes the table above it, so the walk treats the next pipe line as a fresh header candidate rather than as a continuation. That candidate counts as a header only when the line behind it is a separator carrying the same cell count, and one that fails is `row-untabled`, stranded behind a table that already closed. Cell count still has to match the header on every row that clears that test, and a row whose count disagrees is `row-misshapen`, the shape a dropped pipe or a merged column produces.
|
|
28
|
-
|
|
29
|
-
The Plan check reads the row and the task file both, because the two are written by different hands and only the task's own `Plan:` line reaches the archive. A row carrying a plan whose task states none is `plan-uncited`, and a pair where the row's plan is none of the task's links is `plan-mismatched`. Both sides resolve against the board and against the project root before they compare, so a row writing `../plans/x.md` and a task writing `.canon/plans/x.md` name one file rather than two.
|
|
30
|
-
|
|
31
|
-
The other two groups have no Plan column, so the same check reads the task file's own `Plan:` line against what the group name claims. A `## Needs a plan` row whose task cites a plan that sits live on disk is `plan-parked`, since the group says no such plan exists and the row is either misplaced or its plan is stale and belongs in the archive. An `## Up next` row whose task cites no live plan is `plan-absent`, since that group says one is written. A plan already archived, or a cited path with no file behind it, reads as not live on both sides. A row whose task file is gone is left to `task-unresolved`. An `## Up next` row with nothing left to build, waiting on an act only a person can take, reports `plan-absent` once its plan archives while the task stays live. That report is expected, since the check reads the task file and not the `Waiting on` prose, so a row in that state moves to `## Needs a plan` or closes.
|
|
32
|
-
|
|
33
|
-
A `Plan:` line linking several distinct plans is the shape a sliced task carries, so the checks read the whole line rather than reporting it. A `## Run now` row passes when its plan is any one of the links, and a parked row is judged on whether any link is live: `## Up next` needs one, and `## Needs a plan` reports `plan-parked` when one is. The check adds no sliced marker, so an unsliced task given a second link by hand reads as sliced. `plan-several` now means a line listing one plan twice, compared by the file each link resolves to, so `../plans/x.md` beside `.canon/plans/x.md` counts. It is reported in every group and replaces the `plan-uncited` or `plan-absent` report the line would otherwise draw. The message lists the links, and the fix is to drop the repeat.
|
|
34
|
-
|
|
35
|
-
Mapping spans two surfaces, because a task sits on `priority.md` when it would plausibly be planned soon and on `backlog.md` otherwise. A row or a backlog line naming no task file is `task-unresolved`, and a file both surfaces name is `row-duplicated` for the reason a task in two groups is: it claims two things about itself and only one can hold. A task file neither surface names is `unplaced` rather than a finding, since that is the normal state between a session filing it and a live orchestrator placing it on the board. One check across both is what lets a task move between them without the move reading as a dropped file.
|
|
36
|
-
|
|
37
|
-
A backlog line is a bullet carrying a link to a sibling task, since the backlog is a flat unordered list rather than a table. A bullet holding prose is skipped rather than reported, which keeps the file's own intro out of the findings, and the task that bullet meant to name still lands in the `unplaced` array below rather than the findings. A project carrying no `backlog.md` reads as an empty backlog rather than a refusal.
|
|
38
|
-
|
|
39
|
-
Ordering reads a `## Needs a plan` row's `Waiting on` cell for the position it claims, and reports two failures off one walk. A row stating an ordinal is checked against where it actually sits, which is `row-misordered`. A row claiming no position in either form it may take is `row-unranked`, since its cell argues the task matters and ranks it against nothing, which leaves the order recording when each row was filed.
|
|
40
|
-
|
|
41
|
-
The ordinal phrase is prose rather than data, searched for anywhere in the cell rather than at its start, since every live row states its position at the end of a sentence rather than at the front. The vocabulary stops at `first` through `twentieth` plus `last`, since a parser strict enough to catch a gap would otherwise flag a row phrased correctly and differently, and bounding it to those words is what keeps a cell reading `Untestable from here` from matching on `from`. One comparison against the real position catches a gap, a duplicate, and a sequence starting somewhere other than first alike.
|
|
42
|
-
|
|
43
|
-
The comparative phrase is bounded the same way and for the same reason. A closed verb list of `leads`, `heads`, `opens`, `closes`, `trails`, `precedes`, `follows`, `outranks`, and `sits under`, `above`, or `below` has to sit in one clause with a positional object, which is a `vNN.N` phase label, the word `group`, or `row` or `rows`. Both halves are needed, since a cell reading `it closes a gap the reference gate leaves open` carries the verb and claims no position, and the clause bound is what stops a verb in one half of the cell pairing with an object in the other. An ordinal exempts the row, being a comparative claim already. An unusual comparative phrasing reports as unranked, the safe direction for a check over prose, and a vocabulary verb reaching a positional object non-positionally reports as ranked, which only grading prose could separate.
|
|
44
|
-
|
|
45
|
-
The Touches check is the one a person cannot run by eye. Paths come from the backticked spans in the Touches column, a span naming no file is dropped, and a directory shares any file beneath it. A `## Run now` row whose column parses to nothing is `touches-unstated`, since a row stating no file set makes a claim nothing can check.
|
|
46
|
-
|
|
47
|
-
Two `## Run now` rows naming a path in common land in a second array, `shared`, which moves no exit code since the branch merging second rebases. Merge order reads it, as does a dispatcher weighing a contract, relocation, or sweep hold. Where a directory holds the other row's file, the entry names the row that claimed it, reading `both touch src/tasks, which v2.0-second claims as a folder.` The shared strings alone leave an over-broad cell and a genuine overlap identical. <!-- canon-allow-reference: illustrates the entry's own sentence shape, not a citation of a real task -->
|
|
48
|
-
|
|
49
|
-
The blocker check re-takes a measurement the board records once and never repeats. Two of the five blocker kinds put a fact on disk: a dependency is settled by the cited task being archived or by its work reaching the trunk, and a collision is settled by nothing under `## Run now` still holding the file the cell cites. A cited task resolving under `.canon/tasks/declined/` instead reports separately as `blocker-declined`, since a decided-against task is neither the shipped work `blocker-settled` reports nor the dangling pointer `blocker-unresolved` reports.
|
|
50
|
-
|
|
51
|
-
```json
|
|
52
|
-
{
|
|
53
|
-
"findings": [
|
|
54
|
-
{
|
|
55
|
-
"kind": "blocker-declined",
|
|
56
|
-
"group": "Up next",
|
|
57
|
-
"subject": "v50.6-a-standard-no-skill-reads", // canon-allow-reference: shows the subject field's real vXX.Y-slug shape, not a citation of a real task
|
|
58
|
-
"message": "waits on v9.0-superseded, which was declined." // canon-allow-reference: illustrates the finding's own sentence shape, not a citation of a real task
|
|
59
|
-
}
|
|
60
|
-
]
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
A closed outcome is not the fact the dependency half needs. The ship chain marks outcomes as its first step and opens the pull request several steps later, so a check reading the checkbox reports the row settled while the branch is still in review. A live task therefore settles the row only once it closed every outcome and carries a `Pull request:` line the trunk holds. One that carries a `Pending branch:` line, one that names no pull request, and one whose number no trunk ref could answer for, land in the untested array below rather than being settled or left silent.
|
|
65
|
-
|
|
66
|
-
The trunk is read as the clone already holds it, `origin/main` first and local `main` behind it, and no run fetches. A validate runs several times a sweep and a fetch per run is a cost this command does not carry, so a clone behind its remote under-reports rather than claiming work landed.
|
|
67
|
-
|
|
68
|
-
Both halves gate on a citation inside the `Waiting on` cell, never on the columns beside it. The board format gives a collision cell the file held by the running task, so a row whose cell names no file was parked by something else, and testing its Touches column instead reports a cleared collision on a row no collision ever parked while counting that row as re-tested. A cited task is a bare sibling link, the way the Task column spells one, so a pointer into another folder names a plan rather than a task and settles nothing. A cited task carrying no outcome box settles nothing either, since a file the check could not parse is not evidence of a finished one.
|
|
69
|
-
|
|
70
|
-
A citation resolving in none of the board, the archive, or the declined folder is `blocker-unresolved` rather than a settled row. Reading an absent file as archived or declined states a specific fact about a file nobody ever wrote, which is what a renamed task or a typo produces, and only a task that genuinely closed or was genuinely declined releases the row waiting on it.
|
|
71
|
-
|
|
72
|
-
The other three kinds rest on a person's judgment, so a row neither half reached lands in a third array rather than in the findings:
|
|
73
|
-
|
|
74
|
-
```json
|
|
75
|
-
{
|
|
76
|
-
"untested": [
|
|
77
|
-
{
|
|
78
|
-
"group": "Needs a plan",
|
|
79
|
-
"subject": "v50.6-a-standard-no-skill-reads", // canon-allow-reference: shows the subject field's real vXX.Y-slug shape, not a citation of a real task
|
|
80
|
-
"message": "..."
|
|
81
|
-
}
|
|
82
|
-
]
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
An untested row is not a finding and moves no exit code. Reading a clean findings list as a clean board is the failure the array exists to prevent, and `orchestrator-parked.md` is the pass that takes those rows by hand.
|
|
87
|
-
|
|
88
|
-
A `## Run now` row whose Touches column names a bare folder lands in a fourth array on the same reasoning:
|
|
89
|
-
|
|
90
|
-
```json
|
|
91
|
-
{
|
|
92
|
-
"claims": [
|
|
93
|
-
{
|
|
94
|
-
"group": "Run now",
|
|
95
|
-
"subject": "v1.0-first", // canon-allow-reference: shows the subject field's real vXX.Y-slug shape, not a citation of a real task
|
|
96
|
-
"message": "claims the whole src/tasks folder, so it collides with every row written under it."
|
|
97
|
-
}
|
|
98
|
-
]
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
That claim collides with every row a later session writes under the folder, and it is legitimate whenever the row does rewrite the directory, so the array states the reach and moves no exit code. A measure failing on a cell that is right teaches a reader to skip it. Folder against file is decided by asking the tree for a path that resolves, and by the extension only for a path the row has yet to create, since the name alone reads a file carrying no extension as a folder.
|
|
103
|
-
|
|
104
|
-
The scan reaches `## Run now` and stops, where the Touches check stops. A cell in another group describes work nobody has planned, written as a sentence and rewritten once a plan exists, so a claim read off one reports on prose rather than on a file set. A parked folder claim surfaces when its row is promoted, which is when the cell becomes something a dispatcher can act on.
|
|
105
|
-
|
|
106
|
-
A code span in any cell that the preview cannot break lands in a fifth array, `wide`, with the same `group`, `subject`, and `message` fields. The scan measures a span's longest whitespace-free run and flags one past 60 characters, or a brace-glob at any width. A 57-character span fit the pane and a 78-character one did not, so the limit is a heuristic and the array moves no exit code. A link target is skipped, since only its label renders, and the scan reaches every group, since the overflow it exists for sat under `## Up next`.
|
|
107
|
-
|
|
108
|
-
A task file neither surface names lands in a sixth array, on the same reasoning:
|
|
109
|
-
|
|
110
|
-
```json
|
|
111
|
-
{
|
|
112
|
-
"unplaced": [
|
|
113
|
-
{
|
|
114
|
-
"subject": "v50.6-a-standard-no-skill-reads", // canon-allow-reference: shows the subject field's real vXX.Y-slug shape, not a citation of a real task
|
|
115
|
-
"message": "is a task file with no row on the board and no line on the backlog."
|
|
116
|
-
}
|
|
117
|
-
]
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Under the roster-checked hand-off `task-board` and `plan-groundwork` state, filing a task and placing its row are two acts a different session each may take, so a task caught between the two is ordinary rather than a finding and this array moves no exit code. It still reports, since it is the only local detector for a row a hand-edit dropped, a handoff message that never arrived, or an orchestrator that ended before placing it.
|
|
122
|
-
|
|
123
|
-
Exit codes: `0` every check passed, `1` refused, `2` at least one finding. The `reason` field carries which gate refused: `no-board`, `no-ordering`, or `no-groups`. A board grouping under headings of its own trips `no-groups` rather than being read against columns it never declared.
|
|
124
|
-
|
|
125
|
-
Columns are read from each table's own header rather than by position, so a project whose board differs from this one is reported for what it lacks. The `index`, `priority`, and `backlog` siblings are skipped, along with every pre-compaction handoff, which takes one file per session under a `session-` prefix. None of them is a task, and a handoff counted as one would be reported as a task carrying no row on every session that wrote one.
|
|
126
|
-
|
|
127
|
-
Skills branch on the findings rather than on the exit code:
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
canon tasks validate --json | jq -r '.findings[] | "\(.kind): \(.subject)"'
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
The `task-board` skill's `board.md` and `tasks.md` references carry the board format, the `Pull request:` line, and the archive rules.
|
package/docs/agents/tasks.md
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Tasks
|
|
3
|
-
description: Claiming the next phase label, recording a pull request number and closing an outcome, listing each task's readiness, and where archiving, the plan verbs, and board validation are described
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Tasks
|
|
7
|
-
|
|
8
|
-
## Next label
|
|
9
|
-
|
|
10
|
-
`canon tasks next-label` reports the next unused phase label, reading `.canon/tasks/` and its `archive/` sibling together. A scan confined to the live board is blind to every label the archive already spent, which is what let two sessions hand out the same label within minutes of each other. Bare, it reports and never writes. With `--claim` it reserves the label first.
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
canon tasks next-label
|
|
14
|
-
canon tasks next-label --json
|
|
15
|
-
canon tasks next-label --claim --json
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
| Option | Effect |
|
|
19
|
-
| --------------- | ------------------------------------------------ |
|
|
20
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
21
|
-
| `--claim` | Reserve the label so a second claim gets another |
|
|
22
|
-
| `--root <path>` | Board root, defaulting to the main worktree |
|
|
23
|
-
|
|
24
|
-
The record carries `label`, the next free `vXX.Y`, `claimed`, whether the label was reserved, and `highest`, the label it was derived from. `highest` is absent when neither folder carries a label yet, and `label` reads `v01.0` in that case, matching the zero-padded-major shape every other label already takes. <!-- canon-allow-reference: illustrates the verb's answer for a board holding no label yet, not a citation of a real task -->
|
|
25
|
-
|
|
26
|
-
Exit codes: `0` derived, `1` refused with `no-board`, `2` refused with `label-contended` after five lost reservations, on `--claim` only.
|
|
27
|
-
|
|
28
|
-
The minor digit rolls from 9 to 0 on the next major rather than growing a second digit, which is the single-digit-minor shape every phase label already takes. `canon tasks archive` moves a task's file from the live folder into the archive without renumbering it, so the same label counts toward the maximum wherever it currently sits, and a label claimed by two different files folds into the same scan without a dedicated check.
|
|
29
|
-
|
|
30
|
-
A claim is an exclusive `mkdir` at `.canon/ordinal-locks/<label>`, and the scan counts every reservation beside the task files, since the file lands after the verb returns. Reservations never expire, so a claim that died leaves a gap, which `claude/skills/git-commit/references/versioning.md` permits. A bare read still reports without reserving, and it does not count reservations either, so it can return a label a claim already holds until that claim's task file lands. Two bare reads in the same second can also take the same answer.
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
canon tasks next-label --json | jq -r '.label'
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Archive and decline
|
|
37
|
-
|
|
38
|
-
`canon tasks archive` moves a shipped task into `.canon/tasks/archive/`, selected by its stem or by the pull request it carries, and `canon tasks decline` moves one decided against into `.canon/tasks/declined/`. Both carry the plan and the ready folder along when the task is their last citation, and both repoint every link to the moved task from another task file. The selectors, the gates, and the refusal reasons are in `tasks-archive.md`.
|
|
39
|
-
|
|
40
|
-
## Plan verbs
|
|
41
|
-
|
|
42
|
-
Five verbs read or write the plan a task cites. `plan-citations` reports where it sits and who else cites it, `plan-answers` whether it still waits on the operator, `plan-branch` the branch it derives, `plan-reach` what a branch reached against what the plan declared, and `plan-link` writes the task's `Plan:` line. Each verb's record, exit codes, and refusals are in `tasks-plans.md`.
|
|
43
|
-
|
|
44
|
-
## Pull request
|
|
45
|
-
|
|
46
|
-
`canon tasks pull-request` records the number a branch's pull request carries onto the task that branch closes. It adds `Pull request: #NNN` under the `Plan:`, `Groundwork:`, `Intake:`, or `Issue:` lines the task already holds. When the line exists it appends `, #NNN` rather than replacing it, so a task shipped in slices lists every pull request oldest first, and a number already listed leaves the line alone. A line that does not read as a comma-separated list of `#NNN` entries is replaced whole.
|
|
47
|
-
|
|
48
|
-
Name the task by its filename stem, or by the plan its `Plan:` line points at:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
canon tasks pull-request 673 v28.1-trigger-escalation # canon-allow-reference: illustrates the stem-selection form, not a citation of a real task
|
|
52
|
-
canon tasks pull-request 673 --plan worktree-scratch-routing --json
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
| Option | Behavior |
|
|
56
|
-
| --------------- | -------------------------------------------------- |
|
|
57
|
-
| `--plan <slug>` | Select the task whose `Plan:` line names this plan |
|
|
58
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
59
|
-
| `--root <path>` | Board root, defaulting to the main worktree |
|
|
60
|
-
|
|
61
|
-
A plan is matched on the token both spellings share, so `worktree-scratch-routing`, `feature-worktree-scratch-routing`, and `.canon/plans/feature-worktree-scratch-routing.md` all select the same task. The `action` field reports `added`, `appended`, `corrected`, or `unchanged`, which makes a rerun against the same number safe.
|
|
62
|
-
|
|
63
|
-
Run from a branch other than `main` or `master`, the verb also drops that branch from the task's `Pending branch:` line, along with every name its reflog says it was renamed from, and removes the line once it is empty. The renamed names matter because `git-ship` renames a nonconforming branch after `task-complete` wrote the marker. It does so on an `unchanged` run too, so a rerun repairs a marker left behind. The `pending` field lists the branches still on the line after the write.
|
|
64
|
-
|
|
65
|
-
Exit codes: `0` recorded, `1` refused. The `reason` field carries `no-board`, `no-match`, or `ambiguous`. `git-pr` skips silently on those three, because each is a case where a guessed write would archive the wrong task once the branch merges.
|
|
66
|
-
|
|
67
|
-
A malformed argument refuses as `bad-input` instead, which sits outside that set on purpose. `git-pr` derives the number by slicing the address the create call returned, so a non-numeric value is reachable, and folding it into the swallowed set would lose the number with nothing reporting it.
|
|
68
|
-
|
|
69
|
-
## Outcome
|
|
70
|
-
|
|
71
|
-
`canon tasks outcome` marks outcomes `[x]` on a task by their position in its outcome list, counting every checkbox in file order from 1.
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
canon tasks outcome v28.1-trigger-escalation --close 1 --close 3 # canon-allow-reference: illustrates the stem-selection form, not a citation of a real task
|
|
75
|
-
canon tasks outcome --plan worktree-scratch-routing --close 2 --json
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
| Option | Behavior |
|
|
79
|
-
| -------------------- | -------------------------------------------------- |
|
|
80
|
-
| `--close <position>` | Outcome to mark `[x]`, 1-based, repeatable |
|
|
81
|
-
| `--plan <slug>` | Select the task whose `Plan:` line names this plan |
|
|
82
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
83
|
-
| `--root <path>` | Board root, defaulting to the main worktree |
|
|
84
|
-
|
|
85
|
-
An outcome already closed comes back under `alreadyClosed` rather than refusing, so a rerun against the same positions changes nothing. A position past the end of the list refuses as `out-of-range`, since a caller counting wrong should hear about it rather than mark a neighbor.
|
|
86
|
-
|
|
87
|
-
A close that ticks at least one box from a branch other than `main` or `master` names that branch on a `Pending branch:` line, read from `git branch --show-current` in the working directory. The line holds the task against `canon tasks archive --pull-request` until `canon tasks pull-request` records that branch's own number. A detached HEAD, a trunk checkout, or a failed git read writes no line and refuses nothing. The line is also skipped when `gh pr list --head <branch> --state all` lists a number already on the task's `Pull request:` line, since that branch has recorded itself and a marker would outlive it, as in a review-address pass. A failed, offline, or unreadable `gh` lookup counts as no listing and writes the line. The `pending` field lists the branches on the line after the write.
|
|
88
|
-
|
|
89
|
-
Positions skip fenced blocks. A checkbox inside a sample a task displays is not an outcome the task claims, and counting one would shift every position after it. `canon tasks archive` reads the list through the same walker, so the two verbs cannot disagree about which checkboxes are outcomes.
|
|
90
|
-
|
|
91
|
-
Exit codes: `0` closed, `1` refused. The `reason` field adds `no-outcomes`, `out-of-range`, and `bad-input` to the three above.
|
|
92
|
-
|
|
93
|
-
Both verbs exist because the write is an edit inside a file that already exists. `Edit` and `Write` refuse a main-root path from a linked worktree, and a shell stream editor is banned for in-place edits, so a verb resolving the board root in-process is the only route a skill body has. Creating a whole file needs no verb, because a heredoc through `Bash` writes it safely.
|
|
94
|
-
|
|
95
|
-
## List
|
|
96
|
-
|
|
97
|
-
`canon tasks list` reports each live task file with its readiness, so a reader learns which file is parked and which is live without opening `priority.md` or `backlog.md`. It reads and never writes, and every file stays in the folder it is in.
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
canon tasks list
|
|
101
|
-
canon tasks list --json
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
| Option | Effect |
|
|
105
|
-
| --------------- | ------------------------------------------- |
|
|
106
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
107
|
-
| `--root <path>` | Board root, defaulting to the main worktree |
|
|
108
|
-
|
|
109
|
-
The record carries `tasks`, one entry per file with its `stem` and its `readiness`. Readiness is one of `Run now`, `Up next`, `Needs a plan`, `backlog`, `unplaced`, or `both`. The first three are the group names the board standard fixes, `backlog` is a line on `backlog.md`, `unplaced` is a file neither surface names, and `both` is a file both surfaces name, which `validate` reports as a finding and this verb does not judge.
|
|
110
|
-
|
|
111
|
-
The verb calls the parsers `validate` uses, so the two cannot disagree on what a surface names. It lists the live root only, never `archive/` or `declined/`, and skips the same siblings `validate` skips.
|
|
112
|
-
|
|
113
|
-
Exit codes: `0` derived, `1` refused with `no-board`.
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
canon tasks list --json | jq -r '.tasks[] | select(.readiness == "backlog") | .stem'
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## Validate
|
|
120
|
-
|
|
121
|
-
`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 five arrays that move no exit code, and the refusals are in `tasks-validate.md`.
|
|
122
|
-
|
|
123
|
-
For the board format, see `claude/skills/task-board/references/board.md`. For the `Pull request:` line and the archive rules, see `claude/skills/task-board/references/tasks.md`.
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Teach authoring
|
|
3
|
-
description: Resolving what the next lesson needs before it is written, rendering a lesson body's blocks to HTML, and rewriting the root listing, a contents page, and each lesson's chrome and quiz stepper from its marker regions
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Teach authoring
|
|
7
|
-
|
|
8
|
-
The verbs a session runs while writing a lesson. Workspace resolution, the verbs that open and record into a workspace, and the refusal reasons they share are in `teach.md`.
|
|
9
|
-
|
|
10
|
-
## Lesson
|
|
11
|
-
|
|
12
|
-
`canon teach lesson` resolves what the next lesson needs before it is written. It reads and never writes.
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
canon teach lesson regular-expressions \
|
|
16
|
-
--slug capture-groups \
|
|
17
|
-
--questions 3 \
|
|
18
|
-
--options 4
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
| Option | Behavior |
|
|
22
|
-
| ----------------- | --------------------------------------------------------- |
|
|
23
|
-
| `--slug <kebab>` | The lesson's own topic, required |
|
|
24
|
-
| `--questions <n>` | How many questions the quiz carries, required |
|
|
25
|
-
| `--options <n>` | How many options each question carries, defaulting to `4` |
|
|
26
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
27
|
-
| `--root <path>` | Teach root, defaulting to the main worktree |
|
|
28
|
-
|
|
29
|
-
It reports three things. `lesson` is the numbered path the lesson takes, derived from the highest ordinal already in `lessons/` the way an open derives a workspace ordinal. `success` carries the mission's success lines, so a session reports progress against the exit criteria without a second read of `MISSION.md`. `quiz` carries one entry per question.
|
|
30
|
-
|
|
31
|
-
A lesson written against this record carries its chrome as four empty marker pairs rather than hand-composed markup: `canon:teach:style`, `canon:teach:header`, `canon:teach:footnav`, and `canon:teach:scripts`. `canon teach nav`, below, fills them from what the workspace holds on disk.
|
|
32
|
-
|
|
33
|
-
`canon teach stylesheet <topic>` writes the pair every lesson in the workspace renders through, seeded from the design source so a workspace renders in the system every other surface does. The two halves are written on different terms. `assets/base.css` holds the generated design layer and is rewritten on every run, so a change to the shared chrome reaches a workspace that already exists. `canon teach nav` and `canon teach up` rewrite it too. `assets/course.css` is the half a workspace owns, so it refuses to overwrite, reporting `written` as false where the workspace already carries one, and `--force` takes the seed back over it. Each workspace used to carry a hand-authored palette, which is how the course styling forked once per workspace, so a lesson adds its own rules under the seed and reaches a value through its custom property rather than restating the hex.
|
|
34
|
-
|
|
35
|
-
Each `quiz` entry carries `order`, the authored option indices in presentation order where index `0` is the correct answer, and `answer`, the one-based position that answer lands in. Both travel together because a caller deriving the position itself is a caller that can derive it wrongly.
|
|
36
|
-
|
|
37
|
-
The order is drawn here rather than instructed, and that is the point of the verb. An author told to vary the position still varies it by judgment, and the judgment settles on the first slot, which is the defect this design departs from. The draw is uniform over the options, so the position carries no information about which answer is correct.
|
|
38
|
-
|
|
39
|
-
## Render
|
|
40
|
-
|
|
41
|
-
`canon teach render` renders a lesson body's structural blocks to HTML, through the five lesson components. It takes no topic and no `--root`, since the verb is a stateless transform reading nothing off a workspace on disk.
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
echo '[{"type":"heading","level":1,"text":"Compass bearings"}]' | canon teach render --json
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
| Option | Behavior |
|
|
48
|
-
| -------- | ---------------------------------------- |
|
|
49
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
50
|
-
|
|
51
|
-
It reads a JSON array of blocks from stdin, each a `heading`, `paragraph`, `list`, `refs`, `code`, or `raw` block, and reports `{ ok: true, html }` on `--json` or the bare rendered HTML on stdout otherwise. Content none of the components can express takes a `raw` block, carrying its own HTML verbatim and unescaped, which is the shape the quiz and the teach-back block travel in.
|
|
52
|
-
|
|
53
|
-
A `code` block carries `text` and an optional `lang`, a highlight.js name or alias. The source is highlighted at render time into `hljs-*` class spans the workspace stylesheet colors in both themes, so the page carries no script. A `lang` the renderer does not register renders as plain escaped code, and one that does not match `^[a-z0-9+#-]+$` is refused. A workspace seeded before the syntax colors existed shows the spans uncolored until `canon teach nav`, `canon teach up`, or `canon teach stylesheet <topic>` rewrites its base stylesheet.
|
|
54
|
-
|
|
55
|
-
```json
|
|
56
|
-
[{ "type": "code", "lang": "ts", "text": "const a: number = 1" }]
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
A citation takes two shapes. A `paragraph` carries an optional `cites` array of reference numbers, rendered as `sup.cite` markers after its text. The one `refs` block carries `items` of `{ title, url?, note? }`, rendered as `ol.refs` and numbered from 1, so cite `1` links to the first item.
|
|
60
|
-
|
|
61
|
-
```json
|
|
62
|
-
[
|
|
63
|
-
{ "type": "paragraph", "text": "North is fixed.", "cites": [1] },
|
|
64
|
-
{
|
|
65
|
-
"type": "refs",
|
|
66
|
-
"items": [{ "title": "Compass manual", "url": "https://example.com" }]
|
|
67
|
-
}
|
|
68
|
-
]
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
It refuses `bad-input` on empty stdin, on malformed JSON, on stdin that does not parse to an array, and on a block carrying an unrecognized `type` or a field of the wrong shape for its type, naming the block's index in the message. Citations add four refusals, each naming the offending block: a cite that resolves to no reference, `cites` on a lede paragraph, a second `refs` block, and a reference `url` that is not `http` or `https`.
|
|
72
|
-
|
|
73
|
-
## Nav
|
|
74
|
-
|
|
75
|
-
`canon teach nav` rewrites the teach-root listing, a workspace's contents page, and each of its lessons' chrome, from what the workspace holds on disk.
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
canon teach nav
|
|
79
|
-
canon teach nav regular-expressions --json
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
| Option | Behavior |
|
|
83
|
-
| --------------- | ------------------------------------------------- |
|
|
84
|
-
| `[topic]` | Workspace folder or topic, scoping the run to one |
|
|
85
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
86
|
-
| `--root <path>` | Teach root, defaulting to the main worktree |
|
|
87
|
-
|
|
88
|
-
With no topic it rewrites every workspace. The teach-root listing is always rewritten regardless, since it reports on every workspace and a scoped run leaving it stale would be a second kind of drift this verb exists to end.
|
|
89
|
-
|
|
90
|
-
A lesson carries its chrome as four marker pairs the authoring skill writes empty: `canon:teach:style`, `canon:teach:header`, `canon:teach:footnav`, and `canon:teach:scripts`. This verb splices each one from the current workspace state, embedding the toolkit's favicon, the shared stylesheet, and the script that settles the course panel before the page paints, rebuilding the course sidebar, the breadcrumb and jump menus, the prev/next footer nav, and the behavior scripts. It leaves the authored `<h1>`, lede, body, and quiz between the header and the footnav as written, with two exceptions. It wraps them in `<main>` when the lesson holds none, since the column width hangs off that element, and a lesson that already carries a `<main>` keeps its own.
|
|
91
|
-
|
|
92
|
-
It also links each "lesson NNNN" mention in that region to the lesson file in the same workspace carrying that number, skipping text inside `a`, `code`, `pre`, `script`, `style`, `label`, `summary`, and comments. A link the verb writes carries `data-lesson-ref`, and it is the only kind the verb rewrites: every run points one at the current filename for its number, or unwraps it to plain text once that number names no lesson. A link an author wrote is never touched.
|
|
93
|
-
|
|
94
|
-
Every workspace label in that chrome, being the breadcrumb, the sidebar heading, the workspace switcher, and the root listing's row, reads the mission's `title` and falls back to the folder topic in sentence case where the mission names none.
|
|
95
|
-
|
|
96
|
-
It also deletes two links a session used to write by hand into a lesson's `<head>`, each matched as its exact string: the old icon link and `<link rel="stylesheet" href="../assets/course.css" />`. The page keeps only the icon and the stylesheets the `style` region carries. Any other link an author wrote stays.
|
|
97
|
-
|
|
98
|
-
A workspace carrying no `course.css` gets its stylesheet pair seeded first, so the contents page it links is never unstyled, and an existing `course.css` is never overwritten. The `style` region links `assets/base.css` ahead of an embedded copy of `course.css`, so the workspace's own rules still win the cascade. The embedded copy drops the seed's `@import` line, which resolved against the lesson's own folder and never loaded anything, and the link is what reaches the base sheet in its place. A workspace with no `assets/base.css`, one holding a hand-authored `course.css` from before the seed, gets no link rather than one pointing at a missing file.
|
|
99
|
-
|
|
100
|
-
Every run also rewrites `assets/base.css` wherever the workspace already holds one, so a change to the shared chrome reaches an existing workspace through this verb as well as through `canon teach stylesheet <topic>`. `course.css` is still never rewritten. A legacy workspace holding only a `course.css` is not handed a `base.css`, since its lessons were written without the link. `canon teach up` runs this pass before serving, which is the route that keeps a viewed workspace current.
|
|
101
|
-
|
|
102
|
-
A lesson missing one of the four marker pairs is refused by name rather than rewritten, and every other lesson in the run still rewrites. The record's `skipped` list carries the refused files and which marker each is missing.
|
|
103
|
-
|
|
104
|
-
A mention whose number names no lesson in its workspace stays plain text and lands in the record's `unresolved` list, one `{ file, lesson }` entry per distinct number per lesson, with `file` relative to the root the way a `skipped` entry is. The lesson still rewrites and counts toward `lessons`, and the exit stays 0. A mention of the lesson's own number stays plain and is not reported, and a refused lesson reports nothing.
|
|
105
|
-
|
|
106
|
-
The `style` region carries the quiz stepper after the workspace stylesheet, for every lesson but the one shape named three paragraphs down, so it wins the cascade at equal specificity and reaches a workspace seeded before it existed. The stepper is CSS over the radio inputs a quiz is written from: it hides every question that follows one holding no checked input, and hides a question's feedback until that question does. The general sibling combinator is what keeps that from depending on the markup contract holding, since it selects the same questions as the adjacent one for a quiz written correctly and goes on gating when anything sits between two of them, where the adjacent one matches nothing and shows them all at once. Gating there rather than in a script is what keeps a later stem from answering an earlier question with nothing to bind, and the whole block sits inside `@supports selector(:has(*))`, so an engine without `:has()` renders every question rather than a stepper showing nothing. Feedback there follows the workspace stylesheet, shown by one that says nothing about `.fb` and hidden by one grown under the retired script.
|
|
107
|
-
|
|
108
|
-
The block hides and shows and sets nothing else, which leaves a workspace's own layout for a question and its own appearance for an option and a feedback panel. The one value it names is `display: block` on revealed feedback, since every stylesheet grown under the retired script carries `.fb { display: none }` and nothing adds the class that used to reveal it.
|
|
109
|
-
|
|
110
|
-
One detector decides both regions. A lesson carrying `<button class="opt"`, the shape written before the stepper, takes the feedback script and no stepper, and every other lesson takes the stepper and no script. That is a detector rather than a mode, since a lesson has one shape or the other, and removing the script outright would leave the lessons already written showing no feedback at all.
|
|
111
|
-
|
|
112
|
-
Splicing the stepper into a lesson of either shape was measured against a browser and reverted. Its rules match a button question, which can never hold a checked radio, so a lesson given both hides every question past the first for good and outranks the class the script reveals feedback with. Nothing else caught that: the markup is valid, the splice succeeds, and the page renders.
|
|
113
|
-
|
|
114
|
-
### Reference pages
|
|
115
|
-
|
|
116
|
-
Each reference page is rendered to a `reference/<slug>.html` sibling carrying the same chrome, and the contents page links that sibling rather than the markdown, which `canon serve` hands over as plain text. The render drops the frontmatter, turns tables into `<table>` markup, and escapes raw HTML written in the markdown rather than passing it through. A fenced code block in a language the highlighter registers is highlighted the same way a `code` block is, and one in any other language stays plain. A hand-written code block in a lesson body is never highlighted. A body with no H1 gets one from the frontmatter `title`. A relative link to another reference page's markdown points at that page's `.html` sibling, and one to the workspace `GLOSSARY.md` points at the glossary on the contents page, `../index.html#gloss`. Every other link, such as `../RESOURCES.md`, stays as written. The markdown file is never touched.
|
|
117
|
-
|
|
118
|
-
Every rendered page carries a `<meta name="generator" content="canon teach nav">` tag. A `.html` file under `reference/` whose markdown is gone is deleted when it carries that tag, and a hand-written HTML file there, carrying none, is left alone. The record's `reference` field counts the pages rendered. On a Bun with no `Bun.markdown`, each reference page lands in `skipped` with `Bun.markdown` as what is missing, and the rest of the run goes on.
|
package/docs/agents/teach.md
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Teach
|
|
3
|
-
description: Listing learning workspaces with what their records schedule next, opening one with its required files, recording sources and glossary terms, viewing one with its chrome refreshed, where the lesson authoring verbs are described, the refusal reasons, and why every write here runs through a verb
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Teach
|
|
7
|
-
|
|
8
|
-
Learning workspaces sit under `.canon/teach/<nn>-<topic>/`, and `claude/skills/teach-workspace/references/teach.md` fixes their layout, naming, and file formats, apart from the glossary, whose shape `claude/skills/teach-workspace/references/glossary.md` fixes, cited from the `teach-workspace` skill, so it travels with the file wherever a promotion lands it. Every verb here resolves that folder against the main worktree root rather than against the working directory, so a session standing in a linked worktree reaches the one workspace the learner has rather than opening a second.
|
|
9
|
-
|
|
10
|
-
Passing `--root` a path already named `teach` reaches that folder directly rather than wrapping it in the `.canon/`-or-`.claude/` record-root lookup, which is what lets a workspace committed outside the record root, such as a worked example kept in version control on purpose, take every verb below the same as one under `.canon/teach/`.
|
|
11
|
-
|
|
12
|
-
That root resolution is also why the writing verbs exist at all. The file-editing tools refuse a main-root path from a linked worktree and offer a worktree copy instead, and a caller naming only the destination reports a success that did not happen. A whole-file create still goes out as a shell heredoc. Changing a line inside a file that already exists has no shell route, because the stream editors are banned, so `resource` and `glossary` are the route for the two files a running workspace edits.
|
|
13
|
-
|
|
14
|
-
## List
|
|
15
|
-
|
|
16
|
-
`canon teach list` reports the workspaces under `.canon/teach/`, or what one workspace holds. It reads and never writes.
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
canon teach list
|
|
20
|
-
canon teach list regular-expressions --json
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
| Option | Behavior |
|
|
24
|
-
| --------------- | ------------------------------------------- |
|
|
25
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
26
|
-
| `--root <path>` | Teach root, defaulting to the main worktree |
|
|
27
|
-
|
|
28
|
-
With no topic it reports one line per workspace, carrying the lesson, learning-record, reference-page, and glossary-term counts, plus `next`, the ordinal an open would take. With one it reports the filenames behind each count and the glossary entries themselves.
|
|
29
|
-
|
|
30
|
-
A folder not named `NN-<topic>` is not a workspace, so the listing, `nav`, and every selector skip it and it moves no ordinal. That is what lets a teach root carry a sibling folder such as `evidence/` without it drawing as a stub row and receiving a contents page.
|
|
31
|
-
|
|
32
|
-
The listing also names the required files a workspace does not carry, which is `MISSION.md`, `RESOURCES.md`, and `GLOSSARY.md`. That is a report rather than a refusal, because a workspace missing one is still a workspace a session can resume.
|
|
33
|
-
|
|
34
|
-
Every record carries `due`, read from the `## Revisit` sections of the workspace's learning records and sorted soonest first. Each entry names the `item`, its `date`, the `rung` it has reached on the spacing ladder, `overdue` once that date has arrived or passed, and the `record` the surviving entry was written in. A later record supersedes an earlier entry for the same item, matched without regard to case, since the schedule is the state of one topic rather than a log of every time it was set.
|
|
35
|
-
|
|
36
|
-
Each entry also carries `hit` and `miss`, the two dates the next record could write. The ladder is 1, 3, 7, 16, and 35 days, a retrieval the learner passed unaided moving up a rung and a miss dropping back one, both held inside the ladder at either end. Reporting both dates is what lets a session copy one rather than compute it, on the same reasoning that put quiz option order in `canon teach lesson`.
|
|
37
|
-
|
|
38
|
-
The field rides on the listing rather than on `canon teach lesson`, because a session needs the schedule before it has chosen what to teach and that verb takes a `--slug` naming a choice already made. An entry not written in the shape the standard fixes is skipped rather than reported, since a note carrying no date has no date to schedule.
|
|
39
|
-
|
|
40
|
-
## Open
|
|
41
|
-
|
|
42
|
-
`canon teach open` creates a workspace at the next ordinal and writes all three required files.
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
canon teach open regular-expressions \
|
|
46
|
-
--subject "Reading and writing regular expressions" \
|
|
47
|
-
--starting-point "Comfortable with the shell, has never written a group" \
|
|
48
|
-
--success "Write a pattern matching a date" \
|
|
49
|
-
--success "Explain what a backreference does"
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
| Option | Behavior |
|
|
53
|
-
| ------------------------- | ----------------------------------------------------------- |
|
|
54
|
-
| `--subject <line>` | One line stating what the workspace covers, required |
|
|
55
|
-
| `--starting-point <text>` | What the learner already knows, required |
|
|
56
|
-
| `--success <line>` | Observable thing the learner will be able to do, repeatable |
|
|
57
|
-
| `--out-of-scope <line>` | What the workspace does not cover, repeatable |
|
|
58
|
-
| `--title <text>` | Title, defaulting to the topic in sentence case |
|
|
59
|
-
| `--date <YYYY-MM-DD>` | Opening date, defaulting to today |
|
|
60
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
61
|
-
| `--root <path>` | Teach root, defaulting to the main worktree |
|
|
62
|
-
|
|
63
|
-
The ordinal comes from the highest already present, incremented, so the caller derives no name and composes no path. A topic another workspace already covers is refused rather than opened beside it, since two workspaces on one subject fork the learning records the folder exists to keep whole.
|
|
64
|
-
|
|
65
|
-
`--starting-point` is required rather than defaulted because difficulty with no floor under it teaches nobody, and a mission written without one cannot say what the lessons sit above. At least one `--success` line is required for the same reason in the other direction: a mission nothing can test is a mission nothing can call finished.
|
|
66
|
-
|
|
67
|
-
## Resource
|
|
68
|
-
|
|
69
|
-
`canon teach resource` records sources in a workspace `RESOURCES.md`, keeping what was read apart from what was only found.
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
canon teach resource regular-expressions \
|
|
73
|
-
--read "MDN regular expressions=https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions" \
|
|
74
|
-
--lead "RE2 syntax=https://github.com/google/re2/wiki/Syntax"
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
| Option | Behavior |
|
|
78
|
-
| -------------------- | -------------------------------------------------- |
|
|
79
|
-
| `--read <title=url>` | Source that stands behind the material, repeatable |
|
|
80
|
-
| `--lead <title=url>` | Source found and not opened, repeatable |
|
|
81
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
82
|
-
| `--root <path>` | Teach root, defaulting to the main worktree |
|
|
83
|
-
|
|
84
|
-
The pair splits on the first `=`, so a URL carrying its own separator survives intact. Say in the title which claims rest on the source, since the entry is the only place a later session reads that from. Which sources qualify is the source ladder in `claude/skills/teach-workspace/references/teach.md`, and a source below its first two rungs also names why no higher one covers the claim.
|
|
85
|
-
|
|
86
|
-
A URL either heading already lists is refused rather than written twice. Two entries for one source split what rests on it across two lines, and a reader checking a claim then finds half of the answer. The exception is a `--read` URL already under `## Leads`, which moves to `## Read` under the title the call carries, since opening a lead is how it becomes read. A `--read` URL already under `## Read`, and any `--lead` URL already listed, stays refused, and a refusal leaves the whole batch unwritten.
|
|
87
|
-
|
|
88
|
-
A URL a file displays inside a fence does not count as listed, which is how a workspace quoting the entry format in its own prose is read as the sample it is.
|
|
89
|
-
|
|
90
|
-
## Glossary
|
|
91
|
-
|
|
92
|
-
`canon teach glossary` adds terms to a workspace `GLOSSARY.md`, alphabetically.
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
canon teach glossary regular-expressions \
|
|
96
|
-
--term "capture group=A parenthesised part of a pattern whose match is kept" \
|
|
97
|
-
--first-seen 0002-groups.html
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
| Option | Behavior |
|
|
101
|
-
| -------------------------- | --------------------------------------------------------- |
|
|
102
|
-
| `--term <term=definition>` | Term the subject defines, repeatable |
|
|
103
|
-
| `--first-seen <file>` | Lesson or reference page the batch first defines these in |
|
|
104
|
-
| `--json` | Emit a machine-readable record on stdout |
|
|
105
|
-
| `--root <path>` | Teach root, defaulting to the main worktree |
|
|
106
|
-
|
|
107
|
-
One call writes one file, which keeps a batch of terms from racing on the glossary every one of them shares. `--first-seen` names one page for the whole batch rather than one per term, because a batch comes from one lesson.
|
|
108
|
-
|
|
109
|
-
A term already defined is refused rather than replaced. A definition the subject has moved under is a revision of the entry that exists, and nothing on the command line tells that apart from a second definition arriving by mistake.
|
|
110
|
-
|
|
111
|
-
The entry lands as the standard's shape, leading with the term as a bolded span. A definition not ending in sentence punctuation is terminated before the citation is appended, so a bare phrase does not run into the sentence naming where the term first appears.
|
|
112
|
-
|
|
113
|
-
## Lesson, render, and nav
|
|
114
|
-
|
|
115
|
-
`canon teach lesson`, `canon teach render`, and `canon teach nav` build and splice a lesson rather than editing a workspace's records. They are in `teach-authoring.md`.
|
|
116
|
-
|
|
117
|
-
## Opening a workspace
|
|
118
|
-
|
|
119
|
-
`canon teach up` runs the `nav` pass and then serves the teach root, so the page a reader opens carries the chrome the installed CLI generates:
|
|
120
|
-
|
|
121
|
-
```bash
|
|
122
|
-
canon teach up 03-fde-system-design --json
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
The link opens the named workspace's contents page, or the root listing when no topic is named. The verb serves the teach folder at its absolute path, so a run from a linked worktree still reaches the main worktree root. `--port` sets the first port tried and `--root` the teach root, as on every other verb. It runs until interrupted, so a session starts it in the background and reads the record off stdout.
|
|
126
|
-
|
|
127
|
-
The JSON record is one line carrying the `nav` fields, then `served`, the absolute folder, and the server's `host`, `port`, `entry`, `url`, and `entryExists`. A lesson `nav` skips lands in `skipped` and the server still starts, since a partial refresh is still worth viewing. A `nav` refusal starts no server.
|
|
128
|
-
|
|
129
|
-
While it runs, `up` watches the teach root. A change there, such as a lesson, a workspace, a glossary term, or a reference page added by hand or by another `teach` verb, runs the `nav` pass again, and every open page reloads once that pass changes what is served. Passes run one at a time, so a burst of saves costs one more pass rather than several at once. A pass `nav` refuses, such as one over a workspace deleted while it was served, is reported on stderr, the server keeps running, and no page reloads.
|
|
130
|
-
|
|
131
|
-
Each refresh is logged to stderr, so stdout stays the one record line. Dotfiles and editor backups ending in `~` start no pass.
|
|
132
|
-
|
|
133
|
-
The reload script is added to each page as the server sends it and never written to disk, so a page saved from the browser, a promoted lesson, and a committed fixture carry no trace of it. The script listens on `/__live`, which answers only a request naming the server by its loopback address or `localhost`.
|
|
134
|
-
|
|
135
|
-
Viewing writes. Every page and generated stylesheet `nav` owns is rewritten first, which on a committed fixture shows as a diff. To serve without rewriting anything, `canon teach list <topic>` ends its human output with a `canon serve` line for that workspace, with the teach folder written relative to the cwd, or absolute when it sits outside it. The JSON record of `list` carries no such field.
|
|
136
|
-
|
|
137
|
-
`canon serve` itself stays general, since nothing about serving a directory is specific to a learning workspace and the same verb carries a slide render and a design preview. What `up` adds in front of it is the refresh and the watch that repeats it, both teach-specific, and it serves through the same server rather than a second one.
|
|
138
|
-
|
|
139
|
-
Read `url` off the record rather than building one from the port that was asked for. The verb walks forward past a port already in use, which is routine when a second workspace is already open, and the port it took is the one thing a composed URL gets wrong.
|
|
140
|
-
|
|
141
|
-
## Refusal reasons
|
|
142
|
-
|
|
143
|
-
| Reason | Raised when |
|
|
144
|
-
| -------------- | --------------------------------------------------------------- |
|
|
145
|
-
| `no-teach` | The root carries no `.canon/teach/` folder |
|
|
146
|
-
| `no-workspace` | No workspace matches the topic, with the folder names as detail |
|
|
147
|
-
| `ambiguous` | Two workspaces claim one topic, with both names as detail |
|
|
148
|
-
| `exists` | A workspace already covers the topic an open names |
|
|
149
|
-
| `no-file` | The workspace carries no file the verb writes into |
|
|
150
|
-
| `no-section` | `RESOURCES.md` carries no heading the entries belong under |
|
|
151
|
-
| `listed` | A URL is already read, or a lead is offered as listed |
|
|
152
|
-
| `defined` | A term already carries a glossary entry |
|
|
153
|
-
| `bad-input` | The command line is malformed, before any folder is read |
|
|
154
|
-
|
|
155
|
-
`up` also passes through the refusals `canon serve` raises once `nav` has run, `no-port` when every port in reach is taken and `bind-failed` for any other bind error, such as a privileged port, in the same record shape with the server's detail as `message`.
|
|
156
|
-
|
|
157
|
-
A `bad-input` refusal reports the working directory as its root rather than the resolved one, since the command line is rejected before the root is worth resolving. `lesson` raises it for a slug that is not kebab-case, for a quiz carrying no question, and for a question carrying fewer than two options, since a question with one option has nothing to confuse the right answer with.
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Test order
|
|
3
|
-
description: Reading where an implementation reached history ahead of its test, how a pair is decided, the three verdicts, the coverage the pairing cannot reach, and why the check reports rather than gates
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Test order
|
|
7
|
-
|
|
8
|
-
`canon gov test-order` reports where an implementation reached a commit ahead of the test covering the same behavior. It measures the test-first order that the `test-first` skill and `auto-ship` hold, which until this verb existed was prose with nothing measuring it.
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
canon gov test-order
|
|
12
|
-
canon gov test-order --base origin/main --json
|
|
13
|
-
canon gov test-order --root ../my-app
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
| Option | Behavior |
|
|
17
|
-
| --------------- | ---------------------------------------------------------- |
|
|
18
|
-
| `--base <ref>` | Ref the range runs back to, defaulting to the trunk |
|
|
19
|
-
| `--root <path>` | Repository to read, defaulting to the current directory |
|
|
20
|
-
| `--json` | Add a machine-readable record on stdout, keeping the frame |
|
|
21
|
-
|
|
22
|
-
Under `--json` the record holds stdout alone and the frame still renders on stderr, refusals included, which is the split `output-shape.md` fixes for every mode. A consumer reading stdout sees pure data, and an operator reading the terminal sees why a run refused rather than a command that appeared to do nothing.
|
|
23
|
-
|
|
24
|
-
## The range it reads
|
|
25
|
-
|
|
26
|
-
The default range is the branch against the trunk, resolved as the merge base against `origin/main` and then local `main`. A repository carrying neither falls back to the root commit, so a fresh checkout still answers rather than refusing.
|
|
27
|
-
|
|
28
|
-
Reading all history on every run measures work nobody is reviewing and buries the finding that matters under the ones already merged. `--base` widens or narrows it when a reader wants a different window.
|
|
29
|
-
|
|
30
|
-
A ref passed there is resolved the same way, through its merge base with `HEAD`, so it names the far side of the range and not the commit the range starts from. `--base origin/main` therefore reports the same window whether the trunk has moved since the branch left or not. Naming a commit the branch already carries still measures from that commit, since the merge base of `HEAD` and an ancestor is the ancestor, and a ref sharing no history with `HEAD` refuses as `bad-base`.
|
|
31
|
-
|
|
32
|
-
Only history is read. A file sitting in the working tree and in no commit is invisible here, which is the point: the verification run sees a tree at one moment, and the ordering exists nowhere but history.
|
|
33
|
-
|
|
34
|
-
## How a pair is decided
|
|
35
|
-
|
|
36
|
-
A test is paired to an implementation by the module path with the test suffix removed, so `src/parser.test.ts` covers `src/parser.ts`. Every record is keyed on the implementation path, which is what a reader goes looking for.
|
|
37
|
-
|
|
38
|
-
Pairing is the whole difficulty. A test and its implementation are related by behavior rather than by filename, so a behavior split across two modules pairs wrongly or not at all. That is what the unclassified verdict exists to catch rather than hide.
|
|
39
|
-
|
|
40
|
-
## The three verdicts
|
|
41
|
-
|
|
42
|
-
- **Satisfied.** The implementation was added in the range and its test reached history no later. A test that predates the range counts, and so does one commit carrying both sides, since the rule asks that the test not come after and a single commit is the shape a small change takes.
|
|
43
|
-
- **Implementation-first.** The implementation was added in the range and its test was added in a later commit of the same range. This is the only verdict that moves the exit code.
|
|
44
|
-
- **Unclassified.** Everything the pairing cannot read, each carrying its own reason. A module the range modified rather than added lands here, because a refactor and a new behavior cannot be told apart from history. So does an implementation no test names, and a test whose implementation the range never introduced.
|
|
45
|
-
|
|
46
|
-
## What it reads past
|
|
47
|
-
|
|
48
|
-
Only `.ts` and `.tsx` are paired, since a test sits beside its subject under one name across that corpus and a bash script has no such partner. Declaration files carry no behavior to test. Every other changed path is counted under `Read past` rather than dropped silently.
|
|
49
|
-
|
|
50
|
-
Coverage is narrower than the rule the check answers to. The rule speaks to every behavior, and the verb speaks to the behaviors it can identify, so the report states its own scope on every run rather than implying it looked at everything.
|
|
51
|
-
|
|
52
|
-
## Exit codes
|
|
53
|
-
|
|
54
|
-
Exit codes are `0` when nothing reached history ahead of its test, `1` for a refusal, and `2` for at least one implementation-first finding. Unclassified rows move no exit code.
|
|
55
|
-
|
|
56
|
-
Nothing wires this into `bun run check` or into a hook. The unclassified class is large and known, and gating a measure carrying a known false-positive class is what teaches contributors to route around the stage. `canon tasks validate` is the precedent: it exits `2` on findings and is called where the claim is made rather than on every push.
|
|
57
|
-
|
|
58
|
-
An exit code says nothing about a call made from a session, since a shell profile may wrap the binary in a function taking its status from a later command. Read the JSON record's `findings` array rather than the exit when a skill consumes this.
|