@erclx/canon 5.68.0 → 5.69.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 +19 -0
- package/README.md +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/app-drive/REQUIREMENT.md +5 -5
- package/claude/skills/app-drive/SKILL.md +14 -4
- package/claude/skills/app-drive/references/drive-record.md +12 -1
- package/claude/skills/canon-cli/SKILL.md +2 -2
- package/claude/skills/canon-feedback/REQUIREMENT.md +1 -1
- package/claude/skills/canon-feedback/SKILL.md +1 -1
- package/claude/skills/design-identity/REQUIREMENT.md +4 -4
- package/claude/skills/design-identity/SKILL.md +7 -7
- package/claude/skills/{pick-by-eye → design-pick}/REQUIREMENT.md +6 -6
- package/claude/skills/{pick-by-eye → design-pick}/SKILL.md +18 -10
- package/claude/skills/{pick-by-eye → design-pick}/references/board.md +17 -11
- package/claude/skills/design-pick/references/design-config.md +33 -0
- package/claude/skills/design-record/REQUIREMENT.md +2 -2
- package/claude/skills/design-record/SKILL.md +1 -1
- package/claude/skills/design-taste/REQUIREMENT.md +2 -2
- package/claude/skills/design-taste/SKILL.md +3 -3
- package/claude/skills/design-taste/references/preflight.md +1 -1
- package/claude/skills/git-issue/REQUIREMENT.md +1 -1
- package/claude/skills/git-issue/references/issue.md +1 -1
- package/claude/skills/plan-feature/SKILL.md +1 -1
- package/claude/skills/plan-feature/references/plan.md +21 -6
- package/claude/skills/plan-groundwork/SKILL.md +1 -1
- package/claude/skills/pr-evidence/references/evidence.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +3 -3
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-planning-wait.md +24 -0
- package/claude/skills/role-planner/SKILL.md +4 -2
- package/claude/skills/role-worker/SKILL.md +5 -6
- package/claude/skills/task-board/references/tasks.md +1 -1
- package/claude/skills/task-complete/references/receipt-sweep.md +1 -1
- package/claude/skills/{ux-audit → ui-audit}/REQUIREMENT.md +3 -3
- package/claude/skills/{ux-audit → ui-audit}/SKILL.md +7 -7
- package/claude/skills/ui-checklist/REQUIREMENT.md +1 -1
- package/claude/skills/ui-craft/SKILL.md +1 -1
- package/claude/skills/{ui-record → ui-film}/REQUIREMENT.md +2 -2
- package/claude/skills/{ui-record → ui-film}/SKILL.md +2 -2
- package/claude/skills/{ui-record → ui-film}/references/config.md +3 -3
- package/claude/skills/{ui-record → ui-film}/references/handoff.md +4 -4
- package/claude/skills/{ui-record → ui-film}/references/pr-clip.md +5 -5
- package/claude/skills/{ui-record → ui-film}/references/take-craft.md +1 -1
- package/claude/skills/{ux-measure → ui-measure}/REQUIREMENT.md +3 -3
- package/claude/skills/{ux-measure → ui-measure}/SKILL.md +6 -6
- package/docs/agents/audits.md +1 -1
- package/docs/agents/migrate.md +13 -13
- package/docs/target/migrations.md +12 -10
- package/docs/target/projects.md +1 -1
- package/docs/target/sync.md +1 -1
- package/docs/workflow/skill-map-on-request.md +1 -2
- package/docs/workflow/skill-map.md +9 -10
- package/docs/workflow/visual-design-workflow.md +6 -1
- package/governance/ledger/claude/background.md +26 -0
- package/governance/rules/claude/background.md +19 -0
- package/governance/stacks/base.toml +1 -0
- package/package.json +4 -4
- package/scripts/ci/install-playwright-deps.sh +52 -0
- package/src/claude/cases/authoring.ts +2 -2
- package/src/claude/cases/misc.ts +1 -1
- package/src/claude/cases/setup.ts +0 -5
- package/src/claude/cases/workflow.ts +6 -6
- package/src/commands/fixtures/gh.sh +64 -0
- package/src/commands/gov.ts +33 -3
- package/src/commands/migrate.ts +12 -2
- package/src/commands/tasks.ts +183 -0
- package/src/gate/stages.ts +2 -2
- package/src/gov/audit.ts +48 -11
- package/src/gov/detect.ts +1 -1
- package/src/markdown/scope.ts +9 -14
- package/src/migrate/record-layout.ts +5 -3
- package/src/migrate/skill-names.ts +21 -4
- package/src/records/validate.ts +1 -0
- package/src/sandbox/headless/dispatch-shim.sh +15 -0
- package/src/sandbox/headless/dispatch.ts +12 -14
- package/src/sandbox/stub-gh.sh +26 -0
- package/src/sandbox/stub-remote.ts +8 -29
- package/src/sync/engine.ts +1 -1
- package/src/take/camera.ts +2 -2
- package/src/take/film.ts +20 -2
- package/src/take/pointer.ts +2 -1
- package/src/take/stepper.ts +19 -5
- package/src/take/zoom/NOTICE.md +1 -1
- package/src/tasks/launch-wait.ts +160 -0
- package/tooling/claude/reference.md +1 -1
- package/claude/skills/canon-feedback-triage/REQUIREMENT.md +0 -34
- package/claude/skills/canon-feedback-triage/SKILL.md +0 -98
- package/claude/skills/plan-ready/REQUIREMENT.md +0 -35
- package/claude/skills/plan-ready/SKILL.md +0 -136
- package/claude/skills/plan-ready/references/assembly.md +0 -75
- package/claude/skills/plan-ready/references/ready.md +0 -128
- /package/claude/skills/{pick-by-eye → design-pick}/references/live-arms.md +0 -0
package/docs/target/projects.md
CHANGED
|
@@ -56,7 +56,7 @@ Keep the `## Scripts` table in `canon/context/development/local-loop.md` current
|
|
|
56
56
|
Before the first feature session:
|
|
57
57
|
|
|
58
58
|
1. Fill `canon/REQUIREMENTS.md` with the problem, goals, and non-goals. The seed's 600-word cap gates under `canon context audit`.
|
|
59
|
-
2. For a UI project, write `canon/DESIGN.md` from the seed and name the stylesheet in its lead, or work the direction out with `canon:pick
|
|
59
|
+
2. For a UI project, write `canon/DESIGN.md` from the seed and name the stylesheet in its lead, or work the direction out with `canon:design-pick`
|
|
60
60
|
3. Optionally invoke `canon:draw-figure` for an architecture view. The Mermaid CLI downloads on first use.
|
|
61
61
|
4. Start the feature loop in [AI workflow](../workflow/ai-workflow.md)
|
|
62
62
|
|
package/docs/target/sync.md
CHANGED
|
@@ -48,7 +48,7 @@ Add `--json` for a machine-readable report and `--exit-code` to fail CI when a t
|
|
|
48
48
|
- Tooling dependencies: a dependency on a later major than the stack pins reads as `ahead`, and sync leaves it
|
|
49
49
|
- Index regeneration: `canon indexes regen`
|
|
50
50
|
|
|
51
|
-
After `canon gov sync` applies, it lists each rule it copied with its globs and how many project files match. A `ui/` rule adds a pointer to `/canon:
|
|
51
|
+
After `canon gov sync` applies, it lists each rule it copied with its globs and how many project files match. A `ui/` rule adds a pointer to `/canon:ui-audit`. `--json` writes `{ "coverage": [{ "rule", "paths", "files", "ui" }] }` to stdout, empty when nothing was copied, with `files` null outside git. The report never changes the exit code.
|
|
52
52
|
|
|
53
53
|
Try a scenario from [sandbox](../../canon/context/development/sandbox.md) before syncing a real project.
|
|
54
54
|
|
|
@@ -15,11 +15,10 @@ These skills serve a request rather than a moment. With the [skill map](skill-ma
|
|
|
15
15
|
| `canon:create-rule` | A project rule the toolkit does not ship |
|
|
16
16
|
| `canon:create-skill` | A new `SKILL.md` |
|
|
17
17
|
| `canon:write-doc` | A new `docs/` page, context entry, or `README.md` |
|
|
18
|
-
| `canon:plan-ready` | Finished files a worker should copy |
|
|
19
18
|
| `canon:draw-figure` | A figure or architecture view inside a doc |
|
|
20
19
|
| `canon:ci-workflow` | A GitHub Actions workflow file |
|
|
21
20
|
| `canon:design-identity` | A logo mark and social card |
|
|
22
|
-
| `canon:ui-
|
|
21
|
+
| `canon:ui-film` | Real footage of the app for a video |
|
|
23
22
|
|
|
24
23
|
## Answer a question at any point
|
|
25
24
|
|
|
@@ -24,7 +24,7 @@ Groups run in the order a project meets them. Every name `canon claude skills li
|
|
|
24
24
|
| `canon:backlog-triage` | The backlog needs pruning |
|
|
25
25
|
| `canon:plan-groundwork` | The state is unmeasured and approaches compete |
|
|
26
26
|
| `canon:decision-escalate` | Open decisions turn on your preference |
|
|
27
|
-
| `canon:pick
|
|
27
|
+
| `canon:design-pick` | The call is taste, wanting candidates side by side |
|
|
28
28
|
| `canon:task-board` | A decided item needs a file on the board |
|
|
29
29
|
| `canon:plan-feature` | The approach is settled and needs a plan |
|
|
30
30
|
| `canon:codebase-layout` | A change adds a file and needs a folder |
|
|
@@ -53,8 +53,8 @@ Groups run in the order a project meets them. Every name `canon claude skills li
|
|
|
53
53
|
| Skill | When to use |
|
|
54
54
|
| ----------------------- | ---------------------------------------------- |
|
|
55
55
|
| `canon:document-health` | Documents must answer for length and staleness |
|
|
56
|
-
| `canon:
|
|
57
|
-
| `canon:
|
|
56
|
+
| `canon:ui-audit` | To read UI source for missing states |
|
|
57
|
+
| `canon:ui-measure` | To measure paint, processor, and layout cost |
|
|
58
58
|
| `canon:app-drive` | To drive a built app with the operator |
|
|
59
59
|
|
|
60
60
|
## Ship it
|
|
@@ -100,10 +100,9 @@ Groups run in the order a project meets them. Every name `canon claude skills li
|
|
|
100
100
|
|
|
101
101
|
## Keep the project current with the toolkit
|
|
102
102
|
|
|
103
|
-
| Skill
|
|
104
|
-
|
|
|
105
|
-
| `canon:target-check`
|
|
106
|
-
| `canon:target-update`
|
|
107
|
-
| `canon:document-repair`
|
|
108
|
-
| `canon:canon-feedback`
|
|
109
|
-
| `canon:canon-feedback-triage` | In the toolkit repo, to file feedback issues |
|
|
103
|
+
| Skill | When to use |
|
|
104
|
+
| ----------------------- | ------------------------------------------------------- |
|
|
105
|
+
| `canon:target-check` | In a target, to compare each domain with the toolkit |
|
|
106
|
+
| `canon:target-update` | After a release, to catch a target up to the toolkit |
|
|
107
|
+
| `canon:document-repair` | Rewrite a failing docs folder to the limits and gate it |
|
|
108
|
+
| `canon:canon-feedback` | The toolkit is broken, missing, or off |
|
|
@@ -72,7 +72,12 @@ A close posts as `## UI review closed`. The reviewers lift the draft mark on the
|
|
|
72
72
|
|
|
73
73
|
## Design authoring
|
|
74
74
|
|
|
75
|
-
Most projects author design in the loop above and need nothing more. Start with `canon:pick
|
|
75
|
+
Most projects author design in the loop above and need nothing more. Start with `canon:design-pick`, which drafts candidates as frames on a [Frameboard](https://www.npmjs.com/package/frameboard) board and takes your pick, then record the pick with `canon:design-record`. `canon:design-identity` drafts a logo mark and social card the same way.
|
|
76
|
+
|
|
77
|
+
A pick round drafts on `.canon/board/` unless the project names another board in `canon/config/design.toml`.
|
|
78
|
+
|
|
79
|
+
- Set `board` when your design sync reads another Frameboard board, so the picked frame lands without a redraw.
|
|
80
|
+
- Leave `project` unset to keep rounds in their own `picks` project on that board.
|
|
76
81
|
|
|
77
82
|
A graphical tool earns its place when design is the product, a designer owns it, and it changes more often than code. Then the tool is the source of truth, and `canon/DESIGN.md` carries a top note naming the upstream project.
|
|
78
83
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Background rule ledger
|
|
3
|
+
description: Decisions behind governance/rules/claude/background.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Background rule ledger
|
|
7
|
+
|
|
8
|
+
The rule is a core rule, so every row is a house choice rather than a citation. Each names what goes wrong without its bullet and the option the bullet beat, with the date it was decided.
|
|
9
|
+
|
|
10
|
+
## Slow commands
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Background past about a minute | House choice: a foreground run holds the conversation, so the operator waits on a check they could have talked over. It beat a warning hook, which would only warn and still let the run block | 2026-10-26 |
|
|
15
|
+
| Name the slow kinds | House choice: a minute is a guess made before the run, and with no cases to match a session reads a full check as quick. It beat the threshold alone | 2026-10-26 |
|
|
16
|
+
| Keep a seconds command foreground | House choice: each wake costs a turn, so backgrounding a quick command spends two turns where one did. It beat backgrounding every command | 2026-10-26 |
|
|
17
|
+
| End the turn on one status line | House choice: a turn that stops silently leaves the operator unable to tell a wait from a stall. It beat ending on the tool call alone | 2026-10-26 |
|
|
18
|
+
| Keep working beside a server | House choice: a dev server or a watch never exits, so a session awaiting its wake waits forever. It beat treating every background command as awaited | 2026-10-26 |
|
|
19
|
+
|
|
20
|
+
## Waking
|
|
21
|
+
|
|
22
|
+
| Bullet | Source | Read |
|
|
23
|
+
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
24
|
+
| Act on the wake, never a guess | House choice: a session guessing a command is done reads a partial output as the result. It beat checking the output file on a hunch | 2026-10-26 |
|
|
25
|
+
| No sleep, poll, or scheduled wait | House choice: a foreground sleep or poll holds the conversation the rule freed, and a scheduled prompt spends a turn on every check. It beat all three as ways to wait on one command | 2026-10-26 |
|
|
26
|
+
| Keep a skill's own wait | House choice: a skill owns the verb and the timeout it waits on, such as a review-state wait, and one rule-wide wait would override it. It beat a single wait for every caller | 2026-10-26 |
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run a command that may take more than about a minute in the background and resume on the harness wake, so the operator keeps the conversation
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Background standards
|
|
6
|
+
|
|
7
|
+
## Slow commands
|
|
8
|
+
|
|
9
|
+
- Start a command that may run past about a minute with `run_in_background: true`, since a foreground run holds the conversation. Revisit when a foreground Bash call stops blocking the turn.
|
|
10
|
+
- Treat a full check, a sandbox run, a capture or render, a build, and a wait on another session as slow.
|
|
11
|
+
- Keep a command expected to finish in seconds in the foreground, since each wake costs a turn.
|
|
12
|
+
- End the turn on one line naming what runs, when the next step needs that command's result.
|
|
13
|
+
- Keep working beside a server or a watch the session never waits on, since it never exits and its wake never comes.
|
|
14
|
+
|
|
15
|
+
## Waking
|
|
16
|
+
|
|
17
|
+
- Act on the wake the harness sends when an awaited command exits, never on a guess about when it is done. Revisit when a background Bash command stops waking the session at exit.
|
|
18
|
+
- Never wait with a foreground `sleep`, a foreground poll loop, or a scheduled prompt for one wait. The background run and its wake replace all three.
|
|
19
|
+
- Keep a skill's own wait as the skill states it, since the skill owns the verb and the timeout it waits on.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erclx/canon",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.69.0",
|
|
5
5
|
"description": "Runs your agent's work and keeps its code good, on one setup for every repo.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
|
-
"format": "oxfmt . && prettier --write --log-level warn 'web/**/*.astro' && shfmt --write --indent 2 scripts/ tooling/ claude/",
|
|
29
|
-
"check:format": "oxfmt --check . && prettier --check --log-level warn 'web/**/*.astro' && shfmt --diff --indent 2 scripts/ tooling/ claude/",
|
|
30
|
-
"check:shell": "find scripts tooling claude .claude/hooks -name '*.sh' -exec shellcheck --severity=warning {} + && find .husky -maxdepth 1 -type f -exec shellcheck --severity=warning --shell=sh {} +",
|
|
28
|
+
"format": "oxfmt . && prettier --write --log-level warn 'web/**/*.astro' && shfmt --write --indent 2 scripts/ tooling/ claude/ src/",
|
|
29
|
+
"check:format": "oxfmt --check . && prettier --check --log-level warn 'web/**/*.astro' && shfmt --diff --indent 2 scripts/ tooling/ claude/ src/",
|
|
30
|
+
"check:shell": "find scripts tooling claude src .claude/hooks -name '*.sh' -exec shellcheck --severity=warning {} + && find .husky -maxdepth 1 -type f -exec shellcheck --severity=warning --shell=sh {} +",
|
|
31
31
|
"check:lint": "oxlint --disable-nested-config --max-warnings 0",
|
|
32
32
|
"check:types": "tsc --noEmit",
|
|
33
33
|
"check:install": "bun scripts/core/install-check.ts",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Installs the Playwright system packages for the named browsers, killing an
|
|
3
|
+
# attempt that outlives its bound and retrying it. A degraded apt mirror hangs
|
|
4
|
+
# one connection rather than failing it, so a step timeout alone turns the
|
|
5
|
+
# hang into a red check and never recovers it, while a fresh attempt usually
|
|
6
|
+
# lands on a healthy backend.
|
|
7
|
+
#
|
|
8
|
+
# Usage: install-playwright-deps.sh [--package <spec>] <browser>...
|
|
9
|
+
# Exits 0 on the first success, 2 when no browser is named, and the last
|
|
10
|
+
# attempt's code when every attempt fails.
|
|
11
|
+
set -e
|
|
12
|
+
|
|
13
|
+
package=playwright
|
|
14
|
+
if [ "$1" = "--package" ]; then
|
|
15
|
+
package=$2
|
|
16
|
+
shift 2
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if [ $# -eq 0 ]; then
|
|
20
|
+
echo "Usage: install-playwright-deps.sh [--package <spec>] <browser>..." >&2
|
|
21
|
+
exit 2
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
attempts=3
|
|
25
|
+
bound=${INSTALL_ATTEMPT_SECONDS:-150}
|
|
26
|
+
|
|
27
|
+
# The runner image sets these, yet a stalled log shows gaps of minutes, so
|
|
28
|
+
# print what apt actually reads to settle whether they took effect
|
|
29
|
+
apt-config dump | grep -E '^Acquire::(Retries|http::Timeout) ' || true
|
|
30
|
+
|
|
31
|
+
status=0
|
|
32
|
+
for attempt in $(seq 1 "$attempts"); do
|
|
33
|
+
if [ "$attempt" -gt 1 ]; then
|
|
34
|
+
# timeout runs as the runner user, and Linux drops its signal to the root
|
|
35
|
+
# apt-get playwright spawns through sudo. sudo relays it only to its own
|
|
36
|
+
# child shell, so a root apt-get or dpkg can outlive the kill and hold the
|
|
37
|
+
# lock the next attempt needs.
|
|
38
|
+
sudo pkill -KILL -x 'apt-get|dpkg' || true
|
|
39
|
+
# A kill during unpack leaves dpkg interrupted, which refuses the next
|
|
40
|
+
# install. Its own failure must not end the loop.
|
|
41
|
+
sudo dpkg --configure -a || echo "dpkg --configure -a failed before attempt ${attempt}" >&2
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
status=0
|
|
45
|
+
timeout --kill-after=10 "$bound" bunx "$package" install-deps "$@" || status=$?
|
|
46
|
+
if [ "$status" -eq 0 ]; then
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
echo "attempt ${attempt}/${attempts} failed with exit ${status}" >&2
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
exit "$status"
|
|
@@ -26,8 +26,8 @@ export const AUTHORING_CASES: readonly SkillCase[] = [
|
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
prompt:
|
|
29
|
-
'I already wrote the finished
|
|
30
|
-
expect: 'plan-
|
|
29
|
+
'I already wrote the finished wording for a small new reference file, put it in a plan so a worker lands it exactly as written.',
|
|
30
|
+
expect: 'plan-feature',
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
prompt: 'This project has no README.md at all, write one from scratch.',
|
package/src/claude/cases/misc.ts
CHANGED
|
@@ -54,11 +54,6 @@ export const SETUP_CASES: readonly SkillCase[] = [
|
|
|
54
54
|
"Whatever the right toolkit command is, get this project's rules installed.",
|
|
55
55
|
expect: 'target-setup',
|
|
56
56
|
},
|
|
57
|
-
{
|
|
58
|
-
prompt:
|
|
59
|
-
'File the open feedback issues on the toolkit repo into an intake folder.',
|
|
60
|
-
expect: 'canon-feedback-triage',
|
|
61
|
-
},
|
|
62
57
|
// This answers one fixed question across a fixed domain list and repairs
|
|
63
58
|
// nothing, which is what separates it from the setup skill above.
|
|
64
59
|
{
|
|
@@ -143,11 +143,11 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
|
|
|
143
143
|
{
|
|
144
144
|
prompt:
|
|
145
145
|
'Look over the interface and tell me what feels unfinished or confusing.',
|
|
146
|
-
expect: '
|
|
146
|
+
expect: 'ui-audit',
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
149
|
prompt: 'Tell me the paint and layout cost of this page right now.',
|
|
150
|
-
expect: '
|
|
150
|
+
expect: 'ui-measure',
|
|
151
151
|
},
|
|
152
152
|
{
|
|
153
153
|
prompt:
|
|
@@ -162,12 +162,12 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
|
|
|
162
162
|
{
|
|
163
163
|
prompt:
|
|
164
164
|
'I need real footage of the board import flow for the launch video. Film it from our app so HyperFrames can zoom in on the clicks.',
|
|
165
|
-
expect: 'ui-
|
|
165
|
+
expect: 'ui-film',
|
|
166
166
|
},
|
|
167
167
|
{
|
|
168
168
|
prompt:
|
|
169
169
|
'Shoot the takes for the storyboard we wrote, one clip per beat of the app doing its thing.',
|
|
170
|
-
expect: 'ui-
|
|
170
|
+
expect: 'ui-film',
|
|
171
171
|
},
|
|
172
172
|
{
|
|
173
173
|
prompt:
|
|
@@ -216,10 +216,10 @@ export const WORKFLOW_CASES: readonly SkillCase[] = [
|
|
|
216
216
|
},
|
|
217
217
|
{
|
|
218
218
|
prompt: 'Find the rough, unfinished-feeling spots in this interface.',
|
|
219
|
-
expect: '
|
|
219
|
+
expect: 'ui-audit',
|
|
220
220
|
},
|
|
221
221
|
{
|
|
222
222
|
prompt: 'Tell me the render cost of this page in the browser.',
|
|
223
|
-
expect: '
|
|
223
|
+
expect: 'ui-measure',
|
|
224
224
|
},
|
|
225
225
|
]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fake gh for src/commands/pr.test.ts. Reads FAKE_GH_FX, FAKE_GH_COMMENTS, and FAKE_GH_API_LOG.
|
|
3
|
+
set -o pipefail
|
|
4
|
+
fx="$FAKE_GH_FX"
|
|
5
|
+
f=.
|
|
6
|
+
p=
|
|
7
|
+
for a in "$@"; do
|
|
8
|
+
[ "$p" = --jq ] && f=$a
|
|
9
|
+
p=$a
|
|
10
|
+
done
|
|
11
|
+
case "$*" in
|
|
12
|
+
*graphql* | "pr "* | "repo view"*) exit 1 ;;
|
|
13
|
+
api*pulls/7/files*)
|
|
14
|
+
echo "$*" >>"$fx/files-calls.log"
|
|
15
|
+
[ -f "$fx/files-fail" ] && exit 1
|
|
16
|
+
{
|
|
17
|
+
if [ -f "$fx/files.tsv" ]; then
|
|
18
|
+
cat "$fx/files.tsv"
|
|
19
|
+
else
|
|
20
|
+
git diff --name-status --no-renames main...HEAD | sed -e 's/^A/added/' -e 's/^M/modified/' -e 's/^D/removed/'
|
|
21
|
+
fi
|
|
22
|
+
} | jq -Rn '[inputs | select(length > 0) | split("\t") | {status: .[0], filename: .[1]}]' | jq -r "$f"
|
|
23
|
+
;;
|
|
24
|
+
api*git/trees/*)
|
|
25
|
+
[ -f "$fx/base-tree.json" ] || exit 1
|
|
26
|
+
jq -r "$f" "$fx/base-tree.json"
|
|
27
|
+
;;
|
|
28
|
+
api*contents/*)
|
|
29
|
+
a="$*"
|
|
30
|
+
p=${a##*contents/}
|
|
31
|
+
p=${p%%\?*}
|
|
32
|
+
r=${a##*ref=}
|
|
33
|
+
[ -f "$fx/img/$r-$(basename "$p")" ] && cat "$fx/img/$r-$(basename "$p")" || exit 1
|
|
34
|
+
;;
|
|
35
|
+
api*compare/*)
|
|
36
|
+
if [ -f "$fx/merge-base" ]; then cat "$fx/merge-base"; else git merge-base main HEAD; fi
|
|
37
|
+
;;
|
|
38
|
+
"api -X PUT repos/{owner}/{repo}/pulls/7/reviews/"*)
|
|
39
|
+
id=${4##*/}
|
|
40
|
+
echo "$id" >>"$fx/review-puts.log"
|
|
41
|
+
[ -f "$fx/review-put-fail-$id" ] && exit 1
|
|
42
|
+
printf '%s\n' "${@: -1}" >>"$fx/review-bodies.log"
|
|
43
|
+
echo "{}"
|
|
44
|
+
;;
|
|
45
|
+
api*pulls/7/reviews*) [ -f "$fx/ui-reviews.json" ] && jq -r "$f" "$fx/ui-reviews.json" ;;
|
|
46
|
+
api*issues/7/comments*) jq '[.comments[] | {html_url: .url, body}]' "$FAKE_GH_COMMENTS" | jq -r "$f" ;;
|
|
47
|
+
"api -X PATCH repos/{owner}/{repo}/pulls/7 "*)
|
|
48
|
+
[ -f "$fx/body-fail" ] && exit 1
|
|
49
|
+
printf '%s' "${@: -1}" >"$fx/body-write.log"
|
|
50
|
+
echo "{}"
|
|
51
|
+
;;
|
|
52
|
+
"api repos/{owner}/{repo}/pulls/7")
|
|
53
|
+
if [ -f "$fx/head" ]; then h=$(cat "$fx/head"); else h=$(git rev-parse HEAD); fi
|
|
54
|
+
b=
|
|
55
|
+
[ -f "$fx/pr-body" ] && b=$(cat "$fx/pr-body")
|
|
56
|
+
jq -n --arg h "$h" --arg b "$b" '{number:7,head:{ref:"feat/x",sha:$h},base:{ref:"main"},mergeable_state:"clean",body:$b}'
|
|
57
|
+
;;
|
|
58
|
+
"api repos/{owner}/{repo}") echo "{\"full_name\":\"o/r\"}" ;;
|
|
59
|
+
api*)
|
|
60
|
+
printf '%s' "${@: -1}" >"$FAKE_GH_API_LOG"
|
|
61
|
+
echo "{}"
|
|
62
|
+
;;
|
|
63
|
+
*) exit 1 ;;
|
|
64
|
+
esac
|
package/src/commands/gov.ts
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
} from '@/gov/restated'
|
|
35
35
|
import {
|
|
36
36
|
auditExit,
|
|
37
|
+
CORE_FOLDERS,
|
|
37
38
|
hasFinding,
|
|
38
39
|
PRACTICE_FOLDERS,
|
|
39
40
|
readRuleAudit,
|
|
@@ -62,6 +63,11 @@ import {
|
|
|
62
63
|
readTestOrder,
|
|
63
64
|
type TestOrderReport,
|
|
64
65
|
} from '@/gov/test-order'
|
|
66
|
+
import {
|
|
67
|
+
type ChangedSet,
|
|
68
|
+
collectChangedFiles,
|
|
69
|
+
commandRunner,
|
|
70
|
+
} from '@/gate/sequencer'
|
|
65
71
|
import { recordStamp, runDomainSync } from '@/sync/engine'
|
|
66
72
|
import { detectRules } from '@/gov/detect'
|
|
67
73
|
import { writeChainStamp } from '@/sync/stamp'
|
|
@@ -138,6 +144,7 @@ interface AuditOptions {
|
|
|
138
144
|
readonly root?: string
|
|
139
145
|
readonly json?: boolean
|
|
140
146
|
readonly gate?: boolean
|
|
147
|
+
readonly touched?: boolean
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
/** What a reader does about each way the sweep produced no reading. */
|
|
@@ -613,6 +620,10 @@ export function register(program: Command): void {
|
|
|
613
620
|
'--gate',
|
|
614
621
|
'Set 2 only for a finding in a gated folder, which the push stage reads',
|
|
615
622
|
)
|
|
623
|
+
.option(
|
|
624
|
+
'--touched',
|
|
625
|
+
'Owe a ledger for each core rule the branch changed against origin/main',
|
|
626
|
+
)
|
|
616
627
|
.addHelpText(
|
|
617
628
|
'after',
|
|
618
629
|
[
|
|
@@ -632,6 +643,9 @@ export function register(program: Command): void {
|
|
|
632
643
|
` asks a ledger of each rule under ${PRACTICE_FOLDERS.join(', ')}`,
|
|
633
644
|
' at governance/ledger/<folder>/<slug>.md, one table row per bullet',
|
|
634
645
|
' under a heading of the same name.',
|
|
646
|
+
` A rule under ${CORE_FOLDERS.join(', ')} has its ledger checked where`,
|
|
647
|
+
' one exists, and under --touched owes one once the branch changed it.',
|
|
648
|
+
' With no baseline against main, --touched reads no rule as touched.',
|
|
635
649
|
' Any other tree reads .claude/rules/project/ alone and asks no ledger.',
|
|
636
650
|
' .claude/rules/canon/ is fixed at the toolkit source and never read',
|
|
637
651
|
' unless named. A named path resolves against --root and must sit',
|
|
@@ -654,6 +668,7 @@ export function register(program: Command): void {
|
|
|
654
668
|
' canon gov audit',
|
|
655
669
|
' canon gov audit .claude/rules/project/api.md',
|
|
656
670
|
' canon gov audit --json',
|
|
671
|
+
' canon gov audit --gate --touched',
|
|
657
672
|
'',
|
|
658
673
|
].join('\n'),
|
|
659
674
|
)
|
|
@@ -672,7 +687,13 @@ async function runAudit(
|
|
|
672
687
|
opts: AuditOptions,
|
|
673
688
|
): Promise<number> {
|
|
674
689
|
const root = resolve(opts.root ?? process.cwd())
|
|
675
|
-
const
|
|
690
|
+
const changed = opts.touched
|
|
691
|
+
? await collectChangedFiles(commandRunner(root))
|
|
692
|
+
: undefined
|
|
693
|
+
// An unscoped set lists nothing, and reading it as every file would owe a
|
|
694
|
+
// ledger of every core rule on a branch that touched none of them.
|
|
695
|
+
const touched = changed?.scoped ? changed.files : []
|
|
696
|
+
const report = await readRuleAudit(root, paths, { touched })
|
|
676
697
|
const emitJson = opts.json ?? false
|
|
677
698
|
|
|
678
699
|
if (report.kind === 'unreadable') {
|
|
@@ -690,7 +711,7 @@ async function runAudit(
|
|
|
690
711
|
return 1
|
|
691
712
|
}
|
|
692
713
|
|
|
693
|
-
reportAudit(report, root, paths.length > 0)
|
|
714
|
+
reportAudit(report, root, paths.length > 0, changed)
|
|
694
715
|
|
|
695
716
|
if (emitJson) {
|
|
696
717
|
process.stdout.write(
|
|
@@ -700,6 +721,9 @@ async function runAudit(
|
|
|
700
721
|
limits: RULE_LIMITS,
|
|
701
722
|
gated: RULE_GATED_FOLDERS,
|
|
702
723
|
exempt: RULE_GATE_EXEMPT,
|
|
724
|
+
...(changed === undefined
|
|
725
|
+
? {}
|
|
726
|
+
: { touched: { scoped: changed.scoped } }),
|
|
703
727
|
...report,
|
|
704
728
|
})}\n`,
|
|
705
729
|
)
|
|
@@ -728,6 +752,7 @@ function reportAudit(
|
|
|
728
752
|
report: Extract<RuleAuditReport, { kind: 'measured' }>,
|
|
729
753
|
root: string,
|
|
730
754
|
named: boolean,
|
|
755
|
+
changed: ChangedSet | undefined,
|
|
731
756
|
): void {
|
|
732
757
|
intro('canon gov audit')
|
|
733
758
|
|
|
@@ -761,10 +786,15 @@ function reportAudit(
|
|
|
761
786
|
}
|
|
762
787
|
|
|
763
788
|
logStep('Ledgers')
|
|
789
|
+
if (changed !== undefined && !changed.scoped) {
|
|
790
|
+
logInfo(
|
|
791
|
+
'no baseline against main, so --touched reads no core rule as touched',
|
|
792
|
+
)
|
|
793
|
+
}
|
|
764
794
|
if (report.scope === 'target') {
|
|
765
795
|
logInfo('not read, since ledgers live in the toolkit and never ship')
|
|
766
796
|
} else if (ledgers.length === 0) {
|
|
767
|
-
logInfo('every
|
|
797
|
+
logInfo('every rule owing a ledger has one, with a row per bullet')
|
|
768
798
|
} else {
|
|
769
799
|
for (const line of ledgers) logWarn(line)
|
|
770
800
|
}
|
package/src/commands/migrate.ts
CHANGED
|
@@ -1309,6 +1309,13 @@ export function register(program: Command): void {
|
|
|
1309
1309
|
'any ordinal evidence/ already carries. The dry run labels every',
|
|
1310
1310
|
'derived destination, so check the split before passing --write.',
|
|
1311
1311
|
'',
|
|
1312
|
+
'Moves the design board to the folder Frameboard names:',
|
|
1313
|
+
' canvas/ -> board/',
|
|
1314
|
+
'',
|
|
1315
|
+
'Stop a frameboard serve on canvas/ before --write. A project holding',
|
|
1316
|
+
'both folders gets a collision, and a design.toml board key naming',
|
|
1317
|
+
'.canon/canvas is left for you to update.',
|
|
1318
|
+
'',
|
|
1312
1319
|
'A citation reaching into a moved folder or naming a moved file is',
|
|
1313
1320
|
'repointed, live or archived. A bare mention of an emptied folder, such',
|
|
1314
1321
|
'as review/evidence/ with no slug, matches no row and stays as written.',
|
|
@@ -1389,8 +1396,11 @@ export function register(program: Command): void {
|
|
|
1389
1396
|
`It also carries ${Object.keys(SKILL_CITATION_MAP).length} namespaced-only rows for a skill renamed from a single word.`,
|
|
1390
1397
|
'The seven draft- skills move onto verb-first names, such as',
|
|
1391
1398
|
// The old spellings below are the rename itself. canon-keep-retired
|
|
1392
|
-
'draft-doc to write-doc and draft-
|
|
1393
|
-
'pointer left under each old name is never moved.',
|
|
1399
|
+
'draft-doc to write-doc and draft-slides to make-deck, and a',
|
|
1400
|
+
'pointer left under each old name is never moved. Four skills then',
|
|
1401
|
+
'moved into the family their job belongs to, such as',
|
|
1402
|
+
// canon-keep-retired
|
|
1403
|
+
'ux-audit to ui-audit and pick-by-eye to design-pick.',
|
|
1394
1404
|
'It takes no scope. The skill folders it moves are authored in the',
|
|
1395
1405
|
'toolkit and no target holds a copy of that catalog, so in a target it',
|
|
1396
1406
|
'rewrites citations of a renamed skill and moves nothing.',
|