@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.
- package/CHANGELOG.md +29 -0
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/create-rule/SKILL.md +5 -1
- package/claude/skills/create-rule/references/ledger.md +73 -0
- package/claude/skills/create-rule/references/rule.md +1 -1
- package/claude/skills/create-skill/references/skill-practice.md +1 -1
- package/claude/skills/design-identity/REQUIREMENT.md +1 -1
- package/claude/skills/design-identity/SKILL.md +13 -4
- package/claude/skills/design-identity/references/card-route.md +11 -4
- package/claude/skills/git-issue/REQUIREMENT.md +4 -2
- package/claude/skills/git-issue/SKILL.md +32 -3
- package/claude/skills/git-issue/references/issue.md +7 -0
- package/claude/skills/issue-triage/REQUIREMENT.md +35 -0
- package/claude/skills/issue-triage/SKILL.md +130 -0
- package/claude/skills/pr-evidence/references/reel.md +9 -9
- package/claude/skills/proposal-critique/references/adopted.md +44 -0
- package/claude/skills/role-orchestrator/SKILL.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +15 -15
- package/claude/skills/role-orchestrator/scripts/poll.ts +211 -96
- package/claude/skills/role-orchestrator/scripts/repo.ts +53 -1
- package/claude/skills/ui-film/SKILL.md +8 -7
- package/docs/agents/audits.md +24 -24
- package/docs/agents/migrate.md +10 -10
- package/docs/migrations/by-verb.md +10 -4
- package/docs/workflow/ai-workflow.md +1 -1
- package/docs/workflow/skill-map.md +1 -0
- package/governance/detect.toml +4 -0
- package/governance/ledger/claude/rule.md +16 -0
- package/governance/rules/claude/rule.md +13 -0
- package/package.json +1 -1
- package/scripts/ci/refresh-frames-pr.sh +11 -12
- package/src/audits/catalog.ts +7 -2
- package/src/capture/render.ts +30 -5
- package/src/capture/stamp.ts +3 -5
- package/src/claude/cases/git.ts +5 -0
- package/src/claude/cases/workflow.ts +4 -0
- package/src/claude/skills-audit.ts +65 -0
- package/src/cli/help.ts +1 -1
- package/src/commands/capture.ts +38 -1
- package/src/commands/claude.ts +10 -2
- package/src/commands/gov.ts +12 -7
- package/src/commands/migrate.ts +6 -2
- package/src/commands/take.ts +36 -7
- package/src/counts/catalogs.ts +3 -5
- package/src/gate/measures.ts +1 -144
- package/src/gate/stages.ts +0 -23
- package/src/gov/audit.ts +59 -13
- package/src/markdown/scope.ts +4 -0
- package/src/migrate/session-defaults.ts +146 -8
- package/src/take/encode.ts +9 -1
- package/src/take/film.ts +17 -2
- package/src/take/fixtures/busy.html +107 -0
- package/src/take/perform.ts +99 -25
- package/src/take/screencast.ts +4 -3
- package/src/take/stepper.ts +33 -20
- package/tooling/claude/reference.md +4 -2
- package/tooling/claude/seeds/.claude/hooks/plan-guard.sh +239 -0
- package/tooling/claude/seeds/.claude/settings.json +12 -0
- package/scripts/core/regen-hero.ts +0 -113
- 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
|
|
|
@@ -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
|
-
-
|
|
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
|
|
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
|
|
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`
|
|
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>/
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
76
|
-
|
|
|
77
|
-
| No `reason`
|
|
78
|
-
| `reel-failed`
|
|
79
|
-
|
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
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
|
|
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
|