@erclx/canon 5.43.0 → 5.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/backlog-triage/references/apply.md +6 -6
- package/claude/skills/canon-cli/SKILL.md +4 -5
- package/claude/skills/canvas-board/REQUIREMENT.md +1 -1
- package/claude/skills/canvas-board/SKILL.md +1 -1
- package/claude/skills/context-fold/references/context.md +1 -1
- package/claude/skills/create-skill/REQUIREMENT.md +1 -1
- package/claude/skills/create-skill/SKILL.md +2 -2
- package/claude/skills/create-skill/references/skill-requirement.md +68 -9
- package/claude/skills/create-skill/references/skill.md +1 -1
- package/claude/skills/design-identity/REQUIREMENT.md +40 -0
- package/claude/skills/design-identity/SKILL.md +138 -0
- package/claude/skills/{draft-identity → design-identity}/references/card-route.md +2 -2
- package/claude/skills/design-taste/REQUIREMENT.md +6 -6
- package/claude/skills/design-taste/SKILL.md +2 -2
- package/claude/skills/design-taste/references/kinds.md +1 -1
- package/claude/skills/design-taste/references/preflight.md +1 -1
- package/claude/skills/draft-and-pick/REQUIREMENT.md +8 -69
- package/claude/skills/draft-and-pick/SKILL.md +3 -153
- package/claude/skills/draft-doc/REQUIREMENT.md +9 -30
- package/claude/skills/draft-doc/SKILL.md +4 -76
- package/claude/skills/draft-figure/REQUIREMENT.md +8 -42
- package/claude/skills/draft-figure/SKILL.md +3 -77
- package/claude/skills/draft-identity/REQUIREMENT.md +8 -23
- package/claude/skills/draft-identity/SKILL.md +3 -130
- package/claude/skills/draft-ready/REQUIREMENT.md +8 -37
- package/claude/skills/draft-ready/SKILL.md +3 -128
- package/claude/skills/draft-screencast/REQUIREMENT.md +8 -23
- package/claude/skills/draft-screencast/SKILL.md +3 -62
- package/claude/skills/draft-slides/REQUIREMENT.md +8 -27
- package/claude/skills/draft-slides/SKILL.md +3 -72
- package/claude/skills/draw-figure/REQUIREMENT.md +59 -0
- package/claude/skills/draw-figure/SKILL.md +85 -0
- package/claude/skills/{draft-figure → draw-figure}/references/architecture-set.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/hand-drawn.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/mermaid-path.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/mermaid.md +1 -1
- package/claude/skills/make-deck/REQUIREMENT.md +44 -0
- package/claude/skills/make-deck/SKILL.md +80 -0
- package/claude/skills/markdown-craft/REQUIREMENT.md +1 -1
- package/claude/skills/markdown-craft/SKILL.md +2 -2
- package/claude/skills/pick-by-eye/REQUIREMENT.md +36 -0
- package/claude/skills/pick-by-eye/SKILL.md +161 -0
- package/claude/skills/plan-feature/SKILL.md +2 -1
- package/claude/skills/plan-feature/references/plan.md +24 -10
- package/claude/skills/plan-groundwork/SKILL.md +1 -1
- package/claude/skills/plan-intake/SKILL.md +1 -1
- package/claude/skills/plan-ready/REQUIREMENT.md +54 -0
- package/claude/skills/plan-ready/SKILL.md +136 -0
- package/claude/skills/{draft-ready → plan-ready}/references/assembly.md +1 -1
- package/claude/skills/{draft-ready → plan-ready}/references/ready.md +1 -1
- package/claude/skills/record-screencast/REQUIREMENT.md +3 -3
- package/claude/skills/record-screencast/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/REQUIREMENT.md +16 -99
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +5 -2
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +18 -13
- package/claude/skills/role-planner/REQUIREMENT.md +15 -73
- package/claude/skills/role-planner/SKILL.md +2 -1
- package/claude/skills/role-worker/REQUIREMENT.md +13 -41
- package/claude/skills/role-worker/SKILL.md +9 -0
- package/claude/skills/script-screencast/REQUIREMENT.md +40 -0
- package/claude/skills/script-screencast/SKILL.md +70 -0
- package/claude/skills/{draft-screencast → script-screencast}/references/beats-template.md +2 -2
- package/claude/skills/search-craft/REQUIREMENT.md +4 -4
- package/claude/skills/search-craft/SKILL.md +1 -1
- package/claude/skills/target-setup/SKILL.md +3 -2
- package/claude/skills/target-setup/references/detect.md +0 -1
- package/claude/skills/target-setup/references/verify.md +2 -2
- package/claude/skills/task-board/SKILL.md +11 -9
- package/claude/skills/task-board/references/board.md +15 -12
- package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
- package/claude/skills/teach-workspace/SKILL.md +1 -1
- package/claude/skills/teach-workspace/references/lesson-craft.md +2 -2
- package/claude/skills/teach-workspace/references/promotion.md +5 -5
- package/claude/skills/ui-checklist/SKILL.md +2 -0
- package/claude/skills/write-doc/REQUIREMENT.md +46 -0
- package/claude/skills/write-doc/SKILL.md +83 -0
- package/claude/skills/{draft-doc → write-doc}/references/context.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/docs.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/readme.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/wiki-kind.md +3 -3
- package/docs/agents/install-and-sync.md +2 -2
- package/docs/agents/markdown-audit.md +4 -2
- package/docs/agents/migrate.md +2 -2
- package/docs/agents/output-shape.md +1 -2
- package/docs/target/migrations.md +35 -4
- package/docs/target/projects.md +2 -3
- package/docs/target/scenarios.md +4 -1
- package/docs/workflow/parallel-features.md +3 -2
- package/docs/workflow/skill-map-on-request.md +19 -13
- package/docs/workflow/skill-map.md +13 -12
- package/docs/workflow/visual-design-workflow.md +2 -2
- package/governance/detect.toml +4 -0
- package/governance/ledger/framework/astro.md +73 -0
- package/governance/ledger/framework/fastapi.md +76 -0
- package/governance/ledger/framework/nextjs.md +86 -0
- package/governance/ledger/framework/react.md +83 -0
- package/governance/ledger/framework/shadcn.md +60 -0
- package/governance/ledger/framework/tailwind.md +55 -0
- package/governance/ledger/lang/go.md +61 -0
- package/governance/ledger/lang/php.md +50 -0
- package/governance/ledger/lang/python.md +68 -0
- package/governance/ledger/lang/typescript.md +64 -0
- package/governance/ledger/lib/database.md +65 -0
- package/governance/ledger/lib/e2e-reliability.md +57 -0
- package/governance/ledger/lib/pydantic.md +61 -0
- package/governance/ledger/lib/security-server.md +62 -0
- package/governance/ledger/lib/security-web.md +51 -0
- package/governance/ledger/lib/tanstack-query.md +57 -0
- package/governance/ledger/lib/test-scope.md +43 -0
- package/governance/ledger/lib/testing-go.md +57 -0
- package/governance/ledger/lib/testing-php.md +51 -0
- package/governance/ledger/lib/testing-py.md +67 -0
- package/governance/ledger/lib/testing-ts.md +63 -0
- package/governance/ledger/lib/zod.md +47 -0
- package/governance/ledger/tooling/ci-workflow.md +33 -0
- package/governance/ledger/tooling/code-comments.md +48 -0
- package/governance/ledger/tooling/config-comments.md +27 -0
- package/governance/ledger/tooling/dependencies.md +38 -0
- package/governance/ledger/ui/a11y.md +55 -0
- package/governance/ledger/ui/copy.md +49 -0
- package/governance/ledger/ui/design-reference.md +42 -0
- package/governance/ledger/ui/design-taste.md +19 -0
- package/governance/ledger/ui/forms.md +71 -0
- package/governance/ledger/ui/interface-casing.md +47 -0
- package/governance/ledger/ui/keyboard.md +68 -0
- package/governance/ledger/ui/link-behavior.md +29 -0
- package/governance/ledger/ui/motion.md +33 -0
- package/governance/ledger/ui/surface-capture.md +69 -0
- package/governance/ledger/ui/ux-completeness.md +79 -0
- package/governance/rules/framework/astro.md +12 -14
- package/governance/rules/framework/fastapi.md +20 -24
- package/governance/rules/framework/nextjs.md +19 -27
- package/governance/rules/framework/react.md +22 -33
- package/governance/rules/framework/shadcn.md +11 -15
- package/governance/rules/framework/tailwind.md +12 -14
- package/governance/rules/lang/go.md +7 -16
- package/governance/rules/lang/php.md +3 -3
- package/governance/rules/lang/python.md +9 -18
- package/governance/rules/lang/typescript.md +9 -19
- package/governance/rules/lib/database.md +5 -5
- package/governance/rules/lib/e2e-reliability.md +4 -6
- package/governance/rules/lib/pydantic.md +14 -16
- package/governance/rules/lib/security-server.md +4 -4
- package/governance/rules/lib/security-web.md +6 -10
- package/governance/rules/lib/tanstack-query.md +10 -12
- package/governance/rules/lib/test-scope.md +4 -4
- package/governance/rules/lib/testing-go.md +1 -2
- package/governance/rules/lib/testing-php.md +4 -5
- package/governance/rules/lib/testing-py.md +11 -19
- package/governance/rules/lib/testing-ts.md +7 -16
- package/governance/rules/lib/zod.md +6 -8
- package/governance/rules/tooling/code-comments.md +2 -4
- package/governance/rules/tooling/config-comments.md +1 -4
- package/governance/rules/tooling/dependencies.md +1 -1
- package/governance/rules/ui/a11y.md +16 -45
- package/governance/rules/ui/copy.md +6 -9
- package/governance/rules/ui/forms.md +13 -19
- package/governance/rules/ui/interface-casing.md +5 -7
- package/governance/rules/ui/keyboard.md +38 -0
- package/governance/rules/ui/link-behavior.md +3 -3
- package/governance/rules/ui/motion.md +3 -3
- package/governance/rules/ui/surface-capture.md +15 -27
- package/governance/rules/ui/ux-completeness.md +16 -39
- package/governance/rules/writing/markdown.md +1 -1
- package/package.json +1 -1
- package/scripts/core/install-check.sh +1 -1
- package/src/claude/cases/authoring.ts +15 -3
- package/src/claude/cases/misc.ts +12 -0
- package/src/claude/cases/workflow.ts +19 -1
- package/src/claude/skills-audit.ts +113 -1
- package/src/cli/help.ts +0 -1
- package/src/cli.ts +0 -2
- package/src/commands/claude.ts +34 -11
- package/src/commands/demo.ts +1 -1
- package/src/commands/gov.ts +4 -0
- package/src/commands/init.ts +2 -2
- package/src/commands/markdown.ts +1 -1
- package/src/commands/migrate.ts +4 -0
- package/src/commands/pr.ts +30 -6
- package/src/commands/tasks.ts +383 -2
- package/src/commands/tooling.ts +18 -5
- package/src/context/audit.ts +1 -1
- package/src/demo/beats.ts +1 -1
- package/src/design/components.ts +1 -1
- package/src/gov/audit.ts +25 -4
- package/src/init/plan.ts +1 -8
- package/src/init/steps.ts +0 -8
- package/src/inventory/subjects.ts +1 -1
- package/src/markdown/scope.ts +49 -35
- package/src/migrate/record-layout.ts +1 -1
- package/src/migrate/scratch-evidence.ts +12 -4
- package/src/migrate/skill-names.ts +68 -21
- package/src/pr/frames.ts +14 -3
- package/src/records/backup.ts +3 -3
- package/src/records/validate.ts +31 -0
- package/src/roots/record.ts +17 -8
- package/src/tasks/board.ts +854 -0
- package/src/tasks/record.ts +20 -1
- package/src/tasks/validate.ts +206 -9
- package/src/tooling/inject.ts +26 -7
- package/src/tooling/package.ts +32 -0
- package/src/tooling/read.ts +46 -3
- package/src/tooling/scan.ts +8 -2
- package/tooling/astro/configs/card-src/pages/og-card.astro +1 -1
- package/tooling/astro/reference.md +64 -93
- package/tooling/base/configs/.github/workflows/phase-label-gate.yml +1 -1
- package/tooling/base/configs/scripts/verify.ts +1 -1
- package/tooling/base/reference.md +1 -9
- package/tooling/base/seeds/canon/context/{ci.md → development/ci.md} +1 -1
- package/tooling/base/seeds/canon/context/development/index.md +11 -0
- package/tooling/base/seeds/canon/context/{development.md → development/setup.md} +8 -12
- package/tooling/base/seeds/canon/context/index.md +1 -2
- package/tooling/claude/manifest.toml +1 -5
- package/tooling/claude/reference.md +36 -59
- package/tooling/claude/seeds/.claude/hooks/board-validate.sh +65 -0
- package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +2 -7
- package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +2 -7
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +3 -10
- package/tooling/claude/seeds/.claude/hooks/search-reminder.sh +2 -7
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +2 -4
- package/tooling/claude/seeds/.claude/settings.json +4 -0
- package/tooling/nextjs/reference.md +0 -6
- package/tooling/php/reference.md +0 -4
- package/tooling/web/configs/.github/workflows/verify.yml +28 -0
- package/tooling/web/configs/.oxlintrc.json +7 -0
- package/tooling/web/configs/e2e/capture-types.ts +35 -0
- package/tooling/web/configs/e2e/screenshot.ts +23 -72
- package/tooling/web/manifest.toml +1 -1
- package/tooling/web/reference.md +77 -124
- package/tooling/web/seeds/e2e/capture/cases.ts +47 -0
- package/src/commands/wiki.ts +0 -100
- package/src/wiki/init.ts +0 -85
- /package/claude/skills/{draft-figure → draw-figure}/references/figures.md +0 -0
- /package/claude/skills/{draft-slides → make-deck}/references/deck.md +0 -0
- /package/claude/skills/{draft-and-pick → pick-by-eye}/references/live-arms.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/agent-facing.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/application.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/cli.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/library.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/plugin.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/requirements.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/wiki.md +0 -0
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
The astro stack covers Astro + TypeScript projects, being content sites, marketing sites, blogs, and docs. Interactive islands use React only and output is static.
|
|
8
8
|
|
|
9
|
-
It ships golden configs for `astro.config.mjs`, `vitest.config.ts`, `playwright.config.ts` (preview-server backed), and a `tsconfig.json` extending `astro/tsconfigs/strict`. An `eslint.config.js` keeps ESLint for this stack alone, and a `.prettierrc` formats `.astro` files alone.
|
|
10
|
-
|
|
11
9
|
## Scaffold checklist
|
|
12
10
|
|
|
13
11
|
1. Scaffold with `bunx create-astro@latest`. Choose `TypeScript: Strict`. Skip git init and install.
|
|
@@ -17,65 +15,90 @@ It ships golden configs for `astro.config.mjs`, `vitest.config.ts`, `playwright.
|
|
|
17
15
|
5. Extend the `ci` and `development` context entries under `canon/context/` per the web reference's extend sections plus the astro rows below.
|
|
18
16
|
6. Run `bun run lint:fix`, then `bun run format`, then `bun run check`.
|
|
19
17
|
|
|
20
|
-
##
|
|
18
|
+
## Astro config
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
- `astro.config.mjs` adds `@astrojs/react`, `@tailwindcss/vite` in `vite.plugins`, the `@/` alias via `vite.resolve.alias`, and `ASTRO_SITE` for the `site` field
|
|
21
|
+
- It serves port `4321` plus `WORKTREE_PORT_OFFSET` at `server.port`, with `strictPort` under `vite.server` and `vite.preview`
|
|
22
|
+
- Keep the port at the top level and the bind guarantee under `vite`, since Astro feeds `server.port` through as the preview port but merges the `vite` block into both servers
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
## TypeScript
|
|
25
25
|
|
|
26
|
-
- `
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
- `vitest.config.ts` uses `getViteConfig` from `astro/config`, not `mergeConfig`, with jsdom, globals, the setup file, `passWithNoTests: true`, v8 coverage, and `**/*.astro` in coverage excludes.
|
|
30
|
-
- `tsconfig.json` adds `skipLibCheck`, `vitest/globals` and `@testing-library/jest-dom/vitest` in types, and `@/` paths.
|
|
26
|
+
- `tsconfig.json` extends `astro/tsconfigs/strict` and adds `skipLibCheck`, `vitest/globals` and `@testing-library/jest-dom/vitest` in types, and `@/` paths
|
|
27
|
+
- Typecheck with `astro check` in place of `tsc --noEmit`, since `tsc` cannot parse `.astro` files
|
|
28
|
+
- `build` runs `astro check && astro build`
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
## Vitest
|
|
33
31
|
|
|
34
|
-
`
|
|
32
|
+
- `vitest.config.ts` uses `getViteConfig` from `astro/config`, not `mergeConfig`, with jsdom, globals, the setup file, `passWithNoTests: true`, and v8 coverage
|
|
33
|
+
- Exclude `**/*.astro` from coverage, and unit-test React island components (`.tsx`) alone, since Playwright verifies page-level behavior against rendered output
|
|
34
|
+
- Keep Vitest at `^5` from the web manifest, which supports the Vite 8 that Astro 7 bundles
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
- `DIST_PREBUILT` in the environment drops the `build` half, running `bun run preview` alone against a `dist/` a prior CI job produced.
|
|
36
|
+
## Playwright
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
- `playwright.config.ts` runs all browsers, with `webServer` running `bun run build && bun run preview` on port `4321` plus `WORKTREE_PORT_OFFSET` and `reuseExistingServer: false`
|
|
39
|
+
- Test the built `dist/`, since Astro's dev and production gap is wide across MDX, island hydration, and asset optimization
|
|
40
|
+
- Set `DIST_PREBUILT` to drop the `build` half, running `bun run preview` alone against a `dist/` a prior CI job produced
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
## ESLint
|
|
42
43
|
|
|
43
|
-
-
|
|
44
|
-
- `
|
|
45
|
-
-
|
|
46
|
-
- `
|
|
47
|
-
- `canon/context/surfaces/tooling.md` carries the measurement behind both naming rules.
|
|
44
|
+
- Lint with this stack's own flat `eslint.config.js`, since the web layer's oxlint parses no `.astro` file. It adds `eslint-plugin-astro` with `astro-eslint-parser`.
|
|
45
|
+
- Expect `lint` and `lint:fix` overridden back to `eslint . --max-warnings 0`, and the web layer's `.oxlintrc.json` in the target with nothing running it
|
|
46
|
+
- Install `eslint@^9`, `@eslint/js`, `typescript-eslint`, `eslint-config-prettier`, `eslint-plugin-react-hooks`, `eslint-plugin-vitest`, and `globals`, since the web layer ships none of them
|
|
47
|
+
- Pin `eslint@^9`, because ESLint 10 dropped the `LegacyESLint` shim `typescript-eslint@8` still imports
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
## ESLint naming rules
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
- Apply React hooks rules to `.jsx` and `.tsx` alone, since `.astro` is not React
|
|
52
|
+
- Name `.ts`, `.tsx`, and `.astro` files in `KEBAB_CASE`, overriding Astro's PascalCase, since markup names a component by its import binding
|
|
53
|
+
- Keep `.js` and `.jsx` out of that rule, matching `web` and `nextjs`
|
|
54
|
+
- Name every `src/**` folder in kebab case except `__tests__` and the `pages/` subtree, whose own off-block allows bracket-named dynamic routes and slug folders
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
## Formatting (extend)
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
- Format `.astro` files with Prettier and every other file with oxfmt, which loads no Prettier plugin and cannot parse `.astro`
|
|
59
|
+
- Load `prettier-plugin-astro` first in `.prettierrc`'s plugins and `prettier-plugin-tailwindcss` last, with a parser override for `.astro` files
|
|
60
|
+
- Ignore `**/*.astro` in this stack's `.oxfmtrc.json`, so the two formatters never touch one file
|
|
61
|
+
- Expect this stack's `.oxfmtrc.json` to replace base's whole file, so a base option change reaches a target only once this stack copies it
|
|
62
|
+
- Override `check:format` and `format` to run oxfmt, then Prettier over `**/*.astro`
|
|
56
63
|
|
|
57
|
-
|
|
64
|
+
## Social card route
|
|
58
65
|
|
|
59
|
-
|
|
66
|
+
- Keep the social card as a route the project owns, so a re-render after a rename is one string edit and a re-capture rather than another `design-identity` pick loop
|
|
67
|
+
- Expect `canon:design-identity` to write the picked composition into the route and capture it through the card server
|
|
68
|
+
- `card.config.mjs` is a second Astro config whose `srcDir` points at `./card-src`, which the main page router never reads, so the published build excludes the card structurally
|
|
69
|
+
- It serves on base port `4421` plus `WORKTREE_PORT_OFFSET`, outside the band `astro.config.mjs` serves from, so a worktree's card and dev servers never contend
|
|
70
|
+
- `bun run card:dev` serves the route and `bun run card:check` runs the exclusion check, which base's `scripts/verify.ts` also runs after its build stage
|
|
60
71
|
|
|
61
|
-
##
|
|
72
|
+
## Card exclusion and size
|
|
62
73
|
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
- Copy every other option from base's `.oxfmtrc.json` by hand. Scan takes the child config over the parent's whole file, so a change to base's options is lost here until copied.
|
|
67
|
-
- Override `check:format` and `format` to run oxfmt, then Prettier over `**/*.astro`.
|
|
74
|
+
- `scripts/check-card-exclusion.sh` fails when the route's `<meta name="og-card-marker">` tag reaches `dist/`, and when the route dropped the tag the leak test reads
|
|
75
|
+
- Keep the exclusion a check rather than a convention, since the card server and the capture read the route and the published build must not carry it
|
|
76
|
+
- Declare `.card` at 600x315, half the 1200x630 target, since `canon capture` shoots at a fixed 2x device scale and 1200x630 would capture at 2400x1260
|
|
68
77
|
|
|
69
|
-
##
|
|
78
|
+
## Card stylesheet and tokens
|
|
79
|
+
|
|
80
|
+
- `card-src/pages/og-card.astro` ships as a working placeholder, so it serves before any pick
|
|
81
|
+
- It imports `@/styles/global.css` for real tokens and fonts, and a project keeping its stylesheet elsewhere retargets that one line
|
|
82
|
+
- It uses `--color-background` and `--color-text` as `canon design css` emits them, and sets no `font-family`, so the card inherits the body font
|
|
83
|
+
- Expect an unresolved stylesheet path to refuse the card server by name, rather than capture an unstyled card that reports success
|
|
84
|
+
- Name only properties `canon design css` emits, since an undefined one silently takes its fallback, and check the names against a retargeted stylesheet
|
|
85
|
+
|
|
86
|
+
## Card route scope
|
|
87
|
+
|
|
88
|
+
- Expect no card route on another web stack, since Astro is the one whose config gives a route a structural build exclusion
|
|
89
|
+
- Next's App Router has none and needs none, since `ImageResponse` is its production mechanism and a card route there is meant to be served
|
|
90
|
+
- `vite-react` builds a single-page app with no page router, so a card there is a second Rollup entry rather than a route
|
|
70
91
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
92
|
+
## Scenario switcher
|
|
93
|
+
|
|
94
|
+
- Expect `src/components/dev/scenarios.astro` overwritten on every sync, since it is toolkit-authored infrastructure rather than a file a project hand-edits
|
|
95
|
+
- Import it into a page under test to drive candidate treatments of one decision by hand, selected by a query parameter, with a switcher between them
|
|
96
|
+
- Rely on its `import.meta.env.DEV` guard, so it renders nothing and ships nothing in a production build
|
|
97
|
+
- Expect it on Astro alone, since it uses `is:inline`, `define:vars`, and `set:html`, which only the `.astro` format parses
|
|
75
98
|
|
|
76
99
|
## Development docs (extend)
|
|
77
100
|
|
|
78
|
-
Append these rows to the `## Scripts` table
|
|
101
|
+
- Append these rows to the `## Scripts` table
|
|
79
102
|
|
|
80
103
|
| Command | Purpose |
|
|
81
104
|
| ------------------- | --------------------------------------------------------------- |
|
|
@@ -87,57 +110,5 @@ Append these rows to the `## Scripts` table.
|
|
|
87
110
|
|
|
88
111
|
## CI docs (extend)
|
|
89
112
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
## Social card route
|
|
93
|
-
|
|
94
|
-
The social card is a route the project keeps rather than a page a render writes and deletes. Re-rendering it after a rename is an edit to one string and a re-capture, not another pass through the `draft-identity` pick loop.
|
|
95
|
-
|
|
96
|
-
`canon:draft-identity` writes the picked composition into the route and captures it through the server below.
|
|
97
|
-
|
|
98
|
-
### What ships
|
|
99
|
-
|
|
100
|
-
The route is a second config, an exclusion check, and two scripts. The page itself is covered below.
|
|
101
|
-
|
|
102
|
-
- `card.config.mjs` is a second Astro config whose `srcDir` points at `./card-src`, which the main config's page router never reads. That excludes the card from the published build structurally.
|
|
103
|
-
- `card.config.mjs` uses base port `4421` plus `WORKTREE_PORT_OFFSET`, outside the band `astro.config.mjs` serves from, so a worktree's card and dev servers never contend.
|
|
104
|
-
- `scripts/check-card-exclusion.sh` fails when the route's `<meta name="og-card-marker">` tag reaches `dist/`, and when the route dropped the tag, since the leak test reads it.
|
|
105
|
-
- `bun run card:dev` serves the route and `bun run card:check` runs the exclusion check, which base's `scripts/verify.ts` also runs after its build stage when the script is installed.
|
|
106
|
-
|
|
107
|
-
### Stylesheet and tokens
|
|
108
|
-
|
|
109
|
-
`card-src/pages/og-card.astro` ships as a working placeholder, so it serves before any pick. It imports the project's global stylesheet to read real tokens and fonts, uses `--color-background` and `--color-text` as `canon design css` emits them, and sets no `font-family`, so the card inherits the body font.
|
|
110
|
-
|
|
111
|
-
- The import points at `@/styles/global.css`. A project keeping its stylesheet elsewhere retargets that one line.
|
|
112
|
-
- An unresolved path refuses the card server by name. That is the loud direction, replacing a card captured against no stylesheet that renders unstyled and reports success.
|
|
113
|
-
|
|
114
|
-
- A custom property the stylesheet does not define fails the same silent way. It takes its fallback, captures cleanly, and ships typed values.
|
|
115
|
-
- So the route names only properties `canon design css` emits, and a project retargeting the import checks the names against its own stylesheet.
|
|
116
|
-
|
|
117
|
-
### Exclusion and card size
|
|
118
|
-
|
|
119
|
-
The exclusion is a check rather than a convention because the route has three consumers and only one can be enforced. The card server reads it, the capture reads it, and the published build must not carry it.
|
|
120
|
-
|
|
121
|
-
`.card` is declared at 600x315, half of the 1200x630 target. `canon capture` opens every page at a fixed 2x device scale factor with no flag to change it, so a half-size element lands the PNG on the literal size. Declaring 1200x630 captures at 2400x1260.
|
|
122
|
-
|
|
123
|
-
### Why v1 is Astro-only
|
|
124
|
-
|
|
125
|
-
Astro is the one web stack whose config gives a route a structural build exclusion.
|
|
126
|
-
|
|
127
|
-
- Next's App Router has no build-time route exclusion, and needs none on its path, since `ImageResponse` is that framework's production mechanism and a card route there is meant to be served.
|
|
128
|
-
- `vite-react` builds a single-page app with no page router, so a card is a second Rollup entry rather than a route.
|
|
129
|
-
|
|
130
|
-
## Gitignore (extend)
|
|
131
|
-
|
|
132
|
-
These `[gitignore]` groups mirror the manifest.
|
|
133
|
-
|
|
134
|
-
- `"# Astro" = [".astro/"]`
|
|
135
|
-
- `"# Social card" = ["card-dist/"]`
|
|
136
|
-
|
|
137
|
-
## Scenario switcher
|
|
138
|
-
|
|
139
|
-
`src/components/dev/scenarios.astro` ships as a golden config, always overwritten on sync, since it is toolkit-authored infrastructure rather than a file a project hand-edits.
|
|
140
|
-
|
|
141
|
-
- Import it into a page under test to drive candidate treatments of one decision by hand, selected by a query parameter, with a switcher between them.
|
|
142
|
-
- It is guarded by `import.meta.env.DEV`, so it renders nothing and ships nothing in a production build.
|
|
143
|
-
- It is Astro-only, using `is:inline`, `define:vars`, and `set:html`, which only the `.astro` format parses. `vite-react` gets no equivalent until a real decision drives one.
|
|
113
|
+
- Assert `` `astro check` passes `` on the Typecheck row of `canon/context/development/ci.md` <!-- audit-ignore-citations: canon/context/development/ci.md -->
|
|
114
|
+
- Assert `` `astro build` succeeds `` on its Build row
|
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
name: 🏷️ Phase Label Gate
|
|
20
20
|
runs-on: ubuntu-latest
|
|
21
21
|
# Dependabot bodies carry upstream release notes that read as phase labels
|
|
22
|
-
if: github.event.pull_request.user.login != 'dependabot[bot]' && (github.event_name == 'workflow_dispatch' || github.event.pull_request.base.ref == 'main')
|
|
22
|
+
if: github.event.pull_request.user.login != 'dependabot[bot]' && github.event.pull_request.user.login != 'renovate[bot]' && (github.event_name == 'workflow_dispatch' || github.event.pull_request.base.ref == 'main')
|
|
23
23
|
steps:
|
|
24
24
|
- name: Checkout
|
|
25
25
|
uses: actions/checkout@v7
|
|
@@ -225,7 +225,7 @@ function readPhaseList(): PhaseList {
|
|
|
225
225
|
|
|
226
226
|
function readDeclaredScripts(): Set<string> {
|
|
227
227
|
if (!existsSync('package.json')) {
|
|
228
|
-
fail(
|
|
228
|
+
fail('No package.json here. Sync the stack to write one')
|
|
229
229
|
}
|
|
230
230
|
return new Set(
|
|
231
231
|
Object.keys(asRecord(asRecord(readJson('package.json')).scripts)),
|
|
@@ -18,7 +18,6 @@ Two configs fix how output looks and how commits read.
|
|
|
18
18
|
- The `astro` stack repeats those options in its own copy, so copy a change here into it.
|
|
19
19
|
- `commitlint.config.js` is an ESM default export extending `@commitlint/config-conventional`. It sets `header-max-length: 72`, `scope-case: lower-case`, and `subject-full-stop: never`.
|
|
20
20
|
- Its `subject-case` rule is off.
|
|
21
|
-
- `.husky/` holds `pre-commit`, `commit-msg`, `pre-push`, `post-merge`, and `post-rewrite`.
|
|
22
21
|
|
|
23
22
|
### Verify runner
|
|
24
23
|
|
|
@@ -39,7 +38,7 @@ Seeds live in `tooling/base/seeds/`. Sync drops each once on first install and n
|
|
|
39
38
|
- `.lintstagedrc` holds the glob map below.
|
|
40
39
|
- `.github/workflows/verify.yml` runs on pull requests targeting `main` and on `workflow_dispatch`. A multi-root project adds its own jobs here.
|
|
41
40
|
- `.github/pull_request_template.md` has `## Summary`, `## Key changes`, `## Technical context`, and `## Testing`. A project adds its own checklist lines.
|
|
42
|
-
- `canon/context/` holds the `
|
|
41
|
+
- `canon/context/development/` holds the `setup` and `ci` entries under a folder index. Extend them with project-specific commands and workflows, and keep canonical rationale here.
|
|
43
42
|
|
|
44
43
|
## Tool pairing
|
|
45
44
|
|
|
@@ -83,13 +82,6 @@ The seeded baseline globs.
|
|
|
83
82
|
- Steps are checkout, setup Bun at latest, `bun install --frozen-lockfile`, then `check:format`.
|
|
84
83
|
- CI asserts and never writes, so format must be clean before push.
|
|
85
84
|
|
|
86
|
-
## Gitignore
|
|
87
|
-
|
|
88
|
-
- `# System`: `.DS_Store`
|
|
89
|
-
- `# Dependencies`: `node_modules/`
|
|
90
|
-
- `# Secrets`: `.env`, `.env.*`, `*.local`, `!.env.example`
|
|
91
|
-
- `# VSCode`: `.vscode/`
|
|
92
|
-
|
|
93
85
|
## Anti-patterns
|
|
94
86
|
|
|
95
87
|
Negative knowledge that costs a failed run to relearn.
|
|
@@ -7,7 +7,7 @@ description: GitHub Actions workflow triggers and checks
|
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
10
|
-
Owns the GitHub Actions workflow that gates a merge, being which events start a run and which checks have to pass before the branch can land. The checks call package scripts rather than defining commands of their own, so what each one runs is the
|
|
10
|
+
Owns the GitHub Actions workflow that gates a merge, being which events start a run and which checks have to pass before the branch can land. The checks call package scripts rather than defining commands of their own, so what each one runs is the setup entry's subject.
|
|
11
11
|
|
|
12
12
|
## Layout
|
|
13
13
|
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
2
|
+
title: Setup
|
|
3
3
|
description: Local dev workflow, scripts, and husky hooks
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Setup
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
10
10
|
Owns how the project runs on a developer machine, being the toolchain install, the scripts that verify a change, and the git hooks that run them before a commit or a push leaves. CI calls the same scripts from a workflow, which is the CI entry's subject.
|
|
11
11
|
|
|
12
|
+
Install [Bun](https://bun.sh) with `curl -fsSL https://bun.sh/install | bash` and run `bun install`, then use the package scripts below. They call the shell scripts in `scripts/`, which holds every one since none belongs outside it.
|
|
13
|
+
|
|
12
14
|
## Layout
|
|
13
15
|
|
|
14
16
|
### `scripts/`
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
| Command | Purpose |
|
|
19
|
+
| ---------------- | ---------------------------------------------------------------------------------------- |
|
|
20
|
+
| `bun run check` | Gates locally and writes nothing. Fails on formatting drift until `bun run format` runs. |
|
|
21
|
+
| `bun run format` | Auto-fix oxfmt formatting. |
|
|
17
22
|
|
|
18
23
|
### `.husky/`
|
|
19
24
|
|
|
@@ -23,15 +28,6 @@ Holds the git hooks that run the checks before a commit or a push leaves.
|
|
|
23
28
|
- `commit-msg` runs `commitlint` against the conventional commit format
|
|
24
29
|
- `pre-push` runs `bun run check`, and also gates on banned characters across every tracked markdown file except `CHANGELOG.md` when a markdown-bans audit tool is on PATH
|
|
25
30
|
|
|
26
|
-
### Scripts
|
|
27
|
-
|
|
28
|
-
Install [Bun](https://bun.sh) with `curl -fsSL https://bun.sh/install | bash` and run `bun install`, then use these commands.
|
|
29
|
-
|
|
30
|
-
| Command | Purpose |
|
|
31
|
-
| ---------------- | ---------------------------------------------------------------------------------------- |
|
|
32
|
-
| `bun run check` | Gates locally and writes nothing. Fails on formatting drift until `bun run format` runs. |
|
|
33
|
-
| `bun run format` | Auto-fix oxfmt formatting. |
|
|
34
|
-
|
|
35
31
|
## Gotchas
|
|
36
32
|
|
|
37
33
|
- A push fails on formatting drift until `bun run format` runs, since the `pre-push` hook writes nothing
|
|
@@ -7,5 +7,4 @@ subtitle: Per-domain narrative loaded on demand
|
|
|
7
7
|
|
|
8
8
|
Per-domain narrative loaded on demand
|
|
9
9
|
|
|
10
|
-
- [
|
|
11
|
-
- [Development](development.md): Local dev workflow, scripts, and husky hooks
|
|
10
|
+
- [Development](development/index.md): The local loop and CI
|
|
@@ -21,8 +21,4 @@ scaffold = ""
|
|
|
21
21
|
# array no longer tells it to ignore them, so the first sign is a memory file or
|
|
22
22
|
# a task board appearing in someone's commit.
|
|
23
23
|
[gitignore]
|
|
24
|
-
"# Claude" = [
|
|
25
|
-
".canon/",
|
|
26
|
-
".claude/worktrees/",
|
|
27
|
-
".claude/.tmp/", # canon-keep-record-root
|
|
28
|
-
]
|
|
24
|
+
"# Claude" = [".canon/", ".claude/worktrees/"]
|
|
@@ -2,59 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
The claude stack installs the
|
|
5
|
+
The claude stack installs the Claude Code workflow into a project through `canon claude`. Every file it ships is a seed, written once and never overwritten, so a project fills `CLAUDE.md` in after init.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Seeded records
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
- `CLAUDE.md` holds project context and rules, auto-loaded by Claude Code each session
|
|
10
|
+
- `canon/REQUIREMENTS.md` holds the project's problem, goals, and non-goals
|
|
11
|
+
- `canon/DESIGN.md` holds the visual intent and the decisions behind it
|
|
12
|
+
- `canon/context/` holds per-domain narrative, with `index.md` as the discovery anchor
|
|
13
|
+
- `.claude/settings.json` holds project-level Claude Code config. User-level config installs separately through `canon claude setup`.
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
├── REQUIREMENTS.md ← seeded. Project problem, goals, non-goals
|
|
14
|
-
├── DESIGN.md ← seeded. Visual intent and the decisions behind it
|
|
15
|
-
└── context/ ← seeded. Per-domain narrative. `index.md` is the discovery anchor.
|
|
15
|
+
## Record folders
|
|
16
16
|
|
|
17
|
-
.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
- `.canon/tasks/` holds one file per task plus a generated `index.md`, local scratch only, with `archive/` holding the tasks that shipped
|
|
18
|
+
- `.canon/plans/` holds execution detail for multi-step tasks, and `canon tasks archive` moves a `feature-*.md` plan into `archive/` when the `post-merge` hook archives its task
|
|
19
|
+
- `.canon/review/` holds one subfolder per producer, with `design/` for `canon design render`
|
|
20
|
+
- `.canon/tmp/` is ephemeral scratch, safe to delete without loss
|
|
21
|
+
- `.claude/worktrees/` is where `EnterWorktree` creates a linked worktree, which is why it stays under `.claude/`
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
├── tasks/ ← seeded. One file per task plus a generated index.md, local scratch only. `archive/` holds the tasks that shipped.
|
|
24
|
-
├── plans/ ← execution detail for multi-step tasks. `feature-*.md` entries moved into `archive/` by `canon tasks archive` when the `post-merge` hook archives their task.
|
|
25
|
-
├── review/ ← one subfolder per producer. `branch/` no longer written, `design/` for canon design render.
|
|
26
|
-
└── tmp/ ← ephemeral scratch space, safe to delete without loss
|
|
27
|
-
```
|
|
23
|
+
## Ignored roots
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
The `# Claude` group ignores `.canon/` whole, so nothing tracked may land there and a record folder added later needs no new entry.
|
|
26
|
+
|
|
27
|
+
- Keep `.claude/worktrees/` as its own entry, since the harness requires a worktree's target to sit under `.claude/`
|
|
28
|
+
- Expect a project scaffolded before the move to keep its records under `.claude/`, since every command reads either root
|
|
29
|
+
- Expect `canon gov sync` to remove a retired `.claude/GOV.md` left by a prior install
|
|
30
30
|
|
|
31
31
|
## Moving the records to `.canon/`
|
|
32
32
|
|
|
33
33
|
A project installed before the split holds its records under `.claude/`. `canon migrate records` moves them and repoints every tracked file that cites one, reporting the plan until `--write` is passed.
|
|
34
34
|
|
|
35
35
|
- Run `canon tooling sync claude . --write` first. The verb refuses until the project ignores `.canon/`, since a relocated folder under a tracked root commits the task board and plans.
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
- Read the `ok` field out of `--json` rather than the exit code, which a shell wrapper around `canon` can flatten
|
|
36
|
+
- Move soon after the sync, since until then the records sit at a root the shipped ignore set no longer names and a `git add -A` picks them up
|
|
37
|
+
- Run `bun src/cli.ts migrate records --root <project> --write` from a canon checkout while the installed binary answers `unknown command`
|
|
38
|
+
- Read the `ok` field out of `--json` rather than the exit code, which a shell wrapper around `canon` can flatten
|
|
39
39
|
|
|
40
40
|
## Upgrading from a single-file board
|
|
41
41
|
|
|
42
|
-
A project installed before the board became a folder still holds `.claude/TASKS.md`, and nothing migrates it. `canon claude init` seeds an empty `.canon/tasks/index.md` beside it
|
|
43
|
-
|
|
44
|
-
Convert by hand, once per project.
|
|
42
|
+
A project installed before the board became a folder still holds `.claude/TASKS.md`, and nothing migrates it. `canon claude init` seeds an empty `.canon/tasks/index.md` beside it, so every skill reads the empty catalog while the real board sits unreferenced.
|
|
45
43
|
|
|
46
44
|
1. Split each `###` block into `.canon/tasks/vXX.Y-<slug>.md`, adding the `title` and `description` frontmatter the `task-board` skill requires. Compare each new file against its source block.
|
|
47
45
|
2. Run `canon indexes regen --no-stage --root . .canon/tasks/<any-task>.md` to build the catalog.
|
|
48
46
|
3. Delete `.claude/TASKS.md`, and swap its `.gitignore` entry for `.canon/tasks/`.
|
|
49
47
|
|
|
50
|
-
## Gitignore
|
|
51
|
-
|
|
52
|
-
The `# Claude` group holds three entries, being `.canon/`, `.claude/worktrees/`, and `.claude/.tmp/`. <!-- canon-keep-record-root -->
|
|
53
|
-
|
|
54
|
-
- The first entry covers the whole record root, so a record folder added later needs no line here and no sync.
|
|
55
|
-
- The second is the carve-out the harness owns, since it creates a worktree under `.claude/` and requires its target to sit there.
|
|
56
|
-
- The third is the scratch rule's fallback spelling, carried for a project the record move has not reached and retired once none is left.
|
|
57
|
-
|
|
58
48
|
## CLI
|
|
59
49
|
|
|
60
50
|
| Command | What it does |
|
|
@@ -65,37 +55,24 @@ The `# Claude` group holds three entries, being `.canon/`, `.claude/worktrees/`,
|
|
|
65
55
|
|
|
66
56
|
## CLAUDE.md
|
|
67
57
|
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
58
|
+
- Fill in the seed's placeholder project name, description, and key paths after init
|
|
59
|
+
- Keep its sections `Context`, `Commands`, and `Key paths`, since skills detect, extend, or skip by heading
|
|
60
|
+
- Keep the `Context` section to one line pointing at the state docs, since `plan-feature` does the parallel read when invoked
|
|
71
61
|
|
|
72
62
|
## Seed docs
|
|
73
63
|
|
|
74
|
-
|
|
64
|
+
- Read a seed doc's guidelines preamble above the first H2 as instructions for filling the sections below, never as a section to populate
|
|
75
65
|
|
|
76
66
|
## settings.json
|
|
77
67
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
- Three PreToolUse hooks pair with `index-reminder.sh`, `search-reminder.sh`, and `scratch-guard.sh`.
|
|
85
|
-
- A PostToolUse hook pairs with `standards-audit.sh`.
|
|
86
|
-
- Project settings layer over user settings, so a project file carries only what is project-specific.
|
|
87
|
-
|
|
88
|
-
### Session defaults
|
|
89
|
-
|
|
90
|
-
The seed also sets two defaults every session in the target runs under.
|
|
91
|
-
|
|
92
|
-
- `permissions.deny` holds `Agent` and `Task`, so no session spawns a subagent. Parallel work runs as separate background sessions instead.
|
|
93
|
-
- `autoMemoryEnabled` is `false`. A person turns it back on for themselves in `settings.local.json`.
|
|
94
|
-
- `canon migrate session-defaults` adds both to a target seeded before them, since sync never rewrites an existing settings file.
|
|
95
|
-
|
|
96
|
-
### User template
|
|
68
|
+
- Pair the seed's three PreToolUse hooks with `index-reminder.sh`, `search-reminder.sh`, and `scratch-guard.sh`, and its PostToolUse hook with `standards-audit.sh`, all under `.claude/hooks/`
|
|
69
|
+
- Keep only what is project-specific in the project file, since project settings layer over user settings
|
|
70
|
+
- Expect `permissions.deny` to hold `Agent` and `Task`, so no session spawns a subagent and parallel work runs as separate background sessions
|
|
71
|
+
- Expect `autoMemoryEnabled` to be `false`. A person turns it back on for themselves in `settings.local.json`.
|
|
72
|
+
- Run `canon migrate session-defaults` on a target seeded before those two defaults, since sync never rewrites an existing settings file
|
|
97
73
|
|
|
98
|
-
|
|
74
|
+
## User settings
|
|
99
75
|
|
|
100
|
-
-
|
|
101
|
-
-
|
|
76
|
+
- `canon claude setup` installs `tooling/claude/user/settings.template.json` once per machine
|
|
77
|
+
- It allows `Bash(bun run *)` under `permissions.allow`
|
|
78
|
+
- Its `permissions.deny` holds `Read(**/.env)` and `Read(**/.env.*)`, blocking accidental secret exposure across any project
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Reports what `canon tasks validate` finds after an edit to the task board's
|
|
4
|
+
# ordering file, so a hand edit that broke a table or drifted a derived cell is
|
|
5
|
+
# named in the turn that made it.
|
|
6
|
+
#
|
|
7
|
+
# It reports and never blocks. The board verbs do not yet cover every move a
|
|
8
|
+
# controller makes by hand, so a block here would stop work the verbs cannot
|
|
9
|
+
# yet do. A `Bash` write fires no `PostToolUse` hook for a file, so a heredoc
|
|
10
|
+
# write stays unchecked until the next validate run.
|
|
11
|
+
|
|
12
|
+
# Claude Code sends a payload and closes stdin. A bare read with nothing feeding
|
|
13
|
+
# it blocks forever and holds the session open, so the read is bounded. `read`
|
|
14
|
+
# rather than `timeout cat`, which macOS does not ship.
|
|
15
|
+
IFS= read -r -d '' -t 2 input
|
|
16
|
+
[ -n "$input" ] || {
|
|
17
|
+
printf '%s reads a Claude Code hook payload on stdin and cannot be run by hand.\n' "${0##*/}" >&2
|
|
18
|
+
exit 1
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
tool=$(printf '%s' "$input" | jq -r '.tool_name // empty')
|
|
22
|
+
case "$tool" in
|
|
23
|
+
Write | Edit | MultiEdit) ;;
|
|
24
|
+
*) exit 0 ;;
|
|
25
|
+
esac
|
|
26
|
+
|
|
27
|
+
file_path=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty')
|
|
28
|
+
|
|
29
|
+
# Both record roots, since a project the move has reached keeps its records
|
|
30
|
+
# under `.canon/`. The root comes from the path rather than the session, since
|
|
31
|
+
# the board resolves at the main worktree root and a linked worktree's session
|
|
32
|
+
# edits it from outside its own project directory.
|
|
33
|
+
case "$file_path" in
|
|
34
|
+
*/.canon/tasks/priority.md) base=.canon ;;
|
|
35
|
+
*/.claude/tasks/priority.md) base=.claude ;;
|
|
36
|
+
*) exit 0 ;;
|
|
37
|
+
esac
|
|
38
|
+
root="${file_path%/"$base"/tasks/priority.md}"
|
|
39
|
+
[ -n "$root" ] || exit 0
|
|
40
|
+
|
|
41
|
+
report() {
|
|
42
|
+
jq -nc --arg msg "$1" \
|
|
43
|
+
'{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:$msg}}'
|
|
44
|
+
exit 0
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if ! command -v canon >/dev/null 2>&1; then
|
|
48
|
+
report "canon is not on PATH, so the board edit was not validated. Run canon tasks validate by hand."
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Exit 2 carries findings, so the status is read off the record rather than
|
|
52
|
+
# the exit code.
|
|
53
|
+
record=$(canon tasks validate --json --root "$root" 2>/dev/null)
|
|
54
|
+
ok=$(printf '%s' "$record" | jq -r '.ok // empty' 2>/dev/null)
|
|
55
|
+
|
|
56
|
+
if [ "$ok" = false ]; then
|
|
57
|
+
report "canon tasks validate refused the board after this edit: $(printf '%s' "$record" | jq -r '.message // .reason')"
|
|
58
|
+
fi
|
|
59
|
+
[ "$ok" = true ] || report "canon tasks validate wrote no record after this edit, so the board was not validated."
|
|
60
|
+
|
|
61
|
+
findings=$(printf '%s' "$record" | jq -r '.findings[] | "- \(.kind): \(.subject) \(.message)"' | head -10)
|
|
62
|
+
[ -n "$findings" ] || exit 0
|
|
63
|
+
|
|
64
|
+
report "The board edit left findings. Rewrite the rows through canon tasks place, promote, move, or block, which derive every cell but Waiting on.
|
|
65
|
+
$findings"
|
|
@@ -40,14 +40,9 @@ done
|
|
|
40
40
|
|
|
41
41
|
session=$(printf '%s' "$input" | jq -r '.session_id // "none"')
|
|
42
42
|
key=$(printf '%s__%s' "$session" "$index" | tr -c 'A-Za-z0-9' '_')
|
|
43
|
-
# The marker is scratch, so it
|
|
44
|
-
# root the project carries rather than creating a second one beside it.
|
|
43
|
+
# The marker is scratch, so it lives under `.canon/tmp/`.
|
|
45
44
|
project="${CLAUDE_PROJECT_DIR:-.}"
|
|
46
|
-
|
|
47
|
-
marker_dir="$project/.canon/tmp/hooks/index-reminder"
|
|
48
|
-
else
|
|
49
|
-
marker_dir="$project/.claude/.tmp/hooks/index-reminder"
|
|
50
|
-
fi
|
|
45
|
+
marker_dir="$project/.canon/tmp/hooks/index-reminder"
|
|
51
46
|
marker="$marker_dir/$key"
|
|
52
47
|
[ -f "$marker" ] && exit 0
|
|
53
48
|
mkdir -p "$marker_dir"
|
|
@@ -46,13 +46,8 @@ root="${CLAUDE_PROJECT_DIR:-.}"
|
|
|
46
46
|
case "$root" in
|
|
47
47
|
*/.claude/worktrees/*) root="${root%/.claude/worktrees/*}" ;;
|
|
48
48
|
esac
|
|
49
|
-
# The log is scratch, so it
|
|
50
|
-
|
|
51
|
-
if [ -d "$root/.canon" ]; then
|
|
52
|
-
log_dir="$root/.canon/tmp/pr/log"
|
|
53
|
-
else
|
|
54
|
-
log_dir="$root/.claude/.tmp/pr/log"
|
|
55
|
-
fi
|
|
49
|
+
# The log is scratch, so it lives under `.canon/tmp/`.
|
|
50
|
+
log_dir="$root/.canon/tmp/pr/log"
|
|
56
51
|
mkdir -p "$log_dir"
|
|
57
52
|
session=$(printf '%s' "$input" | jq -r '.session_id // "unknown"')
|
|
58
53
|
printf -- '- %s session=%s pr=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$session" "$url" >>"$log_dir/log.md"
|
|
@@ -18,12 +18,9 @@ esac
|
|
|
18
18
|
file_path=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty')
|
|
19
19
|
[ -n "$file_path" ] || exit 0
|
|
20
20
|
|
|
21
|
-
#
|
|
22
|
-
# one, since inside a dotted root the dot hides nothing already hidden. A guard
|
|
23
|
-
# fixed at the old spelling warns on every correct scratch write in a project
|
|
24
|
-
# the move has reached.
|
|
21
|
+
# The scratch folder is `.canon/tmp/`, so a write there is the correct one.
|
|
25
22
|
case "$file_path" in
|
|
26
|
-
*/.
|
|
23
|
+
*/.canon/tmp/*) exit 0 ;;
|
|
27
24
|
esac
|
|
28
25
|
|
|
29
26
|
# A background session's temp folder is the harness's own, told to the session
|
|
@@ -55,11 +52,7 @@ esac
|
|
|
55
52
|
session=$(printf '%s' "$input" | jq -r '.session_id // "none"')
|
|
56
53
|
key=$(printf '%s' "$session" | tr -c 'A-Za-z0-9' '_')
|
|
57
54
|
project="${CLAUDE_PROJECT_DIR:-.}"
|
|
58
|
-
|
|
59
|
-
marker_dir="$project/.canon/tmp/hooks/scratch-guard"
|
|
60
|
-
else
|
|
61
|
-
marker_dir="$project/.claude/.tmp/hooks/scratch-guard"
|
|
62
|
-
fi
|
|
55
|
+
marker_dir="$project/.canon/tmp/hooks/scratch-guard"
|
|
63
56
|
marker="$marker_dir/$key"
|
|
64
57
|
[ -f "$marker" ] && exit 0
|
|
65
58
|
mkdir -p "$marker_dir"
|