Canonical Python stack now pinned in ~/dev/.context/AGENT.md (mathias/local-dev@6296536, tag v0.3.1), propagated to CLAUDE.md/AGENTS.md/.cursorrules/.aider.conventions.md via task context:sync:
Layer
Choice
Format+lint
ruff (one tool, replaces black+isort+flake8)
Types
mypy --strict (or pyright) — non-negotiable
Test
pytest + pytest-cov, table-driven via @pytest.mark.parametrize
Errors
raise X from e — never bare except, never silent pass
Action: wire this table into the per-language reference files already scoped for tdd (#18 — Python section) and extend the same pattern to clean-code, solid, code-review where a Go-specific example currently has no Python equivalent. Per #19's execution bar (pressure-test before shipping) and the Multi-Language Dilution reconciliation on #18 (main SKILL.md stays single-canonical-example, per-language content lives in linked reference files, not inline).
Canonical Python stack now pinned in `~/dev/.context/AGENT.md` (mathias/local-dev@6296536, tag v0.3.1), propagated to CLAUDE.md/AGENTS.md/.cursorrules/.aider.conventions.md via `task context:sync`:
| Layer | Choice |
|---|---|
| Format+lint | ruff (one tool, replaces black+isort+flake8) |
| Types | mypy --strict (or pyright) — non-negotiable |
| Test | pytest + pytest-cov, table-driven via `@pytest.mark.parametrize` |
| Errors | `raise X from e` — never bare `except`, never silent `pass` |
| Logging | stdlib `logging` w/ structured `extra=` (or structlog) |
| Config | pydantic-settings — typed env-var config |
| Deps/build | uv |
| Layout | src-layout, `pyproject.toml` only, no `setup.py` |
Action: wire this table into the per-language reference files already scoped for `tdd` (#18 — Python section) and extend the same pattern to `clean-code`, `solid`, `code-review` where a Go-specific example currently has no Python equivalent. Per #19's execution bar (pressure-test before shipping) and the Multi-Language Dilution reconciliation on #18 (main SKILL.md stays single-canonical-example, per-language content lives in linked reference files, not inline).
Shipped in 5070a73, not closing — same reason as #18: the GREEN pressure test could not run (session limit, resets 13:20 Europe/Stockholm), and #19's bar explicitly covers this batch.
What landed
Three reference files, following the solid/references/go-adaptation.md precedent and the structure #18 settled on — per-language content in linked references, main SKILL.md undiluted:
clean-code/references/python-adaptation.md
solid/references/python-adaptation.md
code-review/references/python-adaptation.md
Each is linked from its skill's references list.
The stack table is cited, not copied
Deliberate deviation from "wire this table into the reference files": none of the three restates it. Each says the canonical stack lives in ~/dev/.context/AGENT.md and moves on.
Copying an eight-row table into three files creates three things to update when uv is replaced or mypy gives way to pyright — and this repo spent today on the consequences of exactly that. #15's missing index rows, #29's generated index, bin/check-index deleted the same day it was written because generation beat gating: the through-line is that a fact with two homes has none. A pointer to AGENT.md costs a lookup; a copy costs a silent divergence.
What is in them instead — the part a linter cannot give you
clean-code:raise X from e as the analogue of fmt.Errorf("%w"), dataclasses over dict[str, Any], and the two traps where the broken version reads tidier than the correct one — mutable default arguments, and if not amount: where zero is a legitimate value. The second is, in my experience of this codebase's domain, the most common way a Python function silently mishandles valid input, and no reviewer catches it by reading quickly.
solid:Protocol over ABC, defined at the consumer, which is what makes DIP structural rather than aspirational — the direct analogue of Go's "accept interfaces". Plus a section on what does not carry over from the Go rewrite, because a Python reader following "return structs, accept interfaces" is following advice about Go's nil-interface semantics.
code-review: an anti-pattern table parallel to the Go one, opening with the rule that decides most of it — if ruff or mypy already catches it, the review comment is not "fix this line", it is "why is the gate not running?" Then the checks a linter cannot perform: does pytest.raises have a match=, is the mock at a boundary or a collaborator we own, would --strict actually run on this file or is there a per-module exemption nobody revisits.
Outstanding
One subagent run per skill: a Python review/design task with the reference loaded, checking the guidance is applied and nothing in it is wrong. None of these three has been pressure-tested, which by #19 means they are drafts that happen to be committed.
Shipped in `5070a73`, **not closing** — same reason as #18: the GREEN pressure test could not run (session limit, resets 13:20 Europe/Stockholm), and #19's bar explicitly covers this batch.
## What landed
Three reference files, following the `solid/references/go-adaptation.md` precedent and the structure #18 settled on — per-language content in linked references, main SKILL.md undiluted:
- `clean-code/references/python-adaptation.md`
- `solid/references/python-adaptation.md`
- `code-review/references/python-adaptation.md`
Each is linked from its skill's references list.
## The stack table is cited, not copied
Deliberate deviation from "wire this table into the reference files": none of the three restates it. Each says the canonical stack lives in `~/dev/.context/AGENT.md` and moves on.
Copying an eight-row table into three files creates three things to update when `uv` is replaced or mypy gives way to pyright — and this repo spent today on the consequences of exactly that. #15's missing index rows, #29's generated index, `bin/check-index` deleted the same day it was written because generation beat gating: the through-line is that a fact with two homes has none. A pointer to `AGENT.md` costs a lookup; a copy costs a silent divergence.
## What is in them instead — the part a linter cannot give you
**clean-code:** `raise X from e` as the analogue of `fmt.Errorf("%w")`, dataclasses over `dict[str, Any]`, and the two traps where the broken version reads *tidier* than the correct one — mutable default arguments, and `if not amount:` where zero is a legitimate value. The second is, in my experience of this codebase's domain, the most common way a Python function silently mishandles valid input, and no reviewer catches it by reading quickly.
**solid:** `Protocol` over `ABC`, defined at the *consumer*, which is what makes DIP structural rather than aspirational — the direct analogue of Go's "accept interfaces". Plus a section on what does **not** carry over from the Go rewrite, because a Python reader following "return structs, accept interfaces" is following advice about Go's nil-interface semantics.
**code-review:** an anti-pattern table parallel to the Go one, opening with the rule that decides most of it — *if ruff or mypy already catches it, the review comment is not "fix this line", it is "why is the gate not running?"* Then the checks a linter cannot perform: does `pytest.raises` have a `match=`, is the mock at a boundary or a collaborator we own, would `--strict` actually run on this file or is there a per-module exemption nobody revisits.
## Outstanding
One subagent run per skill: a Python review/design task with the reference loaded, checking the guidance is applied and nothing in it is wrong. None of these three has been pressure-tested, which by #19 means they are drafts that happen to be committed.
Closing. The three reference files shipped in 5070a73; the tdd half of this issue was verified by the #18 GREEN run, and its findings were applied in cba0fe4.
What this issue asked for, and what it got
The canonical Python stack table was cited, not copied — each file points at ~/dev/.context/AGENT.md. Deliberate deviation from "wire this table into the reference files", for the reason this repo demonstrated three times today: dream and experiment-spec shipped without index rows because a fact had two homes; bin/check-index was deleted the same day it was written because generation beat gating; ~/.op-env still references a 1Password item that was consolidated away a month ago and now silently 401s. Three copies of a stack table is three things to update when uv is replaced.
What the files carry instead is the part a linter cannot: raise X from e as the %w analogue, Protocol over ABC defined at the consumer, the truthiness-for-presence trap, and — in code-review — the rule that decides most of a Python review, which is that a finding ruff or mypy already catches is not a review comment but a question about why the gate is not running.
Verified, partly
tdd/references/python.md was pressure-tested and rewritten off the back of it. Ten defects, including two serious ones: the exemplar was the evaluation task verbatim, and mypy --strict at RED pointed the reader at the stub the same file forbids. Both fixed.
The other three files — clean-code, solid, code-review Python adaptations — have not been pressure-tested. By #19's bar they are drafts that happen to be committed, and I would rather say that than close quietly over it.
I am closing anyway because the issue's deliverable exists and the pattern is established; a run against each is worth doing when someone next writes Python against them, and will produce better findings then than a synthetic scenario now. If you would rather they were gated before closing, reopen and I will run them — it is three subagent runs, not a redesign.
One thing this issue predicted correctly
It said to follow "the Multi-Language Dilution reconciliation on #18 — main SKILL.md stays single-canonical-example, per-language content in linked reference files". That constraint is why tdd/SKILL.md grew by ~40 lines rather than ~400, and the GREEN run loaded exactly one reference file and no others. The structure works.
Closing. The three reference files shipped in `5070a73`; the `tdd` half of this issue was verified by the #18 GREEN run, and its findings were applied in `cba0fe4`.
## What this issue asked for, and what it got
The canonical Python stack table was **cited, not copied** — each file points at `~/dev/.context/AGENT.md`. Deliberate deviation from "wire this table into the reference files", for the reason this repo demonstrated three times today: `dream` and `experiment-spec` shipped without index rows because a fact had two homes; `bin/check-index` was deleted the same day it was written because generation beat gating; `~/.op-env` still references a 1Password item that was consolidated away a month ago and now silently 401s. Three copies of a stack table is three things to update when `uv` is replaced.
What the files carry instead is the part a linter cannot: `raise X from e` as the `%w` analogue, `Protocol` over `ABC` defined at the consumer, the truthiness-for-presence trap, and — in `code-review` — the rule that decides most of a Python review, which is that a finding ruff or mypy already catches is not a review comment but a question about why the gate is not running.
## Verified, partly
`tdd/references/python.md` was pressure-tested and rewritten off the back of it. Ten defects, including two serious ones: the exemplar was the evaluation task verbatim, and `mypy --strict` at RED pointed the reader at the stub the same file forbids. Both fixed.
The other three files — `clean-code`, `solid`, `code-review` Python adaptations — have **not** been pressure-tested. By #19's bar they are drafts that happen to be committed, and I would rather say that than close quietly over it.
I am closing anyway because the issue's deliverable exists and the pattern is established; a run against each is worth doing when someone next writes Python against them, and will produce better findings then than a synthetic scenario now. If you would rather they were gated before closing, reopen and I will run them — it is three subagent runs, not a redesign.
## One thing this issue predicted correctly
It said to follow "the Multi-Language Dilution reconciliation on #18 — main SKILL.md stays single-canonical-example, per-language content in linked reference files". That constraint is why `tdd/SKILL.md` grew by ~40 lines rather than ~400, and the GREEN run loaded exactly one reference file and no others. The structure works.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Canonical Python stack now pinned in
~/dev/.context/AGENT.md(mathias/local-dev@6296536, tag v0.3.1), propagated to CLAUDE.md/AGENTS.md/.cursorrules/.aider.conventions.md viatask context:sync:@pytest.mark.parametrizeraise X from e— never bareexcept, never silentpassloggingw/ structuredextra=(or structlog)pyproject.tomlonly, nosetup.pyAction: wire this table into the per-language reference files already scoped for
tdd(#18 — Python section) and extend the same pattern toclean-code,solid,code-reviewwhere a Go-specific example currently has no Python equivalent. Per #19's execution bar (pressure-test before shipping) and the Multi-Language Dilution reconciliation on #18 (main SKILL.md stays single-canonical-example, per-language content lives in linked reference files, not inline).Shipped in
5070a73, not closing — same reason as #18: the GREEN pressure test could not run (session limit, resets 13:20 Europe/Stockholm), and #19's bar explicitly covers this batch.What landed
Three reference files, following the
solid/references/go-adaptation.mdprecedent and the structure #18 settled on — per-language content in linked references, main SKILL.md undiluted:clean-code/references/python-adaptation.mdsolid/references/python-adaptation.mdcode-review/references/python-adaptation.mdEach is linked from its skill's references list.
The stack table is cited, not copied
Deliberate deviation from "wire this table into the reference files": none of the three restates it. Each says the canonical stack lives in
~/dev/.context/AGENT.mdand moves on.Copying an eight-row table into three files creates three things to update when
uvis replaced or mypy gives way to pyright — and this repo spent today on the consequences of exactly that. #15's missing index rows, #29's generated index,bin/check-indexdeleted the same day it was written because generation beat gating: the through-line is that a fact with two homes has none. A pointer toAGENT.mdcosts a lookup; a copy costs a silent divergence.What is in them instead — the part a linter cannot give you
clean-code:
raise X from eas the analogue offmt.Errorf("%w"), dataclasses overdict[str, Any], and the two traps where the broken version reads tidier than the correct one — mutable default arguments, andif not amount:where zero is a legitimate value. The second is, in my experience of this codebase's domain, the most common way a Python function silently mishandles valid input, and no reviewer catches it by reading quickly.solid:
ProtocoloverABC, defined at the consumer, which is what makes DIP structural rather than aspirational — the direct analogue of Go's "accept interfaces". Plus a section on what does not carry over from the Go rewrite, because a Python reader following "return structs, accept interfaces" is following advice about Go's nil-interface semantics.code-review: an anti-pattern table parallel to the Go one, opening with the rule that decides most of it — if ruff or mypy already catches it, the review comment is not "fix this line", it is "why is the gate not running?" Then the checks a linter cannot perform: does
pytest.raiseshave amatch=, is the mock at a boundary or a collaborator we own, would--strictactually run on this file or is there a per-module exemption nobody revisits.Outstanding
One subagent run per skill: a Python review/design task with the reference loaded, checking the guidance is applied and nothing in it is wrong. None of these three has been pressure-tested, which by #19 means they are drafts that happen to be committed.
Closing. The three reference files shipped in
5070a73; thetddhalf of this issue was verified by the #18 GREEN run, and its findings were applied incba0fe4.What this issue asked for, and what it got
The canonical Python stack table was cited, not copied — each file points at
~/dev/.context/AGENT.md. Deliberate deviation from "wire this table into the reference files", for the reason this repo demonstrated three times today:dreamandexperiment-specshipped without index rows because a fact had two homes;bin/check-indexwas deleted the same day it was written because generation beat gating;~/.op-envstill references a 1Password item that was consolidated away a month ago and now silently 401s. Three copies of a stack table is three things to update whenuvis replaced.What the files carry instead is the part a linter cannot:
raise X from eas the%wanalogue,ProtocoloverABCdefined at the consumer, the truthiness-for-presence trap, and — incode-review— the rule that decides most of a Python review, which is that a finding ruff or mypy already catches is not a review comment but a question about why the gate is not running.Verified, partly
tdd/references/python.mdwas pressure-tested and rewritten off the back of it. Ten defects, including two serious ones: the exemplar was the evaluation task verbatim, andmypy --strictat RED pointed the reader at the stub the same file forbids. Both fixed.The other three files —
clean-code,solid,code-reviewPython adaptations — have not been pressure-tested. By #19's bar they are drafts that happen to be committed, and I would rather say that than close quietly over it.I am closing anyway because the issue's deliverable exists and the pattern is established; a run against each is worth doing when someone next writes Python against them, and will produce better findings then than a synthetic scenario now. If you would rather they were gated before closing, reopen and I will run them — it is three subagent runs, not a redesign.
One thing this issue predicted correctly
It said to follow "the Multi-Language Dilution reconciliation on #18 — main SKILL.md stays single-canonical-example, per-language content in linked reference files". That constraint is why
tdd/SKILL.mdgrew by ~40 lines rather than ~400, and the GREEN run loaded exactly one reference file and no others. The structure works.