@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: draft-context
|
|
3
|
-
description: Why a brand-new canon/context/<domain>.md entry needs a catalog collision check and a confirm step, not the refresh path
|
|
3
|
+
description: Why a brand-new canon/context/<domain>.md entry needs a catalog collision check and a confirm step, not the refresh path context-fold already owns
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Context draft requirement
|
|
@@ -31,7 +31,7 @@ Without this skill, a session documenting a domain that has no context entry yet
|
|
|
31
31
|
|
|
32
32
|
## Out of scope
|
|
33
33
|
|
|
34
|
-
- Refreshing an existing `canon/context/<domain>.md` entry against a diff: `
|
|
34
|
+
- Refreshing an existing `canon/context/<domain>.md` entry against a diff: `context-fold`
|
|
35
35
|
- Drafting a `canon/wireframes/<surface>.md` file: `draft-wireframes`
|
|
36
36
|
- Drafting a `docs/*.md` page: `draft-docs`
|
|
37
37
|
- Drafting a standard, a snippet, or a governance rule: `create-standard`, `create-snippet`, `create-rule`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: draft-context
|
|
3
|
-
description: Drafts a brand-new `canon/context/<domain>.md` entry against the context standard, checks the catalog for a name-or-topic collision, decides flat-file placement, confirms with the user, then writes. Use when asked to "write a context entry for X", "document the X domain", "add a context entry for X", or "create a canon/context page for X" where no existing entry covers the domain. Do NOT use to refresh an existing entry against a diff, which is `
|
|
3
|
+
description: Drafts a brand-new `canon/context/<domain>.md` entry against the context standard, checks the catalog for a name-or-topic collision, decides flat-file placement, confirms with the user, then writes. Use when asked to "write a context entry for X", "document the X domain", "add a context entry for X", or "create a canon/context page for X" where no existing entry covers the domain. Do NOT use to refresh an existing entry against a diff, which is `context-fold`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Context draft
|
|
@@ -18,12 +18,12 @@ Read these files in parallel:
|
|
|
18
18
|
## Guards
|
|
19
19
|
|
|
20
20
|
- If no domain is given, stop: `❌ No domain given. Name the domain this entry should cover.`
|
|
21
|
-
- Derive a kebab-case slug from the domain and check whether `canon/context/<slug>.md` or `canon/context/<slug>/index.md` already exists. Either resolving means the domain is already covered under that exact name. Stop: `❌ <slug> already has a context entry. Use
|
|
21
|
+
- Derive a kebab-case slug from the domain and check whether `canon/context/<slug>.md` or `canon/context/<slug>/index.md` already exists. Either resolving means the domain is already covered under that exact name. Stop: `❌ <slug> already has a context entry. Use context-fold to refresh it instead.`
|
|
22
22
|
|
|
23
23
|
## Placement
|
|
24
24
|
|
|
25
25
|
- Read `canon/context/index.md` and check every title and description it lists against the domain. The Guards check above only catches an exact-slug collision, and a domain already covered under a different name still resolves here at no extra cost, since this read already runs.
|
|
26
|
-
- Stop the same way on a match: `❌ <path> already covers this domain under a different name. Use
|
|
26
|
+
- Stop the same way on a match: `❌ <path> already covers this domain under a different name. Use context-fold to refresh it instead.`
|
|
27
27
|
- Default a brand-new entry to a flat file, `canon/context/<slug>.md`. A domain starts as one page's worth of narrative, and the standard only splits it into a folder once it holds three or more sub-areas, which a fresh domain never does on day one.
|
|
28
28
|
|
|
29
29
|
## Draft
|
|
@@ -31,5 +31,5 @@ Without this skill, a session drafting documentation for a surface that has no p
|
|
|
31
31
|
## Out of scope
|
|
32
32
|
|
|
33
33
|
- Rewriting or syncing an existing `docs/*.md` section against a diff since main: `docs-sync`
|
|
34
|
-
- The
|
|
34
|
+
- The context entries, requirements, architecture, wireframes, and task board: `context-fold`
|
|
35
35
|
- Drafting a standard, a snippet, or a governance rule: `create-standard`, `create-snippet`, `create-rule`
|
|
@@ -75,7 +75,7 @@ canon tasks plan-link <task-stem> .canon/plans/feature-<slug>.md --json
|
|
|
75
75
|
|
|
76
76
|
Branch on the record rather than on the exit. The `Ready:` line orients a worker reading the task, and `canon tasks archive` reads it to move the folder on ship.
|
|
77
77
|
|
|
78
|
-
A task file with no row is a dropped task, so place the row in the same pass. The plan exists, so the row takes `## Run now` when `canon tasks plan-reach` reports nothing claimed and `## Up next` otherwise, with the held path in its `Waiting on` cell, per the tests in `${CLAUDE_SKILL_DIR}/../../standards/
|
|
78
|
+
A task file with no row is a dropped task, so place the row in the same pass. The plan exists, so the row takes `## Run now` when `canon tasks plan-reach` reports nothing claimed and `## Up next` otherwise, with the held path in its `Waiting on` cell, per the tests in `${CLAUDE_SKILL_DIR}/../../standards/board.md`. Check the roster the way `canon:task-board` Step 4 does, reading `canon sessions list --self --json` for this session and `canon sessions list --json` for a row from the same repository, under another `sessionId`, whose `name` starts with `orchestrator-`. Treat a refused read as a roster read that failed.
|
|
79
79
|
|
|
80
80
|
- **Orchestrator found.** Write neither file. Message that session with the row's title, the plan link, and the group with its reason, so it places the row itself rather than two sessions writing the board at once.
|
|
81
81
|
- **Not found, and this session's name starts with `worker-` or `planner-`.** Write neither file, since both role bodies ban a board write with no exception. Report the row and its group in Step 8.
|
|
@@ -31,7 +31,7 @@ Without this skill, a session drafting a wireframe for a surface with no file ye
|
|
|
31
31
|
|
|
32
32
|
## Out of scope
|
|
33
33
|
|
|
34
|
-
- Stubbing a surface a diff touched, or reporting drift in an existing wireframe against a diff: `claude/skills/
|
|
34
|
+
- Stubbing a surface a diff touched, or reporting drift in an existing wireframe against a diff: `claude/skills/context-fold/references/wireframe-sweep.md`
|
|
35
35
|
- Drafting a `canon/context/<domain>.md` entry: `draft-context`
|
|
36
36
|
- Drafting a `docs/*.md` page: `draft-docs`
|
|
37
37
|
- Drafting a standard, a snippet, or a governance rule: `create-standard`, `create-snippet`, `create-rule`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: draft-wireframes
|
|
3
|
-
description: Drafts a brand-new `canon/wireframes/<surface>.md` file against the wireframes standard, walks the tree for a name collision, detects an existing higher visual-design tier without building one, confirms with the user, then writes. Use when asked to "draft a wireframe for X", "write the wireframe for this surface", "add a canon/wireframes entry for X", or "wireframe this screen" where no surface file covers it yet. Do NOT use to fix a stale TODO stub or report wireframe drift against a diff, which is `
|
|
3
|
+
description: Drafts a brand-new `canon/wireframes/<surface>.md` file against the wireframes standard, walks the tree for a name collision, detects an existing higher visual-design tier without building one, confirms with the user, then writes. Use when asked to "draft a wireframe for X", "write the wireframe for this surface", "add a canon/wireframes entry for X", or "wireframe this screen" where no surface file covers it yet. Do NOT use to fix a stale TODO stub or report wireframe drift against a diff, which is `context-fold`'s wireframe-sweep step.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Wireframe draft
|
|
@@ -15,7 +15,7 @@ Read these files in parallel:
|
|
|
15
15
|
- `${CLAUDE_SKILL_DIR}/../../standards/markdown.md`: punctuation and formatting for the prose around the fences
|
|
16
16
|
- The `write-human` skill: voice, rhythm, and sentence construction for the prose around the fences
|
|
17
17
|
|
|
18
|
-
This skill stays fully independent of `
|
|
18
|
+
This skill stays fully independent of `context-fold`'s wireframe coverage sweep, which only ever writes a bare `TODO` stub for a surface a diff touched and reports drift against one a wireframe already covers. Neither the stub nor the drift check is a draft, and this skill never reads or writes through that mechanism.
|
|
19
19
|
|
|
20
20
|
## Guards
|
|
21
21
|
|
|
@@ -32,11 +32,7 @@ Then run these commands in parallel to gather git context:
|
|
|
32
32
|
|
|
33
33
|
## Diff baseline
|
|
34
34
|
|
|
35
|
-
Prefer `origin/main` over local `main`. Both reads resolve against `<base>`, so the commits listed and the changes described come from one scope.
|
|
36
|
-
|
|
37
|
-
`git diff main..HEAD` is the form the diff replaces. A two-dot range compares tips and resolves no merge base, so once local `main` advances past the branch point it reports main's newer commits as reversed changes and the description describes work the branch never did. On `main` itself the local ref resolves to HEAD and every committed change drops out instead.
|
|
38
|
-
|
|
39
|
-
`git log main..HEAD` is the matching defect on the commit side. It excludes what local `main` reaches, so a local `main` trailing `origin/main` leaves commits in the range that are already on the remote and are not this branch's work. The diff resolved from `<base>` excludes those same commits, and the description then lists commits whose changes appear nowhere in it. Reading both against `<base>` is what keeps the two halves describing one branch.
|
|
35
|
+
Prefer `origin/main` over local `main`. Both reads resolve against `<base>`, so the commits listed and the changes described come from one scope. Never read a two-dot range against local `main` for either half: it resolves no merge base, so once local `main` moves, the diff reports its newer commits as reversed changes and the log lists commits already on the remote.
|
|
40
36
|
|
|
41
37
|
The baseline is unusable in two cases:
|
|
42
38
|
|
|
@@ -87,17 +83,17 @@ Then run:
|
|
|
87
83
|
canon labels scan --title "<title>" --body-file .canon/tmp/pr/body.md --json
|
|
88
84
|
```
|
|
89
85
|
|
|
90
|
-
Branch on the JSON record rather than the exit code. An operator's shell can wrap `canon` in a function whose status comes from a trailing command, flattening a non-zero exit to 0, the same reason
|
|
86
|
+
Branch on the JSON record rather than the exit code. An operator's shell can wrap `canon` in a function whose status comes from a trailing command, flattening a non-zero exit to 0, the same reason the labels step below branches on its own record rather than the exit.
|
|
91
87
|
|
|
92
88
|
Stop and fix the title or body on a non-empty `phaseLabels`, `boardReferences`, `sessionLinks`, `unspelledWords`, or `titleFormatIssues`. Do not proceed to `### Final command` until a re-run comes back clean on all five. `titleFormatIssues` names which `## Title` rule in `${CLAUDE_SKILL_DIR}/../../standards/pr.md` the title breaks, structure, casing, or length, so fix the named rule rather than guessing. Leave `cutsRelease` and `semverTags` alone, since a release-please pull request legitimately carries version references its own fixed shape explains.
|
|
93
89
|
|
|
94
|
-
|
|
90
|
+
The scan stays required even where a `pull_request` workflow job runs the same verb, since a project holding an older `canon` carries no such job and no `titleFormatIssues` key.
|
|
95
91
|
|
|
96
92
|
### Resolving the pull request
|
|
97
93
|
|
|
98
94
|
The run resolves the pull request once, in the final command below, and every later step reads what that command printed. Nothing else looks the number up again.
|
|
99
95
|
|
|
100
|
-
`gh pr view
|
|
96
|
+
Never detect with `gh pr view`. It resolves by head branch and ignores state, so a branch name reused after an earlier pull request merged returns the closed one and the run rewrites it. Scoping the lookup with `--state open` returns empty there and sends the run down the create path.
|
|
101
97
|
|
|
102
98
|
The lookup scopes to the base as well as the head. One head can carry open pull requests against two bases, and a lookup reading the first result would pick between them by list order. Resolving the base from the repository's default branch is what makes the detection and `gh pr create` agree on which pull request the run is about.
|
|
103
99
|
|
|
@@ -105,23 +101,7 @@ A detached HEAD gives `git branch --show-current` an empty result, which would r
|
|
|
105
101
|
|
|
106
102
|
### Labels
|
|
107
103
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
canon labels audit --base <base> --json
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
The record carries `labels`, the set this branch earns, and `uncovered`, the changed paths no row of the map reaches. Join `labels` with commas into `pr_labels` below. Report each `uncovered` path beside the result line, naming the map so the reader knows where a row would go, since a surface nobody covered merges bare and nothing else says so.
|
|
115
|
-
|
|
116
|
-
Branch on the record rather than on the exit. An operator's shell profile may wrap `canon` in a function whose status comes from a trailing command, and the binary exits 1 for an unknown subcommand and 1 for an ordinary refusal alike.
|
|
117
|
-
|
|
118
|
-
A `reason` of `no-map` is the answer that the project declared no map, which earns no labels and no warning: a label set this skill supplied would be a guess about that project's surfaces. Stop there and label nothing.
|
|
119
|
-
|
|
120
|
-
Every other `reason` is a map or a range the verb could not read, which is `unreadable-map`, `no-domains`, `no-base`, and `unreadable-changes`, plus `bad-base` for a ref this skill resolved wrongly. Take the fallback below and warn beside the result line, naming the reason. A map with a typo in it still has rows a prefix match can reach, and reading the refusal as an absence would open the pull request with no labels and nothing said, which is the surface merging bare that the verb exists to name.
|
|
121
|
-
|
|
122
|
-
The fallback is reading `canon/config/pr-labels.toml`, or `.claude/canon/pr-labels.toml` when the project has not moved, and matching it against the name-only diff per `${CLAUDE_SKILL_DIR}/references/labels.md`. It also covers no record coming back at all, which is an installed `canon` predating the verb, since a skill reaches a target the moment it merges while the CLI reaches one only when a release publishes. Naming both spellings matters exactly here: the binary old enough to need this fallback is the same binary that may predate the move, so the project's map can still sit at the older path. The fallback labels correctly and reports no uncovered path, which is the half only the verb carries.
|
|
123
|
-
|
|
124
|
-
Leave `pr_labels` empty when no map resolves or no prefix matches, which skips the labelling command rather than running it against nothing.
|
|
104
|
+
Read `${CLAUDE_SKILL_DIR}/references/labels.md` on reaching this step, starting at its `## Labels at run time` section, for the verb to ask, the record to branch on, the fallback, and when `pr_labels` stays empty. The final command below takes the set it resolves as `pr_labels`.
|
|
125
105
|
|
|
126
106
|
### Final command
|
|
127
107
|
|
|
@@ -170,131 +150,13 @@ The check compares a number against a branch and never derives a number from one
|
|
|
170
150
|
|
|
171
151
|
The last output line carries `head=` so a caller relaying the number holds a branch to compare it against rather than a bare integer. A caller that writes to the pull request itself, such as a draft mark, runs the same comparison in the shell first, reading `gh pr view <number> --json headRefName,state` and refusing unless it matches `head` and `OPEN`.
|
|
172
152
|
|
|
173
|
-
###
|
|
174
|
-
|
|
175
|
-
`ui-checklist` writes a visual checklist to `.canon/tmp/handoff/ui-checklist/<slug>.md` at the main worktree root when a change needs visual verification, with `<slug>` derived per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. This skill is the file's sole consumer. Resolve the main root the way `session-worktree` does (`git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd`) and check for the file there. A missing file means no checklist was produced, and the two steps below each skip their checklist half.
|
|
176
|
-
|
|
177
|
-
When it exists, scan it against `${CLAUDE_SKILL_DIR}/../../standards/publish.md` before either step posts it, the same as the pull request body above.
|
|
178
|
-
|
|
179
|
-
Where it lands is decided by the evidence step below rather than here, since a checklist reads next to the screenshots it annotates and posting it on its own is the fallback for a branch that changed no screenshot.
|
|
180
|
-
|
|
181
|
-
### Find the local server
|
|
182
|
-
|
|
183
|
-
A reviewer on the operator's machine can open the branch's running dev server, which screenshots and a checklist cannot stand in for. Ask the verb for the server this worktree is running before the evidence step, so the first evidence comment already carries the link:
|
|
184
|
-
|
|
185
|
-
```bash
|
|
186
|
-
canon pr local --json
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
The verb reads the listening sockets on this machine, keeps those whose process runs inside this worktree, and reports the lowest port serving an HTML page. A server in another worktree or in the main checkout is never reported, since it shows a different branch. `canon docs pr-local` states the contract. Read `reason` on the record rather than the exit code.
|
|
190
|
-
|
|
191
|
-
- `ok`: hold the record's `url` for the evidence step below.
|
|
192
|
-
- `no-server` or `no-listener-reader`: hold the reason for the closing report, pass no link, and move on. Do not start a server here, since one started inside the ship chain has no owner to stop it.
|
|
193
|
-
- Anything else, including no record at all: move on silently. An installed binary older than the verb answers with an unknown-subcommand error, which is a skip rather than a stop.
|
|
194
|
-
|
|
195
|
-
Pass `--local` to the evidence step only on an `ok` here. The binary answering `ok` is the one carrying the flag, so a binary lacking the verb never meets a flag it would reject.
|
|
196
|
-
|
|
197
|
-
With both `--checklist` and `--local`, a diff carrying no evidence image still renders `ok`, with the link opening a comment the checklist closes, so a reviewer gets both in one place. A local link with neither an evidence image nor a checklist still reports `no-evidence`, which keeps a branch that changed no rendered surface from getting a comment holding a link and nothing else.
|
|
198
|
-
|
|
199
|
-
A seeded workflow replaces the line with a note once the pull request closes, so the link does not outlive the branch it points at.
|
|
200
|
-
|
|
201
|
-
### Post the evidence comparison
|
|
202
|
-
|
|
203
|
-
Run the verb once against the number the pull request step above resolved. Pass `--checklist` when the checklist step found a file, and `--local` when the step above returned `ok`. Leave each flag off otherwise:
|
|
204
|
-
|
|
205
|
-
```bash
|
|
206
|
-
canon pr evidence <number> --checklist <main-root>/.canon/tmp/handoff/ui-checklist/<slug>.md --local <url> --json
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
One call answers both questions because a checklist does not decide `no-evidence`. The verb reports `no-evidence` on a diff carrying no evidence image whether or not a checklist came with it, so the branch below reads the same `reason` it would have read without the flag, and the checklist is folded in only on the path that has a comparison to fold it into. The local link is the one exception, stated in the step above.
|
|
210
|
-
|
|
211
|
-
Pass `--checklist` only for a file that exists. The verb refuses as `unreadable-checklist` on a path it cannot read or one holding nothing, which is a caller bug rather than a transient failure, so stop and repair the path rather than posting a body with the checklist silently dropped.
|
|
212
|
-
|
|
213
|
-
Read `reason` on the record rather than the exit code.
|
|
214
|
-
|
|
215
|
-
- `no-evidence`: nothing changed under an `evidence/` segment, so there is no comparison to post and no body was rendered. Say nothing about the evidence and fall through to the checklist step below.
|
|
216
|
-
- `ok`: write `body` to `.canon/tmp/pr/evidence/body-<number>.md` at the main worktree root (resolved the way `session-worktree` does), then post or update the comment:
|
|
217
|
-
|
|
218
|
-
```bash
|
|
219
|
-
gh pr comment <number> --body-file <main-root>/.canon/tmp/pr/evidence/body-<number>.md
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
When the record carries a `commentId`, edit that comment in place instead of posting a second one, reading the body field from the tmp file with `@`, which needs the typed-field flag `-F` because the raw-string flag `-f` posts the path itself as the body:
|
|
223
|
-
|
|
224
|
-
```bash
|
|
225
|
-
gh api -X PATCH repos/{owner}/{repo}/issues/comments/<commentId> -F body=@<main-root>/.canon/tmp/pr/evidence/body-<number>.md
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
Delete the handoff file once that call reports success, per the cleanup below, since the checklist now lives on the pull request. Clean up the tmp body file the same way.
|
|
229
|
-
|
|
230
|
-
Any other `reason` is one of the mirrored git refusals (`gh-missing`, `gh-failed`, `no-base`, `unreadable-tree`, `unreadable-changes`). Report it and move on without stopping the chain: a branch that carries no evidence images most of the time should not fail here on a transient git or `gh` read. Fall through to the checklist step, which posts the checklist alone rather than losing it to a transient read.
|
|
231
|
-
|
|
232
|
-
### Post the UI checklist alone
|
|
233
|
-
|
|
234
|
-
Run this step only when the evidence step above did not carry the checklist, meaning it reported `no-evidence` or one of the git refusals, and a checklist file exists. A local link sends the checklist through the evidence step instead, so this step runs on a branch with no evidence image only when the local step found no server. Post it as its own comment on `<number>`:
|
|
235
|
-
|
|
236
|
-
```bash
|
|
237
|
-
gh pr comment <number> --body-file <main-root>/.canon/tmp/handoff/ui-checklist/<slug>.md
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
Run the cleanup below only once the call that carried the checklist reports success, whichever of the two steps that was. On a failure, stop and leave the file in place: a retry needs the checklist to still be there, and deleting it on a failed post loses the only copy with nothing landed on the pull request.
|
|
241
|
-
|
|
242
|
-
The cleanup is a main-root delete, so it goes out as a plain `rm` and then a plain `rmdir`, the file and then the folder, routed the way `session-worktree` states:
|
|
243
|
-
|
|
244
|
-
```bash
|
|
245
|
-
rm <main-root>/.canon/tmp/handoff/ui-checklist/<slug>.md
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
rmdir <main-root>/.canon/tmp/handoff/ui-checklist 2>/dev/null || true
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
The `rmdir` is a no-op when another branch's pending checklist still sits in the folder, which keeps this step from deleting a handoff that is not its own.
|
|
253
|
-
|
|
254
|
-
Deleting the file is what makes the later re-render safe. `git-followup` re-runs `canon pr evidence` with no `--checklist`, and the verb carries the checklist forward out of the comment it is editing, so the boxes a reviewer already ticked survive the push.
|
|
255
|
-
|
|
256
|
-
### Post the preview address
|
|
257
|
-
|
|
258
|
-
Run this step only when the evidence step above returned `ok` or a checklist was posted, by either step. Either one means the pull request changes a rendered surface, and a reviewer holding a checklist with no screenshots needs the live page most. Otherwise skip it silently.
|
|
153
|
+
### Post the rendered-surface evidence
|
|
259
154
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
```bash
|
|
263
|
-
canon pr preview <number> --json
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
The verb dispatches the project's Pages deploy on the pull request's branch and waits on the run for up to 15 minutes. It refuses before dispatching anything when the deploy command passes no `--branch`, since that deploy would publish the branch to production. `canon docs pr-preview` states the contract. Read `reason` on the record rather than the exit code.
|
|
267
|
-
|
|
268
|
-
- `ok`: re-render the evidence body with the address on its first line, then post or update it exactly as the evidence step does, through the same tmp file and the `commentId` the record carries:
|
|
269
|
-
|
|
270
|
-
```bash
|
|
271
|
-
canon pr evidence <number> --preview <url> --json
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
The hosted address takes the first line and a local link already on the comment moves to the second, carried forward with no flag.
|
|
275
|
-
|
|
276
|
-
- `no-deploy`: the project deploys nothing a dispatch can start. Say nothing and move on.
|
|
277
|
-
- Any other `reason`, being `unfenced`, `no-alias`, `run-failed`, `timeout`, or a mirrored `gh` refusal: report it with the record's `message` and move on without stopping the chain. A missing link costs the reviewer a click, and a held ship costs the whole chain.
|
|
155
|
+
Skip this step silently when no UI checklist sits at `.canon/tmp/handoff/ui-checklist/<slug>.md` at the main worktree root and the diff changes nothing under an `evidence/` segment, since neither half then has anything to post. Otherwise read `${CLAUDE_SKILL_DIR}/references/evidence.md` for finding the checklist and the local server, posting the evidence comparison, posting the checklist alone, and posting the preview address, each against the `<number>` the final command printed.
|
|
278
156
|
|
|
279
157
|
### Record the number on the task
|
|
280
158
|
|
|
281
|
-
Write the `number` the final command printed onto the task the branch is closing
|
|
282
|
-
|
|
283
|
-
The task is the one whose `Plan:` line names the plan this branch implemented. Name that plan by its file, which is `.canon/plans/feature-<slug>.md` at the main worktree root with `<slug>` derived per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. `plan-feature` writes the plan under the branch slug, so the two correspond on any branch that came through the plan-to-execute path. When the session already knows which plan it implemented, because a caller read it earlier in the chain, use that filename instead of re-deriving.
|
|
284
|
-
|
|
285
|
-
```bash
|
|
286
|
-
canon tasks pull-request <number> --plan feature-<slug> --json
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
The slug is a guess at which plan this branch carries rather than a fact about the task, which is why the verb re-checks it against the board and refuses instead of writing on a near miss. A branch whose slug names no plan file falls to the silent skip below, the same as one whose plan no task cites.
|
|
290
|
-
|
|
291
|
-
The verb resolves the board at the main worktree root in-process, adds `Pull request: #NNN` under the `Plan:`, `Groundwork:`, `Intake:`, or `Issue:` lines the task already carries, and corrects the number in place when the line exists. This is the route because the write is an edit inside an existing main-root file, which `session-worktree` routes through a verb. That root is the one `session-worktree` resolves on entry.
|
|
292
|
-
|
|
293
|
-
The correction reaches only a line that does not read as a list of `#NNN` entries. A line that does takes the new number appended rather than replaced, reported as `appended`, so a task shipped in slices keeps every pull request.
|
|
294
|
-
|
|
295
|
-
Skip this silently when the record is `ok: false` and `reason` is `no-board`, `no-match`, or `ambiguous`. Those are the three cases a guessed write would compound: no board, no task naming the plan, or more than one. One pull request names one task, and a wrong match archives the wrong task unattended once the branch merges. Report any other refusal rather than swallowing it.
|
|
296
|
-
|
|
297
|
-
The number is what lets the merge close the task. Every merge on `main` is a squash carrying it in the subject, so the number survives where a branch name does not, and `post-merge` reads it back to call `canon tasks archive`. Writing it here rather than at worktree time is what makes it a pull request number rather than a branch the squash discards.
|
|
159
|
+
Write the `number` the final command printed onto the task the branch is closing, through `canon tasks pull-request`, and never resolve it again. Read `${CLAUDE_SKILL_DIR}/references/task-number.md` on reaching this step for which task it names, the invocation, the refusals it skips silently, and why the number lands here. Skip it when the final command refused on a head mismatch and printed no number.
|
|
298
160
|
|
|
299
161
|
## After execution
|
|
300
162
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Rendered-surface evidence
|
|
3
|
+
description: How git-pr finds the UI checklist and the local server, posts the evidence comparison or the checklist alone, and adds the preview address, for a change that touches a rendered surface
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rendered-surface evidence
|
|
7
|
+
|
|
8
|
+
The rendered-surface step of `git-pr`, reached after the final command printed the pull request number. The session reads this file when a UI checklist exists or the diff changes a path under an `evidence/` segment.
|
|
9
|
+
|
|
10
|
+
## Find the UI checklist
|
|
11
|
+
|
|
12
|
+
`ui-checklist` writes a visual checklist to `.canon/tmp/handoff/ui-checklist/<slug>.md` at the main worktree root when a change needs visual verification, with `<slug>` derived per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. This skill is the file's sole consumer. Resolve the main root the way `session-worktree` does (`git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd`) and check for the file there. A missing file means no checklist was produced, and the two steps below each skip their checklist half.
|
|
13
|
+
|
|
14
|
+
When it exists, scan it against `${CLAUDE_SKILL_DIR}/../../standards/publish.md` before either step posts it, the same as the pull request body in the skill's pre-publish scan.
|
|
15
|
+
|
|
16
|
+
Where it lands is decided by the evidence step below rather than here, since a checklist reads next to the screenshots it annotates and posting it on its own is the fallback for a branch that changed no screenshot.
|
|
17
|
+
|
|
18
|
+
## Find the local server
|
|
19
|
+
|
|
20
|
+
A reviewer on the operator's machine can open the branch's running dev server, which screenshots and a checklist cannot stand in for. Ask the verb for the server this worktree is running before the evidence step, so the first evidence comment already carries the link:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
canon pr local --json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The verb reads the listening sockets on this machine, keeps those whose process runs inside this worktree, and reports the lowest port serving an HTML page. A server in another worktree or in the main checkout is never reported, since it shows a different branch. `canon docs pr-local` states the contract. Read `reason` on the record rather than the exit code.
|
|
27
|
+
|
|
28
|
+
- `ok`: hold the record's `url` for the evidence step below.
|
|
29
|
+
- `no-server` or `no-listener-reader`: hold the reason for the closing report, pass no link, and move on. Do not start a server here, since one started inside the ship chain has no owner to stop it.
|
|
30
|
+
- Anything else, including no record at all: move on silently. An installed binary older than the verb answers with an unknown-subcommand error, which is a skip rather than a stop.
|
|
31
|
+
|
|
32
|
+
Pass `--local` to the evidence step only on an `ok` here. The binary answering `ok` is the one carrying the flag, so a binary lacking the verb never meets a flag it would reject.
|
|
33
|
+
|
|
34
|
+
With both `--checklist` and `--local`, a diff carrying no evidence image still renders `ok`, with the link opening a comment the checklist closes, so a reviewer gets both in one place. A local link with neither an evidence image nor a checklist still reports `no-evidence`, which keeps a branch that changed no rendered surface from getting a comment holding a link and nothing else.
|
|
35
|
+
|
|
36
|
+
A seeded workflow replaces the line with a note once the pull request closes, so the link does not outlive the branch it points at.
|
|
37
|
+
|
|
38
|
+
## Post the evidence comparison
|
|
39
|
+
|
|
40
|
+
Run the verb once against the number the skill's final command printed. Pass `--checklist` when the checklist step found a file, and `--local` when the step above returned `ok`. Leave each flag off otherwise:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
canon pr evidence <number> --checklist <main-root>/.canon/tmp/handoff/ui-checklist/<slug>.md --local <url> --json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
One call answers both questions because a checklist does not decide `no-evidence`. The verb reports `no-evidence` on a diff carrying no evidence image whether or not a checklist came with it, so the branch below reads the same `reason` it would have read without the flag, and the checklist is folded in only on the path that has a comparison to fold it into. The local link is the one exception, stated in the step above.
|
|
47
|
+
|
|
48
|
+
Pass `--checklist` only for a file that exists. The verb refuses as `unreadable-checklist` on a path it cannot read or one holding nothing, which is a caller bug rather than a transient failure, so stop and repair the path rather than posting a body with the checklist silently dropped.
|
|
49
|
+
|
|
50
|
+
Read `reason` on the record rather than the exit code.
|
|
51
|
+
|
|
52
|
+
- `no-evidence`: nothing changed under an `evidence/` segment, so there is no comparison to post and no body was rendered. Say nothing about the evidence and fall through to the checklist step below.
|
|
53
|
+
- `ok`: write `body` to `.canon/tmp/pr/evidence/body-<number>.md` at the main worktree root (resolved the way `session-worktree` does), then post or update the comment:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
gh pr comment <number> --body-file <main-root>/.canon/tmp/pr/evidence/body-<number>.md
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
When the record carries a `commentId`, edit that comment in place instead of posting a second one, reading the body field from the tmp file with `@`, which needs the typed-field flag `-F` because the raw-string flag `-f` posts the path itself as the body:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
gh api -X PATCH repos/{owner}/{repo}/issues/comments/<commentId> -F body=@<main-root>/.canon/tmp/pr/evidence/body-<number>.md
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Delete the handoff file once that call reports success, per the cleanup below, since the checklist now lives on the pull request. Clean up the tmp body file the same way.
|
|
66
|
+
|
|
67
|
+
Any other `reason` is one of the mirrored git refusals (`gh-missing`, `gh-failed`, `no-base`, `unreadable-tree`, `unreadable-changes`). Report it and move on without stopping the chain: a branch that carries no evidence images most of the time should not fail here on a transient git or `gh` read. Fall through to the checklist step, which posts the checklist alone rather than losing it to a transient read.
|
|
68
|
+
|
|
69
|
+
## Post the UI checklist alone
|
|
70
|
+
|
|
71
|
+
Run this step only when the evidence step above did not carry the checklist, meaning it reported `no-evidence` or one of the git refusals, and a checklist file exists. A local link sends the checklist through the evidence step instead, so this step runs on a branch with no evidence image only when the local step found no server. Post it as its own comment on `<number>`:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
gh pr comment <number> --body-file <main-root>/.canon/tmp/handoff/ui-checklist/<slug>.md
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Run the cleanup below only once the call that carried the checklist reports success, whichever of the two steps that was. On a failure, stop and leave the file in place: a retry needs the checklist to still be there, and deleting it on a failed post loses the only copy with nothing landed on the pull request.
|
|
78
|
+
|
|
79
|
+
The cleanup is a main-root delete, so it goes out as a plain `rm` and then a plain `rmdir`, the file and then the folder, routed the way `session-worktree` states:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
rm <main-root>/.canon/tmp/handoff/ui-checklist/<slug>.md
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
rmdir <main-root>/.canon/tmp/handoff/ui-checklist 2>/dev/null || true
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The `rmdir` is a no-op when another branch's pending checklist still sits in the folder, which keeps this step from deleting a handoff that is not its own.
|
|
90
|
+
|
|
91
|
+
Deleting the file is what makes the later re-render safe. `git-followup` re-runs `canon pr evidence` with no `--checklist`, and the verb carries the checklist forward out of the comment it is editing, so the boxes a reviewer already ticked survive the push.
|
|
92
|
+
|
|
93
|
+
## Post the preview address
|
|
94
|
+
|
|
95
|
+
Run this step only when the evidence step above returned `ok` or a checklist was posted, by either step. Either one means the pull request changes a rendered surface, and a reviewer holding a checklist with no screenshots needs the live page most. Otherwise skip it silently.
|
|
96
|
+
|
|
97
|
+
The evidence comment is already posted, so the reviewer has the screenshots while the deploy runs. Mint the preview against the same `<number>`:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
canon pr preview <number> --json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The verb dispatches the project's Pages deploy on the pull request's branch and waits on the run for up to 15 minutes. It refuses before dispatching anything when the deploy command passes no `--branch`, since that deploy would publish the branch to production. `canon docs pr-preview` states the contract. Read `reason` on the record rather than the exit code.
|
|
104
|
+
|
|
105
|
+
- `ok`: re-render the evidence body with the address on its first line, then post or update it exactly as the evidence step does, through the same tmp file and the `commentId` the record carries:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
canon pr evidence <number> --preview <url> --json
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The hosted address takes the first line and a local link already on the comment moves to the second, carried forward with no flag.
|
|
112
|
+
|
|
113
|
+
- `no-deploy`: the project deploys nothing a dispatch can start. Say nothing and move on.
|
|
114
|
+
- Any other `reason`, being `unfenced`, `no-alias`, `run-failed`, `timeout`, or a mirrored `gh` refusal: report it with the record's `message` and move on without stopping the chain. A missing link costs the reviewer a click, and a held ship costs the whole chain.
|
|
@@ -93,3 +93,23 @@ The refusal names the label it rejected and applies none of the set, so a warnin
|
|
|
93
93
|
## Release pull requests
|
|
94
94
|
|
|
95
95
|
Release automation opens its own pull requests without this skill and applies its own labels, so nothing here needs a skip condition for them.
|
|
96
|
+
|
|
97
|
+
## Labels at run time
|
|
98
|
+
|
|
99
|
+
The labels step of `git-pr` reads from here. Ask the CLI first:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
canon labels audit --base <base> --json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The record carries `labels`, the set this branch earns, and `uncovered`, the changed paths no row of the map reaches. Join `labels` with commas into `pr_labels`, which the skill's final command takes. Report each `uncovered` path beside the result line, naming the map so the reader knows where a row would go, since a surface nobody covered merges bare and nothing else says so.
|
|
106
|
+
|
|
107
|
+
Branch on the record rather than on the exit. An operator's shell profile may wrap `canon` in a function whose status comes from a trailing command, and the binary exits 1 for an unknown subcommand and 1 for an ordinary refusal alike.
|
|
108
|
+
|
|
109
|
+
A `reason` of `no-map` is the answer that the project declared no map, which earns no labels and no warning: a label set this skill supplied would be a guess about that project's surfaces. Stop there and label nothing.
|
|
110
|
+
|
|
111
|
+
Every other `reason` is a map or a range the verb could not read, which is `unreadable-map`, `no-domains`, `no-base`, and `unreadable-changes`, plus `bad-base` for a ref this skill resolved wrongly. Take the fallback below and warn beside the result line, naming the reason. A map with a typo in it still has rows a prefix match can reach, and reading the refusal as an absence would open the pull request with no labels and nothing said, which is the surface merging bare that the verb exists to name.
|
|
112
|
+
|
|
113
|
+
The fallback is reading `canon/config/pr-labels.toml`, or `.claude/canon/pr-labels.toml` when the project has not moved, and matching it against the name-only diff per `## Matching` above. It also covers no record coming back at all, which is an installed `canon` predating the verb, since a skill reaches a target the moment it merges while the CLI reaches one only when a release publishes. Naming both spellings matters exactly here: the binary old enough to need this fallback is the same binary that may predate the move, so the project's map can still sit at the older path. The fallback labels correctly and reports no uncovered path, which is the half only the verb carries.
|
|
114
|
+
|
|
115
|
+
Leave `pr_labels` empty when no map resolves or no prefix matches, which skips the labelling command rather than running it against nothing.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Record the number on the task
|
|
3
|
+
description: How git-pr writes the pull request number the final command printed onto the task whose Plan line names the branch's plan, and which refusals it skips silently
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Record the number on the task
|
|
7
|
+
|
|
8
|
+
The last step of `git-pr` before its report, reached on every run whose final command printed a number.
|
|
9
|
+
|
|
10
|
+
Write the `number` the final command printed onto the task the branch is closing. Do not resolve it again. A final command that refused on a head mismatch printed no number, so this step writes nothing on that run. `${CLAUDE_SKILL_DIR}/REQUIREMENT.md` states why: a lookup that resolves by branch alone can return a closed pull request sharing that head, so the number is resolved once and reused rather than re-derived.
|
|
11
|
+
|
|
12
|
+
The task is the one whose `Plan:` line names the plan this branch implemented. Name that plan by its file, which is `.canon/plans/feature-<slug>.md` at the main worktree root with `<slug>` derived per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. `plan-feature` writes the plan under the branch slug, so the two correspond on any branch that came through the plan-to-execute path. When the session already knows which plan it implemented, because a caller read it earlier in the chain, use that filename instead of re-deriving.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
canon tasks pull-request <number> --plan feature-<slug> --json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The slug is a guess at which plan this branch carries rather than a fact about the task, which is why the verb re-checks it against the board and refuses instead of writing on a near miss. A branch whose slug names no plan file falls to the silent skip below, the same as one whose plan no task cites.
|
|
19
|
+
|
|
20
|
+
The verb resolves the board at the main worktree root in-process, adds `Pull request: #NNN` under the `Plan:`, `Groundwork:`, `Intake:`, or `Issue:` lines the task already carries, and corrects the number in place when the line exists. This is the route because the write is an edit inside an existing main-root file, which `session-worktree` routes through a verb. That root is the one `session-worktree` resolves on entry.
|
|
21
|
+
|
|
22
|
+
The correction reaches only a line that does not read as a list of `#NNN` entries. A line that does takes the new number appended rather than replaced, reported as `appended`, so a task shipped in slices keeps every pull request.
|
|
23
|
+
|
|
24
|
+
Skip this silently when the record is `ok: false` and `reason` is `no-board`, `no-match`, or `ambiguous`. Those are the three cases a guessed write would compound: no board, no task naming the plan, or more than one. One pull request names one task, and a wrong match archives the wrong task unattended once the branch merges. Report any other refusal rather than swallowing it.
|
|
25
|
+
|
|
26
|
+
The number is what lets the merge close the task. Every merge on `main` is a squash carrying it in the subject, so the number survives where a branch name does not, and `post-merge` reads it back to call `canon tasks archive`. Writing it here rather than at worktree time is what makes it a pull request number rather than a branch the squash discards.
|
|
@@ -24,7 +24,7 @@ Run `git diff --cached --name-only 2>/dev/null` to check for staged files. If ou
|
|
|
24
24
|
## Sequence
|
|
25
25
|
|
|
26
26
|
1. Invoke `canon:memory-capture` to route what this session learned to the context entries that own it and write the residue to `.canon/memory/`
|
|
27
|
-
2. Invoke `canon:
|
|
27
|
+
2. Invoke `canon:context-fold` to sync internal planning docs against session decisions, folding in the routed facts
|
|
28
28
|
3. Invoke `canon:docs-sync` to sync public docs against changes since main
|
|
29
29
|
4. Run `git add -A` to stage any files the sync skills wrote
|
|
30
30
|
5. Run `canon tasks plan-reach <plan> --json` and report both lists it carries. Name the plan this branch built under, by path or by slug. Read `claimed` first and say who holds each path, since that is the half a reader acts on, then say how many of the changed paths `undeclared` names. Branch on the record rather than on the exit code, which a shell function wrapping `canon` can flatten to zero. This step reports and never stops the sequence.
|
|
@@ -49,14 +49,15 @@ Capture leads the sequence because a routed fact lands in a context entry, which
|
|
|
49
49
|
|
|
50
50
|
## After completion
|
|
51
51
|
|
|
52
|
-
Output up to
|
|
52
|
+
Output up to four lines:
|
|
53
53
|
|
|
54
54
|
```plaintext
|
|
55
55
|
✅ Shipped
|
|
56
56
|
<N facts routed to context entries>
|
|
57
57
|
<N memories captured in .canon/memory/>
|
|
58
|
+
<the review line memory-capture returned>
|
|
58
59
|
```
|
|
59
60
|
|
|
60
|
-
Omit the second line if nothing routed
|
|
61
|
+
Omit the second line if nothing routed, the third if `memory-capture` wrote no memory file this session, and the fourth if `memory-capture` returned no review line. Pass that line through verbatim rather than restating it.
|
|
61
62
|
|
|
62
|
-
Emit nothing here when a wrapping caller states it closes on its own block. `auto-ship` is that caller and its block carries these same
|
|
63
|
+
Emit nothing here when a wrapping caller states it closes on its own block. `auto-ship` is that caller and its block carries these same three trailing lines below a first line naming the draft state, so emitting both reports one run twice. A caller that states no such thing gets this block, which is every direct invocation.
|
|
@@ -27,7 +27,7 @@ The threshold is what the remaining folder lives or dies on. A first-occurrence
|
|
|
27
27
|
|
|
28
28
|
## Must not
|
|
29
29
|
|
|
30
|
-
- Edit a context entry, which `
|
|
30
|
+
- Edit a context entry, which `context-fold` owns
|
|
31
31
|
- Route a feedback, user, or reference candidate, since no context entry owns how to work or who to ask
|
|
32
32
|
- Route anything when the caller does not commit, because a context entry is a tracked file
|
|
33
33
|
- Hand-append a row to the memory index, which is generated from sibling frontmatter
|
|
@@ -45,6 +45,6 @@ The threshold is what the remaining folder lives or dies on. A first-occurrence
|
|
|
45
45
|
|
|
46
46
|
## Out of scope
|
|
47
47
|
|
|
48
|
-
- Editing the context entries themselves, which `
|
|
48
|
+
- Editing the context entries themselves, which `context-fold` owns on its own pass
|
|
49
49
|
- Curating what is already in the folder, which `memory-review` owns
|
|
50
50
|
- Promoting an entry into an instruction file or a skill body, which mutates how the agent operates and ships as its own change
|
|
@@ -16,7 +16,7 @@ The filename and its type prefix, the frontmatter, the body shape each type carr
|
|
|
16
16
|
- All `.canon/memory/` reads and writes resolve at the main worktree root, not the current worktree. Resolve that root the way `session-worktree` does.
|
|
17
17
|
- Every path below sits at the main root, so each write in this skill goes out as a heredoc, routed the way `session-worktree` states. A memory entry holds one fact and this session has read it, so an update rewrites the whole file with a heredoc rather than editing a line inside it.
|
|
18
18
|
- If `.canon/memory/` does not exist at the main worktree root, create it, along with an `index.md` carrying `title` and `subtitle` frontmatter. `canon claude init` seeds both, and a project predating that seed has neither. Regeneration errors without the index, so the first write into a bare folder would report a frontmatter failure against a file that is fine.
|
|
19
|
-
- If the session produced no user corrections, confirmations, or context disclosures worth persisting, stop: `✅ Nothing worth capturing
|
|
19
|
+
- If the session produced no user corrections, confirmations, or context disclosures worth persisting, run Step 6 and stop: `✅ Nothing worth capturing.`, followed by the review line when Step 6 counted one batch due.
|
|
20
20
|
- Routing edits a tracked file, so it runs only where the caller commits. When the session is in the main worktree, or the caller states it does not commit, skip Step 3 and write every candidate as a memory file. `role-orchestrator` is the caller this covers.
|
|
21
21
|
|
|
22
22
|
## Step 1: read context
|
|
@@ -44,7 +44,7 @@ For each project candidate, match its subject against `canon/context/index.md`.
|
|
|
44
44
|
|
|
45
45
|
Fail closed. A project candidate matching no entry stays a memory file, and so does one matching two entries where neither is clearly the owner. The residue is what the folder is for, and a fact filed under the wrong entry is worse than one in memory because a context entry is a surface sessions trust.
|
|
46
46
|
|
|
47
|
-
Do not edit a context entry here. `
|
|
47
|
+
Do not edit a context entry here. `context-fold` owns those edits and folds the routed facts in on its own pass, or two skills write one file at the same step. Write each routed fact to `.canon/tmp/handoff/memory-routing/<slug>.md` at the main worktree root instead, appending when the file exists. Name the heading with the entry's own path from `canon/context/index.md`, flat or the nested `index.md`, since that heading is what tells `context-fold`'s routed-facts fold which file to open. An append is a whole-file operation, so send it as a heredoc:
|
|
48
48
|
|
|
49
49
|
A flat domain takes:
|
|
50
50
|
|
|
@@ -64,11 +64,11 @@ A domain split into a folder takes its own generated index instead:
|
|
|
64
64
|
|
|
65
65
|
Derive `<slug>` per `${CLAUDE_SKILL_DIR}/../../standards/slug.md`. Fall back to `latest` on an empty result.
|
|
66
66
|
|
|
67
|
-
The handoff is a file rather than a spoken result so the routed fact survives a compaction between this step and the `
|
|
67
|
+
The handoff is a file rather than a spoken result so the routed fact survives a compaction between this step and the `context-fold` pass, and so the standalone caller leaves something behind for a later `/context-fold` to consume.
|
|
68
68
|
|
|
69
69
|
## Step 4: dedupe
|
|
70
70
|
|
|
71
|
-
For each remaining candidate, grep the top-level `*.md` files in `.canon/memory/` for an existing file on the same topic, never its `review/` or `archive/` subfolders, since a hit there is a receipt or a retired entry rather than a live one to update. If one exists, update it in place rather than create a new file. Read it first and write the whole file back, since the guard above rules out editing a line inside it.
|
|
71
|
+
For each remaining candidate, grep the top-level `*.md` files in `.canon/memory/` for an existing file on the same topic, never its `review/` or `archive/` subfolders, since a hit there is a receipt or a retired entry rather than a live one to update. If one exists, update it in place rather than create a new file. Read it first and write the whole file back, since the guard above rules out editing a line inside it. Carry a `reviewed` field through the rewrite untouched, since dropping it sends a kept entry back to the front of the review queue.
|
|
72
72
|
|
|
73
73
|
## Step 5: write the residue
|
|
74
74
|
|
|
@@ -86,6 +86,14 @@ canon indexes regen --no-stage --root <main-root> <main-root>/.canon/memory/inde
|
|
|
86
86
|
|
|
87
87
|
Run `canon records validate memory` when the writes are done and fix what it names. It reads the whole pen rather than this session's writes, so treat a finding on a carried entry as one to fix in place rather than as a reason to stop.
|
|
88
88
|
|
|
89
|
+
## Step 6: count what review owes
|
|
90
|
+
|
|
91
|
+
Run `canon records stale memory --json` and count the entries whose `due` is true, and among them the ones carrying a non-empty `unresolved` list. Branch on the record rather than on the exit, which a shell function wrapping `canon` can flatten to zero. When the count reaches 25, one `memory-review` batch, the Output below adds the review line. Below that, add nothing.
|
|
92
|
+
|
|
93
|
+
Stay silent when the record refuses or the installed binary carries no `records stale` subcommand. This step reports and never stops the capture.
|
|
94
|
+
|
|
95
|
+
The line fires on a count rather than on every run. An unconditional prompt after each ship was tried and removed, since a review per ship costs more than it drains, and a hook would reach every session including workers that cannot act on it.
|
|
96
|
+
|
|
89
97
|
## Output
|
|
90
98
|
|
|
91
99
|
Respond with one line per fact routed, written, or updated:
|
|
@@ -94,12 +102,14 @@ Respond with one line per fact routed, written, or updated:
|
|
|
94
102
|
- `✅ Wrote: .canon/memory/<file> (<type>)`
|
|
95
103
|
- `✏️ Updated: .canon/memory/<file> (<type>)`
|
|
96
104
|
|
|
97
|
-
When anything routed, add a line naming the handoff so the caller knows a `
|
|
105
|
+
When anything routed, add a line naming the handoff so the caller knows a `context-fold` pass is owed:
|
|
106
|
+
|
|
107
|
+
`→ Routed facts wait at .canon/tmp/handoff/memory-routing/<slug>.md. Run /context-fold to fold them in.`
|
|
98
108
|
|
|
99
|
-
|
|
109
|
+
Omit that line when the caller runs `context-fold` itself later in its own chain.
|
|
100
110
|
|
|
101
|
-
|
|
111
|
+
When Step 6 counted at least one batch due, add the review line last:
|
|
102
112
|
|
|
103
|
-
|
|
113
|
+
`→ <n> memory entries due for review, <m> citing a moved path. Run /canon:memory-review.`
|
|
104
114
|
|
|
105
|
-
`✅ Nothing worth capturing
|
|
115
|
+
If nothing was captured, output `✅ Nothing worth capturing.`, followed by the review line when Step 6 counted one batch due.
|