@erclx/canon 5.74.0 → 5.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/create-rule/SKILL.md +5 -1
  4. package/claude/skills/create-rule/references/ledger.md +73 -0
  5. package/claude/skills/create-rule/references/rule.md +1 -1
  6. package/claude/skills/create-skill/references/skill-practice.md +1 -1
  7. package/claude/skills/design-identity/REQUIREMENT.md +1 -1
  8. package/claude/skills/design-identity/SKILL.md +13 -4
  9. package/claude/skills/design-identity/references/card-route.md +11 -4
  10. package/claude/skills/git-issue/REQUIREMENT.md +4 -2
  11. package/claude/skills/git-issue/SKILL.md +32 -3
  12. package/claude/skills/git-issue/references/issue.md +7 -0
  13. package/claude/skills/issue-triage/REQUIREMENT.md +35 -0
  14. package/claude/skills/issue-triage/SKILL.md +130 -0
  15. package/claude/skills/pr-evidence/references/reel.md +9 -9
  16. package/claude/skills/proposal-critique/references/adopted.md +44 -0
  17. package/claude/skills/role-orchestrator/SKILL.md +1 -1
  18. package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +1 -1
  19. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +15 -15
  20. package/claude/skills/role-orchestrator/scripts/poll.ts +211 -96
  21. package/claude/skills/role-orchestrator/scripts/repo.ts +53 -1
  22. package/claude/skills/ui-film/SKILL.md +8 -7
  23. package/docs/agents/audits.md +24 -24
  24. package/docs/agents/migrate.md +10 -10
  25. package/docs/migrations/by-verb.md +10 -4
  26. package/docs/workflow/ai-workflow.md +1 -1
  27. package/docs/workflow/skill-map.md +1 -0
  28. package/governance/detect.toml +4 -0
  29. package/governance/ledger/claude/rule.md +16 -0
  30. package/governance/rules/claude/rule.md +13 -0
  31. package/package.json +1 -1
  32. package/scripts/ci/refresh-frames-pr.sh +11 -12
  33. package/src/audits/catalog.ts +7 -2
  34. package/src/capture/render.ts +30 -5
  35. package/src/capture/stamp.ts +3 -5
  36. package/src/claude/cases/git.ts +5 -0
  37. package/src/claude/cases/workflow.ts +4 -0
  38. package/src/claude/skills-audit.ts +65 -0
  39. package/src/cli/help.ts +1 -1
  40. package/src/commands/capture.ts +38 -1
  41. package/src/commands/claude.ts +10 -2
  42. package/src/commands/gov.ts +12 -7
  43. package/src/commands/migrate.ts +6 -2
  44. package/src/commands/take.ts +36 -7
  45. package/src/counts/catalogs.ts +3 -5
  46. package/src/gate/measures.ts +1 -144
  47. package/src/gate/stages.ts +0 -23
  48. package/src/gov/audit.ts +59 -13
  49. package/src/markdown/scope.ts +4 -0
  50. package/src/migrate/session-defaults.ts +146 -8
  51. package/src/take/encode.ts +9 -1
  52. package/src/take/film.ts +17 -2
  53. package/src/take/fixtures/busy.html +107 -0
  54. package/src/take/perform.ts +99 -25
  55. package/src/take/screencast.ts +4 -3
  56. package/src/take/stepper.ts +33 -20
  57. package/tooling/claude/reference.md +4 -2
  58. package/tooling/claude/seeds/.claude/hooks/plan-guard.sh +239 -0
  59. package/tooling/claude/seeds/.claude/settings.json +12 -0
  60. package/scripts/core/regen-hero.ts +0 -113
  61. package/src/capture/frame-values.ts +0 -326
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.76.0](https://github.com/erclx/canon/compare/v5.75.0...v5.76.0) (2026-10-31)
4
+
5
+
6
+ ### Features
7
+
8
+ * **capture:** render light and dark social cards in design-identity ([#2861](https://github.com/erclx/canon/issues/2861)) ([de990f5](https://github.com/erclx/canon/commit/de990f543f7970c04548aa4f793766db5d87b68a))
9
+ * **claude:** allow the exact lease push forms in seeded settings ([#2859](https://github.com/erclx/canon/issues/2859)) ([2f5a6e8](https://github.com/erclx/canon/commit/2f5a6e801e51cefef98f46a7b33b1cd6df7717a3))
10
+ * **orchestrate:** run the review poll in the background until movement ([#2860](https://github.com/erclx/canon/issues/2860)) ([28101da](https://github.com/erclx/canon/commit/28101da96a8ad19e8ded474fad0de15344f4ac41))
11
+ * **skills:** add issue-triage for a repository's open GitHub issues ([#2856](https://github.com/erclx/canon/issues/2856)) ([910372f](https://github.com/erclx/canon/commit/910372fe7026b9e3f5e2b17648a86dd685081acf))
12
+ * **skills:** offer a follow-up on a duplicate issue before filing ([#2857](https://github.com/erclx/canon/issues/2857)) ([ddcbf69](https://github.com/erclx/canon/commit/ddcbf69d5c7cb60242b3d7b0bfb23ffbd4fd858e))
13
+
14
+ ## [5.75.0](https://github.com/erclx/canon/compare/v5.74.0...v5.75.0) (2026-10-29)
15
+
16
+
17
+ ### Features
18
+
19
+ * **claude:** audit proposal-critique against its ledger ([#2845](https://github.com/erclx/canon/issues/2845)) ([397699b](https://github.com/erclx/canon/commit/397699bdeb7da47a39fb210d2850e8502d503a1f))
20
+ * **claude:** seed the plan guard and wire installed projects ([#2851](https://github.com/erclx/canon/issues/2851)) ([e312aab](https://github.com/erclx/canon/commit/e312aabaa69941ce01f656a635b98ef9e01680f1))
21
+ * **gov:** check a project's ledgers under canon/ledger/ ([#2848](https://github.com/erclx/canon/issues/2848)) ([65fb0e6](https://github.com/erclx/canon/commit/65fb0e6e60ef22c16a7743964865fc62d6b55ed8))
22
+ * **governance:** route a rule edit to create-rule ([#2847](https://github.com/erclx/canon/issues/2847)) ([010b639](https://github.com/erclx/canon/commit/010b63970a874905ec9ee3db520b7cb5f96db778))
23
+ * **take:** refuse a take past --max-seconds as over-cap ([#2855](https://github.com/erclx/canon/issues/2855)) ([d9d6424](https://github.com/erclx/canon/commit/d9d642434d082f9f7d4b1508723576044e55f6a4))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * **take:** cut a screencast clip at the stage clock ([#2853](https://github.com/erclx/canon/issues/2853)) ([3588832](https://github.com/erclx/canon/commit/3588832a8263079ff8340b2cf7af3c492bf4cc66))
29
+ * **take:** hold canon take steady on a page whose frames run slow ([#2852](https://github.com/erclx/canon/issues/2852)) ([f2b55f9](https://github.com/erclx/canon/commit/f2b55f9a2f948c46fb4c84720da7596df31107a5))
30
+ * **take:** scroll a target taller than the window ([#2854](https://github.com/erclx/canon/issues/2854)) ([976b85d](https://github.com/erclx/canon/commit/976b85d27391106d2cb7e03072451de5a128d2fd))
31
+
3
32
  ## [5.74.0](https://github.com/erclx/canon/compare/v5.73.0...v5.74.0) (2026-10-28)
4
33
 
5
34
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "canon",
3
3
  "description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
4
- "version": "5.74.0",
4
+ "version": "5.76.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -86,6 +86,10 @@ Read `${CLAUDE_SKILL_DIR}/references/checklist.md` and run every bullet of the w
86
86
 
87
87
  ## After writing
88
88
 
89
- Emit the full path on its own line: `.claude/rules/project/<subdir>/<slug>.md`. Remind the user that Claude Code loads path-scoped rules when it reads or writes a matching file, and always-on rules every session. When a bullet claims outside practice, tell them to cite its source in the commit that adds the rule.
89
+ Emit the full path on its own line: `.claude/rules/project/<subdir>/<slug>.md`. Remind the user that Claude Code loads path-scoped rules when it reads or writes a matching file, and always-on rules every session. When a bullet claims outside practice, tell them to cite its source, in a ledger or in the commit that adds the rule.
90
90
 
91
91
  Show the new rule as a diff in chat and commit nothing until the operator says OK.
92
+
93
+ ### When a bullet cites outside practice
94
+
95
+ Offer to record each source in a ledger at `canon/ledger/rules/<subdir>/<slug>.md`, which `canon gov audit` checks row for bullet. Read `${CLAUDE_SKILL_DIR}/references/ledger.md` for the layout and the row shape before writing one.
@@ -0,0 +1,73 @@
1
+ ---
2
+ title: Project ledger reference
3
+ description: Where a project's rule and skill ledgers live under canon/ledger/, and the row shape both audits check, one table per H2 and one row per bullet
4
+ ---
5
+
6
+ # Project ledger reference
7
+
8
+ ## Contents
9
+
10
+ - [Overview](#overview)
11
+ - [Where it lives](#where-it-lives)
12
+ - [Shape](#shape)
13
+ - [Rows](#rows)
14
+ - [Example](#example)
15
+
16
+ ## Overview
17
+
18
+ A ledger names what stands behind each bullet of a rule or a skill file, kept beside the file rather than inside it, so the reasoning costs no load. A bullet claiming outside practice names its source, and a bullet recording a project decision names what it prevents.
19
+
20
+ A project writes a ledger where it wants one and owes none. `canon gov audit` and `canon claude skills audit` check each ledger that exists against its file and report a ledger whose file is gone.
21
+
22
+ ## Where it lives
23
+
24
+ Every ledger sits under `canon/ledger/`, which Claude Code never loads. A ledger under `.claude/rules/` would load as a rule on every matching read.
25
+
26
+ - A rule at `.claude/rules/project/<subdir>/<slug>.md` takes `canon/ledger/rules/<subdir>/<slug>.md`
27
+ - A skill body at `.claude/skills/<skill>/SKILL.md` takes `canon/ledger/skills/<skill>/SKILL.md`
28
+ - A skill reference at `.claude/skills/<skill>/references/<file>.md` takes `canon/ledger/skills/<skill>/<file>.md`, dropping the `references/` segment
29
+ - Move or delete a ledger in the change that moves or deletes its file, since the audits report one left behind
30
+
31
+ ## Shape
32
+
33
+ The audits match rows to bullets by count under a heading of the same name, so the shape is fixed and whether a row supports its bullet is a review judgment.
34
+
35
+ - Write one `## <heading>` per H2 in the file, spelled as the file spells it
36
+ - Put one table under each heading, with one row per bullet in the file's order
37
+ - Give a file with no H2 one table under the title
38
+ - Write no other table in the ledger, since every table row under a heading counts. A sample table inside a code fence is not counted.
39
+
40
+ A skill file counts a few lines differently from a rule.
41
+
42
+ - Give each top-level ordered step a row, the way a bullet takes one
43
+ - Give `## Contents` no table, since it repeats the headings
44
+ - Stop a `SKILL.md` ledger's tables at the body's first closing section, such as `## Excuses and rebuttals` or `## What this delegates`, since a table under one reads as rows with no bullets
45
+
46
+ ## Rows
47
+
48
+ Each row names the bullet and the one thing behind it.
49
+
50
+ - **Sourced:** a link to the primary source, with the line it says, such as the framework's own docs or the language spec
51
+ - **House choice:** a cell opening `House choice:` and naming the bug class the bullet prevents, when no outside source takes a side
52
+ - **Decision:** a house choice naming what the bullet prevents and the option it beat, with the date it was decided
53
+ - Cut a bullet that has neither a source nor a bug class behind it, rather than writing a row that says so
54
+
55
+ ## Example
56
+
57
+ A project React rule in the `framework/` subdir, with two bullets under `## Component patterns`, takes this ledger at `canon/ledger/rules/framework/react.md`:
58
+
59
+ ```markdown
60
+ ---
61
+ title: React rule ledger
62
+ description: Sources for the project React rule, one row per bullet
63
+ ---
64
+
65
+ # React rule ledger
66
+
67
+ ## Component patterns
68
+
69
+ | Bullet | Source |
70
+ | ---------------------------- | --------------------------------------------------------------------------------------------- |
71
+ | Stable keys over array index | [Rendering lists](https://react.dev/learn/rendering-lists): index keys cause subtle bugs |
72
+ | Named exports | House choice: a default export renamed at import drifts from the component's name in a search |
73
+ ```
@@ -101,7 +101,7 @@ A bullet claiming outside practice, such as a language idiom or a library's corr
101
101
 
102
102
  - Cite the source in the commit that adds the rule, naming a primary page or the bug class the bullet prevents
103
103
  - Cut a bullet that has neither a source nor a bug class behind it
104
- - The toolkit holds its outside-practice rules to a source ledger kept outside the installed tree, so the citation costs no load
104
+ - A project can hold its rules to a source ledger kept outside the installed tree, so the citation costs no load, laid out in `${CLAUDE_SKILL_DIR}/references/ledger.md`
105
105
  - A bullet resting on how Claude Code behaves ends with a `Revisit when` sentence, per `### Phrasing`
106
106
 
107
107
  ## Location
@@ -73,4 +73,4 @@ The audit counts a few lines in a ledgered file apart from a rule's.
73
73
  - It keeps a wider ledger list beside it, adding the skills that teach a craft drawn from outside sources without a practice's closing shape
74
74
  - A skill on the wider list alone owes the ledger and none of the three sections
75
75
  - Never mark a skill as a practice skill in its own frontmatter, since a self-declared kind lets a skill exempt itself.
76
- - Both lists name this toolkit's shipped skills alone, so a project's own practice skill takes this shape with no check behind it.
76
+ - Both lists name this toolkit's shipped skills alone. A project's own skill owes no ledger, and one it writes is checked per `${CLAUDE_SKILL_DIR}/../create-rule/references/ledger.md`.
@@ -17,7 +17,7 @@ A session building this alone restates the render-and-pick loop from scratch, wh
17
17
  - Every arm is drafted inside the card frame, so one pick settles the mark's shape and its composition together
18
18
  - The render, hand-off, pick, and loop follow `design-pick` rather than a copy of it
19
19
  - The final mark lands as its own SVG source, not only baked into a raster
20
- - Every final size and the card render in one capture call, each landing at its literal target size
20
+ - Every final icon size renders in one capture call and the card renders once per theme, each landing at its literal target size
21
21
 
22
22
  ## Must not
23
23
 
@@ -65,6 +65,7 @@ Follow `${CLAUDE_SKILL_DIR}/../design-pick/SKILL.md` Step 1, with the decision f
65
65
  - Draft each arm inside the 1200x630 card frame, mark and type together, and add it with `frameboard frame add <page>/arm-<id> --width 1200 --height 630`, so one pick settles shape and composition
66
66
  - Arm 0 is the current mark when the Step 3 folder already holds a logo file (`favicon.svg`, `favicon.ico`, `logo.svg`, or similar). Arms start at 1 otherwise.
67
67
  - Structure every arm as a `.mark` element, the inline SVG alone, nested inside a `.card` element, the full composition, so Step 6 can address either
68
+ - Give every arm a light and a dark set keyed on the `data-theme` attribute, as `design-pick` themes any arm. Step 7 captures the picked card once per theme.
68
69
 
69
70
  ## Step 5: draft, render, pick, and loop
70
71
 
@@ -94,6 +95,11 @@ The card leaves this batch and takes Step 7 instead. An icon is the mark alone a
94
95
 
95
96
  A card captured from a page this run deletes leaves a PNG and nothing that produced it, so renaming the project means another pick loop to move one string. Write the composition into a route the project keeps, and capture it through a running server so it reads the real stylesheet, tokens, and fonts.
96
97
 
98
+ Both paths write two cards, each captured with `--color-scheme` set to its theme. An `og:image` tag names one URL that every viewer sees whatever their own theme, so only one is wired:
99
+
100
+ - `og-image.png`, the light card, goes into `og:image`, keeping the name an existing tag already points at
101
+ - `og-image-dark.png` stays beside it, so a later switch is a meta tag edit rather than another pick loop
102
+
97
103
  This is Astro-shaped for now, because Astro is the one web stack whose config gives a route a structural build exclusion. A project already holding the card route scaffold takes the route path, and everything else takes the page path. Say which one decided, since the two leave the project different things.
98
104
 
99
105
  ### The route path
@@ -102,17 +108,18 @@ Read `${CLAUDE_SKILL_DIR}/references/card-route.md` on reaching this path, for t
102
108
 
103
109
  ### The page path
104
110
 
105
- Where the project holds no route to write into, write `og-image.html` into `<dest>/render/` with its `.render` wrapper sized 600x315, then capture that one page:
111
+ Where the project holds no route to write into, write the picked composition into `<dest>/card/` twice, as `og-image.html` and `og-image-dark.html`, each with its `.render` wrapper sized 600x315. A file source names its PNG after the page, so the two names are what land the two cards. Capture each page in its own theme:
106
112
 
107
113
  ```bash
108
- canon capture <dest>/render/og-image.html --selector .render --out <write-folder>
114
+ canon capture <dest>/card/og-image.html --selector .render --color-scheme light --out <write-folder>
115
+ canon capture <dest>/card/og-image-dark.html --selector .render --color-scheme dark --out <write-folder>
109
116
  ```
110
117
 
111
- It takes its own call because Step 6's batch is already captured by the time this step picks a path. Say that the card shipped as a PNG with no source, and that the project gains one when it grows a route to hold the composition.
118
+ These take their own calls because Step 6's batch is already captured by the time this step picks a path, and one call renders one scheme. Say that the card shipped as a PNG with no source, and that the project gains one when it grows a route to hold the composition.
112
119
 
113
120
  ### Closing the scratch folder and the board
114
121
 
115
- Delete `<dest>` once both captures land, then close the board through `design-pick` Step 6 items 3 to 8, which archive every arm and delete the pages the run added. The route is not a leftover variant, since it holds the arm the operator picked.
122
+ Delete `<dest>` once the icon and card captures land, then close the board through `design-pick` Step 6 items 3 to 8, which archive every arm and delete the pages the run added. The route is not a leftover variant, since it holds the arm the operator picked.
116
123
 
117
124
  A project whose card was generated before this route existed keeps its PNG. Say so rather than regenerating it, since the pick loop spends attention on a file nobody complained about, and the next real edit picks up the route.
118
125
 
@@ -123,9 +130,11 @@ A project whose card was generated before this route existed keeps its PNG. Say
123
130
  📝 Wrote <write-folder>/icon-16x16.png (16x16)
124
131
  📝 Wrote card-src/pages/og-card.astro (route path only)
125
132
  📝 Wrote <write-folder>/og-image.png (1200x630)
133
+ 📝 Wrote <write-folder>/og-image-dark.png (1200x630)
126
134
 
127
135
  Write folder: <tier that decided>. Move the files if this project's own convention differs.
128
136
  Card source: <the route, captured through the card server|a scratch page, since this project holds no card route>.
137
+ Wire og-image.png into og:image. The dark card stays beside it for a later switch.
129
138
  ```
130
139
 
131
140
  ## What this delegates
@@ -29,16 +29,23 @@ Never run that sync to obtain the route, since it writes the stack's configs bes
29
29
 
30
30
  ## Writing and capturing the route
31
31
 
32
- 1. Write the picked arm's `.card` markup into the route's `.card` element, leaving the marker, the stylesheet import, and the surrounding document alone.
32
+ 1. Write the arm's `.card` markup into the route's `.card` and its `[data-theme]` rules into the route's `<style>`, leaving the rest of the route alone.
33
33
  2. Keep the element at 600x315, half the 1200x630 target, since the capture's 2x scale factor is fixed and a 1200x630 element captures at 2400x1260.
34
34
  3. Start the card server in the background with `bun run card:dev` and read the port off its own output.
35
- 4. Capture the route, naming the destination PNG rather than a directory, which is what a URL source takes for `--out`.
36
- 5. Stop the server. Report the dimensions `canon capture` printed, confirming 1200x630.
35
+ 4. Capture the route twice while the server is up, once per theme, naming each destination PNG rather than a directory, which is what a URL source takes for `--out`.
36
+ 5. Stop the server. Report the dimensions `canon capture` printed for both cards, confirming 1200x630.
37
37
 
38
38
  ```bash
39
- canon capture http://localhost:<port>/og-card --selector .card --out <write-folder>/og-image.png
39
+ canon capture http://localhost:<port>/og-card --selector .card --color-scheme light --out <write-folder>/og-image.png
40
+ canon capture http://localhost:<port>/og-card --selector .card --color-scheme dark --out <write-folder>/og-image-dark.png
40
41
  ```
41
42
 
43
+ The two captures differ only where something keys a second theme:
44
+
45
+ - The scaffold's `.card` reads two color properties a stylesheet may set for one theme only, so the arm's rules from step 1 give the dark capture its dark set
46
+ - `--color-scheme` writes the theme onto the route's root after it loads, so rules keyed on `data-theme`, a class, or the media query all answer to it
47
+ - With no second set in the arm or the stylesheet, the PNGs match. Compare the image digests in their `.stamp` files, and on a match say the project got one card twice.
48
+
42
49
  ### Port and font refusals
43
50
 
44
51
  The card server runs under a strict port, so it refuses rather than walking to the next free one. Where something already holds that port, set `WORKTREE_PORT_OFFSET` by hand and start it again.
@@ -17,16 +17,18 @@ Prose bans hold on files the hook watches and not on text leaving through `gh`,
17
17
  - Each issue carries exactly one label mapped from its type, asked once when the type is ambiguous between a bug and a task
18
18
  - A remote and an authenticated `gh` are confirmed before any body is composed
19
19
  - The body passes through a file that is removed afterward
20
+ - Open issues are searched for the same defect before a body is composed, and a match is offered as a comment in place of a new issue
20
21
 
21
22
  ## Must not
22
23
 
23
24
  - Invent an issue from a thin session
24
25
  - File anywhere other than the current origin
25
26
  - Open a pull request
26
- - Emit anything after the result line
27
+ - Emit anything after the result lines
28
+ - Comment on an existing issue without a yes given for that issue
27
29
 
28
30
  ## Out of scope
29
31
 
30
32
  - Reporting a toolkit defect from a target project, which `canon-feedback` owns
31
- - Triaging the toolkit's feedback issues, which its maintainers do in its own repository
33
+ - Triaging a repository's open issues, which `issue-triage` owns
32
34
  - Opening a pull request, which `git-pr` owns
@@ -27,7 +27,34 @@ Then gather repo context in parallel:
27
27
  - If nothing in session context describes a concrete bug or task, stop: `❌ No issue in session context. Describe the bug or task, then re-invoke.`
28
28
  - If `git remote get-url origin` returns `NO_REMOTE`, stop: `❌ No GitHub remote. gh issue create needs an origin.`
29
29
  - If `gh auth status` returns `NO_AUTH`, stop: `❌ gh is not authenticated. Run gh auth login.`
30
- - If the type is ambiguous between a bug and a task, ask one line before formatting.
30
+ - If the type is ambiguous between a bug and a task, ask one line before formatting, after the duplicate question below.
31
+
32
+ ## Duplicate check
33
+
34
+ Search the open issues before composing anything, since a duplicate costs the repository's triage a second item and a second answer.
35
+
36
+ ```bash
37
+ gh issue list --state open --search "<keywords> in:title,body" --limit 5 --json number,title,url
38
+ ```
39
+
40
+ - Take keywords from the defect's distinctive terms, such as a command or a path, and quote one holding a colon or a space
41
+ - Keep a candidate only when it describes the same defect, reading its body with `gh issue view <NNN> --json body` when the title leaves it open
42
+ - With none kept, go to the preview and file a new issue
43
+ - With one or more kept, ask once whether to add the findings to one of them as a comment or file a new issue, naming each by number and title
44
+ - With no one to answer, as in a headless run, file nothing and name each kept candidate, since a new issue there is the duplicate this check exists to stop
45
+
46
+ ### Comment on the match
47
+
48
+ On a yes for an issue, write the comment in the `### Follow-up` shape `${CLAUDE_SKILL_DIR}/references/issue.md` gives, run the pre-publish scan below over it, then post it in place of the final command. Post nothing on any issue the answer did not name.
49
+
50
+ ```bash
51
+ mkdir -p .canon/tmp/issue
52
+ cat <<'BODY' > .canon/tmp/issue/comment.md
53
+ <comment following the Follow-up section>
54
+ BODY
55
+ gh api -X POST 'repos/{owner}/{repo}/issues/<NNN>/comments' -F body=@.canon/tmp/issue/comment.md --jq .html_url
56
+ rm -rf .canon/tmp/issue
57
+ ```
31
58
 
32
59
  ## Response format
33
60
 
@@ -63,6 +90,8 @@ rm -rf .canon/tmp/issue
63
90
 
64
91
  ## After execution
65
92
 
66
- Respond with exactly one line and add no other text:
93
+ Respond with the one line for what was posted:
67
94
 
68
- `✅ Issue: <url>`
95
+ - A new issue: `✅ Issue: <url>`
96
+ - A comment on a match: `✅ Commented: <url>`
97
+ - Nothing, with candidates kept and nobody to ask: `⏸️ Not filed. Possible duplicate:`, then one line per candidate as `#<NNN> <title>`
@@ -55,6 +55,13 @@ A bug or task body carries up to four `##` sections, in this order.
55
55
  - `## Context`: for a bug, repro steps or commands. For a task, the driving reason, or `none`.
56
56
  - `## Proposed` (optional): one line naming a fix or approach. Omit when open.
57
57
 
58
+ ### Follow-up
59
+
60
+ A comment on an open issue carries what the session adds and nothing the issue already says.
61
+
62
+ - `## Follow-up`: one line naming what is new, such as a second instance, a repro, or a narrowed cause.
63
+ - `## Details`: the observed behavior and the command or path that shows it.
64
+
58
65
  ### Feedback
59
66
 
60
67
  One `## Toolkit feedback` wrapper holds every field as a nested `###` heading. A flat `##` field is not recognized.
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: issue-triage
3
+ description: Why a repository's open issues are filed as measured intake items rather than routed or fixed from the issue text, and why one pass is capped
4
+ ---
5
+
6
+ # Issue triage requirement
7
+
8
+ ## Gap
9
+
10
+ Without this skill, a repository's issue queue fills and nothing drains it into decisions. An issue reads as a request, so a session routes it straight to a fix from a paragraph and skips the scope argument.
11
+
12
+ Triage done by hand fails three ways. A self-contradicting report gets a guess instead of a question. The same issue gets filed twice because two passes cannot see the board carries it. A queue of hundreds files in one pass that no answering session can get through.
13
+
14
+ ## Must
15
+
16
+ - Read the queue through the `gh` CLI, and stop when it is absent, unauthenticated, or the checkout has no GitHub repository
17
+ - File one measured item per kept issue into a folder the CLI claimed, headed so an answer finds its issue, and name each skipped issue with its carrier
18
+ - Measure each claim in an issue's body and comments against the tree rather than carrying the issue's figures forward
19
+ - Cap one pass, oldest first, and name what was left over rather than reading a capped queue as drained
20
+ - Report an empty queue as nothing open rather than widening the label to find work
21
+
22
+ ## Must not
23
+
24
+ - Post to the remote, whether a comment, a close, or a label
25
+ - Implement a fix or write a plan
26
+ - Fill an answer slot, or read an empty one as agreement
27
+ - Write outside the folder it claimed
28
+ - Carry a step only one repository's queue needs, such as stripping a reporting project's specifics, since a caller wrapping this skill adds those around it
29
+
30
+ ## Out of scope
31
+
32
+ - Opening a new issue, which `git-issue` owns
33
+ - Answering the filed items, which `plan-intake-answer` owns
34
+ - Writing the plan a plan-worthy item needs, which `plan-feature` owns
35
+ - Pruning the task backlog, which `backlog-triage` owns
@@ -0,0 +1,130 @@
1
+ ---
2
+ name: issue-triage
3
+ description: Files the open GitHub issues on the current repository into an intake folder as one measured item each, narrowed by an optional label, capped per pass, and skipping any issue the board already carries. Use when asked to "triage the issues", "work through the open issues", "file the GitHub issues", "triage the bug label", or "what issues are open". Do NOT use to open a new issue, which is `git-issue`, to answer the filed items, which is `plan-intake-answer`, or to triage the task backlog, which is `backlog-triage`.
4
+ metadata:
5
+ family: decide
6
+ ---
7
+
8
+ # Issue triage
9
+
10
+ An issue queue only grows while nothing reads it against the tree. This pass measures each open issue and files it as one item for the operator, who answers it before anything is built.
11
+
12
+ The record is an ordinary intake folder at `.canon/intake/<nn>-<slug>-triage/`. Read `${CLAUDE_SKILL_DIR}/../plan-intake/references/intake.md` before writing any file in it, since it holds the item format, the frontmatter, the index shape, and the answer contract this skill is bound by.
13
+
14
+ - Read GitHub issues and write only inside the claimed folder, posting no comment, close, or label to GitHub
15
+ - Measure each issue against this repository, since the repository it was filed on is the one its fix lands in
16
+
17
+ ## Arguments
18
+
19
+ Both are optional and come in either order.
20
+
21
+ - A label narrows the queue to issues carrying it. With none, every open issue is in the queue and the slug is `issue`.
22
+ - A label sets the slug too, lowercased with each run of other characters made one hyphen, so `good first issue` gives `good-first-issue`. Pass the raw label, quoted, to `--label`.
23
+ - A number sets the cap, the most issues one pass files. With none, the cap is 25, about what one answering session gets through.
24
+
25
+ ## Guards
26
+
27
+ - Resolve `.canon/tasks/` and `.canon/intake/` at the main worktree root, from `git worktree list --porcelain | grep -m 1 '^worktree ' | cut -d' ' -f2-`, falling back to `pwd` outside git
28
+ - If `gh` is not on PATH, stop: `❌ gh CLI not found. Install it to read the issue queue.`
29
+ - If `gh auth status` fails, stop: `❌ gh is not authenticated. Run gh auth login.`
30
+ - If `gh repo view --json nameWithOwner` fails, stop: `❌ No GitHub repository here. Run from a checkout with a GitHub remote.`
31
+ - If no open issue matches, stop: `✅ No open issues.`
32
+
33
+ ### Slots and records
34
+
35
+ Two more guards hold through every step.
36
+
37
+ - Never fill a `You:` slot, and never read an empty one as agreement. Answering runs through `plan-intake-answer` or the operator's own edit.
38
+ - Branch on each `canon` record's `ok` and `reason` rather than on the exit code, which a shell function wrapping `canon` can flatten to zero.
39
+
40
+ ## Step 1: list the queue
41
+
42
+ ```bash
43
+ gh issue list --state open --limit 1000 --search "sort:created-asc" --json number,title
44
+ ```
45
+
46
+ Add `--label "<label>"` when one was given, and name that label in the empty-queue stop. The list carries no bodies, so a large queue costs a line per issue rather than its whole text.
47
+
48
+ Sorting by creation keeps the oldest issues when the queue passes the limit. When the list returns 1000 rows, the queue may hold more, so Step 3 writes its left-over count as a floor.
49
+
50
+ ## Step 2: drop what the board already carries
51
+
52
+ An issue is carried when its number appears in a live or archived task's `Issue:` line under `.canon/tasks/`, or in an item heading of an existing `.canon/intake/` folder, written `(#NNN)`. Search both at the main root.
53
+
54
+ Set each carried issue aside with its carrier, a task path or a folder and item.
55
+
56
+ - Name every one in the overview, so a re-run while an earlier folder sits unanswered files nothing twice
57
+ - Read its comments with `gh issue view <NNN> --json comments`, and name their count and newest date beside its carrier, so a follow-up posted after the carrier was filed is seen
58
+ - File no item for a comment on a carried issue, since an item would file the issue twice against a live task
59
+
60
+ When every issue is carried, stop: `✅ Every open issue is already on the board.`
61
+
62
+ ## Step 3: apply the cap
63
+
64
+ Sort the remaining issues by number, oldest first, and keep the first issues up to the cap, so the reports that waited longest drain first. Count the issues past the cap as left over, and name them in the overview and the output.
65
+
66
+ - Give the count and the lowest number left over, so a drained queue and a capped one never read the same
67
+ - Write the count as `at least <n>` when Step 1 hit its limit
68
+
69
+ Then read each kept issue whole, since a follow-up posted as a comment is part of the report:
70
+
71
+ ```bash
72
+ gh issue view <NNN> --json number,title,body,labels,comments
73
+ ```
74
+
75
+ ## Step 4: claim the folder
76
+
77
+ Run `canon records ordinal intake <slug>-triage --claim --json` and take its `name`. The verb creates the folder in the same act, so two sessions opening at once never share one. Where the installed binary carries no such subcommand, report it and stop rather than picking an ordinal by hand.
78
+
79
+ ## Step 5: measure each issue
80
+
81
+ For each kept issue, measure its claim against the tree during this pass, never carrying a figure from the issue forward, since it states what was true when filed.
82
+
83
+ Measure each comment's claim the same way, whoever posted it, and fold it into the issue's one item rather than filing a second. When a comment contradicts the body, measure the later claim and name the comment as the source on `Problem:`.
84
+
85
+ - Grep for the construct its defect names and count the sites
86
+ - Check whether the behavior it asks for exists
87
+ - Search the log for a commit or pull request that shipped it under another task
88
+ - Run the issue's repro in a form that writes nothing when it is one command that works here, and say on `Problem:` what it showed
89
+ - Mark the item `not reproduced here` when the repro needs more, per `## Repro versus groundwork` in the intake standard
90
+
91
+ Classify each against this repository's own surfaces:
92
+
93
+ - **Direct fix.** A size, not an answer, so it suggests `plan` and `Fix:` says a short plan covers it. One surface, one file, no architectural choice. A typo, a stale reference, a one-line correction.
94
+ - **Plan-worthy.** Multiple files, a new module or command, a behavior change, or a cross-surface move.
95
+ - **Needs clarification.** The observed and expected behavior conflict or the surface is unnamed. The item asks the operator for the missing detail.
96
+
97
+ Name the commit the pass measured against in the overview.
98
+
99
+ ## Step 6: write the items
100
+
101
+ Write one item per issue in the intake item format, one cluster file per domain the fix touches, numbered in read order. Head each item `### N. <defect> (#NNN)` so an answer finds its issue.
102
+
103
+ - `Problem:` states what this pass measured, with the count, path, or commit behind it
104
+ - `Fix:` states the class from Step 5 and the change it names
105
+ - `Open:` reads `plan, ask, or decline?` on every item
106
+ - `Suggested:` opens with exactly one of those tokens, then the reason
107
+ - `You:` ships empty
108
+
109
+ Write `00-overview.md` last, per the intake standard. It carries the counts by token, an open-questions list linking each item, the commit measured against, every issue skipped in Step 2 with its carrier, and the left-over count from Step 3. Where the pass runs out of context before the index, write `99-next-session.md` naming the last issue filed.
110
+
111
+ ## Output
112
+
113
+ ```plaintext
114
+ 📂 Opened .canon/intake/<nn>-<slug>-triage/
115
+
116
+ **Filed:** <N> issues across <N> clusters, measured against <sha>
117
+ **Skipped:** <n> already carried
118
+ **Left over:** <n> past the cap of <cap>, from #<lowest>
119
+ **Suggested:** <n> plan, <n> ask, <n> decline
120
+
121
+ Next: /canon:plan-intake-answer to answer the `You:` slots
122
+ ```
123
+
124
+ Drop the `Left over:` line when nothing was past the cap.
125
+
126
+ An answered plan item promotes through `task-board` with an `Issue: #NNN` line, and the shipping pull request's `Closes #NNN` drains the queue. This skill still closes nothing. `plan-intake-answer` offers the close of a declined issue when the decline is answered, and the operator's yes posts it.
127
+
128
+ ## Notes
129
+
130
+ - This skill files and never reimplements. `plan-intake-answer` owns the answers and `task-board` owns the task a plan item becomes.
@@ -62,23 +62,23 @@ Write one plan entry for each beat chosen, then skip when none was.
62
62
 
63
63
  The negative test is a judgment over the box's text. A misread box costs a weaker reel and never a wrong pass, since the still check below guards the footage and the UI reviewer drives every box regardless.
64
64
 
65
- Keep the whole reel at or under 12 seconds. Each beat opens and closes on a 0.6 s hold, so give each action's `wait` only what its result needs to settle.
65
+ Keep the whole reel at or under 12 seconds, which the take below enforces. Each beat opens and closes on a 0.6 s hold, so give each action's `wait` only what its result needs to settle.
66
66
 
67
67
  ## Film the reel
68
68
 
69
69
  ```bash
70
- canon take <url> .canon/tmp/pr/reel/<number>/plan.json --out .canon/tmp/pr/reel/<number>/takes --reel .canon/tmp/pr/reel/<number>/reel.webp --json
70
+ canon take <url> .canon/tmp/pr/reel/<number>/plan.json --out .canon/tmp/pr/reel/<number>/takes --reel .canon/tmp/pr/reel/<number>/reel.webp --max-seconds 12 --json
71
71
  ```
72
72
 
73
73
  Branch on the record's `reason`, never on the exit code.
74
74
 
75
- | Record | Route |
76
- | --------------- | ------------------------------------------------------------------ |
77
- | No `reason` | Sum the beats' `seconds` and continue to the stills |
78
- | `reel-failed` | Skip as `reel-failed` with the record's `message` |
79
- | Any other value | Skip under that `reason`, such as `target-missing` with its `beat` |
80
-
81
- When the beats sum past 12 seconds, drop the last beat from the plan and film once more. A second run past the cap skips as `over-cap`.
75
+ | Record | Route |
76
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
77
+ | No `reason` | Continue to the stills |
78
+ | `reel-failed` | Skip as `reel-failed` with the record's `message` |
79
+ | `target-offscreen` | Add a setup step that brings the target into the window, being a scroll over an element already inside it, such as a fixed header, with `dy` covering the distance, or a click on a link to it, and film once more. Skip as `target-offscreen` on a second refusal |
80
+ | `over-cap` | Drop `beat` and each beat after it, then film once more. Skip as `over-cap` if `beat` is first or it refuses again |
81
+ | Any other value | Skip under that `reason`, such as `target-missing` with its `beat` |
82
82
 
83
83
  ## Check each still
84
84
 
@@ -0,0 +1,44 @@
1
+ ---
2
+ title: Adopted and declined
3
+ description: Which designs this skill considered and dropped, and what would revive each, so a later session extends the position instead of re-deriving it
4
+ ---
5
+
6
+ # Adopted and declined
7
+
8
+ The skill came out of a groundwork track that measured two shipped fixes against the questions a critique would have asked. This file records the options the track dropped. A later session extending the skill adds to it rather than re-arguing an item settled here.
9
+
10
+ A bullet's source is linked in its row of the toolkit's per-bullet ledger at `governance/ledger/skills/proposal-critique/`, outside the plugin, and this file links no source a row cites.
11
+
12
+ ## Declined
13
+
14
+ Each item names the design that stays out, the reason, and what would bring it back.
15
+
16
+ ### A default call from every planning skill
17
+
18
+ An authoring session questioning its own plan is the weak form Kahneman, Lovallo, and Sibony warn against when they ask for reviewers independent of the team that made the recommendation. The planning skills name the critique in one line for a one-way change or a fix with an unverified cause, and never call it.
19
+
20
+ Revisit when a replay finds a case where the on-demand form would not have been invoked in time.
21
+
22
+ ### Folding it into `decision-escalate`
23
+
24
+ That skill batches preference questions. Most critique questions are answered from evidence, and only the remainder goes to the operator.
25
+
26
+ Revisit when critique passes show every question turning out to be preference.
27
+
28
+ ### Running it as a forked subagent
29
+
30
+ A fork reads cold, which is the independence Kahneman, Lovallo, and Sibony ask for. It also loses the conversation, and a background fork loses the tools the operator questions need.
31
+
32
+ Revisit when a critique keeps agreeing with the session that wrote the proposal.
33
+
34
+ ### A written critique section in the plan
35
+
36
+ A plan already carries `## Risks` and `## Questions`, and its validator owns the sections. The verdict goes to chat, and the operator decides what moves into a record.
37
+
38
+ Revisit when critiques are lost to compaction before anyone acts on them.
39
+
40
+ ## Revisit the whole design when
41
+
42
+ - Replaying the chain that shipped two fixes and two or three past plans finds the on-demand form would not have been invoked in time
43
+ - One body cannot hold questions that differ so much per record, whether plan, fix, or triage
44
+ - A replayed case shows a cheap, reversible change still doing real harm
@@ -48,7 +48,7 @@ The human asks for each side of a compaction, and this skill reads the runbook.
48
48
 
49
49
  ### The poll and the parked rows
50
50
 
51
- The review trigger takes the same shape. `references/orchestrator-poll.md` holds the loop prompt and its condition, and `scripts/poll.ts` is what the prompt invokes. No hook starts or cancels that loop, so the condition holds only while whoever holds it applies it.
51
+ The review trigger takes the same shape. `references/orchestrator-poll.md` holds the background start, the routing, and the condition, and `scripts/poll.ts --wait` is the command it runs. No hook starts or stops that poll, so the condition holds only while whoever holds it applies it.
52
52
 
53
53
  A board that is not moving is a third such moment. On a request to re-test the parked rows, follow `${CLAUDE_SKILL_DIR}/references/orchestrator-parked.md`, which re-tests every blocker and backlog row against the tree and plans what it clears. The refill sweep fires on a merge instead.
54
54
 
@@ -69,7 +69,7 @@ Settle all three steps below before the door writes, so one write carries the co
69
69
 
70
70
  Resume by loading the orchestrator skill and asking it to resume after a compaction. This repository spells that `/canon:role-orchestrator` followed by the request. Following <RESUME_RUNBOOK> reaches the same place with no skill loaded at all.
71
71
 
72
- That resume reads the board and stops. It restarts nothing, so the review poll is a second thing owed here, and <POLL_RUNBOOK> holds the prompt and the condition. Do not reach for `session-resume`, which reads tracked work and reports the newest map without restarting this loop.
72
+ That resume reads the board and stops. It restarts nothing, so the review poll is a second thing owed here, and <POLL_RUNBOOK> holds the background start and the condition. Do not reach for `session-resume`, which reads tracked work and reports the newest map without restarting this poll.
73
73
  ```
74
74
 
75
75
  ### What the decisions section leaves out