@erclx/canon 5.43.0 → 5.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/backlog-triage/references/apply.md +6 -6
- package/claude/skills/canon-cli/SKILL.md +4 -5
- package/claude/skills/canvas-board/REQUIREMENT.md +1 -1
- package/claude/skills/canvas-board/SKILL.md +1 -1
- package/claude/skills/context-fold/references/context.md +1 -1
- package/claude/skills/create-skill/REQUIREMENT.md +1 -1
- package/claude/skills/create-skill/SKILL.md +2 -2
- package/claude/skills/create-skill/references/skill-requirement.md +68 -9
- package/claude/skills/create-skill/references/skill.md +1 -1
- package/claude/skills/design-identity/REQUIREMENT.md +40 -0
- package/claude/skills/design-identity/SKILL.md +138 -0
- package/claude/skills/{draft-identity → design-identity}/references/card-route.md +2 -2
- package/claude/skills/design-taste/REQUIREMENT.md +6 -6
- package/claude/skills/design-taste/SKILL.md +2 -2
- package/claude/skills/design-taste/references/kinds.md +1 -1
- package/claude/skills/design-taste/references/preflight.md +1 -1
- package/claude/skills/draft-and-pick/REQUIREMENT.md +8 -69
- package/claude/skills/draft-and-pick/SKILL.md +3 -153
- package/claude/skills/draft-doc/REQUIREMENT.md +9 -30
- package/claude/skills/draft-doc/SKILL.md +4 -76
- package/claude/skills/draft-figure/REQUIREMENT.md +8 -42
- package/claude/skills/draft-figure/SKILL.md +3 -77
- package/claude/skills/draft-identity/REQUIREMENT.md +8 -23
- package/claude/skills/draft-identity/SKILL.md +3 -130
- package/claude/skills/draft-ready/REQUIREMENT.md +8 -37
- package/claude/skills/draft-ready/SKILL.md +3 -128
- package/claude/skills/draft-screencast/REQUIREMENT.md +8 -23
- package/claude/skills/draft-screencast/SKILL.md +3 -62
- package/claude/skills/draft-slides/REQUIREMENT.md +8 -27
- package/claude/skills/draft-slides/SKILL.md +3 -72
- package/claude/skills/draw-figure/REQUIREMENT.md +59 -0
- package/claude/skills/draw-figure/SKILL.md +85 -0
- package/claude/skills/{draft-figure → draw-figure}/references/architecture-set.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/hand-drawn.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/mermaid-path.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/mermaid.md +1 -1
- package/claude/skills/make-deck/REQUIREMENT.md +44 -0
- package/claude/skills/make-deck/SKILL.md +80 -0
- package/claude/skills/markdown-craft/REQUIREMENT.md +1 -1
- package/claude/skills/markdown-craft/SKILL.md +2 -2
- package/claude/skills/pick-by-eye/REQUIREMENT.md +36 -0
- package/claude/skills/pick-by-eye/SKILL.md +161 -0
- package/claude/skills/plan-feature/SKILL.md +2 -1
- package/claude/skills/plan-feature/references/plan.md +24 -10
- package/claude/skills/plan-groundwork/SKILL.md +1 -1
- package/claude/skills/plan-intake/SKILL.md +1 -1
- package/claude/skills/plan-ready/REQUIREMENT.md +54 -0
- package/claude/skills/plan-ready/SKILL.md +136 -0
- package/claude/skills/{draft-ready → plan-ready}/references/assembly.md +1 -1
- package/claude/skills/{draft-ready → plan-ready}/references/ready.md +1 -1
- package/claude/skills/record-screencast/REQUIREMENT.md +3 -3
- package/claude/skills/record-screencast/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/REQUIREMENT.md +16 -99
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +5 -2
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +18 -13
- package/claude/skills/role-planner/REQUIREMENT.md +15 -73
- package/claude/skills/role-planner/SKILL.md +2 -1
- package/claude/skills/role-worker/REQUIREMENT.md +13 -41
- package/claude/skills/role-worker/SKILL.md +9 -0
- package/claude/skills/script-screencast/REQUIREMENT.md +40 -0
- package/claude/skills/script-screencast/SKILL.md +70 -0
- package/claude/skills/{draft-screencast → script-screencast}/references/beats-template.md +2 -2
- package/claude/skills/search-craft/REQUIREMENT.md +4 -4
- package/claude/skills/search-craft/SKILL.md +1 -1
- package/claude/skills/target-setup/SKILL.md +3 -2
- package/claude/skills/target-setup/references/detect.md +0 -1
- package/claude/skills/target-setup/references/verify.md +2 -2
- package/claude/skills/task-board/SKILL.md +11 -9
- package/claude/skills/task-board/references/board.md +15 -12
- package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
- package/claude/skills/teach-workspace/SKILL.md +1 -1
- package/claude/skills/teach-workspace/references/lesson-craft.md +2 -2
- package/claude/skills/teach-workspace/references/promotion.md +5 -5
- package/claude/skills/ui-checklist/SKILL.md +2 -0
- package/claude/skills/write-doc/REQUIREMENT.md +46 -0
- package/claude/skills/write-doc/SKILL.md +83 -0
- package/claude/skills/{draft-doc → write-doc}/references/context.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/docs.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/readme.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/wiki-kind.md +3 -3
- package/docs/agents/install-and-sync.md +2 -2
- package/docs/agents/markdown-audit.md +4 -2
- package/docs/agents/migrate.md +2 -2
- package/docs/agents/output-shape.md +1 -2
- package/docs/target/migrations.md +35 -4
- package/docs/target/projects.md +2 -3
- package/docs/target/scenarios.md +4 -1
- package/docs/workflow/parallel-features.md +3 -2
- package/docs/workflow/skill-map-on-request.md +19 -13
- package/docs/workflow/skill-map.md +13 -12
- package/docs/workflow/visual-design-workflow.md +2 -2
- package/governance/detect.toml +4 -0
- package/governance/ledger/framework/astro.md +73 -0
- package/governance/ledger/framework/fastapi.md +76 -0
- package/governance/ledger/framework/nextjs.md +86 -0
- package/governance/ledger/framework/react.md +83 -0
- package/governance/ledger/framework/shadcn.md +60 -0
- package/governance/ledger/framework/tailwind.md +55 -0
- package/governance/ledger/lang/go.md +61 -0
- package/governance/ledger/lang/php.md +50 -0
- package/governance/ledger/lang/python.md +68 -0
- package/governance/ledger/lang/typescript.md +64 -0
- package/governance/ledger/lib/database.md +65 -0
- package/governance/ledger/lib/e2e-reliability.md +57 -0
- package/governance/ledger/lib/pydantic.md +61 -0
- package/governance/ledger/lib/security-server.md +62 -0
- package/governance/ledger/lib/security-web.md +51 -0
- package/governance/ledger/lib/tanstack-query.md +57 -0
- package/governance/ledger/lib/test-scope.md +43 -0
- package/governance/ledger/lib/testing-go.md +57 -0
- package/governance/ledger/lib/testing-php.md +51 -0
- package/governance/ledger/lib/testing-py.md +67 -0
- package/governance/ledger/lib/testing-ts.md +63 -0
- package/governance/ledger/lib/zod.md +47 -0
- package/governance/ledger/tooling/ci-workflow.md +33 -0
- package/governance/ledger/tooling/code-comments.md +48 -0
- package/governance/ledger/tooling/config-comments.md +27 -0
- package/governance/ledger/tooling/dependencies.md +38 -0
- package/governance/ledger/ui/a11y.md +55 -0
- package/governance/ledger/ui/copy.md +49 -0
- package/governance/ledger/ui/design-reference.md +42 -0
- package/governance/ledger/ui/design-taste.md +19 -0
- package/governance/ledger/ui/forms.md +71 -0
- package/governance/ledger/ui/interface-casing.md +47 -0
- package/governance/ledger/ui/keyboard.md +68 -0
- package/governance/ledger/ui/link-behavior.md +29 -0
- package/governance/ledger/ui/motion.md +33 -0
- package/governance/ledger/ui/surface-capture.md +69 -0
- package/governance/ledger/ui/ux-completeness.md +79 -0
- package/governance/rules/framework/astro.md +12 -14
- package/governance/rules/framework/fastapi.md +20 -24
- package/governance/rules/framework/nextjs.md +19 -27
- package/governance/rules/framework/react.md +22 -33
- package/governance/rules/framework/shadcn.md +11 -15
- package/governance/rules/framework/tailwind.md +12 -14
- package/governance/rules/lang/go.md +7 -16
- package/governance/rules/lang/php.md +3 -3
- package/governance/rules/lang/python.md +9 -18
- package/governance/rules/lang/typescript.md +9 -19
- package/governance/rules/lib/database.md +5 -5
- package/governance/rules/lib/e2e-reliability.md +4 -6
- package/governance/rules/lib/pydantic.md +14 -16
- package/governance/rules/lib/security-server.md +4 -4
- package/governance/rules/lib/security-web.md +6 -10
- package/governance/rules/lib/tanstack-query.md +10 -12
- package/governance/rules/lib/test-scope.md +4 -4
- package/governance/rules/lib/testing-go.md +1 -2
- package/governance/rules/lib/testing-php.md +4 -5
- package/governance/rules/lib/testing-py.md +11 -19
- package/governance/rules/lib/testing-ts.md +7 -16
- package/governance/rules/lib/zod.md +6 -8
- package/governance/rules/tooling/code-comments.md +2 -4
- package/governance/rules/tooling/config-comments.md +1 -4
- package/governance/rules/tooling/dependencies.md +1 -1
- package/governance/rules/ui/a11y.md +16 -45
- package/governance/rules/ui/copy.md +6 -9
- package/governance/rules/ui/forms.md +13 -19
- package/governance/rules/ui/interface-casing.md +5 -7
- package/governance/rules/ui/keyboard.md +38 -0
- package/governance/rules/ui/link-behavior.md +3 -3
- package/governance/rules/ui/motion.md +3 -3
- package/governance/rules/ui/surface-capture.md +15 -27
- package/governance/rules/ui/ux-completeness.md +16 -39
- package/governance/rules/writing/markdown.md +1 -1
- package/package.json +1 -1
- package/scripts/core/install-check.sh +1 -1
- package/src/claude/cases/authoring.ts +15 -3
- package/src/claude/cases/misc.ts +12 -0
- package/src/claude/cases/workflow.ts +19 -1
- package/src/claude/skills-audit.ts +113 -1
- package/src/cli/help.ts +0 -1
- package/src/cli.ts +0 -2
- package/src/commands/claude.ts +34 -11
- package/src/commands/demo.ts +1 -1
- package/src/commands/gov.ts +4 -0
- package/src/commands/init.ts +2 -2
- package/src/commands/markdown.ts +1 -1
- package/src/commands/migrate.ts +4 -0
- package/src/commands/pr.ts +30 -6
- package/src/commands/tasks.ts +383 -2
- package/src/commands/tooling.ts +18 -5
- package/src/context/audit.ts +1 -1
- package/src/demo/beats.ts +1 -1
- package/src/design/components.ts +1 -1
- package/src/gov/audit.ts +25 -4
- package/src/init/plan.ts +1 -8
- package/src/init/steps.ts +0 -8
- package/src/inventory/subjects.ts +1 -1
- package/src/markdown/scope.ts +49 -35
- package/src/migrate/record-layout.ts +1 -1
- package/src/migrate/scratch-evidence.ts +12 -4
- package/src/migrate/skill-names.ts +68 -21
- package/src/pr/frames.ts +14 -3
- package/src/records/backup.ts +3 -3
- package/src/records/validate.ts +31 -0
- package/src/roots/record.ts +17 -8
- package/src/tasks/board.ts +854 -0
- package/src/tasks/record.ts +20 -1
- package/src/tasks/validate.ts +206 -9
- package/src/tooling/inject.ts +26 -7
- package/src/tooling/package.ts +32 -0
- package/src/tooling/read.ts +46 -3
- package/src/tooling/scan.ts +8 -2
- package/tooling/astro/configs/card-src/pages/og-card.astro +1 -1
- package/tooling/astro/reference.md +64 -93
- package/tooling/base/configs/.github/workflows/phase-label-gate.yml +1 -1
- package/tooling/base/configs/scripts/verify.ts +1 -1
- package/tooling/base/reference.md +1 -9
- package/tooling/base/seeds/canon/context/{ci.md → development/ci.md} +1 -1
- package/tooling/base/seeds/canon/context/development/index.md +11 -0
- package/tooling/base/seeds/canon/context/{development.md → development/setup.md} +8 -12
- package/tooling/base/seeds/canon/context/index.md +1 -2
- package/tooling/claude/manifest.toml +1 -5
- package/tooling/claude/reference.md +36 -59
- package/tooling/claude/seeds/.claude/hooks/board-validate.sh +65 -0
- package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +2 -7
- package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +2 -7
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +3 -10
- package/tooling/claude/seeds/.claude/hooks/search-reminder.sh +2 -7
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +2 -4
- package/tooling/claude/seeds/.claude/settings.json +4 -0
- package/tooling/nextjs/reference.md +0 -6
- package/tooling/php/reference.md +0 -4
- package/tooling/web/configs/.github/workflows/verify.yml +28 -0
- package/tooling/web/configs/.oxlintrc.json +7 -0
- package/tooling/web/configs/e2e/capture-types.ts +35 -0
- package/tooling/web/configs/e2e/screenshot.ts +23 -72
- package/tooling/web/manifest.toml +1 -1
- package/tooling/web/reference.md +77 -124
- package/tooling/web/seeds/e2e/capture/cases.ts +47 -0
- package/src/commands/wiki.ts +0 -100
- package/src/wiki/init.ts +0 -85
- /package/claude/skills/{draft-figure → draw-figure}/references/figures.md +0 -0
- /package/claude/skills/{draft-slides → make-deck}/references/deck.md +0 -0
- /package/claude/skills/{draft-and-pick → pick-by-eye}/references/live-arms.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/agent-facing.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/application.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/cli.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/library.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/plugin.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/requirements.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/wiki.md +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Server security rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/security-server.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Server security rule ledger
|
|
7
|
+
|
|
8
|
+
The field is application security, and the authorities are the OWASP Cheat Sheet Series, the OWASP API Security Top 10 2023, and MDN for the CORS protocol. The CORS bullet is worded the way `framework/fastapi.md` states it, so a FastAPI target reads one rule twice rather than two.
|
|
9
|
+
|
|
10
|
+
## Authorization
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Authorize on the server | [Authorization](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html): "Developers must never rely on client-side access control checks", and "Permission should be validated correctly on every request" | 2026-10-18 |
|
|
15
|
+
| Identity from the verified token | House choice: an identity read from a field the client sets is one the client chooses, which turns every authorization check into a check of the attacker's claim | 2026-10-18 |
|
|
16
|
+
| Check the specific record | [Authorization](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html): "Perform access control checks on every request for the specific object or functionality being accessed" | 2026-10-18 |
|
|
17
|
+
| Deny by default | [Authorization](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html): "an application should be configured to deny access by default" | 2026-10-18 |
|
|
18
|
+
|
|
19
|
+
## Credentials
|
|
20
|
+
|
|
21
|
+
| Bullet | Source | Read |
|
|
22
|
+
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
23
|
+
| Argon2id and a salt | [Password Storage](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html): "If Argon2id is not available, use scrypt", a salt is "unique, randomly generated" per password, and "hashing is a one-way function" | 2026-10-18 |
|
|
24
|
+
| Constant-time comparison | [Authentication](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html): a comparison should return "in constant time, to protect against timing attacks" | 2026-10-18 |
|
|
25
|
+
| Expire and revoke on the server | [Session Management](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html): "Session timeout management and expiration must be enforced server-side", and "deleting a cookie alone does not invalidate a stolen copy" | 2026-10-18 |
|
|
26
|
+
|
|
27
|
+
## Secrets
|
|
28
|
+
|
|
29
|
+
| Bullet | Source | Read |
|
|
30
|
+
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
31
|
+
| Secrets at runtime | [Secrets Management](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html): "It is even better when the consumer of the secret retrieves the secret", or an orchestrator injects it as an environment variable | 2026-10-18 |
|
|
32
|
+
| Never commit a secret | [Secrets Management](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html) names the failure: secrets "hardcoded within the source code in plaintext" | 2026-10-18 |
|
|
33
|
+
| Nothing internal in a response | [Error Handling](https://cheatsheetseries.owasp.org/cheatsheets/Error_Handling_Cheat_Sheet.html): a response should "not provide any content as part of the response that would reveal implementation details" | 2026-10-18 |
|
|
34
|
+
|
|
35
|
+
## Abuse limits
|
|
36
|
+
|
|
37
|
+
| Bullet | Source | Read |
|
|
38
|
+
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
39
|
+
| Rate-limit costly endpoints | [API4:2023](https://api-security.owasp.org/editions/2023/en/0xa4-unrestricted-resource-consumption): "Implement a limit on how often a client can interact with the API within a defined timeframe", and "Configure spending limits for all service providers" | 2026-10-18 |
|
|
40
|
+
| Body size and timeout | [API4:2023](https://api-security.owasp.org/editions/2023/en/0xa4-unrestricted-resource-consumption): "Define and enforce a maximum size of data on all incoming parameters and payloads", with missing "Execution timeouts" listed as a weakness | 2026-10-18 |
|
|
41
|
+
| Bound pagination | [API4:2023](https://api-security.owasp.org/editions/2023/en/0xa4-unrestricted-resource-consumption): validate the parameter "that controls the number of records to be returned in the response" | 2026-10-18 |
|
|
42
|
+
|
|
43
|
+
## Transport
|
|
44
|
+
|
|
45
|
+
| Bullet | Source | Read |
|
|
46
|
+
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
|
|
47
|
+
| TLS, and fail plaintext API calls | [Transport Layer Security](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html): "API endpoints should fail requests made over unencrypted HTTP connections instead of redirecting them" | 2026-10-18 |
|
|
48
|
+
| Explicit CORS origins | [MDN CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS): for a credentialed request the server "must not specify the `*` wildcard", and reflecting the origin is a house choice, since it grants every origin the wildcard was refused for | 2026-10-18 |
|
|
49
|
+
|
|
50
|
+
## Declined
|
|
51
|
+
|
|
52
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
53
|
+
|
|
54
|
+
### Contradicted by the docs
|
|
55
|
+
|
|
56
|
+
- Reworded "serve every route over TLS and reject plaintext requests". The TLS cheat sheet allows a public site to redirect port 80 to HTTPS, and requires the refusal for an API endpoint only.
|
|
57
|
+
|
|
58
|
+
### Merged or reworded
|
|
59
|
+
|
|
60
|
+
- Narrowed "hash passwords with a memory-hard algorithm" to the order the password storage page gives, since bcrypt is not memory-hard and the page still lists it.
|
|
61
|
+
- Reworded "restrict CORS to an explicit origin list, never pair a wildcard with credentials" to the wording `framework/fastapi.md` uses, keeping the ban on reflecting the origin.
|
|
62
|
+
- Added stack traces to the response bullet, the leak the error handling page shows first.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Web security rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/security-web.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Web security rule ledger
|
|
7
|
+
|
|
8
|
+
The field is web application security in the browser, and the authorities are the OWASP Cheat Sheet Series and MDN. The two disagreed on `target="_blank"`, and MDN was kept, since it states what browsers do and the extra attribute adds nothing a current browser lacks.
|
|
9
|
+
|
|
10
|
+
## Content sanitization
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| DOMPurify before raw HTML | [XSS Prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) lists "React's `dangerouslySetInnerHTML` without sanitizing the HTML" as an unsafe escape hatch, and "OWASP recommends DOMPurify for HTML Sanitization" | 2026-10-18 |
|
|
15
|
+
| Allow-list URL schemes | [XSS Prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html): React blocking `javascript:` URLs "does not replace application-specific URL validation", which allows only http and HTTPS | 2026-10-18 |
|
|
16
|
+
|
|
17
|
+
## Input validation
|
|
18
|
+
|
|
19
|
+
| Bullet | Source | Read |
|
|
20
|
+
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
21
|
+
| Parse URL params through a schema | House choice: a URL parameter is a string the visitor writes, and one read unparsed reaches rendering or a request in whatever shape it arrived | 2026-10-18 |
|
|
22
|
+
|
|
23
|
+
## Storage and tokens
|
|
24
|
+
|
|
25
|
+
| Bullet | Source | Read |
|
|
26
|
+
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
27
|
+
| Nothing sensitive in web storage | [HTML5 Security](https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html): "Do not store session identifiers in local storage as the data is always accessible by JavaScript" | 2026-10-18 |
|
|
28
|
+
| Tokens in `HttpOnly` cookies | [Session Management](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html): the `HttpOnly` flag "is mandatory to prevent session ID stealing through XSS attacks" | 2026-10-18 |
|
|
29
|
+
|
|
30
|
+
## Third-party scripts
|
|
31
|
+
|
|
32
|
+
| Bullet | Source | Read |
|
|
33
|
+
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
34
|
+
| Pinned, with `integrity` | [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity): it lets browsers "verify that resources they fetch (for example, from a CDN) are delivered without unexpected manipulation" | 2026-10-18 |
|
|
35
|
+
|
|
36
|
+
## Declined
|
|
37
|
+
|
|
38
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
39
|
+
|
|
40
|
+
### Contradicted by the docs
|
|
41
|
+
|
|
42
|
+
- Cut "add `rel="noopener noreferrer"` to all external links with `target="_blank"`". [MDN noopener](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/noopener) says `target="_blank"` "implicitly provides the same `rel` behavior as setting `rel="noopener"`". The [HTML5 Security](https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html) page still asks for it, and MDN was kept as the newer statement of browser behavior.
|
|
43
|
+
|
|
44
|
+
### Merged or reworded
|
|
45
|
+
|
|
46
|
+
- Merged "never use `dangerouslySetInnerHTML` without sanitization" into the DOMPurify bullet, and named `innerHTML` and Astro's `set:html`, since the rule loads on `.html` and `.astro` files too.
|
|
47
|
+
- Reworded "audit and pin versions for all third-party scripts" to the `integrity` attribute, which makes the pin checkable.
|
|
48
|
+
|
|
49
|
+
### Gaps filled
|
|
50
|
+
|
|
51
|
+
- Added the URL scheme bullet. The XSS page names `javascript:` URLs as the hole sanitizing markup leaves.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TanStack Query rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/tanstack-query.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TanStack Query rule ledger
|
|
7
|
+
|
|
8
|
+
The field is client-side data fetching in React, and the authority is the TanStack Query documentation at tanstack.com. The pages read are the latest docs, which name no major but deprecate `prefetchQuery`, so the prefetch bullet names the route loader rather than either method. `queryOptions()` needs TanStack Query 5 or later.
|
|
9
|
+
|
|
10
|
+
## Query conventions
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
14
|
+
| `queryOptions()` at every site | [Query options](https://tanstack.com/query/latest/docs/framework/react/guides/query-options): it lets you "share `queryKey` and `queryFn` between multiple places", with "type inference and type safety for all of them" | 2026-10-18, latest |
|
|
15
|
+
| Every variable in the key | [Query keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys): "If your query function depends on a variable, include it in your query key" | 2026-10-18, latest |
|
|
16
|
+
| `staleTime` per query | [Important defaults](https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults): queries "consider cached data as stale" by default, and setting `staleTime` is "the recommended way to avoid excessive refetches" | 2026-10-18, latest |
|
|
17
|
+
| Throw on a non-`ok` response | [Query functions](https://tanstack.com/query/latest/docs/framework/react/guides/query-functions): a query function "must throw or return a rejected Promise", and `fetch` does not "throw errors by default" | 2026-10-18, latest |
|
|
18
|
+
|
|
19
|
+
## Mutations
|
|
20
|
+
|
|
21
|
+
| Bullet | Source | Read |
|
|
22
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
23
|
+
| Writes through `useMutation` | House choice: a write outside a mutation has no pending or error state and invalidates nothing, so the cache keeps serving what the write replaced | 2026-10-18, latest |
|
|
24
|
+
| Invalidate in `onSuccess` | [Invalidations from mutations](https://tanstack.com/query/latest/docs/framework/react/guides/invalidations-from-mutations): "Returning a Promise on `onSuccess` makes sure the data is updated before the mutation is entirely complete" | 2026-10-18, latest |
|
|
25
|
+
| `onError` with visible feedback | House choice: a failed write with no handler leaves the screen showing the edit as if it saved | 2026-10-18, latest |
|
|
26
|
+
|
|
27
|
+
## Cache management
|
|
28
|
+
|
|
29
|
+
| Bullet | Source | Read |
|
|
30
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
31
|
+
| Prefetch in the route loader | [Prefetching](https://tanstack.com/query/latest/docs/framework/react/guides/prefetching): a loader starts a route's data ahead of render, with "Fetch comments asap, but don't block or throw errors" | 2026-10-18, latest |
|
|
32
|
+
|
|
33
|
+
## Separation of concerns
|
|
34
|
+
|
|
35
|
+
| Bullet | Source | Read |
|
|
36
|
+
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
37
|
+
| Pure query functions | [Important defaults](https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults): stale queries refetch when "The window is refocused" and "The network is reconnected", so a side effect fires on each | 2026-10-18, latest |
|
|
38
|
+
| `enabled`, not `useEffect` | [Dependent queries](https://tanstack.com/query/latest/docs/framework/react/guides/dependent-queries): "using the `enabled` option to tell a query when it is ready to run" | 2026-10-18, latest |
|
|
39
|
+
|
|
40
|
+
## Declined
|
|
41
|
+
|
|
42
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
43
|
+
|
|
44
|
+
### Contradicted by the docs
|
|
45
|
+
|
|
46
|
+
- Reworded "prefetch predictable navigations with `queryClient.prefetchQuery`". The [prefetching guide](https://tanstack.com/query/latest/docs/framework/react/guides/prefetching) calls `prefetchQuery` "now deprecated", and a target on an older major still has it, so the bullet names the loader alone.
|
|
47
|
+
|
|
48
|
+
### Merged or reworded
|
|
49
|
+
|
|
50
|
+
- Merged "define query keys as `readonly` tuple constants", "co-locate query keys with their query functions", and "encapsulate each query in a custom `use*Query` hook" into the `queryOptions()` bullet, the helper the docs name for sharing a key with its function.
|
|
51
|
+
- Merged "never mutate data outside the mutation lifecycle" into the `useMutation` bullet.
|
|
52
|
+
- Merged "use `invalidateQueries` over `setQueryData` unless implementing optimistic updates" into the `onSuccess` bullet.
|
|
53
|
+
|
|
54
|
+
### Gaps filled
|
|
55
|
+
|
|
56
|
+
- Added the bullet putting every variable in the key, the stale-data bug the query keys page warns about.
|
|
57
|
+
- Added the bullet throwing on a response that is not `ok`, since a `fetch` query function otherwise caches an error body as data.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Test scope rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/test-scope.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test scope rule ledger
|
|
7
|
+
|
|
8
|
+
The field is browser test automation, and the authority is the Playwright documentation, read at playwright.dev and from its source in the microsoft/playwright repository. The rule ships to every Playwright target, so it names the Playwright CLI rather than a package script a target may not carry. None of the pages read names a Playwright major, so the rows record the read date alone.
|
|
9
|
+
|
|
10
|
+
## Selecting a run
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Narrow by path or name, never engine | House choice: a run narrowed to one engine passes while another engine fails, and the gap surfaces only in CI | 2026-10-18 |
|
|
15
|
+
| One test by name | [Command line](https://raw.githubusercontent.com/microsoft/playwright/main/docs/src/test-cli-js.md): `-g` will "Only run tests matching this regular expression" | 2026-10-18 |
|
|
16
|
+
| One surface by path | [Command line](https://raw.githubusercontent.com/microsoft/playwright/main/docs/src/test-cli-js.md): "Run a single test file" by passing its path | 2026-10-18 |
|
|
17
|
+
| `--only-changed` | [Command line](https://raw.githubusercontent.com/microsoft/playwright/main/docs/src/test-cli-js.md): "Only run test files that have been changed between 'HEAD' and 'ref'" | 2026-10-18 |
|
|
18
|
+
| Whole suite before push | House choice: a narrowed run skips the specs a change broke without touching, which the push would hand to CI | 2026-10-18 |
|
|
19
|
+
| `--project` only to reproduce | [Command line](https://raw.githubusercontent.com/microsoft/playwright/main/docs/src/test-cli-js.md): `--project` will "Only run tests from the specified list of projects", where the default is to "run all projects" | 2026-10-18 |
|
|
20
|
+
| No one-engine script | House choice: a script pinning one engine becomes the run everyone types, which is the engine narrowing the first bullet bans | 2026-10-18 |
|
|
21
|
+
|
|
22
|
+
## Instruments
|
|
23
|
+
|
|
24
|
+
| Bullet | Source | Read |
|
|
25
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
26
|
+
| A script, not the suite | House choice: a suite run answers a question about the page only through assertions written for another question, and costs the whole run | 2026-10-18 |
|
|
27
|
+
| No `fullyParallel` | [Parallelism](https://playwright.dev/docs/test-parallel): by default "Tests in a single file are run in order, in the same worker process", and parallel tests "cannot share any state or global variables" | 2026-10-18 |
|
|
28
|
+
| Pointer to `surface-capture` | House choice: a pointer to the rule that owns capture scope, claiming no practice of its own | 2026-10-18 |
|
|
29
|
+
| Pointer to `e2e-reliability` | House choice: a pointer to the rule that owns waits and guards, claiming no practice of its own | 2026-10-18 |
|
|
30
|
+
|
|
31
|
+
## Declined
|
|
32
|
+
|
|
33
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
34
|
+
|
|
35
|
+
### A script a target may lack
|
|
36
|
+
|
|
37
|
+
- Replaced `bun run test:e2e -- -g '<name>'` with `playwright test -g '<name>'`. The `test:e2e` scripts are this toolkit's alone, and the rule ships to every Playwright target.
|
|
38
|
+
- Replaced `bun run test:e2e -- e2e/<area>.spec.ts` with the CLI's path argument.
|
|
39
|
+
- Replaced `bun run test:e2e:changed`, which selected specs from the import graph, with `--only-changed`, the CLI's own selection by git diff.
|
|
40
|
+
|
|
41
|
+
### Merged or reworded
|
|
42
|
+
|
|
43
|
+
- Named the Playwright config in the `fullyParallel` bullet rather than the `playwright.config.ts` filename, since a target may write the config in JavaScript.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Go testing rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/testing-go.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Go testing rule ledger
|
|
7
|
+
|
|
8
|
+
The field is Go testing, and the authorities are the `testing` and `net/http/httptest` package documentation at pkg.go.dev, the Go wiki at go.dev, and the race detector article. None of the pages read names a Go release beyond a 1.22 loop-variable note, so the rows record the read date alone.
|
|
9
|
+
|
|
10
|
+
## Layer
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Load `test-craft` first | House choice: a pointer to the skill that owns the choice of layer, claiming no practice of its own | 2026-10-18 |
|
|
15
|
+
|
|
16
|
+
## Framework
|
|
17
|
+
|
|
18
|
+
| Bullet | Source | Read |
|
|
19
|
+
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
20
|
+
| Standard `testing`, no assert library | [Go test comments](https://go.dev/wiki/TestComments): "Avoid the use of 'assert' libraries to help your tests", which "make it too easy to write imprecise tests" | 2026-10-18 |
|
|
21
|
+
| External `_test` package | [Package testing](https://pkg.go.dev/testing): a test file in a package with the "_test" suffix is black-box, where "the package being tested must be imported explicitly" | 2026-10-18 |
|
|
22
|
+
|
|
23
|
+
## Table-driven cases
|
|
24
|
+
|
|
25
|
+
| Bullet | Source | Read |
|
|
26
|
+
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
27
|
+
| Slice of structs through `t.Run` | [Table driven tests](https://go.dev/wiki/TableDrivenTests): "Each table entry is a complete test case with inputs and expected results" | 2026-10-18 |
|
|
28
|
+
| A `name` per case | [Package testing](https://pkg.go.dev/testing): "Run runs f as a subtest of t called name", which is how a failure names its case | 2026-10-18 |
|
|
29
|
+
|
|
30
|
+
## Helpers and setup
|
|
31
|
+
|
|
32
|
+
| Bullet | Source | Read |
|
|
33
|
+
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
34
|
+
| `t.Helper()` first | [Package testing](https://pkg.go.dev/testing): "Helper marks the calling function as a test helper function", skipped when printing file and line | 2026-10-18 |
|
|
35
|
+
| `t.Cleanup` over `defer` | [Package testing](https://pkg.go.dev/testing): Cleanup runs "when the test (or subtest) and all its subtests complete" | 2026-10-18 |
|
|
36
|
+
| `t.TempDir` and `t.Setenv` | [Package testing](https://pkg.go.dev/testing): the directory is removed when the test finishes, and Setenv "uses Cleanup to restore the environment variable" | 2026-10-18 |
|
|
37
|
+
| `httptest` over a listener | [Package httptest](https://pkg.go.dev/net/http/httptest): "NewServer starts and returns a new Server listening on a local network loopback interface" | 2026-10-18 |
|
|
38
|
+
|
|
39
|
+
## Concurrency
|
|
40
|
+
|
|
41
|
+
| Bullet | Source | Read |
|
|
42
|
+
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
43
|
+
| No `time.Sleep` sync | House choice: a sleep long enough on one machine is too short on a loaded runner, so the test fails with no defect in the code | 2026-10-18 |
|
|
44
|
+
| `-race` | [Data race detector](https://go.dev/doc/articles/race_detector): "add the `-race` flag to the go command", as in `go test -race mypkg` | 2026-10-18 |
|
|
45
|
+
| `t.Parallel()` without shared state | [Table driven tests](https://go.dev/wiki/TableDrivenTests): "Parallelizing table tests is simple, but requires precision to avoid bugs" | 2026-10-18 |
|
|
46
|
+
|
|
47
|
+
## Declined
|
|
48
|
+
|
|
49
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
50
|
+
|
|
51
|
+
### A default the toolchain already applies
|
|
52
|
+
|
|
53
|
+
- Cut "place a test beside the code it tests, in a `_test.go` file of the same package". The `go` tool finds tests only in `_test.go` files, and Claude writes them that way unprompted.
|
|
54
|
+
|
|
55
|
+
### Merged or reworded
|
|
56
|
+
|
|
57
|
+
- Gave the `t.Cleanup` bullet its reason, a `defer` in a helper firing as the helper returns.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: PHP testing rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/testing-php.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PHP testing rule ledger
|
|
7
|
+
|
|
8
|
+
The field is PHP testing, and the authority is the PHPUnit documentation at docs.phpunit.de with its release announcements at phpunit.de. The manual pages read are the PHPUnit 12.4 edition. Attributes arrived in PHPUnit 10, so the attribute bullet names that floor and a PHPUnit 9 target is never told to use them.
|
|
9
|
+
|
|
10
|
+
## Layer
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Load `test-craft` first | House choice: a pointer to the skill that owns the choice of layer, claiming no practice of its own | 2026-10-18 |
|
|
15
|
+
|
|
16
|
+
## Framework
|
|
17
|
+
|
|
18
|
+
| Bullet | Source | Read |
|
|
19
|
+
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
20
|
+
| PHPUnit as the runner | House choice: the toolkit's PHP stack configures PHPUnit, so a test written for a second runner never runs in the gate | 2026-10-18, PHPUnit 12.4 |
|
|
21
|
+
| `final`, extending `TestCase` | [Writing tests](https://docs.phpunit.de/en/12.4/writing-tests-for-phpunit.html): "`GreeterTest` inherits from `PHPUnit\Framework\TestCase`", and every example class is declared `final` | 2026-10-18, PHPUnit 12.4 |
|
|
22
|
+
|
|
23
|
+
## Attributes
|
|
24
|
+
|
|
25
|
+
| Bullet | Source | Read |
|
|
26
|
+
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
27
|
+
| Attributes over annotations | [PHPUnit 12 announcement](https://phpunit.de/announcements/phpunit-12.html): "Support for using annotations has now been removed", and the [PHPUnit 10 announcement](https://phpunit.de/announcements/phpunit-10.html): "PHPUnit 10 introduces support for PHP 8 attributes" | 2026-10-18, PHPUnit 12 |
|
|
28
|
+
| `public static` data provider | [Writing tests](https://docs.phpunit.de/en/12.4/writing-tests-for-phpunit.html): "A data provider method must be `public` and `static` and its name must not start with `test`" | 2026-10-18, PHPUnit 12.4 |
|
|
29
|
+
|
|
30
|
+
## Assertions and doubles
|
|
31
|
+
|
|
32
|
+
| Bullet | Source | Read |
|
|
33
|
+
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
34
|
+
| `assertSame` over `assertEquals` | [Assertions](https://docs.phpunit.de/en/12.4/assertions.html): `assertSame()` fails when the values "do not have the same type and value", while `assertEquals()` compares with `==` | 2026-10-18, PHPUnit 12.4 |
|
|
35
|
+
| `expectException` first | [Writing tests](https://docs.phpunit.de/en/12.4/writing-tests-for-phpunit.html): "The `expectException()` method has to be used **before** the exception you expect to be thrown is thrown" | 2026-10-18, PHPUnit 12.4 |
|
|
36
|
+
| Stub unless asserting a call | [Test doubles](https://docs.phpunit.de/en/12.4/test-doubles.html): a stub "(optionally) returns configured return values", and a mock "verifies expectations, for instance asserting that a method has been called" | 2026-10-18, PHPUnit 12.4 |
|
|
37
|
+
| No real network calls | House choice: a test reaching the network fails on an outage or a rate limit with no defect in the code under test | 2026-10-18 |
|
|
38
|
+
|
|
39
|
+
## Declined
|
|
40
|
+
|
|
41
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
42
|
+
|
|
43
|
+
### Cut without a source or a bug class
|
|
44
|
+
|
|
45
|
+
- Cut "place tests under `tests/`, mirroring the `src/` namespace layout, in files named `*Test.php`" as a layout call, which the `codebase-layout` skill owns. The PHPUnit manual shows the layout in examples and states no rule.
|
|
46
|
+
|
|
47
|
+
### Merged or reworded
|
|
48
|
+
|
|
49
|
+
- Prefixed the attribute bullet with "On PHPUnit 10 or later", since attributes do not exist before it.
|
|
50
|
+
- Named `public static` in the data provider bullet, the signature the manual requires.
|
|
51
|
+
- Reworded the `expectException` bullet to the ordering the manual stresses.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Python testing rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/testing-py.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python testing rule ledger
|
|
7
|
+
|
|
8
|
+
The field is Python testing, and the authorities are the pytest documentation at docs.pytest.org and the pytest-asyncio documentation. The pytest pages name no major beyond a feature added in 9.0, and the pytest-asyncio page names none, so the rows record the read date alone.
|
|
9
|
+
|
|
10
|
+
## Framework
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Plain functions, no `TestCase` | [unittest.TestCase support](https://docs.pytest.org/en/stable/how-to/unittest.html): in a `TestCase` subclass, regular fixtures and parametrization do not work, "and likely never will" | 2026-10-18 |
|
|
15
|
+
|
|
16
|
+
## Fixtures
|
|
17
|
+
|
|
18
|
+
| Bullet | Source | Read |
|
|
19
|
+
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
20
|
+
| Yield fixtures for teardown | [Fixtures](https://docs.pytest.org/en/stable/how-to/fixtures.html): "yield fixtures are considered to be the cleaner and more straightforward option" | 2026-10-18 |
|
|
21
|
+
| Narrowest `conftest.py` | [Fixtures](https://docs.pytest.org/en/stable/how-to/fixtures.html): in a `conftest.py`, "tests from multiple test modules in the directory can access the fixture function" | 2026-10-18 |
|
|
22
|
+
| Widen scope only for unmutated state | [Fixtures](https://docs.pytest.org/en/stable/how-to/fixtures.html): "`function`: the default scope, the fixture is destroyed at the end of the test", so a wider scope hands one object to every test in it | 2026-10-18 |
|
|
23
|
+
| `tmp_path` and `monkeypatch` | [tmp_path](https://docs.pytest.org/en/stable/how-to/tmp_path.html) is "a temporary directory unique to each test function", and with [monkeypatch](https://docs.pytest.org/en/stable/how-to/monkeypatch.html) "All modifications will be undone after the requesting test function or fixture has finished" | 2026-10-18 |
|
|
24
|
+
|
|
25
|
+
## Parametrize
|
|
26
|
+
|
|
27
|
+
| Bullet | Source | Read |
|
|
28
|
+
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
29
|
+
| `parametrize` for tables | [Parametrizing tests](https://docs.pytest.org/en/stable/how-to/parametrize.html): it "enables parametrization of arguments for a test function" | 2026-10-18 |
|
|
30
|
+
|
|
31
|
+
## Async
|
|
32
|
+
|
|
33
|
+
| Bullet | Source | Read |
|
|
34
|
+
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
35
|
+
| Mark async tests or go auto | [pytest-asyncio concepts](https://pytest-asyncio.readthedocs.io/en/latest/concepts.html): "In strict mode pytest-asyncio will only run tests that have the _asyncio_ marker", and strict "is the default mode" | 2026-10-18 |
|
|
36
|
+
| The plugin's event loop | [pytest-asyncio concepts](https://pytest-asyncio.readthedocs.io/en/latest/concepts.html): "Pytest-asyncio provides one asyncio event loop for each pytest collector" | 2026-10-18 |
|
|
37
|
+
|
|
38
|
+
## Assertions and mocks
|
|
39
|
+
|
|
40
|
+
| Bullet | Source | Read |
|
|
41
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
42
|
+
| Plain `assert` | [Assertions](https://docs.pytest.org/en/stable/how-to/assert.html): failure details come from "rewriting assert statements before they are run" | 2026-10-18 |
|
|
43
|
+
| `pytest.raises` | [Assertions](https://docs.pytest.org/en/stable/how-to/assert.html): you "can use pytest.raises() as a context manager" for an expected exception | 2026-10-18 |
|
|
44
|
+
| Patch through a fixture | [monkeypatch](https://docs.pytest.org/en/stable/how-to/monkeypatch.html): it "helps you to safely set/delete an attribute", undone after the test, where a hand assignment leaks into later tests | 2026-10-18 |
|
|
45
|
+
| No real network calls | House choice: a test reaching the network fails on an outage or a rate limit with no defect in the code under test | 2026-10-18 |
|
|
46
|
+
|
|
47
|
+
## Declined
|
|
48
|
+
|
|
49
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
50
|
+
|
|
51
|
+
### A default pytest already applies
|
|
52
|
+
|
|
53
|
+
- Cut "name files `test_*.py` and test functions `test_*`". [Good practices](https://docs.pytest.org/en/stable/explanation/goodpractices.html) states it as pytest's default discovery, and Claude names tests that way unprompted.
|
|
54
|
+
- Narrowed "set `scope=` explicitly when reuse matters" to the one case a wider scope breaks, since `function` is already the default.
|
|
55
|
+
|
|
56
|
+
### Cut without a source or a bug class
|
|
57
|
+
|
|
58
|
+
- Cut "place tests under `tests/`, mirroring the `src/` package layout" as a layout call, which the `codebase-layout` skill owns. Good practices says only that a separate directory "might be useful".
|
|
59
|
+
- Cut "use `ids=` to label parametrized cases when their repr is unclear" as readability naming no failure.
|
|
60
|
+
- Cut "arrange, act, assert per test, with one logical assertion group" as test structure, which the `test-craft` skill owns.
|
|
61
|
+
- Cut "use descriptive `test_*` names that read as the assertion, never `should_`" as naming style naming no failure.
|
|
62
|
+
|
|
63
|
+
### Merged or reworded
|
|
64
|
+
|
|
65
|
+
- Merged "use `pytest` for all tests, never `unittest.TestCase`" with the reason the unittest page gives.
|
|
66
|
+
- Reworded "do not start a fresh event loop inside tests" to name the loop the plugin provides and the calls that bypass it.
|
|
67
|
+
- Replaced `httpx_mock`, a fixture name, with `pytest-httpx`, the package that provides it.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TypeScript testing rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/testing-ts.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeScript testing rule ledger
|
|
7
|
+
|
|
8
|
+
The field is JavaScript and TypeScript testing. The authorities are the Vitest documentation at vitest.dev, the Testing Library documentation at testing-library.com, and the Playwright documentation at playwright.dev. None of the pages read names a major, so the rows record the read date alone.
|
|
9
|
+
|
|
10
|
+
## Unit and integration
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Vitest as the runner | House choice: the toolkit's web stacks install and configure Vitest, so a test written for a second runner never runs in the gate | 2026-10-18 |
|
|
15
|
+
| `userEvent` over `fireEvent` | [user-event](https://testing-library.com/docs/user-event/intro): it "factors in that the browser e.g. wouldn't let a user click a hidden element or type in a disabled text box" | 2026-10-18 |
|
|
16
|
+
| MSW, no hand mocks or real calls | [Vitest, mocking requests](https://vitest.dev/guide/mocking/requests): "We recommend Mock Service Worker to accomplish this", since it mocks "http, WebSocket and GraphQL network requests" | 2026-10-18 |
|
|
17
|
+
| Role or label first | [Testing Library queries](https://testing-library.com/docs/queries/about): `getByRole` "should be your top preference for just about everything", and a test ID "is only recommended for cases where you can't match by role or text" | 2026-10-18 |
|
|
18
|
+
|
|
19
|
+
## End-to-end
|
|
20
|
+
|
|
21
|
+
| Bullet | Source | Read |
|
|
22
|
+
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
23
|
+
| Playwright for end-to-end | House choice: the toolkit's web stack configures Playwright, so a second browser runner's tests never run in the gate | 2026-10-18 |
|
|
24
|
+
| Playwright tests under `e2e/` | [Vitest include](https://vitest.dev/config/include) defaults to `**/*.{test,spec}.?(c\|m)[jt]s?(x)`, so a Playwright spec under `src/` is collected by Vitest too, where it fails | 2026-10-18 |
|
|
25
|
+
|
|
26
|
+
## Timers and async
|
|
27
|
+
|
|
28
|
+
| Bullet | Source | Read |
|
|
29
|
+
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
30
|
+
| `advanceTimers` with fake timers | [user-event options](https://testing-library.com/docs/user-event/options): "When using fake timers it is necessary to set this option to your test runner's time advancement function" | 2026-10-18 |
|
|
31
|
+
| Restore after pending timers | [Using fake timers](https://testing-library.com/docs/using-fake-timers): "you usually call `useRealTimers` in `afterEach`", and "It's important to also call `runOnlyPendingTimers` before switching to real timers" | 2026-10-18 |
|
|
32
|
+
|
|
33
|
+
## Conventions
|
|
34
|
+
|
|
35
|
+
| Bullet | Source | Read |
|
|
36
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
|
|
37
|
+
| `.test` for unit, `.spec` otherwise | House choice: [Playwright](https://playwright.dev/docs/test-configuration) runs "`**/*.@(spec\|test).?(c\|m)[jt]s?(x)` files" and Vitest matches the same pair, so the suffix is what a config separates the two runs on | 2026-10-18 |
|
|
38
|
+
|
|
39
|
+
## Declined
|
|
40
|
+
|
|
41
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
42
|
+
|
|
43
|
+
### Contradicted by the docs
|
|
44
|
+
|
|
45
|
+
- Cut "never use `vi.useFakeTimers()` in `beforeEach` when tests use `waitFor`, `act`, or `userEvent`". [Using fake timers](https://testing-library.com/docs/using-fake-timers) shows `beforeEach` as the usual setup, and names `advanceTimers` as the fix for the `userEvent` timeout, which the kept bullet carries.
|
|
46
|
+
- Cut "scope fake timers to the individual test that needs them" with it, since no page read scopes them below the file.
|
|
47
|
+
|
|
48
|
+
### Cut without a source or a bug class
|
|
49
|
+
|
|
50
|
+
- Cut "`it()` descriptions use "should" + sentence case". The rule-standard audit flagged it as contested style naming no bug class.
|
|
51
|
+
- Cut "`describe()` labels use the exact identifier of the subject" as naming style naming no failure.
|
|
52
|
+
|
|
53
|
+
### Owned elsewhere
|
|
54
|
+
|
|
55
|
+
- Cut "co-locate unit tests with their respective components". The `codebase-layout` skill's test reference places a unit test beside its module.
|
|
56
|
+
- Cut "keep specs apart from their fixtures, page objects, and helpers inside `e2e/`", which the same reference owns.
|
|
57
|
+
|
|
58
|
+
### Merged or reworded
|
|
59
|
+
|
|
60
|
+
- Folded "place all Playwright tests within `e2e/`", "never place Playwright tests inside `src/`", and the scope bullet for a Vitest test driving a browser into one placement bullet.
|
|
61
|
+
- Merged "do not make real network calls in unit tests" into the MSW bullet.
|
|
62
|
+
- Merged the two suffix bullets into one.
|
|
63
|
+
- Reworded "use `userEvent` over synthetic events" and the accessible-query bullet to name what each source says the choice prevents.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Zod rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/zod.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Zod rule ledger
|
|
7
|
+
|
|
8
|
+
The field is TypeScript runtime validation, and the authority is the Zod documentation at zod.dev with its changelog in the colinhacks/zod repository. The rule assumes Zod 4, since the methods it replaces stay available as legacy and a Zod 3 target keeps `z.strictObject()`.
|
|
9
|
+
|
|
10
|
+
## Type inference
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- |
|
|
14
|
+
| `z.infer`, no twin interface | [Basic usage](https://zod.dev/basics): "Zod infers a static type from your schema definitions. You can extract this type with the `z.infer<>` utility" | 2026-10-18, Zod 4 |
|
|
15
|
+
|
|
16
|
+
## Boundary validation
|
|
17
|
+
|
|
18
|
+
| Bullet | Source | Read |
|
|
19
|
+
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |
|
|
20
|
+
| `z.strictObject()` on untrusted input | [Zod 4 changelog](https://raw.githubusercontent.com/colinhacks/zod/main/packages/docs/content/v4/changelog.mdx): "Instead use the top-level `z.strictObject()` and `z.looseObject()` functions", and the old methods "are considered legacy" | 2026-10-18, Zod 4 |
|
|
21
|
+
| `.pick()` or `.omit()`, not loose | [Defining schemas](https://zod.dev/api): "Zod provides dedicated APIs for picking and omitting certain keys from an object schema", and a loose schema "allows unknown keys to pass through" | 2026-10-18, Zod 4 |
|
|
22
|
+
| `.parse()` or `.safeParse()` | [Basic usage](https://zod.dev/basics): `.parse()` "will throw a `ZodError`", and `.safeParse()` returns "a plain result object containing either the successfully parsed data or a `ZodError`" | 2026-10-18, Zod 4 |
|
|
23
|
+
| `z.coerce` at string boundaries | [Defining schemas](https://zod.dev/api): "The coerced variant of these schemas attempts to convert the input value to the appropriate type", applying `String(input)` for `z.coerce.string()` | 2026-10-18, Zod 4 |
|
|
24
|
+
|
|
25
|
+
## Schema safety
|
|
26
|
+
|
|
27
|
+
| Bullet | Source | Read |
|
|
28
|
+
| ---------------------------- | --------------------------------------------------------------------------------------------------- | ----------------- |
|
|
29
|
+
| `z.unknown()`, not `z.any()` | [Defining schemas](https://zod.dev/api): `z.any()` infers `any`, and `z.unknown()` infers `unknown` | 2026-10-18, Zod 4 |
|
|
30
|
+
|
|
31
|
+
## Declined
|
|
32
|
+
|
|
33
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
34
|
+
|
|
35
|
+
### Contradicted by the docs
|
|
36
|
+
|
|
37
|
+
- Replaced "use `.strict()` for untrusted external API boundaries". The [Zod 4 changelog](https://raw.githubusercontent.com/colinhacks/zod/main/packages/docs/content/v4/changelog.mdx) marks `.strict()` legacy and names `z.strictObject()`.
|
|
38
|
+
- Replaced "prefer `.strict()` at boundaries or explicit `.pick()`/`.omit()` over `.passthrough()`", which recommended the legacy method and read as a preference. The kept bullet names `z.looseObject()` beside it.
|
|
39
|
+
|
|
40
|
+
### Cut without a source or a bug class
|
|
41
|
+
|
|
42
|
+
- Cut "do not export the runtime Schema if only the inferred Type is required by consumers" as an export taste naming no failure.
|
|
43
|
+
|
|
44
|
+
### Merged or reworded
|
|
45
|
+
|
|
46
|
+
- Merged "do not manually declare interfaces that duplicate Zod schemas" into the `z.infer` bullet.
|
|
47
|
+
- Narrowed "restrict `z.coerce` to I/O boundaries" to string input and named the conversion, which is why `z.coerce.boolean()` reads `"false"` as `true`.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: CI workflow rule ledger
|
|
3
|
+
description: Sources for governance/rules/tooling/ci-workflow.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CI workflow rule ledger
|
|
7
|
+
|
|
8
|
+
The field is continuous integration on GitHub Actions, and the authority is GitHub's own Actions documentation at docs.github.com. The rule is the second half of a pair with the `ci-workflow` skill, which owns triggers, pinning, job shape, and the top-level read-only default, so this ledger covers only what the rule itself states.
|
|
9
|
+
|
|
10
|
+
## Authority
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Load the skill, routing | Routing only: it sends workflow authoring to the `canon:ci-workflow` skill, which carries the sourced directives, and claims no outside source | 2026-10-18 |
|
|
15
|
+
|
|
16
|
+
## Commit-back
|
|
17
|
+
|
|
18
|
+
| Bullet | Source | Read |
|
|
19
|
+
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
20
|
+
| Commit back only what the branch moved | House choice: a refresh an outside input moved, such as a font or a build-time count, lands a diff on a pull request whose change did not cause it, so the reviewer reads churn the author cannot explain | 2026-10-18 |
|
|
21
|
+
| Deterministic output, diff check first | House choice: a step that regenerates differently on each run commits noise on every push, and a commit step with no diff check ahead of it pushes a capture that did not change | 2026-10-18 |
|
|
22
|
+
| Skip a fork's pull request | [Workflow syntax, permissions](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax): "if the workflow was triggered by a pull request event other than `pull_request_target` from a forked repository, and the Send write tokens to workflows from pull requests setting is not selected, the permissions are adjusted to change any write permissions to read only" | 2026-10-18 |
|
|
23
|
+
| Never commit back to the trunk | House choice: a workflow pushing onto the trunk lands a change no pull request carried and no reviewer read | 2026-10-18 |
|
|
24
|
+
|
|
25
|
+
## Token scopes
|
|
26
|
+
|
|
27
|
+
| Bullet | Source | Read |
|
|
28
|
+
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
29
|
+
| Write scope on the job only | [Workflow syntax, permissions](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax): "For a specific job, you can use `jobs.<job_id>.permissions` to modify the default permissions granted to the `GITHUB_TOKEN`, adding or removing access as required, so that you only allow the minimum required access" | 2026-10-18 |
|
|
30
|
+
|
|
31
|
+
## Declined
|
|
32
|
+
|
|
33
|
+
Nothing was cut. Every bullet already sat inside the shape limits, and the rule keeps its split with the skill, a pointer plus at most five directives.
|