@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.
- package/README.md +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/canon-cli/SKILL.md +0 -1
- package/claude/skills/design-taste/REQUIREMENT.md +2 -2
- package/claude/skills/pick-by-eye/REQUIREMENT.md +17 -67
- package/claude/skills/role-orchestrator/REQUIREMENT.md +16 -99
- package/claude/skills/role-planner/REQUIREMENT.md +15 -73
- package/claude/skills/role-worker/REQUIREMENT.md +13 -41
- package/claude/skills/target-setup/SKILL.md +2 -2
- package/claude/skills/target-setup/references/detect.md +0 -1
- package/claude/skills/target-setup/references/verify.md +2 -2
- package/claude/skills/teach-workspace/REQUIREMENT.md +1 -1
- package/claude/skills/teach-workspace/references/promotion.md +5 -5
- package/docs/agents/install-and-sync.md +2 -2
- package/docs/agents/output-shape.md +1 -2
- package/docs/target/migrations.md +18 -0
- package/docs/target/projects.md +1 -2
- package/docs/target/scenarios.md +4 -1
- package/docs/workflow/skill-map-on-request.md +1 -1
- package/governance/detect.toml +4 -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/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/package.json +1 -1
- package/scripts/core/install-check.sh +1 -1
- package/src/cli/help.ts +0 -1
- package/src/cli.ts +0 -2
- package/src/commands/gov.ts +4 -0
- package/src/commands/init.ts +2 -2
- package/src/commands/tooling.ts +18 -5
- package/src/design/components.ts +1 -1
- package/src/gov/audit.ts +22 -2
- 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 +4 -0
- package/src/migrate/scratch-evidence.ts +12 -4
- package/src/records/backup.ts +3 -3
- package/src/records/validate.ts +3 -3
- package/src/roots/record.ts +17 -8
- 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/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/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/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
|
@@ -26,10 +26,10 @@ A page a reader cannot sort by that question is a page whose subject has not bee
|
|
|
26
26
|
|
|
27
27
|
## Where the wiki sits
|
|
28
28
|
|
|
29
|
-
The folder has two spellings and a project carries whichever
|
|
29
|
+
The folder has two spellings and a project carries whichever the project created:
|
|
30
30
|
|
|
31
|
-
- `.claude/wiki/`,
|
|
32
|
-
- A folder named `wiki` at the project root
|
|
31
|
+
- `.claude/wiki/`, the folder inside the Claude setup
|
|
32
|
+
- A folder named `wiki` at the project root
|
|
33
33
|
|
|
34
34
|
Read which one exists rather than composing either. A project carrying both keeps its pages at the root, so propose the root spelling and say the other folder is there.
|
|
35
35
|
|
|
@@ -38,10 +38,10 @@ Read which one exists rather than composing either. A project carrying both keep
|
|
|
38
38
|
A project carrying neither gets a refusal rather than a scaffold:
|
|
39
39
|
|
|
40
40
|
```plaintext
|
|
41
|
-
❌ No wiki folder.
|
|
41
|
+
❌ No wiki folder. Create `.claude/wiki/` with an `index.md`, then promote again.
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Creating the folder as a side effect of promoting one page hands the project a surface it never chose, and the refusal is one
|
|
44
|
+
Creating the folder as a side effect of promoting one page hands the project a surface it never chose, and the refusal is one step from resolved.
|
|
45
45
|
|
|
46
46
|
## What a promoted page owes its destination
|
|
47
47
|
|
|
@@ -9,9 +9,9 @@ What each verb writes, refuses, and leaves alone. `canon --help` lists flags.
|
|
|
9
9
|
|
|
10
10
|
## Bootstrap
|
|
11
11
|
|
|
12
|
-
`canon init [path]` installs up to
|
|
12
|
+
`canon init [path]` installs up to four core domains and reports each independently. A failed domain does not abort the run, which finishes the rest and exits 1 naming the failures.
|
|
13
13
|
|
|
14
|
-
Any flag skips the confirmation prompt, but the `base` default of `--stack` does not count, so a bare `canon init` still prompts. `--skip` takes `
|
|
14
|
+
Any flag skips the confirmation prompt, but the `base` default of `--stack` does not count, so a bare `canon init` still prompts. `--skip` takes `governance` and `records`, and warns on any other value.
|
|
15
15
|
|
|
16
16
|
## Governance install
|
|
17
17
|
|
|
@@ -62,14 +62,13 @@ Discover names at runtime rather than hardcoding them.
|
|
|
62
62
|
Set `CANON_NON_INTERACTIVE=1` to refuse every prompt.
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
|
-
CANON_NON_INTERACTIVE=1 canon init --stack node --skip
|
|
65
|
+
CANON_NON_INTERACTIVE=1 canon init --stack node --skip records /path/to/project
|
|
66
66
|
CANON_NON_INTERACTIVE=1 canon sync /path/to/project
|
|
67
67
|
CANON_NON_INTERACTIVE=1 canon gov install node --add astro,shadcn /path/to/project
|
|
68
68
|
CANON_NON_INTERACTIVE=1 canon gov sync /path/to/project
|
|
69
69
|
CANON_NON_INTERACTIVE=1 canon gov build /path/to/project
|
|
70
70
|
CANON_NON_INTERACTIVE=1 canon tooling diff astro /path/to/project
|
|
71
71
|
CANON_NON_INTERACTIVE=1 canon tooling sync astro /path/to/project --write
|
|
72
|
-
CANON_NON_INTERACTIVE=1 canon wiki init /path/to/project
|
|
73
72
|
canon tooling inject base /path/to/project --configs --seeds
|
|
74
73
|
canon tooling prune-gitignore base /path/to/project
|
|
75
74
|
canon tooling verify vite-react
|
|
@@ -160,3 +160,21 @@ canon migrate session-defaults --write --json
|
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
The verb only adds a key that is absent. A person who wants auto memory back sets it in their own `.claude/settings.local.json`.
|
|
163
|
+
|
|
164
|
+
## Move the capture cases out of the harness, once
|
|
165
|
+
|
|
166
|
+
A web project's `e2e/screenshot.ts` is a config, so sync overwrites it. It now loads `CASES` from a seeded `e2e/capture/cases.ts` that sync copies once and never overwrites. Cases edited in the old harness leave the working tree on the first sync and stay in git history.
|
|
167
|
+
|
|
168
|
+
- Copy your cases from the old `e2e/screenshot.ts` into `e2e/capture/cases.ts` before the next sync.
|
|
169
|
+
- Evidence now defaults to `e2e/evidence/`. Set `export const EVIDENCE_ROOT = 'evidence'` in `e2e/capture/cases.ts` to keep a committed baseline at the old path.
|
|
170
|
+
- A run from a folder with no `e2e/capture/cases.ts` exits 1 and names the file.
|
|
171
|
+
|
|
172
|
+
## Add the keyboard rule beside a held a11y rule, once
|
|
173
|
+
|
|
174
|
+
The `a11y` rule split in two. Focus styles, Escape and shortcuts, composite widgets, focus management, and pointer target size moved to a new `keyboard` rule. `canon gov sync` refreshes only the rules a project holds, so it rewrites `a11y.md` without those bullets and never adds `keyboard.md`.
|
|
175
|
+
|
|
176
|
+
A project holding `.claude/rules/canon/ui/a11y.md` adds the new rule alone:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
canon gov install --add keyboard --yes .
|
|
180
|
+
```
|
package/docs/target/projects.md
CHANGED
|
@@ -70,10 +70,9 @@ The default is `base`. Escalate only for a project writing code, and `target-set
|
|
|
70
70
|
|
|
71
71
|
### Core domains and skips
|
|
72
72
|
|
|
73
|
-
`canon init` installs base tooling, the Claude workflow, and governance
|
|
73
|
+
`canon init` installs base tooling, the Claude workflow, and governance. Pass `--stack <name>` for a language stack.
|
|
74
74
|
|
|
75
75
|
- `--skip governance` leaves `.claude/rules/` empty. The run prints the `canon gov install <stack> <path>` command that restores the rules it dropped.
|
|
76
|
-
- `--skip wiki` skips the `.claude/wiki/` scaffold. A root `wiki/` is kept.
|
|
77
76
|
|
|
78
77
|
## Add a domain later
|
|
79
78
|
|
package/docs/target/scenarios.md
CHANGED
|
@@ -42,4 +42,7 @@ canon tooling sync python ./backend --skip base --write
|
|
|
42
42
|
|
|
43
43
|
`--skip base` keeps husky, oxfmt, commitlint, and CI at the root. Every subtree still receives `scripts/verify.ts` and `scripts/verify.json`, and its own framework configs.
|
|
44
44
|
|
|
45
|
-
A sync into a subfolder withholds `.github/`, so run the subtree from the root workflow with `working-directory` set to the path the sync prints.
|
|
45
|
+
A sync into a subfolder withholds `.github/`, so run the subtree from the root workflow with `working-directory` set to the path the sync prints. Run `bun scripts/verify.ts` in the subtree, since the `check` script lives at the root.
|
|
46
|
+
|
|
47
|
+
- A subtree with no `package.json` gets a minimal one holding the folder's name, version `0.0.0`, and `type: module`, then the install runs into it
|
|
48
|
+
- An existing file is never rewritten, and one that is not valid JSON ends the sync on `package.json is not valid JSON: fix it in <path>, then sync again`
|
|
@@ -40,4 +40,4 @@ These skills serve a request rather than a moment. With the [skill map](skill-ma
|
|
|
40
40
|
| `canon:markdown-craft` | Before a substantial markdown edit |
|
|
41
41
|
| `canon:write-human` | Before drafting or revising prose |
|
|
42
42
|
|
|
43
|
-
Ask `canon:teach-workspace` to promote a learning workspace's reference pages and glossaries. It proposes a destination per page and waits. An Anthropic-owned subject goes to the wiki, an internal one to the matching context entry, and the rest to the public docs. A project with no wiki folder gets a refusal naming
|
|
43
|
+
Ask `canon:teach-workspace` to promote a learning workspace's reference pages and glossaries. It proposes a destination per page and waits. An Anthropic-owned subject goes to the wiki, an internal one to the matching context entry, and the rest to the public docs. A project with no wiki folder gets a refusal naming the folder to create.
|
package/governance/detect.toml
CHANGED
|
@@ -169,6 +169,10 @@ files = ["index.html", "*/index.html"]
|
|
|
169
169
|
deps = ["react", "vue", "svelte", "astro", "next", "solid-js", "preact"]
|
|
170
170
|
files = ["index.html", "*/index.html"]
|
|
171
171
|
|
|
172
|
+
["keyboard"]
|
|
173
|
+
deps = ["react", "vue", "svelte", "astro", "next", "solid-js", "preact"]
|
|
174
|
+
files = ["index.html", "*/index.html"]
|
|
175
|
+
|
|
172
176
|
["forms"]
|
|
173
177
|
deps = ["react", "vue", "svelte", "astro", "next", "solid-js", "preact"]
|
|
174
178
|
files = ["index.html", "*/index.html"]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Go rule ledger
|
|
3
|
+
description: Sources for governance/rules/lang/go.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Go rule ledger
|
|
7
|
+
|
|
8
|
+
The field is Go programming, and the authority is the Go project's own documentation at go.dev and pkg.go.dev, with the Code Review Comments wiki as the team's review guide. The package pages read name go1.27.2, and the wiki and layout pages name no release.
|
|
9
|
+
|
|
10
|
+
## Errors
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
|
14
|
+
| Wrap with `%w` | [errors](https://pkg.go.dev/errors): "An easy way to create wrapped errors is to call fmt.Errorf and apply the %w verb to the error argument" | 2026-10-18, go1.27.2 |
|
|
15
|
+
| Match with `Is` or `As` | [errors](https://pkg.go.dev/errors): `Is` "should be used in preference to simple equality checks", since it walks the wrapped chain | 2026-10-18, go1.27.2 |
|
|
16
|
+
| Never discard with `_` | [Code Review Comments](https://go.dev/wiki/CodeReviewComments#handle-errors): "Do not discard errors using `_` variables" | 2026-10-18 |
|
|
17
|
+
| `panic` for broken invariant | [Code Review Comments](https://go.dev/wiki/CodeReviewComments#dont-panic): "Don't use panic for normal error handling. Use error and multiple return values." | 2026-10-18 |
|
|
18
|
+
|
|
19
|
+
## Context
|
|
20
|
+
|
|
21
|
+
| Bullet | Source | Read |
|
|
22
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- |
|
|
23
|
+
| First parameter, `ctx` | [context](https://pkg.go.dev/context): "The Context should be the first parameter, typically named ctx" | 2026-10-18, go1.27.2 |
|
|
24
|
+
| Never in a struct | [context](https://pkg.go.dev/context): "Do not store Contexts inside a struct type", and "pass a Context explicitly to each function that needs it" | 2026-10-18, go1.27.2 |
|
|
25
|
+
| No `nil`, `context.TODO` | [context](https://pkg.go.dev/context): "Do not pass a nil Context, even if a function permits it. Pass context.TODO if you are unsure about which Context to use." | 2026-10-18, go1.27.2 |
|
|
26
|
+
|
|
27
|
+
## Interfaces
|
|
28
|
+
|
|
29
|
+
| Bullet | Source | Read |
|
|
30
|
+
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
31
|
+
| In the consumer, only its calls | [Code Review Comments](https://go.dev/wiki/CodeReviewComments#interfaces): "Go interfaces generally belong in the package that uses values of the interface type", and [Effective Go](https://go.dev/doc/effective_go#interfaces): "Interfaces with only one or two methods are common" | 2026-10-18 |
|
|
32
|
+
| Accept interfaces, return concrete | [Code Review Comments](https://go.dev/wiki/CodeReviewComments#interfaces): "The implementing package should return concrete (usually pointer or struct) types" | 2026-10-18 |
|
|
33
|
+
|
|
34
|
+
## Packages
|
|
35
|
+
|
|
36
|
+
| Bullet | Source | Read |
|
|
37
|
+
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
38
|
+
| `internal/` for private code | [Organizing a Go module](https://go.dev/doc/modules/layout): `internal` "prevents other modules from depending on packages we don't necessarily want to expose", and "keep packages in internal as much as possible" | 2026-10-18 |
|
|
39
|
+
| No side effects in `init` | House choice: `init` runs on every import, tests included, in an order the reader cannot see from the call site, so a dependency it wires is hidden and cannot be swapped | 2026-10-18 |
|
|
40
|
+
| No mutable package state | House choice: a package-level variable is shared by every caller and every parallel test, so one test's write leaks into another and a data race needs no visible goroutine | 2026-10-18 |
|
|
41
|
+
|
|
42
|
+
## Declined
|
|
43
|
+
|
|
44
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
45
|
+
|
|
46
|
+
### A convention Claude follows unprompted
|
|
47
|
+
|
|
48
|
+
The four casing bullets went under the plan's question 1, whose suggestion stood. `gofmt`, [Effective Go](https://go.dev/doc/effective_go#names), and Code Review Comments set each of them, and Claude writes them without the rule. The error-position bullet went on the same test.
|
|
49
|
+
|
|
50
|
+
- Cut "`MixedCaps` for exported names, never underscores", which Effective Go states as "the convention in Go is to use MixedCaps or mixedCaps rather than underscores".
|
|
51
|
+
- Cut "one short lowercase package name, not `util`, `common`, or `helpers`", which Effective Go and the wiki's "Avoid meaningless package names like util, common, misc" both state.
|
|
52
|
+
- Cut "do not repeat the package name in an exported identifier", which Effective Go shows with `bufio.Reader` over `BufReader`.
|
|
53
|
+
- Cut "keep initialisms in one case". No sampled session was run to test it, so the suggestion's condition for keeping it was never met.
|
|
54
|
+
- Cut "return an error as the last result value", a convention every Go signature Claude writes already follows.
|
|
55
|
+
|
|
56
|
+
### Merged or reworded
|
|
57
|
+
|
|
58
|
+
- Merged "keep an interface to the methods its consumer calls" into the consumer-placement bullet, since both state where an interface's method set comes from.
|
|
59
|
+
- Reworded the `errors.Is` bullet to ban `==` as well as string matching, since the package page sets `Is` against "simple equality checks".
|
|
60
|
+
- Gave the discard ban the wiki's own alternative, to handle, return, or panic.
|
|
61
|
+
- Gave the package-state ban an alternative, a struct the caller owns.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: PHP rule ledger
|
|
3
|
+
description: Sources for governance/rules/lang/php.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PHP rule ledger
|
|
7
|
+
|
|
8
|
+
The field is PHP programming, and the authority is the PHP manual at php.net, with the PHP Framework Interop Group's standards at php-fig.org for autoloading and file layout. The manual pages read belong to the PHP 8.5 manual.
|
|
9
|
+
|
|
10
|
+
## Strictness
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
|
|
14
|
+
| `strict_types=1` | [Type declarations](https://www.php.net/manual/en/language.types.declarations.php): "By default, PHP will coerce values of the wrong type into the expected scalar type declaration if possible", and strict mode is enabled "on a per-file basis" | 2026-10-18, PHP 8.5 |
|
|
15
|
+
| Native types over docblocks | [Type declarations](https://www.php.net/manual/en/language.types.declarations.php): native declarations "ensure that the value is of the specified type at call time, otherwise a TypeError is thrown", which no docblock does | 2026-10-18, PHP 8.5 |
|
|
16
|
+
| `===` over `==` | [Comparison operators](https://www.php.net/manual/en/language.operators.comparison.php): `==` is "equal after type juggling", and the page's warning shows `"1" == "01"` and `100 == "1e2"` both true in PHP 8 | 2026-10-18, PHP 8.5 |
|
|
17
|
+
|
|
18
|
+
## Layout
|
|
19
|
+
|
|
20
|
+
| Bullet | Source | Read |
|
|
21
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
22
|
+
| PSR-4 autoloading | [PSR-4](https://www.php-fig.org/psr/psr-4/): "This PSR describes a specification for autoloading classes from file paths", and "The terminating class name corresponds to a file name ending in .php" | 2026-10-18 |
|
|
23
|
+
| One symbol per file | [PSR-1](https://www.php-fig.org/psr/psr-1/): "This means each class is in a file by itself, and is in a namespace of at least one level" | 2026-10-18 |
|
|
24
|
+
|
|
25
|
+
## Data shapes
|
|
26
|
+
|
|
27
|
+
| Bullet | Source | Read |
|
|
28
|
+
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
|
|
29
|
+
| `enum` for a closed set | [Enumerations](https://www.php.net/manual/en/language.enumerations.overview.php): a type "limited to one of a discrete number of possible values", and [backed enums](https://www.php.net/manual/en/language.enumerations.backed.php) exist for a case that "needs to be able to round-trip to a database or similar datastore" | 2026-10-18, PHP 8.5 |
|
|
30
|
+
| `readonly` value objects | [Properties](https://www.php.net/manual/en/language.oop5.properties.php): `readonly` "prevents modification of the property after initialization", and [a readonly class](https://www.php.net/manual/en/language.oop5.basic.php) adds it "to every declared property" | 2026-10-18, PHP 8.5 |
|
|
31
|
+
| `final` unless designed for it | House choice: a subclass nobody planned for couples to the parent's internals, so a refactor of the parent breaks code its author never saw | 2026-10-18 |
|
|
32
|
+
| No associative array boundary | House choice: a misspelled or missing array key reads as `null` with a warning rather than failing, and no native type declares an array's keys, so the shape drifts unchecked | 2026-10-18 |
|
|
33
|
+
|
|
34
|
+
## Errors
|
|
35
|
+
|
|
36
|
+
| Bullet | Source | Read |
|
|
37
|
+
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
|
|
38
|
+
| Domain exception, not `false` | House choice: a caller that forgets to test a `false` or `null` return carries on with a bad value, and `0` and `false` look alike under the loose checks the strictness bullets ban | 2026-10-18 |
|
|
39
|
+
| No `@` | House choice: [Error control](https://www.php.net/manual/en/language.operators.errorcontrol.php) says `@` suppresses "any diagnostic error that might be generated by that expression", so the failure leaves no trace for whoever debugs it | 2026-10-18, PHP 8.5 |
|
|
40
|
+
| No `extract` or `eval` | [eval](https://www.php.net/manual/en/function.eval.php): "very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged.", and [extract](https://www.php.net/manual/en/function.extract.php) overwrites a colliding variable by default | 2026-10-18, PHP 8.5 |
|
|
41
|
+
|
|
42
|
+
## Declined
|
|
43
|
+
|
|
44
|
+
Every bullet stayed, so nothing was cut. Three were reshaped.
|
|
45
|
+
|
|
46
|
+
### Merged or reworded
|
|
47
|
+
|
|
48
|
+
- Reworded "a backed `enum` for a closed set" to an `enum`, backed only when stored or serialized. The manual gives a pure enum for a closed set and a backing value for a datastore round trip.
|
|
49
|
+
- Gave the `@` ban an alternative, to check the condition first or catch the exception.
|
|
50
|
+
- Gave the `extract`, variable variables, and `eval` ban an alternative, to name each variable and call each function directly.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Python rule ledger
|
|
3
|
+
description: Sources for governance/rules/lang/python.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python rule ledger
|
|
7
|
+
|
|
8
|
+
The field is Python programming, and the authority is the Python documentation at docs.python.org with the PEPs at peps.python.org. The library pages read are the Python 3.15 documentation.
|
|
9
|
+
|
|
10
|
+
## Type hints
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
|
|
14
|
+
| Annotate public functions | House choice: a gradual type checker treats an unannotated function as dynamically typed and leaves its body unchecked, so a public function with no hints hides its callers' mistakes | 2026-10-18 |
|
|
15
|
+
| `X \| None` and built-in generics | [typing](https://docs.python.org/3/library/typing.html): the `int \| str` shorthand "is recommended", `Optional[X]` "is equivalent to `X \| None`", and `typing.List` is a "Deprecated alias to list" since 3.9 | 2026-10-18, Python 3.15 |
|
|
16
|
+
| No `from __future__ import annotations` | [`__future__`](https://docs.python.org/3/library/__future__.html): "This feature will eventually be deprecated and removed. See PEP 649 and PEP 749." | 2026-10-18, Python 3.15 |
|
|
17
|
+
| `Protocol` over an ABC | [PEP 544](https://peps.python.org/pep-0544/): with an ABC "a class has to be explicitly marked to support them", and protocols provide "structural subtyping (static duck typing)" | 2026-10-18 |
|
|
18
|
+
| `object` over `Any` | [typing](https://docs.python.org/3/library/typing.html#the-any-type): "Use object to indicate that a value could be any type in a typesafe manner. Use Any to indicate that a value is dynamically typed." | 2026-10-18, Python 3.15 |
|
|
19
|
+
|
|
20
|
+
## Errors
|
|
21
|
+
|
|
22
|
+
| Bullet | Source | Read |
|
|
23
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
|
|
24
|
+
| Specific exception class | [PEP 8](https://peps.python.org/pep-0008/#programming-recommendations): "Design exception hierarchies based on the distinctions that code catching the exceptions is likely to need" | 2026-10-18 |
|
|
25
|
+
| Narrowest catch | [PEP 8](https://peps.python.org/pep-0008/#programming-recommendations): "When catching exceptions, mention specific exceptions whenever possible instead of using a bare `except:` clause" | 2026-10-18 |
|
|
26
|
+
| `raise ... from err` | [PEP 8](https://peps.python.org/pep-0008/#programming-recommendations): "`raise X from Y` should be used to indicate explicit replacement without losing the original traceback" | 2026-10-18 |
|
|
27
|
+
|
|
28
|
+
## Data shapes
|
|
29
|
+
|
|
30
|
+
| Bullet | Source | Read |
|
|
31
|
+
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
|
|
32
|
+
| Frozen, slotted dataclass | [dataclasses](https://docs.python.org/3/library/dataclasses.html): with `frozen`, "assigning to fields will generate an exception", and `slots` generates `__slots__`, added in 3.10 | 2026-10-18, Python 3.15 |
|
|
33
|
+
| `Enum` for a closed set | [enum](https://docs.python.org/3/library/enum.html): an enumeration "is a set of symbolic names (members) bound to unique values", and `StrEnum` members "are also strings", added in 3.11 | 2026-10-18, Python 3.15 |
|
|
34
|
+
| `pathlib` over `os.path` | [pathlib](https://docs.python.org/3/library/pathlib.html): "`Path` is most likely what you need", and its normalization is "more opinionated and consistent than `os.path`", keeping `..` segments "for greater safety" | 2026-10-18, Python 3.15 |
|
|
35
|
+
|
|
36
|
+
## Imports and modules
|
|
37
|
+
|
|
38
|
+
| Bullet | Source | Read |
|
|
39
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
|
|
40
|
+
| Absolute imports | [PEP 8](https://peps.python.org/pep-0008/#imports): "Absolute imports are recommended, as they are usually more readable and tend to be better behaved" | 2026-10-18 |
|
|
41
|
+
| No shortening aliases | House choice: an internal name aliased at import no longer matches its definition, so a search for the name misses every call site behind the alias | 2026-10-18 |
|
|
42
|
+
| `__main__` guard | [`__main__`](https://docs.python.org/3/library/__main__.html): imported "to unit test it, the script code would unintentionally execute as well", and "Most often, a function named main encapsulates the program's primary behavior" | 2026-10-18, Python 3.15 |
|
|
43
|
+
|
|
44
|
+
## Declined
|
|
45
|
+
|
|
46
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
47
|
+
|
|
48
|
+
### Contradicted by the docs
|
|
49
|
+
|
|
50
|
+
- Reworded the pydantic-only `__future__` caveat to a plain ban, since the docs say the feature "will eventually be deprecated and removed".
|
|
51
|
+
- Reworded the ban on parent-relative imports to a preference for absolute ones. PEP 8 calls explicit relative imports "an acceptable alternative to absolute imports", so a ban overstated it.
|
|
52
|
+
- Cut `typing.cast` as the alternative to `Any`, since the docs say it "returns the value unchanged" and checks nothing. The bullet now names `object`.
|
|
53
|
+
|
|
54
|
+
### A convention Claude follows unprompted
|
|
55
|
+
|
|
56
|
+
PEP 8's naming conventions set all four casing bullets, and Claude writes them without the rule.
|
|
57
|
+
|
|
58
|
+
- Cut "`snake_case` for modules, functions, variables, and methods".
|
|
59
|
+
- Cut "`PascalCase` for classes, type aliases, `TypeVar`, and `ParamSpec`".
|
|
60
|
+
- Cut "`UPPER_SNAKE_CASE` for module-level constants and environment variables", which PEP 8 states as "written in all capital letters with underscores separating words".
|
|
61
|
+
- Cut "`_leading_underscore` for module-private names, `__dunder__` for the protocol".
|
|
62
|
+
|
|
63
|
+
### Merged or reworded
|
|
64
|
+
|
|
65
|
+
- Merged the `X | None` bullet and the built-in generics bullet, since both replace a `typing` spelling with the native one.
|
|
66
|
+
- Merged "raise specific built-in exceptions" and "define a project-rooted exception hierarchy" into one bullet naming both kinds a raise may take.
|
|
67
|
+
- Narrowed "annotate all public functions, methods, and class attributes" to functions and methods, and gave it the unchecked-body reason.
|
|
68
|
+
- Gave the `__main__` guard the docs' own shape, a `main` function the guard calls.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TypeScript rule ledger
|
|
3
|
+
description: Sources for governance/rules/lang/typescript.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeScript rule ledger
|
|
7
|
+
|
|
8
|
+
The field is TypeScript programming, and the authority is the TypeScript documentation at typescriptlang.org, with MDN for the JavaScript runtime the language compiles to. The handbook names no release, so rows read the handbook as TypeScript 5.
|
|
9
|
+
|
|
10
|
+
## File names
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
|
|
14
|
+
| `kebab-case` filenames | House choice: an import differing from its file only in case resolves on a case-insensitive filesystem such as macOS and fails on Linux CI, and one casing leaves no such pair | 2026-10-18 |
|
|
15
|
+
|
|
16
|
+
## Type declarations
|
|
17
|
+
|
|
18
|
+
| Bullet | Source | Read |
|
|
19
|
+
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
20
|
+
| `unknown` over `any` | [More on Functions](https://www.typescriptlang.org/docs/handbook/2/functions.html): `unknown` "is similar to the `any` type, but is safer because it's not legal to do anything with an `unknown` value" | 2026-10-18, TypeScript 5 |
|
|
21
|
+
| `interface` until `type` is needed | [Everyday Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html): "If you would like a heuristic, use `interface` until you need to use features from `type`." | 2026-10-18, TypeScript 5 |
|
|
22
|
+
| `as const` over `enum` | [Enums](https://www.typescriptlang.org/docs/handbook/enums.html): "you may not need an enum when an object with `as const` could suffice", which "keeps your codebase aligned with the state of JavaScript" | 2026-10-18, TypeScript 5 |
|
|
23
|
+
|
|
24
|
+
## Type safety
|
|
25
|
+
|
|
26
|
+
| Bullet | Source | Read |
|
|
27
|
+
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ |
|
|
28
|
+
| Narrow over assert or `!` | [Everyday Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html): "there is no runtime checking associated with a type assertion", and the non-null `!` likewise "doesn't change the runtime behavior of your code" | 2026-10-18, TypeScript 5 |
|
|
29
|
+
| `Promise.all` for independent work | [Promise.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all): sequential `await` makes the function "pause until the choice is made, and then again until the prices are fetched", and `Promise.all` runs "them concurrently" | 2026-10-18 |
|
|
30
|
+
|
|
31
|
+
## Imports and configuration
|
|
32
|
+
|
|
33
|
+
| Bullet | Source | Read |
|
|
34
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
35
|
+
| `@/` alias where mapped | House choice: a `../../` chain breaks whenever the importing file moves. The condition comes from [Modules reference](https://www.typescriptlang.org/docs/handbook/modules/reference.html), where `paths` "does not change the import path in the code emitted", so an alias the bundler lacks "will crash at runtime" | 2026-10-18, TypeScript 5 |
|
|
36
|
+
| No barrel re-exports | House choice: importing through a barrel loads every module it re-exports, which slows tests and opens import cycles that surface as `undefined` at load time | 2026-10-18 |
|
|
37
|
+
| `import type` | [TypeScript 3.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html): `import type` "always gets fully erased, so there's no remnant of it at runtime" | 2026-10-18, TypeScript 5 |
|
|
38
|
+
| `strict: true` | [TSConfig strict](https://www.typescriptlang.org/tsconfig/#strict): "The strict flag enables a wide range of type checking behavior that results in stronger guarantees of program correctness" | 2026-10-18, TypeScript 5 |
|
|
39
|
+
|
|
40
|
+
## Declined
|
|
41
|
+
|
|
42
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
43
|
+
|
|
44
|
+
### Cut without a source or a bug class
|
|
45
|
+
|
|
46
|
+
- Cut "enforce explicit types or strict inference", which no reviewer can check against a line of code.
|
|
47
|
+
- Cut "discriminated unions for error handling over throwing exceptions". No TypeScript page takes that side, and this repository's own CLI throws, so the bullet loaded against the code it governs.
|
|
48
|
+
- Cut "built-in utility types over manual type manipulation". The utility types page describes them and sets them against nothing.
|
|
49
|
+
- Cut "prefer `readonly` properties for data objects". The object types page describes `readonly` and takes no side on when to use it.
|
|
50
|
+
|
|
51
|
+
### A convention Claude follows unprompted
|
|
52
|
+
|
|
53
|
+
- Cut "`camelCase` for variables, functions, and methods".
|
|
54
|
+
- Cut "`PascalCase` for types, interfaces, classes, and components".
|
|
55
|
+
- Cut "`UPPER_SNAKE_CASE` for constants and environment variables".
|
|
56
|
+
- Cut "do not prefix interfaces with `I`", a prefix Claude does not write without being asked.
|
|
57
|
+
|
|
58
|
+
### Merged or reworded
|
|
59
|
+
|
|
60
|
+
- Reworded the `interface` bullet to the handbook's heuristic, and merged "`type` for unions, intersections, and utility types" into it as the point where `type` takes over.
|
|
61
|
+
- Merged "do not use non-null assertions" into the narrowing bullet, since the handbook groups `!` with the type assertion it is.
|
|
62
|
+
- Reworded the `@/` bullet to apply only where both tsconfig.json and the bundler map the alias, under the plan's question 2, whose suggestion stood.
|
|
63
|
+
- Moved the `kebab-case` filename bullet under a `File names` heading, since it was the one casing bullet kept, and gave it the case-sensitivity reason.
|
|
64
|
+
- Gave the `unknown` bullet the narrowing step `unknown` requires before use.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Persistence rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/database.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Persistence rule ledger
|
|
7
|
+
|
|
8
|
+
The field is database engineering, plus application security for query construction. The authorities are the migration and ORM documentation of Rails, Prisma, Django, and SQLAlchemy, the PostgreSQL manual, Martin Fowler's article on evolutionary database design, and the OWASP Cheat Sheet Series. Spike S1 of the rule-standard groundwork drafted this ledger, and its three unopened leads were read for it.
|
|
9
|
+
|
|
10
|
+
## Schema changes
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Every change is a migration | [Evolutionary Database Design](https://martinfowler.com/articles/evodb.html): every change is "a database migration script which is version controlled together with application code changes" | 2026-10-18 |
|
|
15
|
+
| A way back, down or forward | [Rails migrations](https://guides.rubyonrails.org/active_record_migrations.html): "you can raise `ActiveRecord::IrreversibleMigration` in your `down` block", and [Prisma down migrations](https://www.prisma.io/docs/orm/prisma-migrate/workflows/generating-down-migrations) undoes an applied one with "you will instead need to revert your `schema.prisma` file" and a new migration | 2026-10-18 |
|
|
16
|
+
| No change outside a migration | [Evolutionary Database Design](https://martinfowler.com/articles/evodb.html): the authors never "run ad hoc DDL or DML", and "all changes are made by migrations" | 2026-10-18 |
|
|
17
|
+
| Never edit a run migration | [Rails migrations](https://guides.rubyonrails.org/active_record_migrations.html): "editing existing migrations that have been already committed to source control is not a good idea" | 2026-10-18 |
|
|
18
|
+
| Drop after reads and writes stop | [Expand and contract](https://www.prisma.io/dataguide/types/relational/expand-and-contract-pattern): "Once all clients have been updated to stop writing to the original schema, you can safely delete the original data structure" | 2026-10-18 |
|
|
19
|
+
|
|
20
|
+
## Query construction
|
|
21
|
+
|
|
22
|
+
| Bullet | Source | Read |
|
|
23
|
+
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
24
|
+
| Bind every runtime value | [SQL Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html): developers "should be told to use prepared statements with variable binding (aka parameterized queries)" | 2026-10-18 |
|
|
25
|
+
| Never interpolate a value | [SQL Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html): building the string by hand ranks last, as "STRONGLY DISCOURAGED: Escaping All User Supplied Input" | 2026-10-18 |
|
|
26
|
+
| Allow-list identifiers | [SQL Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html): for "table names, column names, or sort order indicators", "input validation or query redesign is the most appropriate defense" | 2026-10-18 |
|
|
27
|
+
|
|
28
|
+
## Transactions
|
|
29
|
+
|
|
30
|
+
| Bullet | Source | Read |
|
|
31
|
+
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
|
|
32
|
+
| One owner per transaction | [SQLAlchemy session basics](https://docs.sqlalchemy.org/en/20/orm/session_basics.html): "Keep the lifecycle of the session (and usually the transaction) separate and external" | 2026-10-18 |
|
|
33
|
+
| Savepoint, never commit a handed one | [Django transactions](https://docs.djangoproject.com/en/5.2/topics/db/transactions/): Django "creates a savepoint when entering an inner `atomic` block" and commits "when exiting the outermost block", and [PostgreSQL SAVEPOINT](https://www.postgresql.org/docs/current/sql-savepoint.html) "establishes a new savepoint within the current transaction" | 2026-10-18 |
|
|
34
|
+
| Side effects after commit | [Django transactions](https://docs.djangoproject.com/en/5.2/topics/db/transactions/): `on_commit()` callbacks run "after the open transaction is successfully committed", for "a background task, an email notification, or a cache invalidation" | 2026-10-18 |
|
|
35
|
+
|
|
36
|
+
## Access patterns
|
|
37
|
+
|
|
38
|
+
| Bullet | Source | Read |
|
|
39
|
+
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
40
|
+
| Load relations with parents | [Django optimization](https://docs.djangoproject.com/en/5.2/topics/db/optimization/): "Retrieve everything at once if you know you will need it", naming `select_related()` and `prefetch_related()`, and [Rails querying](https://guides.rubyonrails.org/active_record_querying.html) solves N + 1 queries with `includes` | 2026-10-18 |
|
|
41
|
+
| Session per request | [SQLAlchemy session basics](https://docs.sqlalchemy.org/en/20/orm/session_basics.html): "create a Session at the start of a web request" and "close the session at the end of web request" | 2026-10-18 |
|
|
42
|
+
|
|
43
|
+
## Declined
|
|
44
|
+
|
|
45
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
46
|
+
|
|
47
|
+
### Contradicted by the docs
|
|
48
|
+
|
|
49
|
+
- Cut "name the columns a read needs, never select every column by wildcard". The [Django optimization](https://docs.djangoproject.com/en/5.2/topics/db/optimization/) page says "Don't be too aggressive in deferring fields without profiling", and no page read backs the ban.
|
|
50
|
+
- Narrowed "never open a transaction inside a function that was handed one". Django designs a nested `atomic` as a savepoint, so the bullet now bans committing the caller's transaction.
|
|
51
|
+
- Reworded "ship a tested rollback path with every migration". Prisma has no down step and undoes a migration with a new one, which the old wording read as missing.
|
|
52
|
+
|
|
53
|
+
### Merged or reworded
|
|
54
|
+
|
|
55
|
+
- Reworded "separate a destructive migration from the deploy that stops reading the dropped column" to the order expand and contract states, removal after reads and writes stop.
|
|
56
|
+
|
|
57
|
+
### Gaps filled
|
|
58
|
+
|
|
59
|
+
- Added the allow-list bullet. OWASP names identifiers as the part of a query no bind reaches, the case a model gets wrong after it binds every value.
|
|
60
|
+
|
|
61
|
+
### Leads read
|
|
62
|
+
|
|
63
|
+
- Fowler's article backs the migration bullets but says nothing on editing an applied migration, so Rails carries that row.
|
|
64
|
+
- The PostgreSQL `SAVEPOINT` page backs the savepoint wording beside Django's.
|
|
65
|
+
- The Rails querying guide backs eager loading through `includes`.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: End-to-end reliability rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/e2e-reliability.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# End-to-end reliability rule ledger
|
|
7
|
+
|
|
8
|
+
The field is browser test automation. The authorities are the Playwright documentation at playwright.dev, MDN for the `scrollend` event, and the Chrome DevTools Protocol definition for CPU throttling. A guard bullet is a house choice wherever no page takes a side, and each such row names the false pass it prevents. None of the pages read names a Playwright major, so the rows record the read date alone.
|
|
9
|
+
|
|
10
|
+
## Waiting
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
14
|
+
| Settle, never `waitForTimeout` | [Page API](https://playwright.dev/docs/api/class-page): `waitForTimeout` is "Discouraged", since "Tests that wait for time are inherently flaky", and [assertions](https://playwright.dev/docs/test-assertions): "You can convert any synchronous `expect` to an asynchronous polling one using `expect.poll`" | 2026-10-18 |
|
|
15
|
+
| Fixed window for absence | House choice: an absence has no signal to settle on, so a web-first assertion of it passes before the thing it rules out has had time to happen | 2026-10-18 |
|
|
16
|
+
| Bound every settle | [Assertions](https://playwright.dev/docs/test-assertions): "by default `toPass` has timeout 0 and does not respect custom" expect timeouts | 2026-10-18 |
|
|
17
|
+
| No raised timeout under load | House choice: a longer timeout hides a race that the load exposed, and it returns on a slower runner | 2026-10-18 |
|
|
18
|
+
| `scrollend`, then stillness | [MDN scrollend](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollend_event): "If the scroll position did not change, then no scrollend event fires" | 2026-10-18 |
|
|
19
|
+
| Reduced motion by default | [Test options](https://playwright.dev/docs/api/class-testoptions): `reducedMotion` "Emulates `'prefers-reduced-motion'` media feature" | 2026-10-18 |
|
|
20
|
+
|
|
21
|
+
## Falsifiable guards
|
|
22
|
+
|
|
23
|
+
| Bullet | Source | Read |
|
|
24
|
+
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
25
|
+
| Non-empty before every | House choice: a property asserted over an empty set holds vacuously, so the guard passes on the page that lost every member | 2026-10-18 |
|
|
26
|
+
| Raise when refused | House choice: an instrument returning a default on refusal hands the assertion a value it accepts, so the guard passes without measuring | 2026-10-18 |
|
|
27
|
+
| See the guard fail first | House choice: a guard never run against its defect may pass on the defect too, and nothing tells the two apart | 2026-10-18 |
|
|
28
|
+
| Never weaken to pass | House choice: a loosened assertion passes on the defect it was written to catch, where a narrowed wait keeps the check | 2026-10-18 |
|
|
29
|
+
|
|
30
|
+
## Reproducing a failure that only appears in CI
|
|
31
|
+
|
|
32
|
+
| Bullet | Source | Read |
|
|
33
|
+
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
34
|
+
| CPU throttling to reproduce | [DevTools Protocol, Emulation](https://chromedevtools.github.io/devtools-protocol/tot/Emulation/): `setCPUThrottlingRate` "Enables CPU throttling to emulate slow CPUs", as a "slowdown factor" | 2026-10-18 |
|
|
35
|
+
| Read the trace | [Best practices](https://playwright.dev/docs/best-practices): "For CI failures, use the Playwright trace viewer instead of videos and screenshots" | 2026-10-18 |
|
|
36
|
+
| Vary one condition | House choice: two runs that differ in an uncontrolled condition cannot attribute a change in count to the one under suspicion | 2026-10-18 |
|
|
37
|
+
|
|
38
|
+
## Authority
|
|
39
|
+
|
|
40
|
+
| Bullet | Source | Read |
|
|
41
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------- | ---------- |
|
|
42
|
+
| Pointer to `testing-ts` | House choice: a pointer to the rule that owns placement and naming, claiming no practice of its own | 2026-10-18 |
|
|
43
|
+
|
|
44
|
+
## Declined
|
|
45
|
+
|
|
46
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
47
|
+
|
|
48
|
+
### Cut from the subject
|
|
49
|
+
|
|
50
|
+
- Cut "read the check conclusion as its own act, since a green diff review reports nothing about the gate". It governs reading CI after a push, which the ship and review skills own, and an end-to-end file is the wrong trigger for it.
|
|
51
|
+
|
|
52
|
+
### Merged or reworded
|
|
53
|
+
|
|
54
|
+
- Merged "do not read a value once after a pause, poll it" into the settle bullet, which now names `page.waitForTimeout` as the call it bans.
|
|
55
|
+
- Reworded "bound every settle with an explicit timeout" to name `toPass`, the one settle the docs leave unbounded.
|
|
56
|
+
- Reworded "run with motion reduced" to name the `reducedMotion` option.
|
|
57
|
+
- Reworded "read the state the assertion does not" to read it from the trace, which the best practices page names for a CI failure.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Pydantic rule ledger
|
|
3
|
+
description: Sources for governance/rules/lib/pydantic.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pydantic rule ledger
|
|
7
|
+
|
|
8
|
+
The field is Python data validation, and the authority is the Pydantic documentation at pydantic.dev, which moved from docs.pydantic.dev. Every page read is the Pydantic 2 docs, so a version-bound bullet opens with "On Pydantic 2" and a Pydantic 1 target is never told to call an API it lacks.
|
|
9
|
+
|
|
10
|
+
## Models
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
|
|
14
|
+
| `BaseModel` at boundaries | House choice: a standard library `dataclass` checks no type at runtime, so malformed input passes the boundary unvalidated | 2026-10-18, Pydantic 2 |
|
|
15
|
+
| `model_config`, not `Config` | [Migration guide](https://pydantic.dev/docs/validation/latest/get-started/migration/): "to specify config on a model, you should set a class attribute called `model_config`" | 2026-10-18, Pydantic 2 |
|
|
16
|
+
| `extra="forbid"` on input | [Models](https://pydantic.dev/docs/validation/latest/concepts/models/): "`'forbid'`: Providing extra data is not permitted", and "`'ignore'`: Providing extra data is ignored (the default)" | 2026-10-18, Pydantic 2 |
|
|
17
|
+
| Constraints in `Field(...)` | [Fields](https://pydantic.dev/docs/validation/latest/concepts/fields/): "The Field() function can also be used to add constraints to specific types" | 2026-10-18, Pydantic 2 |
|
|
18
|
+
|
|
19
|
+
## Validation
|
|
20
|
+
|
|
21
|
+
| Bullet | Source | Read |
|
|
22
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- |
|
|
23
|
+
| `model_validate` and `model_validate_json` | [Models](https://pydantic.dev/docs/validation/latest/concepts/models/): for a JSON payload, `model_validate_json()` "is generally considered faster" than parsing it to a dictionary first | 2026-10-18, Pydantic 2 |
|
|
24
|
+
| v2 validator decorators | [Migration guide](https://pydantic.dev/docs/validation/latest/get-started/migration/): "`@validator` has been deprecated", and so has `@root_validator`, each replaced by its v2 decorator | 2026-10-18, Pydantic 2 |
|
|
25
|
+
| Cross-field checks after | [Validators](https://pydantic.dev/docs/validation/latest/concepts/validators/): after model validators "run after the whole model has been validated" | 2026-10-18, Pydantic 2 |
|
|
26
|
+
|
|
27
|
+
## Types
|
|
28
|
+
|
|
29
|
+
| Bullet | Source | Read |
|
|
30
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- |
|
|
31
|
+
| `SecretStr` for credentials | [Pydantic Types](https://pydantic.dev/docs/validation/latest/api/pydantic/types/): "A string used for storing sensitive information that you do not want to be visible in logging or tracebacks" | 2026-10-18, Pydantic 2 |
|
|
32
|
+
| Constrained types over regex | [Pydantic Types](https://pydantic.dev/docs/validation/latest/api/pydantic/types/): `PositiveInt` is "An integer that must be greater than zero" | 2026-10-18, Pydantic 2 |
|
|
33
|
+
| `Annotated` for reuse | [Fields](https://pydantic.dev/docs/validation/latest/concepts/fields/): Pydantic "supports the Annotated typing construct", and "Types can be made reusable" | 2026-10-18, Pydantic 2 |
|
|
34
|
+
| No `Any` fields | [Standard Library Types](https://pydantic.dev/docs/validation/latest/api/standard_library_types/): `Any` "Allows any value, including `None`" | 2026-10-18, Pydantic 2 |
|
|
35
|
+
|
|
36
|
+
## Settings
|
|
37
|
+
|
|
38
|
+
| Bullet | Source | Read |
|
|
39
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
|
|
40
|
+
| One `BaseSettings` model | [Settings Management](https://pydantic.dev/docs/validation/latest/concepts/pydantic_settings/): the initializer "will attempt to determine the values of any fields not passed as keyword arguments" from the environment | 2026-10-18, Pydantic 2 |
|
|
41
|
+
|
|
42
|
+
## Serialization
|
|
43
|
+
|
|
44
|
+
| Bullet | Source | Read |
|
|
45
|
+
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
|
|
46
|
+
| `model_dump(mode="json")` | [Serialization](https://pydantic.dev/docs/validation/latest/concepts/serialization/): "The `mode` argument can be set to `'json'` to ensure JSON-compatible types are used", and the [migration guide](https://pydantic.dev/docs/validation/latest/get-started/migration/) says "The `.json()` method is deprecated" | 2026-10-18, Pydantic 2 |
|
|
47
|
+
| `by_alias=True` outward | [Fields](https://pydantic.dev/docs/validation/latest/concepts/fields/): "the by_alias keyword argument defaults to False, and must be specified explicitly" | 2026-10-18, Pydantic 2 |
|
|
48
|
+
|
|
49
|
+
## Declined
|
|
50
|
+
|
|
51
|
+
Every bullet the rewrite removed or reshaped, grouped by why.
|
|
52
|
+
|
|
53
|
+
### Merged or reworded
|
|
54
|
+
|
|
55
|
+
- Merged "use `@field_validator` over the legacy `@validator`" with the `@root_validator` swap, which the old rule never named.
|
|
56
|
+
- Merged "use `model.model_dump_json()` over `json.dumps(model.dict())`" into the serialization bullet beside `model_dump(mode="json")`.
|
|
57
|
+
- Merged "use `Model.model_validate_json(s)` over `json.loads`" into the `model_validate` bullet. `Model(**data)` validates too, so the old contrast named no bug.
|
|
58
|
+
- Merged "use `pydantic_settings.BaseSettings` for env-driven configuration" into the one-model bullet.
|
|
59
|
+
- Narrowed "use `AnyUrl`, `EmailStr`, `SecretStr`, and `PositiveInt` over hand-rolled regex" into a `SecretStr` bullet for the log leak and a constrained-type bullet.
|
|
60
|
+
- Prefixed every legacy-API swap with "On Pydantic 2", since the rule loads on every Python file and a Pydantic 1 target lacks the v2 names.
|
|
61
|
+
- Reworded "use `Field(...)` over docstring conventions" to name the enforcement a docstring lacks.
|