@erclx/canon 5.44.0 → 5.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +1 -1
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/canon-cli/SKILL.md +0 -1
  4. package/claude/skills/design-taste/REQUIREMENT.md +2 -2
  5. package/claude/skills/pick-by-eye/REQUIREMENT.md +17 -67
  6. package/claude/skills/role-orchestrator/REQUIREMENT.md +16 -99
  7. package/claude/skills/role-planner/REQUIREMENT.md +15 -73
  8. package/claude/skills/role-worker/REQUIREMENT.md +13 -41
  9. package/claude/skills/target-setup/SKILL.md +2 -2
  10. package/claude/skills/target-setup/references/detect.md +0 -1
  11. package/claude/skills/target-setup/references/verify.md +2 -2
  12. package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
  13. package/claude/skills/teach-workspace/references/promotion.md +5 -5
  14. package/docs/agents/install-and-sync.md +2 -2
  15. package/docs/agents/output-shape.md +1 -2
  16. package/docs/target/migrations.md +18 -0
  17. package/docs/target/projects.md +1 -2
  18. package/docs/target/scenarios.md +4 -1
  19. package/docs/workflow/skill-map-on-request.md +1 -1
  20. package/governance/detect.toml +4 -0
  21. package/governance/ledger/lang/go.md +61 -0
  22. package/governance/ledger/lang/php.md +50 -0
  23. package/governance/ledger/lang/python.md +68 -0
  24. package/governance/ledger/lang/typescript.md +64 -0
  25. package/governance/ledger/lib/database.md +65 -0
  26. package/governance/ledger/lib/e2e-reliability.md +57 -0
  27. package/governance/ledger/lib/pydantic.md +61 -0
  28. package/governance/ledger/lib/security-server.md +62 -0
  29. package/governance/ledger/lib/security-web.md +51 -0
  30. package/governance/ledger/lib/tanstack-query.md +57 -0
  31. package/governance/ledger/lib/test-scope.md +43 -0
  32. package/governance/ledger/lib/testing-go.md +57 -0
  33. package/governance/ledger/lib/testing-php.md +51 -0
  34. package/governance/ledger/lib/testing-py.md +67 -0
  35. package/governance/ledger/lib/testing-ts.md +63 -0
  36. package/governance/ledger/lib/zod.md +47 -0
  37. package/governance/ledger/tooling/ci-workflow.md +33 -0
  38. package/governance/ledger/tooling/code-comments.md +48 -0
  39. package/governance/ledger/tooling/config-comments.md +27 -0
  40. package/governance/ledger/tooling/dependencies.md +38 -0
  41. package/governance/ledger/ui/a11y.md +55 -0
  42. package/governance/ledger/ui/copy.md +49 -0
  43. package/governance/ledger/ui/design-reference.md +42 -0
  44. package/governance/ledger/ui/design-taste.md +19 -0
  45. package/governance/ledger/ui/forms.md +71 -0
  46. package/governance/ledger/ui/interface-casing.md +47 -0
  47. package/governance/ledger/ui/keyboard.md +68 -0
  48. package/governance/ledger/ui/link-behavior.md +29 -0
  49. package/governance/ledger/ui/motion.md +33 -0
  50. package/governance/ledger/ui/surface-capture.md +69 -0
  51. package/governance/ledger/ui/ux-completeness.md +79 -0
  52. package/governance/rules/lang/go.md +7 -16
  53. package/governance/rules/lang/php.md +3 -3
  54. package/governance/rules/lang/python.md +9 -18
  55. package/governance/rules/lang/typescript.md +9 -19
  56. package/governance/rules/lib/database.md +5 -5
  57. package/governance/rules/lib/e2e-reliability.md +4 -6
  58. package/governance/rules/lib/pydantic.md +14 -16
  59. package/governance/rules/lib/security-server.md +4 -4
  60. package/governance/rules/lib/security-web.md +6 -10
  61. package/governance/rules/lib/tanstack-query.md +10 -12
  62. package/governance/rules/lib/test-scope.md +4 -4
  63. package/governance/rules/lib/testing-go.md +1 -2
  64. package/governance/rules/lib/testing-php.md +4 -5
  65. package/governance/rules/lib/testing-py.md +11 -19
  66. package/governance/rules/lib/testing-ts.md +7 -16
  67. package/governance/rules/lib/zod.md +6 -8
  68. package/governance/rules/tooling/code-comments.md +2 -4
  69. package/governance/rules/tooling/config-comments.md +1 -4
  70. package/governance/rules/tooling/dependencies.md +1 -1
  71. package/governance/rules/ui/a11y.md +16 -45
  72. package/governance/rules/ui/copy.md +6 -9
  73. package/governance/rules/ui/forms.md +13 -19
  74. package/governance/rules/ui/interface-casing.md +5 -7
  75. package/governance/rules/ui/keyboard.md +38 -0
  76. package/governance/rules/ui/link-behavior.md +3 -3
  77. package/governance/rules/ui/motion.md +3 -3
  78. package/governance/rules/ui/surface-capture.md +15 -27
  79. package/governance/rules/ui/ux-completeness.md +16 -39
  80. package/package.json +1 -1
  81. package/scripts/core/install-check.sh +1 -1
  82. package/src/cli/help.ts +0 -1
  83. package/src/cli.ts +0 -2
  84. package/src/commands/gov.ts +4 -0
  85. package/src/commands/init.ts +2 -2
  86. package/src/commands/tooling.ts +18 -5
  87. package/src/design/components.ts +1 -1
  88. package/src/gov/audit.ts +22 -2
  89. package/src/init/plan.ts +1 -8
  90. package/src/init/steps.ts +0 -8
  91. package/src/inventory/subjects.ts +1 -1
  92. package/src/markdown/scope.ts +4 -0
  93. package/src/migrate/scratch-evidence.ts +12 -4
  94. package/src/records/backup.ts +3 -3
  95. package/src/records/validate.ts +3 -3
  96. package/src/roots/record.ts +17 -8
  97. package/src/tooling/inject.ts +26 -7
  98. package/src/tooling/package.ts +32 -0
  99. package/src/tooling/read.ts +46 -3
  100. package/src/tooling/scan.ts +8 -2
  101. package/tooling/astro/reference.md +64 -93
  102. package/tooling/base/configs/.github/workflows/phase-label-gate.yml +1 -1
  103. package/tooling/base/configs/scripts/verify.ts +1 -1
  104. package/tooling/base/reference.md +1 -9
  105. package/tooling/base/seeds/canon/context/{ci.md → development/ci.md} +1 -1
  106. package/tooling/base/seeds/canon/context/development/index.md +11 -0
  107. package/tooling/base/seeds/canon/context/{development.md → development/setup.md} +8 -12
  108. package/tooling/base/seeds/canon/context/index.md +1 -2
  109. package/tooling/claude/manifest.toml +1 -5
  110. package/tooling/claude/reference.md +36 -59
  111. package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +2 -7
  112. package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +2 -7
  113. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +3 -10
  114. package/tooling/claude/seeds/.claude/hooks/search-reminder.sh +2 -7
  115. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +2 -4
  116. package/tooling/nextjs/reference.md +0 -6
  117. package/tooling/php/reference.md +0 -4
  118. package/tooling/web/configs/.github/workflows/verify.yml +28 -0
  119. package/tooling/web/configs/.oxlintrc.json +7 -0
  120. package/tooling/web/configs/e2e/capture-types.ts +35 -0
  121. package/tooling/web/configs/e2e/screenshot.ts +23 -72
  122. package/tooling/web/manifest.toml +1 -1
  123. package/tooling/web/reference.md +77 -124
  124. package/tooling/web/seeds/e2e/capture/cases.ts +47 -0
  125. package/src/commands/wiki.ts +0 -100
  126. package/src/wiki/init.ts +0 -85
@@ -0,0 +1,69 @@
1
+ ---
2
+ title: Surface capture rule ledger
3
+ description: Sources for governance/rules/ui/surface-capture.md, one row per bullet
4
+ ---
5
+
6
+ # Surface capture rule ledger
7
+
8
+ The rule states canon's own evidence procedure for a rendered change, so most rows are house choices naming the bug the bullet prevents. The 320 floor rests on WCAG 2.2, and each explanation the old rule carried moved into a row here.
9
+
10
+ ## What a surface is
11
+
12
+ | Bullet | Source | Read |
13
+ | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
14
+ | Generated pages are surfaces | House choice: reading a generated page's markup reports nothing about how it composes, so a page nobody captured ships its layout unseen | 2026-10-18 |
15
+ | Skip build-stripped components | House choice: the production build removes a dev-only switcher before anything renders, so a capture of it compares nothing a person sees | 2026-10-18 |
16
+
17
+ ## When to capture
18
+
19
+ | Bullet | Source | Read |
20
+ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
21
+ | Preview, not dev server | House choice: a dev server injects overlays and skips the production build's transforms, so its frame differs from what ships | 2026-10-18 |
22
+ | Every theme, and again | House choice: a defect in the dark theme passes a light-only capture, and a repair inside a shared stylesheet can cancel a rule written earlier in the same file | 2026-10-18 |
23
+
24
+ ## What a capture covers
25
+
26
+ | Bullet | Source | Read |
27
+ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
28
+ | Full page | House choice: a component captured alone shows none of the spacing, overflow, or stacking its page imposes on it | 2026-10-18 |
29
+ | 320 plus breakpoints | [Understanding Reflow](https://www.w3.org/WAI/WCAG22/Understanding/reflow.html): the criterion tests "a width equivalent to 320 CSS pixels". A 320 frame proves a frame was taken there, never that the page reflows, which `a11y.md` states. | 2026-10-18, WCAG 2.2 |
30
+ | Chrome at both clamps | House choice: a sidebar widened to its clamp leaves the content under the floor at a window width the criterion passes, and a frame where the chrome turned into an overlay shows none of the width it takes | 2026-10-18 |
31
+ | State case at one width | House choice: a case driving a menu open tests the state, and repeating it at every layout width multiplies the evidence with no layout to compare | 2026-10-18 |
32
+ | Drive unreachable states | House choice: an initial render reports nothing about a menu that opens, an answer that is chosen, or a rail that tracks scrolling | 2026-10-18 |
33
+ | Case lives with the surface | House choice: a surface with no case ships uncaptured, and a case left after its surface goes fails every later capture run | 2026-10-18 |
34
+
35
+ ## Sharing a capture
36
+
37
+ | Bullet | Source | Read |
38
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
39
+ | An `evidence` folder | House choice: the pull request comment collects an image only when a path segment is literally `evidence`, so any other name drops the capture. Several such folders, one per surface, is the ordinary shape. | 2026-10-18 |
40
+ | Commit flagged cases only | House choice: recommitting an unbounded sweep on every run reaches a gigabyte of history inside a hundred merges, while a small committed set lets GitHub draw its own before and after comparison | 2026-10-18 |
41
+ | Byte-identical twice | House choice: a case whose output varies between runs shows a diff on every pull request, so the comparison stops meaning a change | 2026-10-18 |
42
+ | Mask the moving value | House choice: an elapsed-time counter or a relative date differs on every run, and masking the smallest element keeps the check running on the rest of the frame | 2026-10-18 |
43
+ | Name the variance | House choice: a value whose width reflows its neighbors survives the mask, and re-running until two frames match hides the variance rather than removing it | 2026-10-18 |
44
+
45
+ ## Declined
46
+
47
+ Every bullet the rewrite removed or reshaped, grouped by why.
48
+
49
+ ### Moved into a row as the reason
50
+
51
+ - Moved "judge a generated page by the same rule as a route, since reading its markup reports nothing" into the generated pages row.
52
+ - Moved "read a capture at 320 as proof that a frame was taken there, not that the page reflows" into the 320 row.
53
+ - Moved "several `evidence/` folders in one project is the ordinary shape" into the folder row.
54
+ - Moved "recommitting an unbounded sweep reaches a gigabyte of repository history" into the commit row.
55
+ - Moved "do not commit the state as evidence when the masked outputs still differ" into the variance bullet and its row.
56
+
57
+ ### Cut as stated elsewhere
58
+
59
+ - Cut "`git-pr` and `git-followup` maintain a comparison comment automatically", which those skills own and which directs nothing at the edit.
60
+ - Cut "fall back to the 320 floor alone when the project has no `DESIGN.md`", merged into the 320 bullet's "or at 320 alone".
61
+ - Cut "read those bounds from the code that clamps the chrome", merged into the chrome bullet's "clamp".
62
+ - Cut "do not impose another project's breakpoints", which the bullet reading `## Layout` already prevents.
63
+
64
+ ### Merged or reworded
65
+
66
+ - Merged "run the capture command", "against a running preview", "every theme", and "capture again" into two bullets.
67
+ - Merged "do not commit the sweep" with "commit a flagged case's evidence" and the established convention exception.
68
+ - Merged "add a case" and "remove a surface's case" into one bullet.
69
+ - Shortened every bullet past 200 characters, cutting the rule from 27 bullets and 840 words to 15 and under 300.
@@ -0,0 +1,79 @@
1
+ ---
2
+ title: UX completeness rule ledger
3
+ description: Sources for governance/rules/ui/ux-completeness.md, one row per bullet
4
+ ---
5
+
6
+ # UX completeness rule ledger
7
+
8
+ The field is interaction design, and the authorities are the Nielsen Norman Group's research articles, WCAG 2.2 for timing, MDN for the `title` attribute and CSS truncation, and web.dev for layout shift.
9
+
10
+ ## Feedback
11
+
12
+ | Bullet | Source | Read |
13
+ | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
14
+ | Confirm success and failure | [Visibility of system status](https://www.nngroup.com/articles/visibility-system-status/): "Whenever users interact with a system, they need to know whether the interaction was successful" | 2026-10-18 |
15
+ | Auto-dismiss only a redundant message | [Understanding Timing Adjustable](https://www.w3.org/WAI/WCAG22/Understanding/timing-adjustable.html): "If the user has no other means of discovering the same information (or performing the same function), then each message would need to meet this success criterion" | 2026-10-18, WCAG 2.2 |
16
+ | No optimistic status | House choice: a "connected" dot shown over unsaved edits reports a state the system has not reached, and the person leaves believing their change took | 2026-10-18 |
17
+
18
+ ## Empty states
19
+
20
+ | Bullet | Source | Read |
21
+ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
22
+ | Empty state with a next action | [Designing empty states](https://www.nngroup.com/articles/empty-state-interface-design/): designed empty states "Provide direct pathways for getting started with key tasks" | 2026-10-18 |
23
+ | Distinguish new from filtered | [Designing empty states](https://www.nngroup.com/articles/empty-state-interface-design/): after a filter returns nothing, people wonder "Did an error occur? Did I set the wrong filters or parameters?", and "Totally empty states cause confusion" | 2026-10-18 |
24
+
25
+ ## Loading states
26
+
27
+ | Bullet | Source | Read |
28
+ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
29
+ | Indicator past one second | [Progress indicators](https://www.nngroup.com/articles/progress-indicators/): "Use a progress indicator for any action that takes longer than about 1.0 second" | 2026-10-18 |
30
+ | Mark stale content as updating | [Progress indicators](https://www.nngroup.com/articles/progress-indicators/): "Without any visual change from the system, most users will assume the action was not registered and they will try again" | 2026-10-18 |
31
+
32
+ ## Destructive actions
33
+
34
+ | Bullet | Source | Read |
35
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
36
+ | Confirm unless undoable | [Confirmation dialogs](https://www.nngroup.com/articles/confirmation-dialog/): "consider a confirmation dialog before actions that cannot be undone", and "do try your best to offer undo" | 2026-10-18 |
37
+
38
+ ## Element states
39
+
40
+ | Bullet | Source | Read |
41
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
42
+ | Distinct interaction states | [Visibility of system status](https://www.nngroup.com/articles/visibility-system-status/): "Did the system actually catch that button press", and an element with no pressed or hover response leaves the person unable to tell | 2026-10-18 |
43
+ | No intermediate flash | [Cumulative Layout Shift](https://web.dev/articles/cls): "Unexpected layout shifts can disrupt the user experience", caused by content "dynamically added to the page before existing content" | 2026-10-18 |
44
+
45
+ ## Overflow
46
+
47
+ | Bullet | Source | Read |
48
+ | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
49
+ | Ellipsis over overflow | [text-overflow](https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow): `text-overflow: ellipsis` with `overflow: hidden` and `white-space: nowrap` clips text at the box with a visible marker | 2026-10-18 |
50
+ | Never `title` alone | [The title attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/title): "Use of the title attribute is highly problematic for" people using touch-only devices, keyboards, and assistive technology | 2026-10-18 |
51
+ | Focus rings clear of clipping | House choice: a ring cut off by a parent's `overflow` shows on the middle items of a list and vanishes on the first and last, so the focus indicator fails exactly where the list ends | 2026-10-18 |
52
+
53
+ ## Declined
54
+
55
+ Every bullet the rewrite removed or reshaped, grouped by why.
56
+
57
+ ### Contradicted by the source
58
+
59
+ - Replaced the `title`-or-expand truncation bullet with the never-`title`-alone bullet, since MDN calls the attribute "highly problematic" for touch and keyboard users.
60
+ - Qualified "feedback must auto-dismiss after 2 to 3 seconds" against WCAG 2.2.1, since a message carrying the only copy of its information sets a time limit on reading it.
61
+ - Rewrote "do not show stale content while new content is loading", which offered keeping the previous content as its own alternative, to the marking bullet.
62
+
63
+ ### Moved to a sibling rule
64
+
65
+ - Moved "confirmation UI must be dismissible via Escape" and "inline confirmation rows must be dismissible via Escape" to the Escape bullet in `keyboard.md`.
66
+ - Moved "status indicators must reset to a neutral state when their source input changes" to `forms.md`, which held the same bullet for derived values.
67
+ - Cut "confirmation labels and destructive buttons must have sufficient contrast", which `a11y.md` holds for every control.
68
+
69
+ ### Cut without a source or a bug class
70
+
71
+ - Cut "disabled elements must use muted colors and `cursor: not-allowed`", a convention Claude already follows that no source sets.
72
+ - Cut "use CSS transitions or deferred rendering to prevent visible flicker", merged into the intermediate flash bullet as its remedy.
73
+
74
+ ### Merged or reworded
75
+
76
+ - Merged the `## Inline confirmations`, `## Status indicators`, `## State transitions`, `## Interactive element states`, and `## Scroll and clipping` H2s into six, the merge the plan suggested.
77
+ - Merged "every list must handle the empty state" with "empty states must include a next action", and the two filter bullets into one.
78
+ - Merged "do not rely on a single state to communicate interactivity" into the distinct states bullet.
79
+ - Reworded "every async operation must show a loading indicator" to NN/g's one second threshold, since a shorter spinner distracts.
@@ -1,40 +1,31 @@
1
1
  ---
2
- description: Enforce Go naming, error wrapping, context passing, and package boundaries
2
+ description: Enforce Go error wrapping, context passing, interface placement, and package boundaries
3
3
  paths:
4
4
  - '**/*.go'
5
5
  ---
6
6
 
7
7
  # Go standards
8
8
 
9
- ## Casing conventions
10
-
11
- - Use `MixedCaps` for exported names and `mixedCaps` for unexported ones. Never use underscores in a Go identifier.
12
- - Name packages with one short lowercase word. Do not use `util`, `common`, or `helpers` as a package name.
13
- - Do not repeat the package name in an exported identifier (`user.New`, not `user.NewUser`).
14
- - Keep initialisms in one case (`userID`, `HTTPClient`), never `userId` or `HttpClient`.
15
-
16
9
  ## Errors
17
10
 
18
- - Return an error as the last result value.
19
11
  - Wrap an error with `fmt.Errorf("...: %w", err)` when adding context.
20
- - Match an error with `errors.Is` or `errors.As`. Never compare `err.Error()` strings.
21
- - Never discard an error with `_`.
22
- - Do not `panic` for a failure a caller can expect. Reserve it for a broken invariant.
12
+ - Match an error with `errors.Is` or `errors.As`. Never compare with `==` or on `err.Error()` strings, which miss a wrapped error.
13
+ - Never discard an error with `_`. Handle it, return it, or panic on a broken invariant.
14
+ - Return an `error` for a failure a caller can expect. Reserve `panic` for a broken invariant.
23
15
 
24
16
  ## Context
25
17
 
26
18
  - Pass `context.Context` as the first parameter, named `ctx`.
27
- - Never store a `context.Context` in a struct field.
19
+ - Never store a `context.Context` in a struct field. Pass it to each function that needs it.
28
20
  - Do not pass a `nil` context. Use `context.TODO()` where none is available yet.
29
21
 
30
22
  ## Interfaces
31
23
 
32
- - Declare an interface in the package that consumes it, not the one that implements it.
33
- - Keep an interface to the methods its consumer calls.
24
+ - Declare an interface in the package that consumes it, holding only the methods that consumer calls.
34
25
  - Accept interfaces and return concrete types.
35
26
 
36
27
  ## Packages
37
28
 
38
29
  - Place code no other module may import under `internal/`.
39
30
  - Do not perform side effects in `init`. Wire dependencies explicitly from `main`.
40
- - Do not hold mutable state in a package-level variable.
31
+ - Do not hold mutable state in a package-level variable. Pass it through a struct the caller owns.
@@ -19,7 +19,7 @@ paths:
19
19
 
20
20
  ## Data shapes
21
21
 
22
- - Use a backed `enum` for a closed set over class constants.
22
+ - Use an `enum` for a closed set over class constants, backed by a string or int when the value is stored or serialized.
23
23
  - Use `readonly` properties or a `readonly` class for value objects.
24
24
  - Declare classes `final` unless they are designed for extension.
25
25
  - Do not pass an associative array across a public boundary where a typed object fits.
@@ -27,5 +27,5 @@ paths:
27
27
  ## Errors
28
28
 
29
29
  - Throw a domain exception for a failure. Do not return `false` or `null` to signal one.
30
- - Never suppress an error with the `@` operator.
31
- - Do not use `extract`, variable variables, or `eval`.
30
+ - Never suppress an error with the `@` operator. Check the condition first or catch the exception.
31
+ - Do not use `extract`, variable variables, or `eval`. Name each variable and call each function directly.
@@ -1,31 +1,22 @@
1
1
  ---
2
- description: Enforce strict Python type hints, casing, and import patterns
2
+ description: Enforce strict Python type hints, exception handling, data shapes, and import patterns
3
3
  paths:
4
4
  - '**/*.py'
5
5
  ---
6
6
 
7
7
  # Python standards
8
8
 
9
- ## Casing conventions
10
-
11
- - Use `snake_case` for modules, functions, variables, and methods.
12
- - Use `PascalCase` for classes, type aliases, `TypeVar`, and `ParamSpec`.
13
- - Use `UPPER_SNAKE_CASE` for module-level constants and environment variables.
14
- - Use `_leading_underscore` for module-private names. Reserve `__dunder__` for the standard protocol.
15
-
16
9
  ## Type hints
17
10
 
18
- - Annotate all public functions, methods, and class attributes.
19
- - Use `X | None` over `Optional[X]` and `X | Y` over `Union[X, Y]`.
20
- - Use built-in generics (`list[str]`, `dict[str, int]`) over `typing.List` and `typing.Dict`.
21
- - Avoid `from __future__ import annotations` in modules that pydantic, FastAPI, or other libraries introspect at runtime.
22
- - Use `typing.Protocol` for structural typing over abstract base classes for interfaces.
23
- - Do not use `Any`. Reach for `object` or `typing.cast` at boundaries instead.
11
+ - Annotate every public function and method, since a type checker skips the body of an unannotated function.
12
+ - Use `X | None`, `X | Y`, and built-in generics such as `list[str]` over `Optional`, `Union`, and `typing.List`.
13
+ - Do not add `from __future__ import annotations`. Python plans to deprecate and remove it.
14
+ - Use `typing.Protocol` for an interface a class satisfies by shape, over an abstract base class it must subclass.
15
+ - Type a value that can be anything as `object` and narrow it. Never use `Any`, which turns checking off.
24
16
 
25
17
  ## Errors
26
18
 
27
- - Raise specific built-in exceptions over bare `Exception`.
28
- - Define a project-rooted exception hierarchy for domain errors.
19
+ - Raise a specific built-in exception or one from a project-rooted hierarchy, never bare `Exception`.
29
20
  - Catch the narrowest exception class. Never use bare `except:`.
30
21
  - Re-raise with `raise ... from err` to preserve the cause chain.
31
22
 
@@ -37,6 +28,6 @@ paths:
37
28
 
38
29
  ## Imports and modules
39
30
 
40
- - Use absolute imports rooted at the package. Do not use parent-relative (`from ..pkg`) imports.
31
+ - Use absolute imports rooted at the package over relative ones.
41
32
  - Do not alias imports to shorten internal names. Ecosystem conventions (`numpy as np`, `pandas as pd`) are exceptions.
42
- - Do not place executable code at module scope. Guard with `if __name__ == "__main__":`.
33
+ - Do not place executable code at module scope. Guard it with `if __name__ == "__main__":` and call a `main` function.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Enforce strict TypeScript type safety and patterns
2
+ description: Enforce strict TypeScript type safety, declarations, and import patterns
3
3
  paths:
4
4
  - '**/*.ts'
5
5
  - '**/*.tsx'
@@ -7,34 +7,24 @@ paths:
7
7
 
8
8
  # TypeScript standards
9
9
 
10
- ## Casing conventions
10
+ ## File names
11
11
 
12
- - Use `kebab-case` for filenames and directories.
13
- - Use `camelCase` for variables, functions, and methods.
14
- - Use `PascalCase` for types, interfaces, classes, and components.
15
- - Use `UPPER_SNAKE_CASE` for constants and environment variables.
12
+ - Use `kebab-case` for filenames and directories, since a case-only difference resolves on macOS and fails on Linux.
16
13
 
17
14
  ## Type declarations
18
15
 
19
- - Enforce explicit types or strict inference.
20
- - Use `unknown` over `any`.
21
- - Use `interface` for object shapes and component props.
22
- - Use `type` for unions, intersections, and utility types.
23
- - Do not prefix interfaces with `I`.
24
- - Use constant objects or union types instead of `enum`.
16
+ - Use `unknown` over `any`, and narrow it before use.
17
+ - Use `interface` for an object shape until it needs a feature only `type` has, such as a union or a mapped type.
18
+ - Use a constant object with `as const` or a union of literals instead of `enum`.
25
19
 
26
20
  ## Type safety
27
21
 
28
- - Use type guards and narrowing over type assertions.
29
- - Use discriminated unions for error handling over throwing exceptions.
30
- - Use built-in utility types (`Partial`, `Pick`, `Omit`) over manual type manipulation.
31
- - Prefer `readonly` properties for data objects.
32
- - Do not use non-null assertions.
33
- - Use `Promise.all()` for independent async operations.
22
+ - Narrow with a type guard or a runtime check over a type assertion or a non-null `!`, neither of which checks at runtime.
23
+ - Run independent async operations through `Promise.all()` rather than awaiting each in turn.
34
24
 
35
25
  ## Imports and configuration
36
26
 
37
- - Use absolute imports mapping `@/` to `src/`.
27
+ - Import through the `@/` alias where tsconfig.json and the bundler both map it to `src/`, over a `../../` chain.
38
28
  - Import from the module's source file directly over barrel `index` re-exports.
39
29
  - Use `import type` for type-only imports.
40
30
  - Enable `strict: true` in tsconfig.json with no exceptions.
@@ -14,22 +14,22 @@ paths:
14
14
  ## Schema changes
15
15
 
16
16
  - Express every schema change as a migration file committed to version control.
17
- - Ship a tested rollback path with every migration that changes a schema.
17
+ - Ship a way back with every schema migration, as a down step or a forward migration that reverses it.
18
18
  - Never alter a deployed schema outside a migration.
19
19
  - Never edit a migration that has run in a shared environment. Add a new one.
20
- - Separate a destructive migration from the deploy that stops reading the dropped column.
20
+ - Drop a column or table only after a deploy that stops reading and writing it.
21
21
 
22
22
  ## Query construction
23
23
 
24
24
  - Pass every runtime value as a bound parameter.
25
25
  - Never interpolate, concatenate, or format a value into a statement string.
26
- - Name the columns a read needs. Do not select every column by wildcard.
26
+ - Map a table name, column, or sort direction through an allow-list, since none of them can be bound.
27
27
 
28
28
  ## Transactions
29
29
 
30
30
  - Give each transaction one owner that opens it, commits it, and rolls it back.
31
- - Never open a transaction inside a function that was handed one.
32
- - Keep network calls, queue publishes, and filesystem writes outside an open transaction.
31
+ - Never commit a transaction a function was handed. Take a savepoint, such as a nested `atomic`, for a partial rollback.
32
+ - Keep network calls, queue publishes, and filesystem writes outside an open transaction, running them after commit.
33
33
 
34
34
  ## Access patterns
35
35
 
@@ -8,13 +8,12 @@ paths:
8
8
 
9
9
  ## Waiting
10
10
 
11
- - Settle on the condition a step waits for. Use `expect.poll` or a web-first assertion ahead of any read.
11
+ - Settle on the condition a step waits for with a web-first assertion or `expect.poll`, never `page.waitForTimeout`.
12
12
  - Reserve a fixed duration for an assertion that nothing happened across a window.
13
- - Bound every settle with an explicit timeout.
13
+ - Bound every settle with an explicit timeout. `toPass` defaults to none and ignores the expect timeout.
14
14
  - Do not raise a timeout to clear a failure that reproduces under load. Replace the wait with a settle.
15
- - Do not read a value once after a pause. Poll it.
16
15
  - Settle a smooth scroll on the `scrollend` event. Fall back to a stillness window only when the target was already in view.
17
- - Run with motion reduced unless the test asserts motion, and opt back in per test.
16
+ - Run with `reducedMotion: 'reduce'` unless the test asserts motion, and opt back in per test.
18
17
 
19
18
  ## Falsifiable guards
20
19
 
@@ -26,9 +25,8 @@ paths:
26
25
  ## Reproducing a failure that only appears in CI
27
26
 
28
27
  - Reproduce under `Emulation.setCPUThrottlingRate` rather than by rerunning the gate.
29
- - Read the state the assertion does not: which markers were set, which listeners fired, how far a transition ran.
28
+ - Read the failure's trace for the state the assertion does not read, such as which markers were set and which listeners fired.
30
29
  - Vary the condition under suspicion deliberately. Do not compare counts across runs that differed in something uncontrolled.
31
- - Read the check conclusion as its own act. A green diff review reports nothing about the gate.
32
30
 
33
31
  ## Authority
34
32
 
@@ -8,31 +8,29 @@ paths:
8
8
 
9
9
  ## Models
10
10
 
11
- - Use `pydantic.BaseModel` over `dataclass` at I/O and API boundaries.
12
- - Use `model_config = ConfigDict(...)` over the legacy nested `Config` class.
13
- - Set `extra="forbid"` on models receiving external input to prevent data pollution.
14
- - Use `Field(...)` for defaults, descriptions, and constraints over docstring conventions.
11
+ - Use `pydantic.BaseModel` over a standard library `dataclass` at I/O and API boundaries, since a `dataclass` validates nothing.
12
+ - On Pydantic 2, configure a model with `model_config = ConfigDict(...)`, never the deprecated nested `Config` class.
13
+ - Set `extra="forbid"` on a model receiving external input, since the default ignores an unknown key silently.
14
+ - Declare a constraint with `Field(...)`, such as `gt=0`, so validation enforces it rather than a docstring.
15
15
 
16
16
  ## Validation
17
17
 
18
- - Use `Model.model_validate(data)` over `Model(**data)` for untrusted dict input.
19
- - Use `Model.model_validate_json(s)` over `json.loads` followed by validation.
20
- - Use `@field_validator` over the legacy `@validator`.
21
- - Use `@model_validator(mode="after")` for cross-field invariants.
18
+ - Validate an untrusted dict with `Model.model_validate(data)`, and a JSON string with `Model.model_validate_json(s)` rather than `json.loads` first.
19
+ - On Pydantic 2, write `@field_validator` and `@model_validator`, never the deprecated `@validator` or `@root_validator`.
20
+ - Check a cross-field invariant in `@model_validator(mode="after")`.
22
21
 
23
22
  ## Types
24
23
 
25
- - Use `AnyUrl`, `EmailStr`, `SecretStr`, and `PositiveInt` over hand-rolled regex constraints.
26
- - Use `Annotated[T, Field(...)]` over default-value `Field(...)` for reusable constrained types.
27
- - Do not use `Any` in fields. Reach for `JsonValue` or a `Union` of concrete types.
24
+ - Use `SecretStr` for a credential, so its value stays out of logs and tracebacks.
25
+ - Use a constrained type such as `PositiveInt` or `EmailStr` over a hand-rolled regex.
26
+ - Share a constrained type as `Annotated[T, Field(...)]`.
27
+ - Never type a field `Any`, which allows any value including `None`. Use `JsonValue` or a union of concrete types.
28
28
 
29
29
  ## Settings
30
30
 
31
- - Use `pydantic_settings.BaseSettings` for env-driven configuration.
32
- - Load environment through a single `Settings` model. Do not read `os.environ` directly elsewhere.
31
+ - Load environment through one `pydantic_settings.BaseSettings` model. Never read `os.environ` directly elsewhere.
33
32
 
34
33
  ## Serialization
35
34
 
36
- - Use `model.model_dump(mode="json")` over the legacy `.dict()`.
37
- - Use `model.model_dump_json()` over `json.dumps(model.dict())`.
38
- - Use `by_alias=True` when serializing to external schemas with non-Python field names.
35
+ - On Pydantic 2, serialize with `model_dump(mode="json")` or `model_dump_json()`, never the deprecated `.dict()` or `.json()`.
36
+ - Pass `by_alias=True` when serializing to an external schema, since a dump ignores aliases by default.
@@ -19,7 +19,7 @@ paths:
19
19
 
20
20
  ## Credentials
21
21
 
22
- - Hash passwords with a memory-hard algorithm and a per-record salt. Never store a reversible password.
22
+ - Hash passwords with Argon2id, or scrypt or bcrypt where it is missing, and a per-password salt. Never store a reversible password.
23
23
  - Compare tokens, signatures, and password hashes with a constant-time function.
24
24
  - Expire and revoke sessions and refresh tokens on the server. Do not rely on client-side deletion.
25
25
 
@@ -27,7 +27,7 @@ paths:
27
27
 
28
28
  - Read every secret from the environment or a secret manager at runtime.
29
29
  - Never commit a secret, connection string, or private key to the repository.
30
- - Never return a secret, connection string, or internal hostname in a response body.
30
+ - Never return a secret, connection string, stack trace, or internal hostname in a response body.
31
31
 
32
32
  ## Abuse limits
33
33
 
@@ -37,5 +37,5 @@ paths:
37
37
 
38
38
  ## Transport
39
39
 
40
- - Serve every route over TLS and reject plaintext requests.
41
- - Restrict CORS to an explicit origin list. Never reflect the request origin or pair a wildcard with credentials.
40
+ - Serve every route over TLS. Fail a plaintext API request rather than redirecting it.
41
+ - List CORS origins explicitly whenever credentials are allowed, since a wildcard is refused then. Never reflect the request origin.
@@ -9,24 +9,20 @@ paths:
9
9
 
10
10
  # Web security standards
11
11
 
12
- ## Link safety
13
-
14
- - Add `rel="noopener noreferrer"` to all external links with `target="_blank"`.
15
-
16
12
  ## Content sanitization
17
13
 
18
- - Sanitize all user-generated content rendered via `dangerouslySetInnerHTML` using DOMPurify or equivalent.
19
- - Never use `dangerouslySetInnerHTML` without sanitization.
14
+ - Sanitize HTML with DOMPurify before passing it to `dangerouslySetInnerHTML`, `innerHTML`, or `set:html`. Never render it raw.
15
+ - Allow only `http:` and `https:` in a user-supplied URL before rendering it in `href` or `src`.
20
16
 
21
17
  ## Input validation
22
18
 
23
- - Validate all URL parameters and query strings using schema validation before use.
19
+ - Parse URL parameters and query strings through a schema before use.
24
20
 
25
21
  ## Storage and tokens
26
22
 
27
- - Do not store sensitive data (tokens, passwords, PII) in `localStorage` or `sessionStorage`.
28
- - Store authentication tokens in httpOnly cookies over exposing them to JavaScript.
23
+ - Never store a session token, password, or personal data in `localStorage` or `sessionStorage`, which any script on the page can read.
24
+ - Keep an authentication token in an `HttpOnly` cookie, out of reach of JavaScript.
29
25
 
30
26
  ## Third-party scripts
31
27
 
32
- - Audit and pin versions for all third-party scripts loaded in the browser.
28
+ - Pin a third-party script to a version and load it with an `integrity` hash.
@@ -9,24 +9,22 @@ paths:
9
9
 
10
10
  ## Query conventions
11
11
 
12
- - Define query keys as `readonly` tuple constants.
13
- - Co-locate query keys with their query functions.
14
- - Encapsulate each query in a custom `use*Query` hook over calling `useQuery` inline in components.
15
- - Set explicit `staleTime` per query based on data volatility over relying on the default.
12
+ - Define a query's key and function together with `queryOptions()`, and reuse that object at every call site.
13
+ - Include every variable the query function reads in its query key.
14
+ - Set `staleTime` per query from how often its data changes, since the default treats cached data as stale at once.
15
+ - Throw from a query function on a response that is not `ok`, since `fetch` does not reject on an HTTP error.
16
16
 
17
17
  ## Mutations
18
18
 
19
- - Use `useMutation` for all write operations.
20
- - Never mutate data outside the mutation lifecycle.
21
- - Invalidate related query keys in `onSuccess` over manual cache updates unless optimistic UI is required.
22
- - Handle `onError` at the mutation site with user-facing feedback.
19
+ - Write data through `useMutation`, never by calling the API from an event handler and patching the cache.
20
+ - Invalidate the related query keys in `onSuccess` and return that promise. Reserve `setQueryData` for an optimistic update.
21
+ - Handle `onError` at the mutation site with feedback the user sees.
23
22
 
24
23
  ## Cache management
25
24
 
26
- - Use `queryClient.invalidateQueries` over `queryClient.setQueryData` unless implementing optimistic updates.
27
- - Prefetch predictable navigations with `queryClient.prefetchQuery` at route boundaries.
25
+ - Prefetch a predictable navigation's data in its route loader.
28
26
 
29
27
  ## Separation of concerns
30
28
 
31
- - Keep query functions as pure async data fetchers with no UI logic or side effects.
32
- - Use the query `enabled` option for conditional fetching over `useEffect`.
29
+ - Keep a query function a pure data fetcher with no UI logic or side effects, since a background refetch runs it again.
30
+ - Use the query `enabled` option for a conditional or dependent fetch, never `useEffect`.
@@ -9,9 +9,9 @@ paths:
9
9
  ## Selecting a run
10
10
 
11
11
  - Narrow an end-to-end run by spec path or by test name. Never narrow it by engine.
12
- - Run one named test while iterating on a behavior: `bun run test:e2e -- -g '<name>'`.
13
- - Run one surface while iterating on that surface: `bun run test:e2e -- e2e/<area>.spec.ts`.
14
- - Run `bun run test:e2e:changed` to select specs from the import graph.
12
+ - Run `playwright test -g '<name>'` for one named test while iterating on a behavior.
13
+ - Run `playwright test e2e/<area>.spec.ts` for one surface while iterating on that surface.
14
+ - Run `playwright test --only-changed` to select the specs a branch changed.
15
15
  - Run the whole suite before pushing.
16
16
  - Pass `--project` in a local run only to reproduce a failure that engine has already reported. The CI matrix passes it on every job, one engine per leg, which is the gate rather than a narrowed run.
17
17
  - Do not add a script that pins a default run to one engine.
@@ -19,6 +19,6 @@ paths:
19
19
  ## Instruments
20
20
 
21
21
  - Answer a question about the running page with a script against the dev server rather than with the suite.
22
- - Do not enable `fullyParallel` in `playwright.config.ts`.
22
+ - Do not enable `fullyParallel` in the Playwright config.
23
23
  - Follow `.claude/rules/canon/ui/surface-capture.md` for capture scope.
24
24
  - Follow `.claude/rules/canon/lib/e2e-reliability.md` for waits and guards.
@@ -13,7 +13,6 @@ paths:
13
13
  ## Framework
14
14
 
15
15
  - Use the standard `testing` package. Do not add an assertion library for what `t.Errorf` and `t.Fatalf` already state.
16
- - Place a test beside the code it tests, in a `_test.go` file of the same package.
17
16
  - Use an external `<pkg>_test` package when a test should see only the exported API.
18
17
 
19
18
  ## Table-driven cases
@@ -24,7 +23,7 @@ paths:
24
23
  ## Helpers and setup
25
24
 
26
25
  - Call `t.Helper()` first in every test helper.
27
- - Use `t.Cleanup` over `defer` for teardown a helper registers.
26
+ - Use `t.Cleanup` over `defer` for teardown a helper registers, since a `defer` runs when the helper returns.
28
27
  - Use `t.TempDir` and `t.Setenv` over hand-rolled temp directories or environment stashes.
29
28
  - Use `net/http/httptest` over a real listener.
30
29
 
@@ -15,16 +15,15 @@ paths:
15
15
 
16
16
  - Use PHPUnit for all tests.
17
17
  - Declare each test class `final` and extend `PHPUnit\Framework\TestCase`.
18
- - Place tests under `tests/`, mirroring the `src/` namespace layout, in files named `*Test.php`.
19
18
 
20
19
  ## Attributes
21
20
 
22
- - Use attributes (`#[Test]`, `#[DataProvider]`, `#[CoversClass]`) over docblock annotations.
23
- - Use a static data provider method for table-driven cases over per-case test methods.
21
+ - On PHPUnit 10 or later, use attributes (`#[Test]`, `#[DataProvider]`, `#[CoversClass]`) over docblock annotations, which PHPUnit 12 no longer reads.
22
+ - Use a `public static` data provider method for table-driven cases over per-case test methods.
24
23
 
25
24
  ## Assertions and doubles
26
25
 
27
- - Use `assertSame` over `assertEquals`.
28
- - Use `expectException` over `try` and `catch` for expected exceptions.
26
+ - Use `assertSame` over `assertEquals`, which compares with `==`.
27
+ - Call `expectException` before the throwing call, over `try` and `catch`, for an expected exception.
29
28
  - Use `createStub` where no interaction is asserted. Reserve `createMock` for asserting a call.
30
29
  - Do not make real network calls. Use a fake or a stubbed client.