@erclx/canon 5.36.0 → 5.38.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 (165) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/api-design/SKILL.md +6 -0
  3. package/claude/skills/auto-ship/SKILL.md +1 -1
  4. package/claude/skills/backlog-triage/SKILL.md +8 -8
  5. package/claude/skills/build-in-slices/SKILL.md +7 -1
  6. package/claude/skills/build-in-slices/references/adopted.md +3 -0
  7. package/claude/skills/canon-cli/SKILL.md +3 -0
  8. package/claude/skills/canon-feedback/SKILL.md +5 -0
  9. package/claude/skills/canon-feedback-triage/SKILL.md +3 -0
  10. package/claude/skills/canvas-board/SKILL.md +3 -0
  11. package/claude/skills/canvas-board/references/canvas.md +12 -0
  12. package/claude/skills/ci-workflow/SKILL.md +5 -0
  13. package/claude/skills/code-craft/SKILL.md +6 -0
  14. package/claude/skills/context-fold/references/context.md +48 -34
  15. package/claude/skills/create-rule/SKILL.md +11 -9
  16. package/claude/skills/create-rule/references/rule.md +6 -0
  17. package/claude/skills/create-skill/SKILL.md +11 -2
  18. package/claude/skills/create-skill/references/skill-disclosure.md +26 -0
  19. package/claude/skills/create-skill/references/skill-paths.md +3 -0
  20. package/claude/skills/create-skill/references/skill-reference.md +12 -0
  21. package/claude/skills/create-skill/references/skill.md +25 -24
  22. package/claude/skills/deprecation-migration/SKILL.md +6 -0
  23. package/claude/skills/deprecation-migration/references/adopted.md +3 -0
  24. package/claude/skills/design-taste/SKILL.md +6 -0
  25. package/claude/skills/design-taste/references/craft.md +3 -0
  26. package/claude/skills/design-taste/references/preflight.md +16 -0
  27. package/claude/skills/docs-sync/references/docs.md +6 -0
  28. package/claude/skills/docs-sync/references/readme.md +15 -0
  29. package/claude/skills/draft-and-pick/SKILL.md +12 -0
  30. package/claude/skills/draft-doc/references/context.md +1 -1
  31. package/claude/skills/draft-doc/references/readme.md +3 -0
  32. package/claude/skills/draft-figure/references/mermaid.md +6 -0
  33. package/claude/skills/draft-slides/SKILL.md +3 -0
  34. package/claude/skills/git-branch/references/branch.md +16 -11
  35. package/claude/skills/git-commit/references/commit.md +19 -11
  36. package/claude/skills/git-commit/references/versioning.md +9 -0
  37. package/claude/skills/git-issue/references/issue.md +13 -9
  38. package/claude/skills/git-pr/SKILL.md +5 -0
  39. package/claude/skills/git-pr/references/pr.md +24 -6
  40. package/claude/skills/git-ship/SKILL.md +3 -0
  41. package/claude/skills/git-split/SKILL.md +6 -0
  42. package/claude/skills/git-stage/SKILL.md +3 -0
  43. package/claude/skills/git-worktree/SKILL.md +6 -6
  44. package/claude/skills/markdown-craft/references/markdown.md +25 -6
  45. package/claude/skills/plan-feature/SKILL.md +9 -0
  46. package/claude/skills/plan-feature/references/plan.md +20 -8
  47. package/claude/skills/plan-groundwork/SKILL.md +6 -0
  48. package/claude/skills/plan-groundwork/references/groundwork.md +61 -29
  49. package/claude/skills/plan-intake/references/intake.md +6 -6
  50. package/claude/skills/read-frames/SKILL.md +3 -0
  51. package/claude/skills/review-craft/SKILL.md +3 -0
  52. package/claude/skills/review-pr/references/post.md +1 -1
  53. package/claude/skills/review-ui/SKILL.md +6 -3
  54. package/claude/skills/role-orchestrator/SKILL.md +4 -1
  55. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +7 -4
  56. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +1 -1
  57. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +3 -0
  58. package/claude/skills/role-planner/SKILL.md +4 -1
  59. package/claude/skills/role-worker/SKILL.md +13 -1
  60. package/claude/skills/search-craft/SKILL.md +3 -0
  61. package/claude/skills/systematic-debugging/SKILL.md +6 -0
  62. package/claude/skills/target-check/references/domains.md +5 -0
  63. package/claude/skills/target-setup/SKILL.md +8 -4
  64. package/claude/skills/target-setup/references/verify.md +6 -5
  65. package/claude/skills/task-board/references/tasks.md +12 -7
  66. package/claude/skills/task-complete/references/archive.md +3 -0
  67. package/claude/skills/teach-workspace/SKILL.md +3 -0
  68. package/claude/skills/teach-workspace/references/glossary.md +3 -0
  69. package/claude/skills/teach-workspace/references/lesson-build.md +3 -0
  70. package/claude/skills/teach-workspace/references/lesson-craft.md +3 -0
  71. package/claude/skills/teach-workspace/references/teach.md +8 -8
  72. package/claude/skills/test-craft/SKILL.md +5 -0
  73. package/claude/skills/video-craft/SKILL.md +6 -0
  74. package/claude/skills/write-human/SKILL.md +9 -0
  75. package/docs/agents/capture.md +2 -0
  76. package/docs/agents/commands.md +54 -54
  77. package/docs/agents/context-audit-checks.md +6 -0
  78. package/docs/agents/context-audit.md +8 -8
  79. package/docs/agents/context-classify.md +2 -0
  80. package/docs/agents/drift-surfaces.md +5 -5
  81. package/docs/agents/gate.md +6 -7
  82. package/docs/agents/index.md +1 -1
  83. package/docs/agents/install-and-sync.md +2 -2
  84. package/docs/agents/markdown-audit-weight.md +11 -3
  85. package/docs/agents/markdown-audit.md +2 -2
  86. package/docs/agents/pr-frames.md +17 -3
  87. package/docs/agents/sessions-transfer.md +8 -6
  88. package/docs/agents/skills-audit.md +9 -1
  89. package/docs/target/projects.md +1 -1
  90. package/docs/target/scenarios.md +2 -2
  91. package/docs/target/standards-moved.md +20 -18
  92. package/docs/workflow/ai-workflow.md +3 -0
  93. package/docs/workflow/visual-design-workflow.md +5 -0
  94. package/governance/detect.toml +6 -1
  95. package/package.json +7 -4
  96. package/scripts/core/install-check.sh +1 -1
  97. package/src/cli/help.ts +1 -1
  98. package/src/commands/claude.ts +0 -1
  99. package/src/commands/context.ts +29 -4
  100. package/src/commands/gate.ts +1 -8
  101. package/src/commands/markdown.ts +52 -0
  102. package/src/commands/pr.ts +18 -11
  103. package/src/commands/records.ts +0 -1
  104. package/src/context/audit.ts +83 -0
  105. package/src/context/classify/prompts.ts +6 -1
  106. package/src/context/gate.ts +6 -3
  107. package/src/docs/read.ts +15 -21
  108. package/src/gate/measures.ts +186 -1
  109. package/src/gate/sequencer.ts +0 -4
  110. package/src/gate/stages.ts +46 -27
  111. package/src/git/files.ts +30 -0
  112. package/src/gov/adapter.ts +2 -2
  113. package/src/gov/install.ts +2 -2
  114. package/src/gov/list.ts +1 -1
  115. package/src/gov/payload.ts +1 -1
  116. package/src/gov/stacks.ts +1 -1
  117. package/src/markdown/gate.ts +10 -0
  118. package/src/markdown/growth.ts +43 -0
  119. package/src/markdown/stacks.ts +74 -0
  120. package/src/markdown/structure.ts +73 -5
  121. package/src/migrate/rule-layout.ts +12 -12
  122. package/src/pr/frames.ts +66 -1
  123. package/src/sandbox/equivalence.ts +7 -9
  124. package/src/sync/check.ts +23 -23
  125. package/src/teach/browser/head.ts +2 -2
  126. package/src/teach/browser/sidebar.ts +3 -3
  127. package/src/teach/browser/theme.ts +2 -2
  128. package/src/tooling/inject.ts +2 -2
  129. package/src/tooling/package.ts +15 -18
  130. package/src/tooling/verify-stack.ts +6 -3
  131. package/tooling/astro/configs/.oxfmtrc.json +8 -0
  132. package/tooling/astro/configs/.prettierrc +13 -0
  133. package/tooling/astro/configs/tsconfig.json +1 -1
  134. package/tooling/astro/manifest.toml +13 -0
  135. package/tooling/astro/reference.md +17 -7
  136. package/tooling/base/configs/.oxfmtrc.json +7 -0
  137. package/tooling/base/configs/scripts/verify.json +1 -1
  138. package/tooling/base/configs/scripts/verify.ts +3 -9
  139. package/tooling/base/manifest.toml +6 -6
  140. package/tooling/base/reference.md +20 -17
  141. package/tooling/base/seeds/.lintstagedrc +1 -3
  142. package/tooling/base/seeds/canon/context/ci.md +7 -7
  143. package/tooling/base/seeds/canon/context/development.md +10 -10
  144. package/tooling/claude/manifest.toml +5 -1
  145. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +8 -0
  146. package/tooling/go/reference.md +2 -2
  147. package/tooling/nextjs/configs/.oxlintrc.json +223 -0
  148. package/tooling/nextjs/manifest.toml +2 -5
  149. package/tooling/nextjs/reference.md +4 -4
  150. package/tooling/php/manifest.toml +6 -1
  151. package/tooling/php/reference.md +2 -2
  152. package/tooling/python/manifest.toml +6 -1
  153. package/tooling/python/reference.md +4 -4
  154. package/tooling/vite-react/configs/tsconfig.json +1 -1
  155. package/tooling/vite-react/configs/vitest.config.ts +1 -1
  156. package/tooling/vite-react/manifest.toml +1 -4
  157. package/tooling/vite-react/reference.md +5 -5
  158. package/tooling/web/configs/.oxlintrc.json +213 -0
  159. package/tooling/web/configs/src/test/setup.ts +1 -1
  160. package/tooling/web/manifest.toml +12 -16
  161. package/tooling/web/reference.md +15 -15
  162. package/tooling/base/configs/.prettierrc +0 -12
  163. package/tooling/base/seeds/.prettierignore +0 -0
  164. package/tooling/nextjs/configs/eslint.config.js +0 -91
  165. package/tooling/web/configs/eslint.config.js +0 -82
@@ -0,0 +1,213 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "plugins": ["typescript", "unicorn"],
4
+ "categories": {
5
+ "correctness": "off"
6
+ },
7
+ "env": {
8
+ "builtin": true
9
+ },
10
+ "ignorePatterns": [
11
+ "dist",
12
+ "dist-ssr",
13
+ "coverage",
14
+ "release",
15
+ ".claude",
16
+ ".vscode",
17
+ ".husky",
18
+ "test-results",
19
+ "playwright-report",
20
+ "blob-report",
21
+ "playwright/.cache"
22
+ ],
23
+ "rules": {
24
+ "constructor-super": "error",
25
+ "for-direction": "error",
26
+ "getter-return": "error",
27
+ "no-async-promise-executor": "error",
28
+ "no-case-declarations": "error",
29
+ "no-class-assign": "error",
30
+ "no-compare-neg-zero": "error",
31
+ "no-cond-assign": "error",
32
+ "no-const-assign": "error",
33
+ "no-constant-binary-expression": "error",
34
+ "no-constant-condition": "error",
35
+ "no-control-regex": "error",
36
+ "no-debugger": "error",
37
+ "no-delete-var": "error",
38
+ "no-dupe-class-members": "error",
39
+ "no-dupe-else-if": "error",
40
+ "no-dupe-keys": "error",
41
+ "no-duplicate-case": "error",
42
+ "no-empty": "error",
43
+ "no-empty-character-class": "error",
44
+ "no-empty-pattern": "error",
45
+ "no-empty-static-block": "error",
46
+ "no-ex-assign": "error",
47
+ "no-extra-boolean-cast": "error",
48
+ "no-fallthrough": "error",
49
+ "no-func-assign": "error",
50
+ "no-global-assign": "error",
51
+ "no-import-assign": "error",
52
+ "no-invalid-regexp": "error",
53
+ "no-irregular-whitespace": "error",
54
+ "no-loss-of-precision": "error",
55
+ "no-misleading-character-class": "error",
56
+ "no-new-native-nonconstructor": "error",
57
+ "no-nonoctal-decimal-escape": "error",
58
+ "no-obj-calls": "error",
59
+ "no-prototype-builtins": "error",
60
+ "no-redeclare": "error",
61
+ "no-regex-spaces": "error",
62
+ "no-self-assign": "error",
63
+ "no-setter-return": "error",
64
+ "no-shadow-restricted-names": "error",
65
+ "no-sparse-arrays": "error",
66
+ "no-this-before-super": "error",
67
+ "no-unassigned-vars": "error",
68
+ "no-unreachable": "error",
69
+ "no-unsafe-finally": "error",
70
+ "no-unsafe-negation": "error",
71
+ "no-unsafe-optional-chaining": "error",
72
+ "no-unused-labels": "error",
73
+ "no-unused-private-class-members": "error",
74
+ "no-unused-vars": "error",
75
+ "no-useless-backreference": "error",
76
+ "no-useless-catch": "error",
77
+ "no-useless-escape": "error",
78
+ "no-with": "error",
79
+ "preserve-caught-error": "error",
80
+ "require-yield": "error",
81
+ "use-isnan": "error",
82
+ "valid-typeof": "error",
83
+ "no-array-constructor": "error",
84
+ "no-unused-expressions": "error",
85
+ "typescript/ban-ts-comment": "error",
86
+ "typescript/no-duplicate-enum-values": "error",
87
+ "typescript/no-empty-object-type": "error",
88
+ "typescript/no-explicit-any": "error",
89
+ "typescript/no-extra-non-null-assertion": "error",
90
+ "typescript/no-misused-new": "error",
91
+ "typescript/no-namespace": "error",
92
+ "typescript/no-non-null-asserted-optional-chain": "error",
93
+ "typescript/no-require-imports": "error",
94
+ "typescript/no-this-alias": "error",
95
+ "typescript/no-unnecessary-type-constraint": "error",
96
+ "typescript/no-unsafe-declaration-merging": "error",
97
+ "typescript/no-unsafe-function-type": "error",
98
+ "typescript/no-wrapper-object-types": "error",
99
+ "typescript/prefer-as-const": "error",
100
+ "typescript/prefer-namespace-keyword": "error",
101
+ "typescript/triple-slash-reference": "error"
102
+ },
103
+ "overrides": [
104
+ {
105
+ "files": ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
106
+ "rules": {
107
+ "constructor-super": "off",
108
+ "getter-return": "off",
109
+ "no-class-assign": "off",
110
+ "no-const-assign": "off",
111
+ "no-dupe-class-members": "off",
112
+ "no-dupe-keys": "off",
113
+ "no-func-assign": "off",
114
+ "no-import-assign": "off",
115
+ "no-new-native-nonconstructor": "off",
116
+ "no-obj-calls": "off",
117
+ "no-redeclare": "off",
118
+ "no-setter-return": "off",
119
+ "no-this-before-super": "off",
120
+ "no-unreachable": "off",
121
+ "no-unsafe-negation": "off",
122
+ "no-var": "error",
123
+ "no-with": "off",
124
+ "prefer-const": "error",
125
+ "prefer-rest-params": "error",
126
+ "prefer-spread": "error"
127
+ }
128
+ },
129
+ {
130
+ "files": ["**/*.{ts,tsx,js,jsx}"],
131
+ "rules": {
132
+ "simple-import-sort/imports": "error",
133
+ "simple-import-sort/exports": "error",
134
+ "check-file/filename-naming-convention": [
135
+ "error",
136
+ {
137
+ "**/!(App).{ts,tsx}": "KEBAB_CASE"
138
+ },
139
+ {
140
+ "ignoreMiddleExtensions": true
141
+ }
142
+ ],
143
+ "check-file/folder-naming-convention": [
144
+ "error",
145
+ {
146
+ "src/**/!(__tests__)/": "KEBAB_CASE"
147
+ }
148
+ ],
149
+ "no-unused-vars": [
150
+ "error",
151
+ {
152
+ "varsIgnorePattern": "^_",
153
+ "argsIgnorePattern": "^_"
154
+ }
155
+ ],
156
+ "react/rules-of-hooks": "error",
157
+ "react/exhaustive-deps": "warn",
158
+ "react/static-components": "error",
159
+ "react/use-memo": "error",
160
+ "react/preserve-manual-memoization": "error",
161
+ "react/incompatible-library": "warn",
162
+ "react/immutability": "error",
163
+ "react/globals": "error",
164
+ "react/refs": "error",
165
+ "react/set-state-in-effect": "error",
166
+ "react/error-boundaries": "error",
167
+ "react/purity": "error",
168
+ "react/set-state-in-render": "error",
169
+ "react/unsupported-syntax": "warn",
170
+ "react/only-export-components": [
171
+ "warn",
172
+ {
173
+ "allowConstantExport": true
174
+ }
175
+ ]
176
+ },
177
+ "jsPlugins": [
178
+ "eslint-plugin-simple-import-sort",
179
+ "eslint-plugin-check-file"
180
+ ],
181
+ "plugins": ["react"],
182
+ "env": {
183
+ "browser": true
184
+ }
185
+ },
186
+ {
187
+ "files": ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
188
+ "rules": {
189
+ "vitest/expect-expect": "error",
190
+ "vitest/no-identical-title": "error",
191
+ "vitest/no-commented-out-tests": "error",
192
+ "vitest/valid-title": "error",
193
+ "vitest/valid-expect": "error",
194
+ "vitest/valid-describe-callback": "error",
195
+ "vitest/require-local-test-context-for-concurrent-snapshots": "error",
196
+ "vitest/no-import-node-test": "error",
197
+ "vitest/no-focused-tests": "error"
198
+ },
199
+ "plugins": ["vitest"]
200
+ },
201
+ {
202
+ "files": [
203
+ "*.config.{js,mjs,cjs,ts}",
204
+ "vite.config.ts",
205
+ "vitest.config.ts",
206
+ "playwright.config.ts"
207
+ ],
208
+ "env": {
209
+ "node": true
210
+ }
211
+ }
212
+ ]
213
+ }
@@ -1,4 +1,4 @@
1
- import '@testing-library/jest-dom'
1
+ import '@testing-library/jest-dom/vitest'
2
2
 
3
3
  import { cleanup } from '@testing-library/react'
4
4
  import { afterEach } from 'vitest'
@@ -6,25 +6,18 @@ scaffold = ""
6
6
 
7
7
  [dependencies.dev]
8
8
  packages = [
9
- "eslint@^9",
10
- "eslint-config-prettier",
9
+ "oxlint@1.87.0",
11
10
  "eslint-plugin-check-file",
12
- "eslint-plugin-react-hooks",
13
- "eslint-plugin-react-refresh",
14
11
  "eslint-plugin-simple-import-sort",
15
- "eslint-plugin-vitest",
16
- "globals",
17
- "typescript-eslint",
18
- "@eslint/js",
19
12
  "typescript@^6",
20
13
  "react",
21
14
  "react-dom",
22
15
  "@types/react",
23
16
  "@types/react-dom",
24
17
  "@types/node",
25
- "vitest@^3",
26
- "@vitest/coverage-v8@^3",
27
- "@vitest/ui@^3",
18
+ "vitest@^5",
19
+ "@vitest/coverage-v8@^5",
20
+ "@vitest/ui@^5",
28
21
  "jsdom",
29
22
  "@testing-library/react",
30
23
  "@testing-library/jest-dom",
@@ -32,12 +25,9 @@ packages = [
32
25
  "@playwright/test",
33
26
  "tailwindcss",
34
27
  "@tailwindcss/vite",
35
- "prettier-plugin-tailwindcss",
36
28
  ]
37
29
 
38
30
  [scripts]
39
- "lint" = "eslint . --max-warnings 0"
40
- "lint:fix" = "eslint . --fix --max-warnings 0"
41
31
  "test" = "vitest"
42
32
  "test:run" = "vitest run --reporter=verbose"
43
33
  "test:ui" = "vitest --ui"
@@ -50,12 +40,18 @@ packages = [
50
40
  "smoke:prod" = "bun e2e/screenshot.ts --check-console-clean --require-base-url"
51
41
 
52
42
  [scripts.override]
53
- "lint" = "eslint . --max-warnings 0"
43
+ "lint" = "oxlint --max-warnings 0"
44
+ "lint:fix" = "oxlint --fix --max-warnings 0"
54
45
  "screenshot" = "PREVIEW_PORT=$(bash scripts/worktree-port.sh 4173) && export PREVIEW_PORT && bash scripts/screenshot.sh"
55
46
  "readme-screenshot" = "PREVIEW_PORT=$(bash scripts/worktree-port.sh 4173) && export PREVIEW_PORT && bash scripts/readme-screenshot.sh"
56
47
 
57
48
  [gitignore]
58
49
  "# Build" = ["dist/"]
59
50
  "# Coverage" = ["coverage/"]
60
- "# Playwright" = ["test-results/", "playwright-report/", "blob-report/", "playwright/.cache/"]
51
+ "# Playwright" = [
52
+ "test-results/",
53
+ "playwright-report/",
54
+ "blob-report/",
55
+ "playwright/.cache/",
56
+ ]
61
57
  "# Screenshots" = ["screenshots/"]
@@ -4,14 +4,14 @@
4
4
 
5
5
  ## Overview
6
6
 
7
- The web layer covers web-universal tooling shared across Vite + React, Astro, and any future web stack. It ships golden configs for ESLint, Vitest, Playwright, Tailwind v4, screenshots, VS Code integration, and CI. Framework-specific wiring (Vite plugins, Astro islands, Next.js config) lives in per-stack adapter folders that extend this layer.
7
+ The web layer covers web-universal tooling shared across Vite + React, Astro, and any future web stack. It ships golden configs for oxlint, Vitest, Playwright, Tailwind v4, screenshots, VS Code integration, and CI. Framework-specific wiring (Vite plugins, Astro islands, Next.js config) lives in per-stack adapter folders that extend this layer.
8
8
 
9
9
  ## What ships as golden configs
10
10
 
11
11
  Golden config files live in `tooling/web/configs/` and are copied into the target on `canon tooling sync web . --write`, replacing whatever sits at those paths. They are the source of truth. The reference covers rationale and tradeoffs. Configs show the concrete setup.
12
12
 
13
- - `eslint.config.js`: flat config with `@eslint/js`, `typescript-eslint`, React hooks, import sort, check-file, vitest rules scoped to test files, `eslint-config-prettier` last.
14
- - `src/test/setup.ts`: `@testing-library/jest-dom` import, `cleanup` after each test.
13
+ - `.oxlintrc.json`: migrated from the ESLint config it replaced. Runs the recommended JS and TypeScript rules, the React hooks and refresh rules, test-scoped vitest rules plus `no-focused-tests`, and `check-file` and `simple-import-sort` as JS plugins.
14
+ - `src/test/setup.ts`: `@testing-library/jest-dom/vitest` import, `cleanup` after each test.
15
15
  - `e2e/screenshot.ts`: capture template. A single `CASES` record at the top carries one entry per output file, each naming a section, a theme, and a route, and the loop below crosses every case with the `VIEWPORTS` array and writes `screenshots/<hostname>/<section>/<width>--<theme>.png`, keyed on `SCREENSHOT_BASE_URL`'s hostname so a local and a deployed run land in different folders. `VIEWPORTS` holds four breakpoint buckets named by width, 320, 768, 1280 and 1536, with 320 as the base a page has to hold without wrapping or clipping. A case flagged `evidence: true` additionally writes `evidence/<section>/<width>--<theme>.png`, with no hostname segment, at the viewports flagged `evidence` alone, which are 320 and 1280. Committing all four multiplies the tracked frames by the width count, so the flag is what keeps the tracked set at twice the sections rather than four times. A case may name `excludeViewports` to skip a width, for a route whose long-form prose reflows at 320 instead of breaking. `SCREENSHOT_FILTER` narrows a run to the cases whose label, `<section>/<width>--<theme>`, contains any comma-separated term, exits 1 when nothing matches and lists the labels that exist, and leaves the rest of the sweep alone. `--require-base-url` skips a flagged case entirely, so a production smoke run neither writes to the committed path nor counts an evidence route in its console-clean check, and exits 1 when that leaves zero cases run, so flagging every case cannot silence the production check without saying so. Per-project cases extend the one record. A route's widths and themes sit together under its section folder, so the filename carries both. A case may also name `sections`, one `{ name, selector }` per addressable part of the route, and each writes its own frame at `screenshots/<hostname>/<section>/<name>/<width>--<theme>.png` beside the whole-page one, plus `evidence/<section>/<name>/<width>--<theme>.png` where the case is flagged and the width carries `evidence`. The field is optional, so a case that names none captures the page alone as before, and a selector matching nothing is reported and skipped rather than raised, since `CASES` is a template a project rewrites and a target that syncs this file before editing its own list would otherwise capture nothing at all. Testing that every named section resolves belongs to the project that owns the selectors, beside its own page. A case may also name `mask`, a list of selectors whose boxes are painted over in every frame the case writes, whole-page and section alike, for a value that moves between runs with no code change, so a double capture still compares everything else. A mask selector matching nothing is reported and skipped the same way. Every case settles before anything is shot: the context is created with `reducedMotion: 'reduce'`, each named section is scrolled into view so an `IntersectionObserver` fires and a frame marked `loading="lazy"` loads, the page returns to the top, and fonts, images and same-origin frames are waited out on a bounded condition rather than on a second `networkidle`, which is a lifecycle event of the navigation and does not re-arm for a load a scroll started. A section frame drops the page's floating chrome, being whatever computes to `position: sticky` or `fixed`, because a section taller than the viewport is scrolled into view to be cut and a sticky bar otherwise rides that scroll into the middle of the frame. The whole-page frame is shot first and keeps its chrome. `--check-console-clean` collects `console`-level error messages per case and exits 1 with the list if any fired, turning the capture into a smoke check. `--require-base-url` exits 1 before launching a browser when `SCREENSHOT_BASE_URL` is unset, guarding a script meant to run against a real deployment from silently capturing `localhost`.
16
16
  - `scripts/screenshot.sh`: sources `scripts/lib/preview-server.sh`, then runs the capture against the served preview.
17
17
  - `scripts/lib/preview-server.sh`: sourced, never executed. Builds, serves on `PREVIEW_PORT`, blocks until the port answers, and traps the whole process group on exit, including the detached grandchild `lsof` finds. Both capture scripts source it so the startup lives once.
@@ -54,9 +54,8 @@ Two worktrees of one repository run the same stack, so a fixed port makes the se
54
54
 
55
55
  Sticky negative knowledge. Do not relearn.
56
56
 
57
- - Do NOT use `tsc -b` in a Vite project. Composite mode emits `.js` next to `.ts` and ESLint lints the emitted files. Use `tsc --noEmit`.
58
- - Do NOT accept `eslint@^10` alongside `typescript-eslint@^8`. Chain breaks with `TypeError: Class extends value undefined` from `LegacyESLint`. Pin `eslint@^9` until `typescript-eslint@^9` with ESLint 10 support ships.
59
- - Do NOT trust `vite-react`'s scaffolded `lint` script. It ships its own `"lint": "oxlint"` under a bare `.oxlintrc.json`, which runs a different rule set from the stack's ESLint config and never gates on it. `[scripts.override]` forces the stack's own eslint invocation back over it.
57
+ - Do NOT use `tsc -b` in a Vite project. Composite mode emits `.js` next to `.ts` and the linter lints the emitted files. Use `tsc --noEmit`.
58
+ - Do NOT trust `vite-react`'s scaffolded `lint` script. It ships a bare `"lint": "oxlint"`, which exits 0 on warnings. `[scripts.override]` forces `--max-warnings 0` over it, and the golden `.oxlintrc.json` overwrites the scaffold's bare one.
60
59
  - Do NOT rely on bare-folder exclude globs like `exclude: ["e2e"]`. Use `"e2e/**/*"`.
61
60
  - Do NOT ship Vitest with no-tests-fail. Fresh scaffolds have zero tests. Use `passWithNoTests: true` or equivalent until the project has at least one test.
62
61
  - Do NOT put Playwright `trace` at the top level of `defineConfig`. It lives under `use`.
@@ -68,17 +67,18 @@ Sticky negative knowledge. Do not relearn.
68
67
  - Unit tests: Vitest with jsdom, globals on, setup file at `src/test/setup.ts`, `@testing-library/react`, `@testing-library/user-event`.
69
68
  - E2E: Playwright in `e2e/`. Chromium-only for Chrome extensions, all browsers for web apps.
70
69
  - Tailwind: v4 via `@tailwindcss/vite`. Never the v3 integration.
71
- - Prettier: `prettier-plugin-tailwindcss` last in plugins array. Astro also adds `prettier-plugin-astro` first.
70
+ - Lint: oxlint at an exact pin, since its JS plugin host is alpha and a minor release could break `check-file` or `simple-import-sort`. Astro keeps ESLint, which its own reference covers.
72
71
 
73
- ## Prettier (extend)
72
+ ## oxlint
74
73
 
75
- - Base ships `.prettierrc` with `semi: false`, `singleQuote: true`. Web layer adds `jsxSingleQuote: true` and `prettier-plugin-tailwindcss` via the manifest.
76
- - Per-stack overrides go in the stack's configs (Astro adds `prettier-plugin-astro` and the `.astro` parser override).
74
+ - Set `lint` and `lint:fix` through `[scripts.override]`, so an already-synced target stops running ESLint on its next sync.
75
+ - Delete a leftover `eslint.config.js` or `eslint.config.mjs`, and `bun remove` the ESLint packages this stack stopped shipping, by hand after that sync. Sync deletes nothing a stack stopped shipping.
76
+ - Keep import sorting on `simple-import-sort` rather than oxfmt's built-in sort, which orders imports differently and would rewrite every import block.
77
77
 
78
78
  ## lint-staged (extend)
79
79
 
80
- - Add `**/*.{js,jsx,ts,tsx}` glob running `eslint --fix --max-warnings 0`, and `prettier --write`.
81
- - Extend the prettier glob to include `css`: `**/*.{json,css,md,mdc}`.
80
+ - Add `**/*.{js,jsx,ts,tsx}` glob running `oxlint --fix --max-warnings 0`, and `oxfmt --no-error-on-unmatched-pattern`.
81
+ - Extend the oxfmt glob to include `css`: `**/*.{json,css,md}`.
82
82
 
83
83
  ## CI docs (extend)
84
84
 
@@ -87,7 +87,7 @@ Extend `canon/context/ci.md` so the `## Checks` table reflects the web jobs. <!-
87
87
  Append rows:
88
88
 
89
89
  | Typecheck | `bun run typecheck` | framework-specific typecheck passes |
90
- | Lint | `bun run lint` | ESLint passes with zero warnings |
90
+ | Lint | `bun run lint` | oxlint passes with zero warnings |
91
91
  | Tests | `bun run test:coverage` | Vitest passes with coverage thresholds |
92
92
  | Build | `bun run build` | Production build succeeds |
93
93
  | E2E | `bun run test:e2e` | Playwright passes against the built preview |
@@ -100,8 +100,8 @@ Extend the `development` context entry under `canon/context/` so the `## Scripts
100
100
 
101
101
  Append rows:
102
102
 
103
- | `bun run lint` | Run ESLint with zero warnings allowed. |
104
- | `bun run lint:fix` | Auto-fix ESLint issues. |
103
+ | `bun run lint` | Run oxlint with zero warnings allowed. |
104
+ | `bun run lint:fix` | Auto-fix oxlint issues. |
105
105
  | `bun run test` | Run Vitest in watch mode. |
106
106
  | `bun run test:run` | Run Vitest once with verbose reporter. |
107
107
  | `bun run test:coverage` | Run Vitest with coverage. |
@@ -1,12 +0,0 @@
1
- {
2
- "semi": false,
3
- "singleQuote": true,
4
- "overrides": [
5
- {
6
- "files": "*.mdc",
7
- "options": {
8
- "parser": "markdown"
9
- }
10
- }
11
- ]
12
- }
File without changes
@@ -1,91 +0,0 @@
1
- import js from '@eslint/js'
2
- import { defineConfig, globalIgnores } from 'eslint/config'
3
- import prettier from 'eslint-config-prettier'
4
- import checkFile from 'eslint-plugin-check-file'
5
- import reactHooks from 'eslint-plugin-react-hooks'
6
- import reactRefresh from 'eslint-plugin-react-refresh'
7
- import simpleImportSort from 'eslint-plugin-simple-import-sort'
8
- import vitest from 'eslint-plugin-vitest'
9
- import globals from 'globals'
10
- import tseslint from 'typescript-eslint'
11
-
12
- export default defineConfig([
13
- globalIgnores([
14
- '.next',
15
- 'next-env.d.ts',
16
- 'dist',
17
- 'dist-ssr',
18
- 'coverage',
19
- 'release',
20
- '.claude',
21
- '.vscode',
22
- '.husky',
23
- 'test-results',
24
- 'playwright-report',
25
- 'blob-report',
26
- 'playwright/.cache',
27
- ]),
28
- js.configs.recommended,
29
- ...tseslint.configs.recommended,
30
- {
31
- files: ['**/*.{ts,tsx,js,jsx}'],
32
- plugins: {
33
- 'react-hooks': reactHooks,
34
- 'react-refresh': reactRefresh,
35
- 'simple-import-sort': simpleImportSort,
36
- 'check-file': checkFile,
37
- },
38
- languageOptions: {
39
- globals: {
40
- ...globals.browser,
41
- },
42
- },
43
- rules: {
44
- ...reactHooks.configs.recommended.rules,
45
- 'react-refresh/only-export-components': [
46
- 'warn',
47
- { allowConstantExport: true },
48
- ],
49
- 'simple-import-sort/imports': 'error',
50
- 'simple-import-sort/exports': 'error',
51
- '@typescript-eslint/no-unused-vars': [
52
- 'error',
53
- { varsIgnorePattern: '^_', argsIgnorePattern: '^_' },
54
- ],
55
- 'check-file/filename-naming-convention': [
56
- 'error',
57
- { '**/*.{ts,tsx}': 'KEBAB_CASE' },
58
- { ignoreMiddleExtensions: true },
59
- ],
60
- 'check-file/folder-naming-convention': [
61
- 'error',
62
- { 'src/**/!(__tests__)/': 'KEBAB_CASE' },
63
- ],
64
- },
65
- },
66
- {
67
- // App Router route and layout files export non-component values (metadata, route handlers) and use bracket, paren, and at-sign folder syntax (dynamic segments, route groups, parallel slots), which these rules flag as violations.
68
- files: ['src/app/**/*.{ts,tsx}'],
69
- rules: {
70
- 'react-refresh/only-export-components': 'off',
71
- 'check-file/folder-naming-convention': 'off',
72
- },
73
- },
74
- {
75
- files: ['**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}'],
76
- ...vitest.configs.recommended,
77
- },
78
- {
79
- files: [
80
- '*.config.{js,mjs,cjs,ts}',
81
- 'vitest.config.ts',
82
- 'playwright.config.ts',
83
- ],
84
- languageOptions: {
85
- globals: {
86
- ...globals.node,
87
- },
88
- },
89
- },
90
- prettier,
91
- ])
@@ -1,82 +0,0 @@
1
- import js from '@eslint/js'
2
- import { defineConfig, globalIgnores } from 'eslint/config'
3
- import prettier from 'eslint-config-prettier'
4
- import checkFile from 'eslint-plugin-check-file'
5
- import reactHooks from 'eslint-plugin-react-hooks'
6
- import reactRefresh from 'eslint-plugin-react-refresh'
7
- import simpleImportSort from 'eslint-plugin-simple-import-sort'
8
- import vitest from 'eslint-plugin-vitest'
9
- import globals from 'globals'
10
- import tseslint from 'typescript-eslint'
11
-
12
- export default defineConfig([
13
- globalIgnores([
14
- 'dist',
15
- 'dist-ssr',
16
- 'coverage',
17
- 'release',
18
- '.claude',
19
- '.vscode',
20
- '.husky',
21
- 'test-results',
22
- 'playwright-report',
23
- 'blob-report',
24
- 'playwright/.cache',
25
- ]),
26
- js.configs.recommended,
27
- ...tseslint.configs.recommended,
28
- {
29
- files: ['**/*.{ts,tsx,js,jsx}'],
30
- plugins: {
31
- 'react-hooks': reactHooks,
32
- 'react-refresh': reactRefresh,
33
- 'simple-import-sort': simpleImportSort,
34
- 'check-file': checkFile,
35
- },
36
- languageOptions: {
37
- globals: {
38
- ...globals.browser,
39
- },
40
- },
41
- rules: {
42
- ...reactHooks.configs.recommended.rules,
43
- 'react-refresh/only-export-components': [
44
- 'warn',
45
- { allowConstantExport: true },
46
- ],
47
- 'simple-import-sort/imports': 'error',
48
- 'simple-import-sort/exports': 'error',
49
- '@typescript-eslint/no-unused-vars': [
50
- 'error',
51
- { varsIgnorePattern: '^_', argsIgnorePattern: '^_' },
52
- ],
53
- 'check-file/filename-naming-convention': [
54
- 'error',
55
- { '**/!(App).{ts,tsx}': 'KEBAB_CASE' },
56
- { ignoreMiddleExtensions: true },
57
- ],
58
- 'check-file/folder-naming-convention': [
59
- 'error',
60
- { 'src/**/!(__tests__)/': 'KEBAB_CASE' },
61
- ],
62
- },
63
- },
64
- {
65
- files: ['**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}'],
66
- ...vitest.configs.recommended,
67
- },
68
- {
69
- files: [
70
- '*.config.{js,mjs,cjs,ts}',
71
- 'vite.config.ts',
72
- 'vitest.config.ts',
73
- 'playwright.config.ts',
74
- ],
75
- languageOptions: {
76
- globals: {
77
- ...globals.node,
78
- },
79
- },
80
- },
81
- prettier,
82
- ])