@erclx/canon 5.39.0 → 5.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +39 -22
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
  4. package/claude/skills/auto-ship/SKILL.md +6 -0
  5. package/claude/skills/auto-ship/references/fan-in.md +59 -0
  6. package/claude/skills/canvas-board/SKILL.md +1 -1
  7. package/claude/skills/create-skill/references/skill.md +1 -1
  8. package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
  9. package/claude/skills/decision-escalate/SKILL.md +1 -2
  10. package/claude/skills/draft-slides/SKILL.md +2 -3
  11. package/claude/skills/draft-slides/references/deck.md +183 -0
  12. package/claude/skills/git-branch/REQUIREMENT.md +17 -6
  13. package/claude/skills/git-branch/SKILL.md +63 -7
  14. package/claude/skills/git-pr/references/evidence.md +2 -2
  15. package/claude/skills/git-pr/references/final-command.md +1 -1
  16. package/claude/skills/git-pr/references/labels.md +1 -1
  17. package/claude/skills/git-pr/references/publish.md +1 -1
  18. package/claude/skills/plan-feature/references/batches.md +48 -1
  19. package/claude/skills/plan-feature/references/plan.md +2 -0
  20. package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
  21. package/claude/skills/plan-intake-answer/SKILL.md +1 -2
  22. package/claude/skills/review-address/references/rebase-conflicts.md +7 -0
  23. package/claude/skills/review-ui/SKILL.md +3 -0
  24. package/claude/skills/review-ui/references/close-out.md +47 -0
  25. package/claude/skills/review-ui/references/post.md +3 -1
  26. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -0
  27. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
  28. package/claude/skills/role-worker/REQUIREMENT.md +1 -0
  29. package/claude/skills/role-worker/SKILL.md +9 -0
  30. package/claude/skills/task-board/references/board.md +1 -1
  31. package/claude/skills/task-board/references/tasks.md +1 -1
  32. package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
  33. package/claude/skills/teach-workspace/SKILL.md +1 -2
  34. package/claude/skills/ux-measure/SKILL.md +1 -1
  35. package/docs/agents/audits.md +61 -60
  36. package/docs/agents/context-audit.md +40 -58
  37. package/docs/agents/index.md +5 -51
  38. package/docs/agents/install-and-sync.md +1 -0
  39. package/docs/agents/markdown-audit.md +53 -99
  40. package/docs/agents/records.md +37 -79
  41. package/docs/agents/sessions-transfer.md +26 -70
  42. package/docs/target/index.md +0 -1
  43. package/docs/target/migrations.md +57 -66
  44. package/docs/target/projects.md +36 -78
  45. package/docs/target/scenarios.md +9 -19
  46. package/docs/target/sync.md +27 -68
  47. package/docs/workflow/ai-workflow.md +22 -68
  48. package/docs/workflow/operating-model.md +32 -186
  49. package/docs/workflow/parallel-features.md +30 -66
  50. package/docs/workflow/skill-map-on-request.md +22 -24
  51. package/docs/workflow/skill-map.md +72 -76
  52. package/docs/workflow/visual-design-workflow.md +25 -118
  53. package/docs/workflow/zshrc-aliases.md +18 -37
  54. package/package.json +1 -1
  55. package/src/canvas/client/inspector/fields.css +4 -0
  56. package/src/canvas/index.html +6 -0
  57. package/src/claude/cases/git.ts +4 -0
  58. package/src/claude/skills-reach.ts +2 -2
  59. package/src/cli/ui.ts +3 -1
  60. package/src/commands/canvas.ts +23 -0
  61. package/src/commands/capture.ts +14 -0
  62. package/src/commands/claude.ts +13 -0
  63. package/src/commands/demo.ts +22 -0
  64. package/src/commands/deps.ts +6 -0
  65. package/src/commands/driver.ts +28 -2
  66. package/src/commands/gov.ts +52 -18
  67. package/src/commands/intake.ts +17 -0
  68. package/src/commands/labels.ts +38 -5
  69. package/src/commands/pr.ts +192 -52
  70. package/src/commands/records.ts +19 -3
  71. package/src/commands/secrets.ts +6 -0
  72. package/src/commands/sessions.ts +8 -0
  73. package/src/commands/tasks.ts +52 -0
  74. package/src/commands/teach.ts +13 -0
  75. package/src/commands/upstream.ts +25 -1
  76. package/src/commands/worktrees.ts +5 -0
  77. package/src/context/audit.ts +22 -4
  78. package/src/context/folders.ts +1 -1
  79. package/src/context/shape-gated.ts +5 -2
  80. package/src/counts/scan.ts +1 -1
  81. package/src/design/components.ts +1 -1
  82. package/src/design/document.ts +1 -1
  83. package/src/design/tokens.ts +1 -1
  84. package/src/gate/boundaries.ts +4 -1
  85. package/src/gate/measures.ts +1 -1
  86. package/src/gate/stages.ts +7 -7
  87. package/src/git/env.ts +6 -0
  88. package/src/markdown/scope.ts +11 -19
  89. package/src/pr/frames.ts +53 -9
  90. package/src/pr/lift.ts +153 -19
  91. package/src/pr/painted.ts +87 -0
  92. package/src/records/validate.ts +1 -0
  93. package/src/roots/record.ts +18 -1
  94. package/src/sandbox/census.ts +1 -1
  95. package/src/sandbox/headless/run.ts +3 -2
  96. package/src/sandbox/headless/watch.ts +3 -4
  97. package/src/slides/convert/master.ts +3 -0
  98. package/src/sync/format.ts +46 -0
  99. package/src/sync/stamp.ts +6 -0
  100. package/src/tasks/answers.ts +79 -2
  101. package/src/tasks/fan-in.ts +176 -0
  102. package/src/tasks/stack.ts +1 -1
  103. package/tooling/astro/reference.md +80 -39
  104. package/tooling/base/reference.md +63 -32
  105. package/tooling/base/seeds/canon/context/ci.md +7 -15
  106. package/tooling/base/seeds/canon/context/development.md +14 -14
  107. package/tooling/claude/reference.md +40 -22
  108. package/tooling/cloudflare/reference.md +31 -12
  109. package/tooling/go/reference.md +47 -23
  110. package/tooling/nextjs/reference.md +52 -19
  111. package/tooling/php/reference.md +35 -16
  112. package/tooling/python/reference.md +47 -30
  113. package/tooling/shell/reference.md +6 -5
  114. package/tooling/vite-react/reference.md +49 -21
  115. package/tooling/web/reference.md +131 -71
  116. package/docs/agents/canvas.md +0 -86
  117. package/docs/agents/capture.md +0 -65
  118. package/docs/agents/census.md +0 -23
  119. package/docs/agents/comments.md +0 -34
  120. package/docs/agents/context-audit-checks.md +0 -108
  121. package/docs/agents/context-classify.md +0 -101
  122. package/docs/agents/counts.md +0 -74
  123. package/docs/agents/demo.md +0 -140
  124. package/docs/agents/driver.md +0 -103
  125. package/docs/agents/gate.md +0 -90
  126. package/docs/agents/hooks.md +0 -78
  127. package/docs/agents/intake.md +0 -81
  128. package/docs/agents/key-changes.md +0 -116
  129. package/docs/agents/label-coverage.md +0 -86
  130. package/docs/agents/markdown-audit-density.md +0 -39
  131. package/docs/agents/markdown-audit-weight.md +0 -83
  132. package/docs/agents/pr-evidence-layout.md +0 -70
  133. package/docs/agents/pr-evidence.md +0 -273
  134. package/docs/agents/pr-frames.md +0 -131
  135. package/docs/agents/pr-lift.md +0 -132
  136. package/docs/agents/pr-local.md +0 -111
  137. package/docs/agents/pr-preview.md +0 -155
  138. package/docs/agents/pr-reads.md +0 -111
  139. package/docs/agents/pr-tick.md +0 -72
  140. package/docs/agents/records-push.md +0 -61
  141. package/docs/agents/records-stale.md +0 -40
  142. package/docs/agents/records-validate.md +0 -57
  143. package/docs/agents/restated.md +0 -83
  144. package/docs/agents/routing.md +0 -61
  145. package/docs/agents/rule-citations.md +0 -90
  146. package/docs/agents/sandbox.md +0 -126
  147. package/docs/agents/sessions.md +0 -128
  148. package/docs/agents/skills-audit.md +0 -120
  149. package/docs/agents/skills-reach.md +0 -64
  150. package/docs/agents/slides.md +0 -176
  151. package/docs/agents/state-scoped-risk.md +0 -105
  152. package/docs/agents/superseded.md +0 -85
  153. package/docs/agents/tasks-archive.md +0 -83
  154. package/docs/agents/tasks-plans.md +0 -152
  155. package/docs/agents/tasks-validate.md +0 -133
  156. package/docs/agents/tasks.md +0 -123
  157. package/docs/agents/teach-authoring.md +0 -118
  158. package/docs/agents/teach.md +0 -157
  159. package/docs/agents/test-order.md +0 -58
  160. package/docs/agents/upstream.md +0 -94
  161. package/docs/agents/worktrees.md +0 -118
  162. package/docs/target/standards-moved.md +0 -34
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- The claude stack installs the `.claude/` workflow directory into a project. State docs are seeds, written once and never overwritten by tooling. `CLAUDE.md` is a seed, filled in per project after init.
5
+ The claude stack installs the `.claude/` workflow directory into a project. Tooling writes state docs once and never overwrites them. `CLAUDE.md` is one of those seeds, and a project fills it in after init.
6
6
 
7
7
  ## Structure
8
8
 
@@ -26,52 +26,70 @@ canon/
26
26
  └── tmp/ ← ephemeral scratch space, safe to delete without loss
27
27
  ```
28
28
 
29
- One ignore entry covers `.canon/` whole, so nothing under it is named separately and nothing tracked may ever land there. A project scaffolded before the move keeps its records under `.claude/`, and every command reads either root.
29
+ One ignore entry covers `.canon/` whole, so nothing under it is named separately and nothing tracked may land there. A project scaffolded before the move keeps its records under `.claude/`, and every command reads either root.
30
30
 
31
31
  ## Moving the records to `.canon/`
32
32
 
33
- A project installed before the split still 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.
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
- Run `canon tooling sync claude . --write` first. The verb refuses while the project does not ignore `.canon/`, because every folder it relocates is ignored where it stands and landing one under a tracked root commits the task board and the plans. Until the move runs, this project's records sit at a root the shipped ignore set no longer names, so a `git add -A` in that window picks them up.
36
-
37
- The verb ships with the change that split the roots and is not in a published release yet, so an installed binary answers `unknown command` until one carries it. Run it out of a canon checkout in the meantime, as `bun src/cli.ts migrate records --root <project> --write`, and read the `ok` field out of `--json` rather than the exit code, which a shell wrapper around `canon` can flatten.
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
+ - Until the move runs, the records sit at a root the shipped ignore set no longer names, so a `git add -A` in that window picks them up.
37
+ - An installed binary answers `unknown command` until a published release carries the verb. Meanwhile run `bun src/cli.ts migrate records --root <project> --write` from a canon checkout.
38
+ - Read the `ok` field out of `--json` rather than the exit code, which a shell wrapper around `canon` can flatten.
38
39
 
39
40
  ## Upgrading from a single-file board
40
41
 
41
- A project installed before the board became a folder still holds `.claude/TASKS.md`. Nothing migrates it. `canon claude init` seeds an empty `.canon/tasks/index.md` beside it and leaves the old file untouched, so every skill reads the empty catalog while the real board sits unreferenced.
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 and leaves the old file untouched, so every skill reads the empty catalog while the real board sits unreferenced.
42
43
 
43
- Convert by hand, once per project:
44
+ Convert by hand, once per project.
44
45
 
45
- 1. Split each `###` block into `.canon/tasks/vXX.Y-<slug>.md`, adding the `title` and `description` frontmatter the tasks reference in the `task-board` skill requires. Compare each new file against the block it came from rather than checking that every block landed somewhere.
46
+ 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.
46
47
  2. Run `canon indexes regen --no-stage --root . .canon/tasks/<any-task>.md` to build the catalog.
47
48
  3. Delete `.claude/TASKS.md`, and swap its `.gitignore` entry for `.canon/tasks/`.
48
49
 
49
50
  ## Gitignore
50
51
 
51
- - `# Claude`: `.canon/`, `.claude/worktrees/`, `.claude/.tmp/` <!-- canon-keep-record-root -->
52
+ The `# Claude` group holds three entries, being `.canon/`, `.claude/worktrees/`, and `.claude/.tmp/`. <!-- canon-keep-record-root -->
52
53
 
53
- The first entry covers the whole record root, so a record folder added later needs no line here and no sync. The second is the carve-out the harness owns, since it creates a worktree under `.claude/` and requires its target to sit there. 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 to reach.
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.
54
57
 
55
58
  ## CLI
56
59
 
57
- | Command | What it does |
58
- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
59
- | `canon claude init` | Seeds `.claude/` workflow docs, updates `.gitignore` |
60
- | `canon claude sync` | Reconciles `.gitignore` against the manifest. Reports seeded file status. |
61
- | `canon claude setup` | Installs user-level config to `~/.claude/`: statusline, attribution, and the `permissions.allow` and `permissions.deny` lists. Run once per machine. |
60
+ | Command | What it does |
61
+ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
62
+ | `canon claude init` | Seeds `.claude/` workflow docs, updates `.gitignore` |
63
+ | `canon claude sync` | Reconciles `.gitignore` against the manifest. Reports seeded file status. |
64
+ | `canon claude setup` | Installs user-level config to `~/.claude/` once per machine: statusline, attribution, and the permissions allow and deny lists. |
62
65
 
63
66
  ## CLAUDE.md
64
67
 
65
- - Ships as a seed with placeholder project name, description, and key paths. Fill these in after init.
66
- - Section layout: `Context`, `Commands`, `Key paths`. Each section groups rules that share a surface so skills can detect, extend, or skip by heading.
67
- - The `Context` section points at `.claude/` state docs one line. `plan-feature` does the parallel read when invoked. The seed intentionally does not re-list every file.
68
+ - It ships as a seed with placeholder project name, description, and key paths, to fill in after init.
69
+ - Its sections are `Context`, `Commands`, and `Key paths`. Each groups rules that share a surface, so skills can detect, extend, or skip by heading.
70
+ - The `Context` section points at `.claude/` state docs in one line. `plan-feature` does the parallel read when invoked, so the seed does not re-list every file.
68
71
 
69
72
  ## Seed docs
70
73
 
71
- - Seed docs ship with a guidelines preamble above the first H2. These are instructions Claude internalizes before filling in the sections below. They are not sections to populate.
74
+ Seed docs ship with a guidelines preamble above the first H2. Claude internalizes those instructions before filling in the sections below, and they are not sections to populate.
72
75
 
73
76
  ## settings.json
74
77
 
75
- - Project seed at `tooling/claude/seeds/.claude/settings.json` ships three PreToolUse hooks (pairing with `.claude/hooks/index-reminder.sh`, `.claude/hooks/search-reminder.sh`, and `.claude/hooks/scratch-guard.sh`) and a PostToolUse hook (pairs with `.claude/hooks/standards-audit.sh`). Project settings layer over user settings, so per-project files only carry what is genuinely project-specific.
76
- - User-level template at `tooling/claude/user/settings.template.json` carries `attribution.commit` and `attribution.pr` (empty strings to suppress co-author tags), `permissions.allow` with `Bash(bun run *)`, and `permissions.deny` with `Read(**/.env)` and `Read(**/.env.*)` to block accidental secret exposure across any project. Installed once per machine via `canon claude setup`.
78
+ The project and user files split what is project-specific from what is personal.
79
+
80
+ ### Project seed
81
+
82
+ The seed at `tooling/claude/seeds/.claude/settings.json` ships hooks that pair with scripts under `.claude/hooks/`.
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
+ ### User template
89
+
90
+ The template at `tooling/claude/user/settings.template.json` is installed once per machine by `canon claude setup`.
91
+
92
+ - It sets `attribution.commit` and `attribution.pr` to empty strings to suppress co-author tags.
93
+ - It allows `Bash(bun run *)` under `permissions.allow`.
94
+ - Its `permissions.deny` holds `Read(**/.env)` and `Read(**/.env.*)`, blocking accidental secret exposure across any project.
77
95
  - The deprecated `includeCoAuthoredBy` key is not used.
@@ -2,25 +2,44 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- The cloudflare stack ships one parameterized GitHub Actions workflow that deploys a static build to Cloudflare Pages. It is keyed on host rather than on framework and carries no dependencies, seeds, or scripts of its own, so it syncs onto a project already carrying `astro`, `vite-react`, or any other stack at the same root without colliding.
5
+ The cloudflare stack ships one parameterized GitHub Actions workflow that deploys a static build to Cloudflare Pages. It is keyed on host rather than on framework and carries no dependencies, seeds, or scripts of its own, so it syncs onto a project already carrying `astro`, `vite-react`, or any other stack without colliding.
6
6
 
7
7
  ## Deploy workflow
8
8
 
9
- - Separate `build` and `deploy` jobs, connected by an uploaded artifact, converged on the shape already running in the closest prior art rather than invented fresh.
10
- - The deploy job checks for `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` before doing anything else, and skips with a `::notice::` rather than failing when either is absent. A first merge lands ahead of the Pages project existing, and this guard is what keeps that push green.
11
- - `--branch=${{ github.ref_name }}` on every deploy, which gives every branch a Cloudflare preview URL rather than only `main`. A `workflow_dispatch` run from a branch therefore lands on a preview and never on production, which is what makes dispatching the workflow from an unmerged branch safe.
12
- - The Publish step carries `id: publish` and pins `wranglerVersion`, since the `pages-deployment-alias-url` output it reads first appears in wrangler 3.78. A step after it prints that alias to the job log as `canon-preview-alias: <url>` and to the step summary. `canon pr preview` reads the line out of the run log, so the marker text is a contract with the CLI rather than decoration.
13
- - The concurrency group carries `${{ github.ref_name }}`. GitHub cancels a pending run when a newer one queues in the same group, so a shared group would let a branch preview displace a production deploy waiting its turn.
14
- - A `pull_request` trigger on `closed` runs only the `cleanup-preview` job, which deletes every preview deployment Cloudflare holds for that branch. It lists them through the Pages REST API, because the wrangler listing has no branch filter, and deletes each with `force=true` so a deployment still holding the branch alias goes too. It skips with a notice when the secrets are absent, the same as the deploy job.
15
- - The deploy job checks out the repository and installs Bun even though it builds nothing. `wrangler-action` resolves its package manager from `bun.lock` in the checkout, and dropping that step breaks the deploy on every push once the toolchain it relies on silently goes missing.
9
+ ### Job shape
10
+
11
+ Separate `build` and `deploy` jobs connect through an uploaded artifact, converged on the shape already running in the closest prior art rather than invented fresh.
12
+
13
+ - The deploy job checks for `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` first, and skips with a `::notice::` when either is absent, so a first merge ahead of the Pages project stays green.
14
+ - The deploy job checks out the repository and installs Bun though it builds nothing, since `wrangler-action` resolves its package manager from `bun.lock` in the checkout.
15
+ - The concurrency group carries `${{ github.ref_name }}`. GitHub cancels a pending run when a newer one queues in its group, so a shared group lets a preview displace a production deploy.
16
+
17
+ ### Previews
18
+
19
+ Every branch gets a preview URL rather than only `main`.
20
+
21
+ - `--branch=${{ github.ref_name }}` on every deploy gives each branch a Cloudflare preview URL.
22
+ - A `workflow_dispatch` run from an unmerged branch therefore lands on a preview and never on production, which makes dispatching it safe.
23
+ - The Publish step carries `id: publish` and pins `wranglerVersion`, since the `pages-deployment-alias-url` output it reads first appears in wrangler 3.78.
24
+ - A step after Publish prints the alias to the job log as `canon-preview-alias: <url>` and to the step summary.
25
+ - `canon pr preview` reads that line out of the run log, so the marker text is a contract with the CLI.
26
+
27
+ ### Cleanup
28
+
29
+ A `pull_request` trigger on `closed` runs only the `cleanup-preview` job, which deletes every preview deployment Cloudflare holds for that branch.
30
+
31
+ - It lists them through the Pages REST API, because the wrangler listing has no branch filter.
32
+ - It deletes each with `force=true`, so a deployment still holding the branch alias goes too.
33
+ - It skips with a notice when the secrets are absent, the same as the deploy job.
16
34
 
17
35
  ## Parameters a project fills in by hand
18
36
 
19
- Three values in the seeded file have no generic default and are set once, right after sync:
37
+ Three values in the seeded file have no generic default, and a project sets them once right after sync.
20
38
 
21
- - The `paths:` filter under the `push` trigger. The shipped default is a single `src/**` glob. Widen it to cover every path the build reads when the build draws on catalogs or generated content living outside `src/`.
22
- - The `concurrency.group` name. The shipped default is `deploy-${{ github.ref_name }}`, which is enough for a single-deploy project. Give the `deploy` prefix a project-specific name only if the project runs more than one deploy workflow, and keep the ref suffix.
23
- - The Pages project name, written twice as `REPLACE_WITH_PROJECT_NAME`: once in the `--project-name` flag on the `wrangler pages deploy` command and once as `PROJECT_NAME` in the cleanup job. Set both to the name the `deploy-app` skill creates the Pages project under.
39
+ - The `paths:` filter under the `push` trigger ships as a single `src/**` glob. Widen it to every path the build reads when the build draws on catalogs or generated content outside `src/`.
40
+ - The `concurrency.group` name ships as `deploy-${{ github.ref_name }}`, enough for one deploy. Rename the `deploy` prefix only for a second deploy workflow, and keep the ref suffix.
41
+ - The Pages project name appears twice as `REPLACE_WITH_PROJECT_NAME`, in the `--project-name` flag and as `PROJECT_NAME` in the cleanup job.
42
+ - Set both to the name the `deploy-app` skill creates the project under.
24
43
 
25
44
  ## Re-sync
26
45
 
@@ -9,30 +9,48 @@ The go stack covers Go modules linted and formatted by `golangci-lint` v2, type-
9
9
  ## Scaffold checklist
10
10
 
11
11
  1. Scaffold with `go mod init <module-path>`. The module path is the project's own identity, so this stack never ships a `go.mod`.
12
- 2. Keep `./...` out of `node_modules`: `go mod edit -ignore=./node_modules`. At a root sync the base layer installs `node_modules/` inside the module, and a package there can ship a `.go` file that `go vet`, `go test`, and `golangci-lint` would otherwise walk. The directive needs Go 1.25 or later.
13
- 3. Seed `package.json` so the base layer's bun-side tools (husky, oxfmt, commitlint) have a target to install into: `bun init -y`.
14
- 4. Install base tooling: `canon tooling sync base . --write`
15
- 5. Install go tooling: `canon tooling sync go . --write`
16
- 6. Install the linter: `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest`. The manifest declares no `[dependencies.dev]`, because manifest injection runs `bun add -D`, which cannot install a Go tool. This step stays manual.
12
+ 2. Keep `./...` out of `node_modules` with `go mod edit -ignore=./node_modules` (Go 1.25 or later). A root sync installs `node_modules/` in the module, and a package there can ship `.go` files.
13
+ 3. Seed `package.json` with `bun init -y`, so the base layer's bun-side tools (husky, oxfmt, commitlint) have a target to install into.
14
+ 4. Install base tooling with `canon tooling sync base . --write`.
15
+ 5. Install go tooling with `canon tooling sync go . --write`.
16
+ 6. Install the linter with `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest`. This stays manual, since manifest injection runs `bun add -D`, which cannot install a Go tool.
17
17
  7. Run `bun run lint:fix`, then `bun run format`, then `bun run check`.
18
18
 
19
- The go stack also ships `smoke_test.go` at the module root as a copy-once seed. A module with no `.go` file fails `golangci-lint run`, `go vet`, and `go test` on an empty package list, so the seed is what lets `bun run check` pass on a fresh scaffold. It declares `package main`, so delete or rename it once the root package takes another name, or the build reports two packages in one folder.
19
+ ### The smoke test seed
20
+
21
+ The stack ships `smoke_test.go` at the module root as a copy-once seed. A module with no `.go` file fails the linter, `go vet`, and `go test` on an empty package list, so the seed lets `bun run check` pass on a fresh scaffold.
22
+
23
+ It declares `package main`. Delete or rename it once the root package takes another name, or the build reports two packages in one folder.
20
24
 
21
25
  ## golangci-lint
22
26
 
23
- - Use config `version: '2'`.
24
- - Start from `default: standard`.
25
- - Enable `bodyclose`, `errorlint`, `gosec`, `misspell`, `noctx`, `revive`, `sqlclosecheck`, `rowserrcheck`, and `unconvert`. The two SQL linters report only on `database/sql` call sites, so they cost nothing in a module without a database.
26
- - Enable formatters `gofmt` and `goimports`. `lint:fix` runs `golangci-lint fmt` ahead of `golangci-lint run --fix`.
27
- - Exclude `node_modules` from both linters and formatters. The `go.mod` `ignore` directive in the checklist covers every Go tool, but `go.mod` belongs to the project and sync cannot write it, so the config keeps `golangci-lint run` from reporting and `golangci-lint fmt` from rewriting a vendored `.go` file in a module that never added the directive.
27
+ The config uses `version: '2'` and starts from `default: standard`.
28
+
29
+ - Enable `bodyclose`, `errorlint`, `gosec`, `misspell`, `noctx`, `revive`, `sqlclosecheck`, `rowserrcheck`, and `unconvert`.
30
+ - The two SQL linters report only on `database/sql` call sites, so they cost nothing without a database.
31
+ - Enable formatters `gofmt` and `goimports`, with `lint:fix` running `golangci-lint fmt` ahead of `golangci-lint run --fix`.
32
+ - Exclude `node_modules` from both linters and formatters. The `go.mod` `ignore` directive covers every Go tool, but `go.mod` belongs to the project and sync cannot write it.
33
+ - That exclusion keeps `golangci-lint run` from reporting, and `golangci-lint fmt` from rewriting, a vendored `.go` file in a module that never added the directive.
28
34
 
29
35
  ## verify.json
30
36
 
31
- - Require Typecheck (`go vet`), Lint (`golangci-lint run`), and Unit tests (`go test -v`). Base's runner adds the four base phases wherever the folder declares their scripts, then the markdown bans stage.
37
+ ### Phases
38
+
39
+ The go phase list requires three phases and probes the tools first.
40
+
41
+ - Require Typecheck (`go vet`), Lint (`golangci-lint run`), and Unit tests (`go test -v`).
42
+ - Base's runner adds the four base phases where the folder declares their scripts, then the markdown bans stage.
32
43
  - Probe `go` and `golangci-lint` first, and name the `go install` command when `golangci-lint` is missing.
33
- - Keep `build` out of `verify.json`, even though the stack declares the script. `go build ./...` exits 1 with `no packages to build` on a module holding only a test file, and a seeded `main.go` would assume every module is a binary rather than a library.
34
- - Keep `-race` out of `test:run`. The race detector needs cgo and a C toolchain, so it belongs in CI rather than in a local `bun run check`.
35
- - The manifest's `[verify] prepare` runs checklist steps 3 and 6 for `canon tooling verify go`: `bun init -y`, then the `golangci-lint` `go install` when the binary is not already on PATH. The scaffold writes no `package.json`, and a verify that reaches its end with none fails.
44
+
45
+ ### Left out and prepared
46
+
47
+ Two things stay out of the list, and one step runs before it.
48
+
49
+ - Keep `build` out, though the stack declares the script. `go build ./...` exits 1 with `no packages to build` on a module holding only a test file.
50
+ - A seeded `main.go` would assume every module is a binary rather than a library.
51
+ - Keep `-race` out of `test:run`. The race detector needs cgo and a C toolchain, so it belongs in CI rather than a local `bun run check`.
52
+ - The manifest's `[verify] prepare` runs checklist steps 3 and 6 for `canon tooling verify go`, being `bun init -y` and the `go install` when the binary is not on PATH.
53
+ - The scaffold writes no `package.json`, and a verify that reaches its end with none fails.
36
54
 
37
55
  ## CI
38
56
 
@@ -48,15 +66,21 @@ The go stack also ships `smoke_test.go` at the module root as a copy-once seed.
48
66
 
49
67
  ## Development docs (extend)
50
68
 
51
- Append to the `## Scripts` table:
69
+ Append these rows to the `## Scripts` table.
52
70
 
53
- | `bun run lint` | Run `golangci-lint run`. |
54
- | `bun run lint:fix` | Format with `golangci-lint fmt`, then auto-fix with `golangci-lint run --fix`. |
55
- | `bun run typecheck` | Run `go vet`. |
56
- | `bun run test` | Run `go test`. |
57
- | `bun run test:run` | Run `go test -v` (used by `verify`). |
58
- | `bun run build` | Run `go build`. |
71
+ | Command | Purpose |
72
+ | ------------------- | ------------------------------------------------------------------------------ |
73
+ | `bun run lint` | Run `golangci-lint run`. |
74
+ | `bun run lint:fix` | Format with `golangci-lint fmt`, then auto-fix with `golangci-lint run --fix`. |
75
+ | `bun run typecheck` | Run `go vet`. |
76
+ | `bun run test` | Run `go test`. |
77
+ | `bun run test:run` | Run `go test -v` (used by `verify`). |
78
+ | `bun run build` | Run `go build`. |
59
79
 
60
80
  ## CI docs (extend)
61
81
 
62
- In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `go vet ./...` passes ``. Add a Lint row whose assertion reads: `` `golangci-lint run` passes ``. Add a Tests row whose assertion reads: `` `go test -race ./...` exits 0 ``. <!-- audit-ignore-citations: canon/context/ci.md -->
82
+ In `canon/context/development/ci.md`, add or change three rows. <!-- audit-ignore-citations: canon/context/development/ci.md -->
83
+
84
+ - The Typecheck row asserts `` `go vet ./...` passes ``.
85
+ - A Lint row asserts `` `golangci-lint run` passes ``.
86
+ - A Tests row asserts `` `go test -race ./...` exits 0 ``.
@@ -4,50 +4,83 @@
4
4
 
5
5
  ## Overview
6
6
 
7
- The nextjs stack covers Next.js + TypeScript projects using the App Router. It ships golden configs for `next.config.ts`, `vitest.config.ts` (plain `defineConfig`, no bundler merge), `playwright.config.ts` (build-then-preview), and an `.oxlintrc.json` that replaces the web layer's with `.next` ignores and an App Router override. Shared web tooling (oxlint base, screenshot template, VS Code, CI, verify script) comes from the `web` layer.
7
+ The nextjs stack covers Next.js + TypeScript projects using the App Router. It ships golden configs for `next.config.ts`, `vitest.config.ts`, `playwright.config.ts` (build-then-preview), and an `.oxlintrc.json` that replaces the web layer's.
8
+
9
+ Shared web tooling (oxlint base, screenshot template, VS Code, CI, verify script) comes from the `web` layer.
8
10
 
9
11
  ## Scaffold checklist
10
12
 
11
- 1. Scaffold with `bunx create-next-app@latest <name> --typescript --tailwind --no-linter --app --src-dir --import-alias "@/*" --use-bun --skip-install --disable-git --no-agents-md --yes`. `--no-agents-md` skips the scaffold-time `AGENTS.md`/`CLAUDE.md` write, which otherwise duplicates the root `CLAUDE.md`. `--no-linter` skips the scaffold's `eslint.config.mjs` and `lint` script, and is accepted though `--help` lists only `--eslint` and `--biome`.
12
- 2. Install web tooling: `canon tooling sync web . --write`
13
- 3. Install nextjs configs: `canon tooling sync nextjs . --write`
13
+ 1. Scaffold with `bunx create-next-app@latest <name> --typescript --tailwind --no-linter --app --src-dir --import-alias "@/*" --use-bun --skip-install --disable-git --no-agents-md --yes`.
14
+ 2. Install web tooling with `canon tooling sync web . --write`.
15
+ 3. Install nextjs configs with `canon tooling sync nextjs . --write`.
14
16
  4. Extend the `ci` and `development` context entries under `canon/context/` per the web reference's extend sections plus the nextjs rows below.
15
17
  5. Run `bun run lint:fix`, then `bun run format`, then `bun run check`.
16
18
 
19
+ ### Scaffold flags
20
+
21
+ Two flags in step 1 keep the scaffold from writing what the toolkit already owns.
22
+
23
+ - `--no-agents-md` skips the scaffold-time `AGENTS.md` and `CLAUDE.md` write, which would duplicate the root `CLAUDE.md`.
24
+ - `--no-linter` skips the scaffold's `eslint.config.mjs` and `lint` script. It is accepted though `--help` lists only `--eslint` and `--biome`.
25
+
17
26
  ## What ships as golden configs
18
27
 
19
- - `next.config.ts`: `agentRules: false` stops Next from regenerating `AGENTS.md`/`CLAUDE.md` on every run, which would otherwise compete with the root `CLAUDE.md`. `turbopack.root: import.meta.dirname` pins the workspace root, silencing Next's multi-lockfile inference warning in any checkout carrying more than one lockfile above the project, a worktree included.
20
- - `vitest.config.ts`: plain `defineConfig`, no `mergeConfig` or `getViteConfig` since Next has no Vite config to merge from. jsdom, globals, setup file, `passWithNoTests: true`, v8 coverage, `.next/**` in test excludes.
21
- - `playwright.config.ts`: all browsers, `webServer` runs `bun run build && bun run preview` on port `3000` plus `WORKTREE_PORT_OFFSET`, `reuseExistingServer: false`. `DIST_PREBUILT` drops the `build` half, matching the astro stack's flag.
22
- - `.oxlintrc.json`: replaces the web layer's whole file, since oxlint reads one config, with `.next` and `next-env.d.ts` added to `ignorePatterns`. `react/only-export-components` is `off` for `src/app/**`, since route and layout files export non-component values the rule would otherwise flag. `check-file/folder-naming-convention` is `off` for the same subtree, since App Router's bracket, paren, and at-sign folder syntax (dynamic segments, route groups, parallel slots) fails `KEBAB_CASE`.
28
+ ### next.config.ts
29
+
30
+ Two settings keep Next from fighting the checkout.
31
+
32
+ - `agentRules: false` stops Next regenerating `AGENTS.md` and `CLAUDE.md` on every run, which would compete with the root `CLAUDE.md`.
33
+ - `turbopack.root: import.meta.dirname` pins the workspace root, silencing Next's multi-lockfile inference warning in a checkout with more than one lockfile above the project, a worktree included.
34
+
35
+ ### Test configs
36
+
37
+ Both test configs differ from the other web stacks.
38
+
39
+ - `vitest.config.ts` is a plain `defineConfig`, since Next has no Vite config to merge from.
40
+ - It sets jsdom, globals, the setup file, `passWithNoTests: true`, v8 coverage, and `.next/**` in test excludes.
41
+ - `playwright.config.ts` runs all browsers, with `webServer` running `bun run build && bun run preview` on port `3000` plus `WORKTREE_PORT_OFFSET` and `reuseExistingServer: false`.
42
+ - `DIST_PREBUILT` drops the `build` half, matching the astro stack's flag.
43
+
44
+ ### Lint config
45
+
46
+ `.oxlintrc.json` replaces the web layer's whole file, since oxlint reads one config. It adds `.next` and `next-env.d.ts` to `ignorePatterns`, and turns two rules off for `src/app/**`.
47
+
48
+ - `react/only-export-components` is `off`, since route and layout files export non-component values the rule would flag.
49
+ - `check-file/folder-naming-convention` is `off`, since App Router's bracket, paren, and at-sign folders (dynamic segments, route groups, parallel slots) fail `KEBAB_CASE`.
23
50
 
24
51
  ## Port
25
52
 
26
- Next has no config-file port hook, unlike `astro.config.mjs`'s `server.port` or `vite.config.ts`'s `server.port`. The `${base} + WORKTREE_PORT_OFFSET` formula is computed twice instead of once: in `playwright.config.ts` as a JS expression, and in `[scripts.override]` for `dev` and `preview` as shell arithmetic passed to `next`'s own `--port` flag. Default port is `3000`, Next's own default.
53
+ Next has no config-file port hook, unlike `server.port` in `astro.config.mjs` or `vite.config.ts`. The `${base} + WORKTREE_PORT_OFFSET` formula is therefore computed twice.
54
+
55
+ - `playwright.config.ts` computes it as a JS expression.
56
+ - `[scripts.override]` computes it as shell arithmetic for `dev` and `preview`, passed to `next`'s own `--port` flag.
57
+ - The default port is `3000`, Next's own.
27
58
 
28
59
  ## Typecheck
29
60
 
30
- `typecheck` runs `next typegen && tsc --noEmit`. The App Router's route-level types (`LayoutProps`, `PageProps`) are generated into `.next/types/`, gitignored and absent from a fresh checkout, and `tsc` fails on them unresolved without the typegen step first.
61
+ `typecheck` runs `next typegen && tsc --noEmit`. The App Router's route-level types (`LayoutProps`, `PageProps`) are generated into `.next/types/`, which is gitignored and absent from a fresh checkout. Without the typegen step first, `tsc` fails on them unresolved.
31
62
 
32
- ## No golden `tsconfig.json`
63
+ ## No golden tsconfig
33
64
 
34
- `create-next-app`'s own default `tsconfig.json` needs no changes beyond project-specific path aliases, so a golden copy here would ship nothing the scaffold does not already write.
65
+ `create-next-app`'s default `tsconfig.json` needs no changes beyond project-specific path aliases, so a golden copy would ship nothing the scaffold does not already write.
35
66
 
36
67
  ## Gitignore (extend)
37
68
 
38
- `[gitignore]` groups this stack edits, restated here per the manifest-to-reference symmetry:
69
+ These `[gitignore]` groups mirror the manifest.
39
70
 
40
71
  - `"# Next" = [".next/"]`
41
72
 
42
73
  ## Development docs (extend)
43
74
 
44
- Append to the `## Scripts` table:
75
+ Append these rows to the `## Scripts` table.
45
76
 
46
- | `bun run dev` | Start the Next dev server on port 3000, plus this worktree's port offset. |
47
- | `bun run build` | Build the production bundle. |
48
- | `bun run preview` | Serve the built bundle locally, on port 3000 plus the worktree offset. |
49
- | `bun run typecheck` | Run `next typegen` then `tsc --noEmit`. |
77
+ | Command | Purpose |
78
+ | ------------------- | -------------------------------------------------------------- |
79
+ | `bun run dev` | Start the Next dev server on 3000 plus this worktree's offset. |
80
+ | `bun run build` | Build the production bundle. |
81
+ | `bun run preview` | Serve the built bundle on 3000 plus the worktree offset. |
82
+ | `bun run typecheck` | Run `next typegen` then `tsc --noEmit`. |
50
83
 
51
84
  ## CI docs (extend)
52
85
 
53
- In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `next typegen && tsc --noEmit` passes ``. <!-- audit-ignore-citations: canon/context/ci.md -->
86
+ In `canon/context/development/ci.md`, the Typecheck row asserts `` `next typegen && tsc --noEmit` passes ``. <!-- audit-ignore-citations: canon/context/development/ci.md -->
@@ -9,17 +9,19 @@ The php stack covers Composer projects on PHP 8.3 or later, formatted by PHP CS
9
9
  ## Scaffold checklist
10
10
 
11
11
  1. Scaffold with `composer init -n --name <vendor>/<name> --autoload src/`. The package name and namespace are the project's own identity, so this stack never ships a `composer.json`.
12
- 2. Seed `package.json` so the base layer's bun-side tools (husky, oxfmt, commitlint) have a target to install into: `bun init -y`.
13
- 3. Install base tooling: `canon tooling sync base . --write`
14
- 4. Install php tooling: `canon tooling sync php . --write`
15
- 5. Install the dev tools: `composer require --dev friendsofphp/php-cs-fixer phpstan/phpstan phpunit/phpunit`. The manifest declares no `[dependencies.dev]`, because manifest injection runs `bun add -D`, which cannot install a Composer package. This step stays manual.
12
+ 2. Seed `package.json` with `bun init -y`, so the base layer's bun-side tools (husky, oxfmt, commitlint) have a target to install into.
13
+ 3. Install base tooling with `canon tooling sync base . --write`.
14
+ 4. Install php tooling with `canon tooling sync php . --write`.
15
+ 5. Install the dev tools with `composer require --dev friendsofphp/php-cs-fixer phpstan/phpstan phpunit/phpunit`. This stays manual, since manifest injection runs `bun add -D`.
16
16
  6. Run `bun run lint:fix`, then `bun run format`, then `bun run check`.
17
17
 
18
- Use PHP 8.3 or later, since PHPUnit 12 requires it.
18
+ ### Requirements
19
19
 
20
- Keep both `src/` and `tests/` in place. All three tools read both paths and fail when either is missing. The scaffold creates `src/`, but git does not commit an empty folder, so a fresh clone with no source yet fails `lint` and `typecheck` until the first class lands there.
20
+ Use PHP 8.3 or later, since PHPUnit 12 requires it.
21
21
 
22
- The php stack also ships `tests/SmokeTest.php` as a copy-once seed, so `bun run check` passes on a fresh scaffold. Delete or replace it with real tests.
22
+ - Keep both `src/` and `tests/` in place, since all three tools read both paths and fail when either is missing.
23
+ - The scaffold creates `src/`, but git does not commit an empty folder. A fresh clone with no source fails `lint` and `typecheck` until the first class lands there.
24
+ - The stack ships `tests/SmokeTest.php` as a copy-once seed, so `bun run check` passes on a fresh scaffold. Delete or replace it with real tests.
23
25
 
24
26
  ## PHP CS Fixer
25
27
 
@@ -41,10 +43,21 @@ The php stack also ships `tests/SmokeTest.php` as a copy-once seed, so `bun run
41
43
 
42
44
  ## verify.json
43
45
 
46
+ ### Phases
47
+
48
+ The php phase list probes its tools and holds one precondition.
49
+
44
50
  - Probe `php` and `composer` first.
45
51
  - Hold a precondition on `vendor/bin/phpunit`, so the run stops and names `composer install` when it is missing.
46
- - Require Typecheck (`phpstan analyse`), Lint (`php-cs-fixer check`), and Unit tests (`phpunit --testdox`). Base's runner adds the four base phases wherever the folder declares their scripts, then the markdown bans stage.
47
- - The manifest's `[verify] prepare` runs checklist steps 2 and 5 for `canon tooling verify php`: `bun init -y`, then the `composer require --dev` line. The scaffold writes no `package.json`, and a verify that reaches its end with none fails.
52
+ - Require Typecheck (`phpstan analyse`), Lint (`php-cs-fixer check`), and Unit tests (`phpunit --testdox`).
53
+ - Base's runner adds its own four phases where the folder declares their scripts, then the markdown bans stage.
54
+
55
+ ### Prepare
56
+
57
+ The manifest prepares a scaffold that lacks a `package.json`.
58
+
59
+ - The manifest's `[verify] prepare` runs checklist steps 2 and 5 for `canon tooling verify php`, being `bun init -y` and the `composer require --dev` line.
60
+ - The scaffold writes no `package.json`, and a verify that reaches its end with none fails.
48
61
 
49
62
  ## Gitignore
50
63
 
@@ -63,14 +76,20 @@ The php stack also ships `tests/SmokeTest.php` as a copy-once seed, so `bun run
63
76
 
64
77
  ## Development docs (extend)
65
78
 
66
- Append to the `## Scripts` table:
79
+ Append these rows to the `## Scripts` table.
67
80
 
68
- | `bun run lint` | Run `php-cs-fixer check --diff`. |
69
- | `bun run lint:fix` | Auto-fix with `php-cs-fixer fix`. |
70
- | `bun run typecheck` | Run `phpstan analyse`. |
71
- | `bun run test` | Run `phpunit`. |
72
- | `bun run test:run` | Run `phpunit --testdox` (used by `verify`). |
81
+ | Command | Purpose |
82
+ | ------------------- | ------------------------------------------- |
83
+ | `bun run lint` | Run `php-cs-fixer check --diff`. |
84
+ | `bun run lint:fix` | Auto-fix with `php-cs-fixer fix`. |
85
+ | `bun run typecheck` | Run `phpstan analyse`. |
86
+ | `bun run test` | Run `phpunit`. |
87
+ | `bun run test:run` | Run `phpunit --testdox` (used by `verify`). |
73
88
 
74
89
  ## CI docs (extend)
75
90
 
76
- In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `phpstan analyse` passes ``. Add a Lint row whose assertion reads: `` `php-cs-fixer check` passes ``. Add a Tests row whose assertion reads: `` `phpunit` exits 0 ``. <!-- audit-ignore-citations: canon/context/ci.md -->
91
+ In `canon/context/development/ci.md`, add or change three rows. <!-- audit-ignore-citations: canon/context/development/ci.md -->
92
+
93
+ - The Typecheck row asserts `` `phpstan analyse` passes ``.
94
+ - A Lint row asserts `` `php-cs-fixer check` passes ``.
95
+ - A Tests row asserts `` `phpunit` exits 0 ``.
@@ -6,60 +6,77 @@
6
6
 
7
7
  The python stack covers Python 3.13+ projects managed with `uv`. It ships golden sidecar configs for `ruff`, `mypy`, `pytest`, and `coverage`, plus a `.python-version` pin and a python phase list for base's verify runner. Framework adapters layer on top with their own deps and configs.
8
8
 
9
- Configs ship as sidecar files (`ruff.toml`, `mypy.ini`, `pytest.ini`, `.coveragerc`) rather than `[tool.*]` sections in `pyproject.toml`. Sync overwrites configs on every run, so keeping them sidecar avoids stomping the user-owned `[project]` block in `pyproject.toml`.
9
+ Configs ship as sidecar files (`ruff.toml`, `mypy.ini`, `pytest.ini`, `.coveragerc`) rather than `[tool.*]` sections in `pyproject.toml`. Sync overwrites configs on every run, so sidecars avoid stomping the user-owned `[project]` block.
10
10
 
11
11
  ## Scaffold checklist
12
12
 
13
- 1. Scaffold with `uv init --package <name>`. This creates `pyproject.toml` with a `uv_build` backend, `.python-version` pinned to 3.14, and `src/<name with underscores>/__init__.py` holding an annotated `def main() -> None:`, so source lands under `src/` where `mypy.ini`, `pytest.ini`, and `.coveragerc` look for it. `uv init` defaults `requires-python` to `>=3.14`, which matches the `.python-version` pin this stack ships.
14
- 2. Seed `package.json` so the base layer's bun-side tools (husky, oxfmt, commitlint) have a target to install into: `bun init -y`. Without this the sync drops base configs but skips the dep install, since `resolve_missing_deps` short-circuits when `package.json` is absent.
15
- 3. Install base tooling: `canon tooling sync base . --write`
16
- 4. Install python tooling: `canon tooling sync python . --write`
17
- 5. Install Python tooling deps: `uv add --dev ruff mypy pytest pytest-cov`. v1 of this stack does not declare these in `[dependencies.dev]` because manifest injection hardcodes `bun add -D`, which can not install Python packages. Until the injector branches on `runtime`, this step is manual.
18
- 6. Sync the lockfile and create the venv: `uv sync`.
13
+ 1. Scaffold with `uv init --package <name>`. This creates `pyproject.toml` with a `uv_build` backend and `src/<name with underscores>/__init__.py` holding an annotated `def main() -> None:`.
14
+ 2. Seed `package.json` with `bun init -y`, so the base layer's bun-side tools (husky, oxfmt, commitlint) have a target. Without it the sync skips the dep install.
15
+ 3. Install base tooling with `canon tooling sync base . --write`.
16
+ 4. Install python tooling with `canon tooling sync python . --write`.
17
+ 5. Install the deps with `uv add --dev ruff mypy pytest pytest-cov`. This stays manual, since the manifest injector hardcodes `bun add -D`.
18
+ 6. Sync the lockfile and create the venv with `uv sync`.
19
19
  7. Run `bun run lint:fix`, then `bun run format`, then `bun run check`.
20
20
 
21
- The python stack also ships `tests/test_smoke.py` as a copy-once seed. `pytest` collects at least one test on first run, so `bun run test:run` exits 0 instead of the empty-collection exit code 5. Delete or replace the smoke test with real tests.
21
+ ### What the scaffold gives
22
+
23
+ Step 1 puts source under `src/`, where `mypy.ini`, `pytest.ini`, and `.coveragerc` look for it. `uv init` also pins `.python-version` to 3.14 and defaults `requires-python` to `>=3.14`, which matches the pin this stack ships.
24
+
25
+ The stack ships `tests/test_smoke.py` as a copy-once seed. `pytest` then collects at least one test on first run, so `bun run test:run` exits 0 instead of the empty-collection exit code 5. Delete or replace it with real tests.
22
26
 
23
27
  ## What ships as golden configs
24
28
 
25
- - `ruff.toml`: line length 88, target `py314`, strict lint rules (`E`, `W`, `F`, `I`, `B`, `C4`, `UP`, `N`), single-quote format, banned relative imports.
26
- - `mypy.ini`: strict mode, `python_version = 3.14`, `mypy_path = src`, excludes `tests/` and `docs/`.
27
- - `pytest.ini`: tests live under `tests/`, source under `src/`.
28
- - `.coveragerc`: branch coverage on `src/`, html report under `.coverage_cache/html`.
29
- - `.python-version`: pinned to `3.14` to match `uv init` defaults.
30
- - `scripts/verify.json`: requires Typecheck (`mypy`), Lint (`ruff check && ruff format --check`), and Tests (`pytest -v`) on top of base's runner, and stops naming `uv` when it is not on PATH. The markdown bans phase runs as it does everywhere.
29
+ - `ruff.toml` sets line length 88, target `py314`, strict lint rules (`E`, `W`, `F`, `I`, `B`, `C4`, `UP`, `N`), single-quote format, and banned relative imports.
30
+ - `mypy.ini` sets strict mode, `python_version = 3.14`, and `mypy_path = src`, and excludes `tests/` and `docs/`.
31
+ - `pytest.ini` puts tests under `tests/` and source under `src/`.
32
+ - `.coveragerc` measures branch coverage on `src/` with the html report under `.coverage_cache/html`.
33
+ - `.python-version` is pinned to `3.14` to match `uv init` defaults.
34
+
35
+ ### Verify list
36
+
37
+ `scripts/verify.json` requires Typecheck (`mypy`), Lint (`ruff check && ruff format --check`), and Tests (`pytest -v`) on top of base's runner. It stops naming `uv` when `uv` is not on PATH, and the markdown bans phase runs as it does everywhere.
31
38
 
32
39
  ## Hybrid project shape
33
40
 
34
- A python project synced with this stack ends up with both `package.json` from base and `pyproject.toml` from `uv init`. The base layer brings oxfmt, commitlint, and husky for non-Python files. Both `node_modules/` and `.venv/` coexist at the project root. The hybrid shape gives Python projects access to the toolkit's cross-cutting tools without forking the base stack.
41
+ A python project synced with this stack holds both `package.json` from base and `pyproject.toml` from `uv init`. Base brings oxfmt, commitlint, and husky for non-Python files, and `node_modules/` and `.venv/` coexist at the project root. That gives Python projects the toolkit's cross-cutting tools without forking the base stack.
35
42
 
36
43
  ## Dependencies
37
44
 
38
- The manifest declares no `[dependencies.dev]`. Manifest injection currently calls `bun add -D` for any declared deps, which would fail for Python packages. v1 sidesteps this by leaving the section empty. Framework adapters that need Python deps require the injector to branch on `runtime` or detect `pyproject.toml` and call `uv add --dev` instead.
45
+ The manifest declares no `[dependencies.dev]`, because manifest injection calls `bun add -D` for any declared deps, which fails for Python packages. Framework adapters that need Python deps require the injector to branch on `runtime`, or to detect `pyproject.toml` and call `uv add --dev`.
39
46
 
40
47
  ## Verify command
41
48
 
42
- `canon tooling verify python` runs the manifest's `[verify] prepare` before Sync, which is checklist steps 2, 5, and 6: `bun init -y`, `uv add --dev ruff mypy pytest pytest-cov`, and `uv sync`. `uv init` writes no `package.json`, and a verify that reaches its end with none fails, so the prepare is what lets `bun run lint:fix`, `bun run format`, and `bun run check` run at all. Python's `verify.json` requires the `lint`, `typecheck`, and `test:run` scripts that delegate to `uv run`, so the run needs `uv` on the machine and a network for the installs. The end-to-end test and screenshot phases are gated on `package.json` script keys that python does not declare, so they skip.
49
+ `canon tooling verify python` runs the manifest's `[verify] prepare` before Sync. That covers checklist steps 2, 5, and 6, being `bun init -y`, `uv add --dev ruff mypy pytest pytest-cov`, and `uv sync`.
50
+
51
+ - `uv init` writes no `package.json`, and a verify that reaches its end with none fails, so the prepare is what lets `bun run lint:fix`, `bun run format`, and `bun run check` run at all.
52
+ - Python's `verify.json` requires the `lint`, `typecheck`, and `test:run` scripts that delegate to `uv run`, so the run needs `uv` on the machine and a network for the installs.
53
+ - The end-to-end test and screenshot phases are gated on `package.json` script keys python does not declare, so they skip.
43
54
 
44
55
  ## Anti-patterns
45
56
 
46
- - Do not use `pip` directly. Always go through `uv add` / `uv sync` / `uv run`.
47
- - Do not move `[tool.ruff]`, `[tool.mypy]`, `[tool.pytest.ini_options]`, or `[tool.coverage.*]` into `pyproject.toml`. Sync owns the sidecar files. Editing `pyproject.toml` tool sections will conflict with the sidecars and produce silent precedence bugs.
48
- - Do not pin Python via `[project] requires-python` and `.python-version` independently. Treat `.python-version` as the source of truth and let `requires-python` in `pyproject.toml` track it.
49
- - Do not add `commitizen` for conventional commits. The base stack already ships commitlint + husky. Two enforcers is one too many.
50
- - Do not ship a `Dockerfile` or `mkdocs` config under tooling. Those are framework or deployment choices and belong in the project itself or in a future adapter stack.
57
+ - Do not use `pip` directly. Always go through `uv add`, `uv sync`, or `uv run`.
58
+ - Do not move `[tool.ruff]`, `[tool.mypy]`, `[tool.pytest.ini_options]`, or `[tool.coverage.*]` into `pyproject.toml`. Sync owns the sidecars, and tool sections there cause silent precedence bugs.
59
+ - Do not pin Python via `[project] requires-python` and `.python-version` independently. Treat `.python-version` as the source of truth and let `requires-python` track it.
60
+ - Do not add `commitizen` for conventional commits. The base stack already ships commitlint + husky, and two enforcers is one too many.
61
+ - Do not ship a `Dockerfile` or `mkdocs` config under tooling. Those are framework or deployment choices and belong in the project or a future adapter stack.
51
62
 
52
63
  ## Development docs (extend)
53
64
 
54
- Append to the `## Scripts` table:
65
+ Append these rows to the `## Scripts` table.
55
66
 
56
- | `bun run lint` | Run `ruff check` and `ruff format --check`. |
57
- | `bun run lint:fix` | Auto-fix with `ruff check --fix` then `ruff format`. |
58
- | `bun run typecheck` | Run `mypy` in strict mode. |
59
- | `bun run test` | Run `pytest`. |
60
- | `bun run test:run` | Run `pytest -v` (used by `verify`). |
61
- | `bun run test:cov` | Run `pytest` with branch coverage and HTML report. |
67
+ | Command | Purpose |
68
+ | ------------------- | ---------------------------------------------------- |
69
+ | `bun run lint` | Run `ruff check` and `ruff format --check`. |
70
+ | `bun run lint:fix` | Auto-fix with `ruff check --fix` then `ruff format`. |
71
+ | `bun run typecheck` | Run `mypy` in strict mode. |
72
+ | `bun run test` | Run `pytest`. |
73
+ | `bun run test:run` | Run `pytest -v` (used by `verify`). |
74
+ | `bun run test:cov` | Run `pytest` with branch coverage and HTML report. |
62
75
 
63
76
  ## CI docs (extend)
64
77
 
65
- In `canon/context/ci.md`, the Typecheck row's assertion reads: `` `mypy .` passes ``. Add a Lint row whose assertion reads: `` `ruff check` and `ruff format --check` pass ``. Add a Tests row whose assertion reads: `` `pytest` exits 0 ``. <!-- audit-ignore-citations: canon/context/ci.md -->
78
+ In `canon/context/development/ci.md`, add or change three rows. <!-- audit-ignore-citations: canon/context/development/ci.md -->
79
+
80
+ - The Typecheck row asserts `` `mypy .` passes ``.
81
+ - A Lint row asserts `` `ruff check` and `ruff format --check` pass ``.
82
+ - A Tests row asserts `` `pytest` exits 0 ``.