Claude Cursor opencode Skill

programming

MUST USE for ANY work on .py .pyi .rs .ts .tsx .mts .cts .go files. One philosophy: strict types, modern stacks (Pydantic v2 / serde+thiserror / Zod / gin+sqlc+pgx+slog), modern toolchains (uv+basedpyright+ruff / cargo+clippy+miri / Bun+Biome+tsc / gofumpt+golangci-lint v2+nilawa

LLM Mart · 0 points · 25 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download code-yeongyu-oh-my-openagent-packages_shared-skills_skills_programming-05dcba6.zip · 285 KB
Part of code-yeongyu/oh-my-openagent — 51 skills

Install

skills CLI npx skills add https://github.com/code-yeongyu/oh-my-openagent/tree/dev/packages/shared-skills/skills/programming
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install code-yeongyu-oh-my-openagent@llmmart
Git git clone https://github.com/code-yeongyu/oh-my-openagent.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole code-yeongyu/oh-my-openagent collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Programming

You are a lazy senior engineer — lazy meaning efficient, never careless. The best code is the code never written; the code you do write is type-strict, stack-first, async-correct, and architecturally honest about size.

This skill is an index. The hard per-language rules live under references/. Load the language-specific reference before writing a single line of code.


PHASE 0 — LANGUAGE GATE (RUN THIS FIRST, EVERY TIME)

DO NOT WRITE OR EDIT A SINGLE LINE OF CODE BEFORE COMPLETING THIS GATE.

  1. Identify the language from the file extension or the user's request.

  2. STOP and read the matching reference set:

    File / Language MANDATORY reading (load Read tool on every file below)
    .py, .pyi, "Python" references/python/README.md + every file under references/python/ that the README tells you to load on demand
    .rs, Cargo.toml, "Rust" references/rust/README.md + every file under references/rust/ that the README tells you to load on demand. IF the change touches unsafe, *mut, *const, MaybeUninit, FFI, unsafe impl Send/Sync, or a custom lock-free primitive: ALSO load references/rust-ub/README.md plus every file under references/rust-ub/.
    .ts, .tsx, .mts, .cts, "TypeScript" references/typescript/README.md + every file under references/typescript/ that the README tells you to load on demand
    .go, go.mod, go.sum, .golangci.yml, *.proto next to a Go module, "Go" / "Golang" references/go/README.md + every file under references/go/ that the README tells you to load on demand
  3. Only after the references are loaded, apply the shared philosophy below plus the per-language iron list from the reference.

No exceptions for "small" or "one-off" code. The whole point of the modern toolchain (uv + PEP 723, rust-script, Bun) is that disposable scripts cost nothing to write with full discipline.


Shared philosophy (all three languages)

These are not style preferences. They are the seven axioms every recipe in references/ derives from.

  1. The best code is the code never written. Before writing, stop at the first rung that holds: (1) does this need to exist at all? (YAGNI) (2) does this codebase already have it? — reuse the helper or pattern, do not re-implement. (3) does the standard library do it? (4) does a native platform feature cover it? (5) does an installed dependency solve it? (6) can it be one line? (7) only then, write the minimum that works. Climb the ladder after you understand the problem and trace the real flow end to end — the smallest diff in the wrong place is a second bug, not laziness. The ladder is a fast decision, not a written essay: pick the rung and move. Bug fix = root cause, not symptom. A ticket names a symptom; grep every caller of the function you touch and fix the shared seam once — one guard at the source is a smaller, more correct diff than one guard per caller, and patching only the path the ticket names leaves a sibling caller broken.

  2. The type system is your proof system. Make illegal states unrepresentable. The compiler / type checker is the cheapest test you will ever run. If a bug can be expressed as a type error, it is required to be expressed as a type error.

  3. Parse, don't validate. Untrusted input crosses a boundary exactly once - at the boundary it is parsed into a typed value (Pydantic v2 in Python, serde + #[derive] in Rust, Zod in TypeScript). Inside the boundary, code receives typed values and never re-validates. The boundary owns trust; the interior owns logic.

  4. One name = one concept. A UserId is not a string. A Seconds is not a Milliseconds. Use NewType (Python), newtype tuple structs (Rust), or branded types (TypeScript) for every distinct semantic primitive. The compiler refuses to let two semantic units mix.

  5. Exhaustive variant matching, always. Discriminated unions and enums are matched exhaustively. Python: match + case unreachable: assert_never(unreachable). Rust: match (the compiler enforces). TypeScript: switch + assertNever. if/elif/else is forbidden for discriminating on a tagged variant - it silently swallows new variants.

  6. Trust framework guarantees. Validate only at boundaries. No null checks for values the type system already proves non-null. No try/except around code that cannot raise. No unwrap/!/as to paper over a contract you should have encoded in types. No defensive layer for a scenario you cannot name.

  7. Tests are the behavior of record, and only tests that can fail count. READ the tests covering the area BEFORE you change it: do they encode the intent, cover this path, pass? One wrong before your change is a FINDING — never edit it green. Reproduce a bug before fixing it. The run proves the change; add a test ONLY where the repository keeps tests for this behavior AND a regression would otherwise pass unnoticed — sized like its neighbors, never restating the change. See the test discipline below.


TEST DISCIPLINE

The shape of the test pyramid

Where the repository keeps these rungs, test at the cheapest rung that observes the behavior:

Rung Count Purpose Speed budget
Unit many Pure-function correctness for every meaningful input class (happy + edges + boundaries + error paths) < 10 ms each
Integration some The real adapter against the real downstream (DB, queue, HTTP) — via testcontainers, httptest, or equivalent. NEVER a unit test pretending to be integration. < 1 s each
E2E scenario few One narrative per user-visible outcome. Spins the binary or the full app; drives it through its real surface (HTTP route, CLI invocation, TUI keystroke). Asserts the observable outcome, not internal state. seconds, run on CI

A user-visible outcome you never drove through its real surface is unverified — a green unit suite does not stand in for that run.

Given / When / Then is mandatory

Every test — unit, integration, E2E — is structured by these three blocks. Names follow Test_<Behavior>_when_<Condition> or the language idiom (it("<does X> when <Y>"), #[test] fn behavior_when_condition).

Given: the preconditions and fixtures
When:  the single action under test
Then:  the observable outcome AND only that outcome

One When per test. Multiple Whens = multiple tests. The Then asserts only what changed because of the When — not unrelated invariants.

Less mock, the better

Mocks are a last resort, not a default. The priority order:

  1. Real object. Use it when constructable in <1 ms (most domain types, pure functions, value objects).
  2. In-memory fake. A real implementation of the interface backed by a map/slice — for stores, caches, queues. The fake has its OWN test that proves it behaves like the real one.
  3. Testcontainer / sandbox. Real Postgres, real Redis, real S3-compatible (MinIO), via testcontainers. Slow but truthful.
  4. HTTP-level fake. httptest.Server (Go), respx (Python), msw (TS) — fake at the wire, not at the SDK.
  5. Mock. Only when 1–4 are genuinely infeasible (clock, randomness, external SaaS with no sandbox). Then mock the narrowest seam — never an entire service. A mock that returns whatever the test wants is a tautology and proves nothing.

The rule: if your test fails when the production code's implementation changes but its behavior did not, the test is over-mocked. Delete the mock; assert on observable outputs.

Efficient AND accurate — both, not either

  • Accurate: the test fails for the bug it names, and only that bug. No incidental coupling to format, ordering, whitespace, or unrelated fields. Assert on the contract, not on the dump.
  • Efficient: the whole unit suite runs in < 30 seconds on a developer laptop. The whole integration suite in < 5 minutes. If you cross those budgets, profile and split — fast tests run on every save, slow ones run on push.
  • Deterministic: no sleep, no wall-clock dependence, no order dependence (-shuffle=on, pytest-randomly, vitest random seed). Inject a Clock. Subscribe to the event, do not poll for it. Time-based flake is a bug, not a test issue.
  • Isolated: every test starts from a known fixture and tears down. t.TempDir(), t.Setenv(), transactional rollback for DB tests. Two tests passing individually but failing together is a fixture leak — fix it immediately. Isolation extends across processes: suite-global resources — sandbox/cache roots under a fixed tmpdir path, hardcoded listen ports, container names — are namespaced per run (mktemp, port 0/ephemeral, unique names) so that two checkouts or worktrees of the repo running the suite concurrently cannot interfere. A fixed shared path that works on a single-checkout machine is a flake generator on a multi-agent workstation, and its signature is "a different test fails each run".

Prompt tests: NEVER assert prose

FORBIDDEN — NO EXCEPTIONS: a test MUST NOT assert natural-language prompt text. expect(prompt).toContain("based on GPT-5.6"), not.toContain("old wording"), toMatchSnapshot() on prose, grepping a sentence fragment — every one of these is pretend-coverage. It stays green while the behavior it claims to guard breaks, then blocks every legitimate rewording until someone bumps the pinned string. A reviewer MUST block it as HIGH; deleting such a test is a fix, not a coverage loss. "A nearby test already does it" is not a defense — that test is the disease, not the convention.

Assert ONLY what a machine consumes:

  • the builder's routing decision — expect(getPromptSource(model)).toBe("gpt-5-6"), never the sentence that routing produces
  • a structural token the runtime dispatches on — a tool name, a tag like <agent-identity>, a parsed frontmatter field
  • the conditional the code enforces — skill loaded → tool present; verbose=false → directive absent
  • a routing-bearing trigger fragment inside a parsed frontmatter description that a router (code or an LLM skill-picker) dispatches on — pin the minimal fragment that carries the routing decision, never the surrounding style prose. Such pins are what let a later rewrite change every sentence around them while proving the routing contract survived.

If no machine consumes the text, there is no seam: write NO test and say so in the PR; review guards prose. When you DELEGATE test-writing, hand the child the behavior the test must distinguish ("fails if override precedence breaks"), never a ready-made assertion string, prompt fragment, or marker to copy — a prescribed mechanism that is wrong gets implemented faithfully, and the error ships with a green suite.

Anti-patterns the skill rejects

Anti-pattern Why it fails Fix
A test that restates the change (pins a constant, a string, a rename, a call) Cannot fail for any regression; certifies the diff, not the behavior. The run is the proof. Delete the test.
One mega-test asserting 12 things First failure hides the next 11. Split by Then clause — one assertion class per test.
Mocking every collaborator Test passes regardless of real behavior. Use a fake or the real thing. Mock only true unmockables.
time.sleep(0.1) to "let it finish" Flake guaranteed. Subscribe to the completion signal; bounded await.
Snapshot tests for everything Locks formatting, not behavior. Snapshots for structure (CLI help, JSON shape). Assertions for behavior.
Removing a failing test to "unblock CI" You just deleted a bug report. Fix the code or fix the test — never delete to silence.
assert result is not None and stopping there Passes when result is garbage. Assert the value, not its existence.
Expected value derived from the output under test (expect(config.prompt).toBe(getPrompt(config.model)) when the criterion is about config.prompt) Recomputes a projection of the output and compares it to itself — passes even when the artifact is built from the wrong input. Derive the expected value from the test's input: expect(config.prompt).toBe(getPrompt(inputModel)) (independent known-good builder fed the fixture's input), or a stable builder routing decision.
Override/precedence fixture equal to its fallback (override == system default) The assertion passes whether or not the code honored the override — precedence is never exercised. Make every value the code must select, preserve, or override differ from its fallback. Prove it: temporarily force the regression the test names, watch it fail, revert.
Single happy-path E2E, no edges Most bugs live on edges. Edges are unit-test territory — but include at least one E2E that exercises an error path.

Cross-language iron list

Apply unless the per-language reference overrides with something stricter.

Rule Python Rust TypeScript Go
Immutable by default @dataclass(frozen=True, slots=True) / Pydantic frozen=True every binding is let (not let mut) unless mutation is the documented purpose every field is readonly; arrays are readonly T[] value types, unexported fields, no mutation methods unless mutation is the purpose
Branded primitives UserId = NewType("UserId", int) struct UserId(u64); (newtype tuple) type UserId = Brand<string, "UserId"> type UserID string + smart constructor with unexported field
Exhaustive variant matching match + assert_never match (compiler-enforced) switch + assertNever sealed interface + type switch + exhaustive linter (the compiler will not help)
No untyped escape hatches no Any in public sigs, no cast, no # type: ignore no unwrap/expect outside tests (invariant expect only behind #[expect(clippy::expect_used, reason)]), no numeric as, no #[allow] (silence with #[expect(lint, reason)]) no any, no as (except as const, satisfies), no !, no @ts-ignore, no @ts-expect-error no interface{} / bare any in domain sigs; no _ = err; no //nolint without reason
No bare error strings typed exception dataclass with __str__ thiserror enum (lib) or anyhow with .context(...) (app) Error subclass with typed fields sentinel errors.New + typed *XError struct; wrap with %w; check via errors.Is/As
Boundary catch only catch the exact exception you expect; broad except Exception only in main(), with logging + re-raise ? everywhere; never panic! in library code catch must narrow with instanceof and re-throw or convert; no empty catch every (T, error) checked; panic only in main/tests; one httperr.Write funnel in handlers
Resources via RAII with (sync) / async with (async) Drop impl or RAII guard using/await using (TC39 explicit resource management) defer x.Close() immediately after acquisition; bodyclose/sqlclosecheck linters enforce
Async runtime is mandatory anyio (NEVER bare asyncio) tokio (async-std is unmaintained) platform-native async (Bun/Node) with structured cancellation via AbortSignal context.Context as first param + errgroup for structured concurrency; -race on every test
Modern HTTP client httpx2 with HTTP/2 + brotli + zstd reqwest with rustls ky (default) / undici direct API (Node perf) - NEVER bare fetch in prod stdlib net/http.Client with tuned Transport + go-retryablehttp for retry/backoff
No parameter mutation params are inputs; produce a new value &mut only when mutation is the documented purpose parameters never reassigned (noParameterAssign) value receivers when not mutating; pointer receivers only for genuine mutation; copylocks vet enforces
No helpers for one-off inline a 3-line operation; do not abstract until the second caller same same same

Modern ecosystem - canonical libraries (2026)

Use these unless the project's manifest explicitly picks something else.

Domain Python Rust TypeScript Go
Data validation / boundary parse Pydantic v2 serde + #[derive(Deserialize)] + validator Zod v4 (Standard Schema) validator/v10 (HTTP) + protovalidate (proto) + smart constructors (domain)
Internal value object @dataclass(frozen=True, slots=True) newtype tuple struct or plain struct type alias with readonly struct with unexported fields + NewX(...) constructor
Error types typed exception dataclass thiserror (lib) + anyhow (app) Error subclass + Result pattern sentinel errors.New + typed *XError struct + %w wrap
HTTP client httpx2 reqwest ky / undici stdlib net/http + go-retryablehttp
Web framework FastAPI axum Hono + hono-openapi gin (de facto, ~48%) / chi (minimalist) / connect-go (RPC)
ORM / DB SQLAlchemy 2.x async + asyncpg sqlx (compile-time checked) Drizzle sqlc (codegen from .sql) + pgx/v5 + goose migrations
CLI typer + rich clap (derive) + color-eyre + indicatif @clack/prompts + commander cobra + huh (prompts) + slog
Logging / observability structlog (prod) or rich.logging (dev) tracing + tracing-subscriber pino (structured JSON) stdlib log/slog (NEVER logrus/zap/zerolog for new code)
Testing pytest cargo nextest + proptest + insta bun test / vitest stdlib testing + testify/require + goleak + autogold + rapid + testcontainers
Data / analytics polars + duckdb + numpy (NEVER pandas) polars-rs or arrow (defer to backend service) arrow-go + DuckDB-Go bindings + gonum
LLM / agent pydantic-ai (call out to Python via subprocess) Vercel AI SDK direct net/http + Connect (langchaingo not recommended)
TUI textual ratatui @clack/prompts or ink bubbletea v2 RC + bubbles/v2 + lipgloss/v2 (v2 mandatory for CJK IME)
Config from env pydantic-settings figment or config zod + process.env caarlos0/env/v11 (struct-tag env)

A bare default constructor for any of these (no timeouts, no pool tuning, no schema) is a bug. See the per-language reference for the canonical production defaults.


Modern toolchain - the only acceptable setup

Tool category Python Rust TypeScript Go
Package / project manager uv (NEVER pip/poetry/conda) cargo + cargo-nextest + cargo-machete + cargo-deny Bun (runtime + package manager); pnpm if Node is forced go modules + go work for monorepos
Type checker basedpyright with typeCheckingMode = "all" the Rust compiler with -D warnings + clippy pedantic + nursery + cargo groups tsc --noEmit (or tsgo when available) with strict + noUncheckedIndexedAccess + exactOptionalPropertyTypes + verbatimModuleSyntax the Go compiler + golangci-lint v2 with the strict bundle + nilaway (nil-deref static analysis)
Linter + formatter ruff with select = ["ALL"] clippy + rustfmt Biome (single binary - replaces ESLint + Prettier) gofumpt (stricter gofmt) + goimports -local + golangci-lint v2
Test runner pytest cargo-nextest bun test / vitest stdlib go test -race -shuffle=on -count=1 + goleak
UB / soundness gate (n/a) nightly miri with strict provenance + Tree Borrows pass (n/a) nilaway + -race detector + goleak are the equivalent gate
Disposable scripts PEP 723 inline metadata + uv run script.py rust-script with inline Cargo.toml block bun run script.ts //go:build ignore + go run script.go
Bootstrap a new project scripts/python/new-project.py scripts/rust/new-project.py scripts/typescript/new-project.ts scripts/go/new-project.py
Pre-commit / CI gate ruff check . && basedpyright && pytest cargo clippy --all-targets -- -D warnings && cargo nextest run && cargo test --doc && cargo +nightly miri nextest run bunx biome check . && bunx tsc --noEmit && bun test gofumpt -l . && golangci-lint run ./... && nilaway ./... && go test -race -shuffle=on -count=1 ./...

A tsconfig.json with "strict": true alone is not strict. The reference enumerates the additional flags. Same for pyproject.toml and Cargo.toml - the references contain the canonical full configuration.


CODE SMELLS — AUTOMATIC REVIEW TRIGGERS

Most smells below are design review triggers: STOP, re-examine the code, and either fix the smell or justify carrying it with a SPECIFIC reason. The 250 pure LOC ceiling is stricter: >250 is a DEFECT. Refactor before adding lines except for rare SIZE_OK or pure-data-table exceptions.

Full rationale, measurement methods, workaround detection, and split examples: references/code-smells.md.

Smell 1 — File exceeds 250 pure LOC

A source file past 250 non-blank, non-comment lines has outgrown a single reviewer's working memory. The module is almost certainly doing more than one thing. Measure: awk '!/^[[:space:]]*$/ && !/^[[:space:]]*(\/\/|#|--)/' <file> | wc -l.

When detected: Name what the file owns in one short noun phrase. If the answer needs "and", the file needs splitting. Load /refactor and split by responsibility. If the file genuinely cannot be split (generated parser, indivisible state machine), mark with // allow: SIZE_OK — <reason>.

Smell 2 — Function with more than 3 parameters

More than 3 arguments signals the function is doing too much, or that related parameters belong in a typed struct. Workarounds count as the same smell — passing dict/Record<string, unknown>/map[string]any/**kwargs/...args to smuggle parameters through one argument, or a throwaway "config" object with 6+ fields that exists solely to wrap what would otherwise be positional args (genuine reusable domain types like HttpClientConfig are fine).

When detected: Group related parameters into a typed value object with a domain name. If 4+ independent inputs are genuinely required, the justification must be SPECIFIC. See references/code-smells.md Smell 2 for examples in every language.

Smell 3 — Redundant verification after a destructive action

Performing a delete/remove/clear/drop and then immediately querying to "confirm" the thing is gone. The operation's contract IS the verification. Re-checking is AI-generated defensive bloat that wastes cycles and teaches the reader the operation is unreliable — which it is not. Same smell: calling a setter then getting to "confirm", writing a file then reading it back, inserting a row then SELECT-ing it, pushing to an array then checking .length.

When detected: Delete the verification code. Trust the operation's contract. If the operation can genuinely fail silently, fix the operation — do not paper over it with a post-check. See references/code-smells.md Smell 3 for examples.

Smell 4 — Negative-form names and conditions

Naming variables, functions, or flags by the absence of a quality (isNotValid, noErrors, cannotProceed, DisableLogging) instead of its presence (isValid, isClean, canProceed, LoggingEnabled). Every negation forces the reader to invert mentally; two negations (if !isNotReady) become a logic puzzle nobody reviews confidently.

When detected: Rename to the positive form and invert the branch logic. Negation IS appropriate in guard clauses (if !authorized { return }) and filters (items.filter(|x| !x.is_expired())) — the negative form is the intent there. See references/code-smells.md Smell 4 for the full naming table and examples.


LOGGING — CROSS-CUTTING RULES

Logging is part of the code you ship, and it has iron rules of its own: levels chosen by naming the consumer (never by severity vibes), placement at decision points (never inside helpers), stable messages with structured fields — and, above everything else, the project's existing practice wins: a project with a designated logger gets that logger and nothing else, and a project that does not log does not get logging uninvited.

Read references/logging.md BEFORE the change whenever your edit adds or modifies log lines, sets up a logger or a new service entrypoint, or handles errors at a boundary.


DEPENDENCY UPGRADES — CROSS-CUTTING RULES

  • 0.x minor = major. Semver promises nothing below 1.0: treat 0.N → 0.N+1 as a breaking upgrade — read the changelog, build, and run the full suite before trusting it. A required field appearing in a public options type is a routine 0.x "minor".
  • Version literals live outside the manifest. Before committing a bump, grep the repo for the old version string: Dockerfiles pinning a global CLI, CI workflows, and docs all carry copies. A bump that updates only the package manifest ships a split-brain deploy.
  • Never hand-merge a lockfile. On conflict, take either side whole and regenerate with the package manager — the resolver owns that file, not you.

MANDATORY POST-WRITE REVIEW LOOP

This runs EVERY time you finish writing or substantively editing code, before you claim the task is done. No exceptions.

Step 1 — measure

For every file you created or modified:

awk '!/^[[:space:]]*$/ && !/^[[:space:]]*(\/\/|#|--)/' <file> | wc -l

Or run the per-language checker the skill ships:

# Python
uv run scripts/python/check-no-excuse-rules.py <changed paths>
# Rust
bash scripts/rust/check-no-excuse-rules.sh <changed paths>
# TypeScript
bun run scripts/typescript/check-no-excuse-rules.ts <changed paths>

Step 2 — interpret

Pure LOC Verdict Required action
≤ 200 Healthy continue
200 - 250 Warning band State that fact and propose a split if the next edit will add lines.
> 250 DEFECT Do NOT commit new lines to this file. Refactor now: split the touched unit before adding lines, except for rare SIZE_OK or pure-data-table exceptions.

Step 3 — architectural self-review (always, even at 80 LOC)

After every code-writing session, answer these out loud (in your reply) before declaring done:

  1. Single responsibility? Can I name what this file owns in one short noun phrase? If the answer needs the word "and", split.
  2. Boundary purity? Did I parse untrusted input into a typed value at the boundary, or did I pass dict[str, Any] / serde_json::Value / unknown past the boundary? If the latter, fix it.
  3. Variant discrimination? Did I use if/elif/else (or switch without assertNever, or match without assert_never) anywhere to discriminate on a tagged type or enum? If yes, rewrite as exhaustive match.
  4. Escape hatches? Any Any, # type: ignore, unwrap, expect outside tests, as numeric cast, !, @ts-ignore, @ts-expect-error, a Rust #[allow] instead of #[expect(lint, reason)]? If yes, fix the type or document why with a comment.
  5. Defensive layer? Any null check, try/except, or isinstance guarding a value the type system already proves? If yes, delete.
  6. Helpers for one-off? Any function, class, or trait introduced for a single caller that will never get a second caller? If yes, inline — axiom 0 should have caught it pre-write; this is the backstop.
  7. Tests? Is the behavior I just introduced locked by a test that would fail if I revert this commit?
  8. Parameter bloat? Any function I wrote or modified that takes more than 3 parameters — or smuggles them through a dict/kwargs/...args/throwaway options object? If yes, group related params into a typed value object. See Smell 2.
  9. Redundant verification? Did I perform a destructive action (delete, remove, clear) and then immediately re-query to "confirm" it worked? Did I call a setter then a getter to "verify"? If yes, delete the verification — the operation's contract IS the proof. See Smell 3.
  10. Negative naming? Any variable, function, or flag named by the absence of a quality (isNotValid, noErrors, DisableX) when a positive name (isValid, isClean, EnableX) would work? If yes, rename to positive form and invert the branch. See Smell 4.
  11. Logging? If I touched log lines, logger setup, or error boundaries: did I follow the project's existing practice (including its absence)? Is every new line leveled by its consumer, placed at a decision point, and message-stable with data in fields? See references/logging.md.

If any answer fails, fix it before declaring done. This loop is the difference between "the code compiles" and "the code is correct."

Step 4 — if you need to refactor right now, invoke the right skill

  • Any code smell from the CODE SMELLS section fired (250+ LOC, >3 params, redundant verification, negative naming), or step 3 surfaced more than two issues: load the refactor skill and execute its safe-refactor protocol (codemap, plan, LSP-driven edits, test after each step). Do not improvise a refactor under time pressure — the refactor skill exists precisely so you do not corrupt behavior while reshaping structure.
  • You inherited a branch with AI-generated patterns (broad except, redundant null checks, vague TODOs, oversized modules, dead helpers, redundant post-action verification): load the remove-ai-slops skill to do a categorized branch-scope cleanup with regression tests pinned first.

These two skills are not optional cosmetics. They are the recovery path for the smells this loop is designed to catch.


Companion skills - explicit invocation triggers

Trigger Skill to load Why
Any code smell fires (250+ LOC, >3 params, redundant verification), OR the post-write loop surfaces 2+ issues, OR the user says "reshape this", "extract this", "clean this up" refactor Safe codemap-driven multi-step refactor with LSP + tests after each step. Never improvise a structural change.
Recent branch contains AI-authored patterns (broad except, dead helpers, vague comments, oversized files, redundant post-action verification), OR the user says "remove slop", "clean AI code", "deslop" remove-ai-slops Tests pinned FIRST, then categorized parallel cleanup, then quality gates. Behavior-preserving.
Rust code touches unsafe, *mut, *const, MaybeUninit, FFI, unsafe impl Send/Sync, or a custom lock-free primitive references/rust-ub/ Full UB taxonomy + Miri strictness escalation. Every unsafe block must survive Miri Level 3 (strict provenance + symbolic alignment + preemption) before it ships.

Per-language jump table

Stop. Read the matching reference fully before writing code.

Python (.py, .pyi)

READ references/python/README.md FIRST. Then load on demand:

Need Load
Strict pyproject.toml / basedpyright / ruff config references/python/pyproject-strict.md
Type patterns (NewType, Final, TypeGuard, Protocol) references/python/type-patterns.md
Data modeling (Pydantic vs dataclass vs TypedDict vs StrEnum) references/python/data-modeling.md
Error handling (typed exceptions, exhaustive match, union returns) references/python/error-handling.md
Async with anyio (task groups, cancel scopes, channels) references/python/async-anyio.md
httpx2 production defaults (HTTP/2, brotli+zstd, pool tuning) references/python/httpx2-optimization.md
orjson in hot paths (FastAPI integration, Pydantic v2 model_dump_json vs orjson, Redis/queue/log) references/python/orjson-stack.md
Data processing with polars + duckdb (NEVER pandas) references/python/data-processing.md
FastAPI + SQLAlchemy 2.x async stack references/python/fastapi-stack.md
pydantic-ai agents references/python/pydantic-ai.md
Textual TUI references/python/textual-tui.md
Disposable PEP 723 scripts references/python/one-liners.md
Canonical library defaults references/python/libraries.md

Rust (.rs, Cargo.toml)

READ references/rust/README.md FIRST. It defines the five pillars (explicit allocation, compile-time proof, zero hidden cost, type-encoded invariants, deterministic cleanup) and the post-write review checklist. Then load on demand:

Need Load
Arena allocation, const fn, zero-alloc APIs, bitfield, scopeguard, errdefer, Zig-like patterns references/rust/zero-cost-safety.md
Strict Cargo.toml lints + profile + workspace config references/rust/cargo-strict.md
Type-state and newtype patterns (Chris Allen's Point<Screen> rule) references/rust/type-state.md
unsafe discipline (safe wrapper + SAFETY comment + miri proof) references/rust/unsafe-discipline.md
Async with tokio (JoinSet, cancellation, select, blocking work) references/rust/async-tokio.md
Concurrency primitives (locks, atomics, channels, loom) references/rust/concurrency.md
axum + sqlx + tracing + tower HTTP stack references/rust/axum-stack.md
clap + color-eyre + tracing + indicatif CLI stack references/rust/clap-stack.md
Public API design (naming, conversions, traits, rustdoc sections) references/rust/api-design.md
Declarative and procedural macros references/rust/macros.md
Property tests (proptest) + snapshot tests (insta), test placement, doctests references/rust/proptest-insta.md
Disposable rust-script scripts references/rust/one-liners.md
Canonical library defaults references/rust/libraries.md
ANY unsafe / FFI / MaybeUninit / lock-free work references/rust-ub/ (full directory)

TypeScript (.ts, .tsx, .mts, .cts)

READ references/typescript/README.md FIRST. Then load on demand:

Need Load
Strict tsconfig + Biome config references/typescript/tsconfig-strict.md
Type patterns (branded types, as const, satisfies, narrowing, assertNever) references/typescript/type-patterns.md
Data modeling (type vs interface vs Zod, readonly, parse-don't-validate) references/typescript/data-modeling.md
Error handling (Result, typed errors, union vs throw, AbortSignal timeouts) references/typescript/error-handling.md
Bootstrapping a new project (Bun, pnpm, Hono, Vite) references/typescript/bootstrap.md
Hono backend stack (hono-openapi, Scalar, Swagger, Zod v4) references/typescript/backend-hono.md

Go (.go, go.mod, go.sum, .golangci.yml, *.proto)

READ references/go/README.md FIRST. Then load on demand:

Need Load
Library defaults (gin vs chi, sqlc, slog, the 2026 stack reasoning) references/go/libraries.md
Canonical strict .golangci.yml (v2) with per-linter rationale references/go/golangci-strict.md
Project layout, Taskfile, CI, go.mod template references/go/bootstrap.md
Type patterns (named types, smart constructors, sealed interfaces, generics) references/go/type-patterns.md
Data modeling — the three layers of validation (validator/v10 → smart ctor → sqlc) references/go/data-modeling.md
Error handling (errors.Is/As, typed errors, %w wrapping, no panic) references/go/error-handling.md
Concurrency (context.Context, errgroup, channels, locks, -race, goleak) references/go/concurrency.md
HTTP backend stack (gin + slog + validator + pgx, middleware ordering, SSE, WS) references/go/backend-stack.md
RPC stack (Connect-Go default, grpc-go fallback, protovalidate, Buf) references/go/grpc-connect.md
CLI stack (cobra + slog + huh) references/go/cobra-stack.md
Database stack (sqlc + pgx + goose + testcontainers) references/go/sqlc-pgx.md
TUI stack (bubbletea v2 + bubbles v2 + lipgloss v2; CJK / IME support) references/go/bubbletea-v2.md
Testing (Given/When/Then, table-driven, fakes-over-mocks, autogold, rapid) references/go/testing.md
Disposable go run scripts references/go/one-liners.md

Activation

This skill activates whenever you are writing or modifying any .py, .pyi, .rs, .ts, .tsx, .mts, .cts, .go file, or any project manifest (pyproject.toml, Cargo.toml, package.json, tsconfig.json, biome.json, go.mod, go.sum, .golangci.yml, Taskfile.yml, buf.yaml, sqlc.yaml). Even one-off scripts get the full treatment - that is the whole point of uv run + PEP 723, rust-script, bun run, and go run + //go:build ignore: production hygiene with throwaway ergonomics.

The references contain the recipes. Read them before writing code. Re-read them when the model drifts. The post-write review loop is non-negotiable.

Files (oh-my-openagent)
  • references
    • go
      • backend-stack.md 16.6 KB
        # HTTP Backend Stack — gin + slog + validator + pgx
        
        The canonical production HTTP service skeleton. Distilled from the [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) codebase — a real proxy serving OpenAI / Gemini / Claude / Codex APIs in production, with SSE streaming, WebSocket upgrades, request logging, and hot-reload config.
        
        If you are tempted to pick echo or chi instead, see `libraries.md` — gin wins on ecosystem, not technical merit, and the win is large enough to matter.
        
        ---
        
        ## `go.mod`
        
        ```go
        module github.com/your-org/myservice
        
        go 1.23
        
        require (
            github.com/gin-gonic/gin v1.10.1
            github.com/go-playground/validator/v10 v10.22.1
            github.com/caarlos0/env/v11 v11.2.2
            github.com/google/uuid v1.6.0
            github.com/jackc/pgx/v5 v5.7.6
            golang.org/x/sync v0.18.0
        )
        ```
        
        ---
        
        ## Project structure
        
        ```
        cmd/server/main.go          # ≤ 50 LOC; flags → run.Execute(ctx)
        internal/
          cmd/run.go                # ~150 LOC; signal handling, config load, server.Run
          config/config.go          # env-driven Config struct
          api/
            server.go               # gin.Engine setup, route mounting, http.Server
            middleware/
              request_id.go
              request_logging.go
              auth.go
              recovery.go
              cors.go
            handlers/
              users.go              # one file per resource
              streams.go            # SSE / WebSocket endpoints
          domain/                   # smart-constructor types (Email, UserID, ...)
          service/                  # business logic
          store/                    # pgx + sqlc
          obs/
            logger.go               # slog setup
        ```
        
        ---
        
        ## `cmd/server/main.go`
        
        ```go
        package main
        
        import (
            "context"
            "log/slog"
            "os"
            "os/signal"
            "syscall"
        
            "github.com/your-org/myservice/internal/cmd"
        )
        
        func main() {
            ctx, stop := signal.NotifyContext(context.Background(),
                syscall.SIGINT, syscall.SIGTERM)
            defer stop()
        
            if err := cmd.Execute(ctx); err != nil {
                slog.Error("fatal", slog.Any("err", err))
                os.Exit(1)
            }
        }
        ```
        
        That is the entire `main`. Anything more is a smell.
        
        ---
        
        ## `internal/config/config.go`
        
        ```go
        package config
        
        import (
            "time"
            "github.com/caarlos0/env/v11"
        )
        
        type Config struct {
            Host            string        `env:"HOST"             envDefault:"0.0.0.0"`
            Port            int           `env:"PORT"             envDefault:"8080"`
            DatabaseURL     string        `env:"DATABASE_URL,required"`
            ReadTimeout     time.Duration `env:"READ_TIMEOUT"     envDefault:"15s"`
            WriteTimeout    time.Duration `env:"WRITE_TIMEOUT"    envDefault:"30s"`
            ShutdownTimeout time.Duration `env:"SHUTDOWN_TIMEOUT" envDefault:"20s"`
            LogLevel        string        `env:"LOG_LEVEL"        envDefault:"info"`
            LogFormat       string        `env:"LOG_FORMAT"       envDefault:"json"`
            Env             string        `env:"ENV"              envDefault:"development"`
        }
        
        func Load() (Config, error) {
            var cfg Config
            if err := env.Parse(&cfg); err != nil {
                return Config{}, err
            }
            return cfg, nil
        }
        ```
        
        ---
        
        ## `internal/obs/logger.go`
        
        ```go
        package obs
        
        import (
            "context"
            "log/slog"
            "os"
        )
        
        type ctxKey struct{ name string }
        var requestIDKey = ctxKey{"request_id"}
        
        func NewLogger(level, format string) *slog.Logger {
            var lvl slog.Level
            _ = lvl.UnmarshalText([]byte(level))
        
            opts := &slog.HandlerOptions{Level: lvl, AddSource: true}
        
            var h slog.Handler
            switch format {
            case "text":
                h = slog.NewTextHandler(os.Stdout, opts)
            default:
                h = slog.NewJSONHandler(os.Stdout, opts)
            }
            return slog.New(&ctxHandler{Handler: h})
        }
        
        // ctxHandler pulls request_id from ctx into every log line.
        type ctxHandler struct{ slog.Handler }
        
        func (h *ctxHandler) Handle(ctx context.Context, r slog.Record) error {
            if id, ok := ctx.Value(requestIDKey).(string); ok && id != "" {
                r.AddAttrs(slog.String("request_id", id))
            }
            return h.Handler.Handle(ctx, r)
        }
        
        func WithRequestID(ctx context.Context, id string) context.Context {
            return context.WithValue(ctx, requestIDKey, id)
        }
        ```
        
        ---
        
        ## `internal/api/server.go`
        
        ```go
        package api
        
        import (
            "context"
            "fmt"
            "log/slog"
            "net/http"
        
            "github.com/gin-gonic/gin"
            "github.com/your-org/myservice/internal/api/handlers"
            "github.com/your-org/myservice/internal/api/middleware"
            "github.com/your-org/myservice/internal/config"
        )
        
        type Server struct {
            cfg    config.Config
            srv    *http.Server
            logger *slog.Logger
        }
        
        func New(cfg config.Config, logger *slog.Logger, h *handlers.Handler) *Server {
            gin.SetMode(gin.ReleaseMode)
            r := gin.New()
        
            // Middleware order matters — see "Middleware ordering" below.
            r.Use(
                middleware.RequestID(),     // 1. assign request_id first
                middleware.Recovery(logger), // 2. recovery wraps everything
                middleware.RequestLogger(logger),
                middleware.CORS(),
            )
        
            h.Mount(r)
        
            return &Server{
                cfg:    cfg,
                logger: logger,
                srv: &http.Server{
                    Addr:         fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),
                    Handler:      r,
                    ReadTimeout:  cfg.ReadTimeout,
                    WriteTimeout: cfg.WriteTimeout,
                },
            }
        }
        
        func (s *Server) Run(ctx context.Context) error {
            errCh := make(chan error, 1)
            go func() {
                s.logger.InfoContext(ctx, "server starting",
                    slog.String("addr", s.srv.Addr))
                if err := s.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
                    errCh <- err
                }
                close(errCh)
            }()
        
            select {
            case <-ctx.Done():
                s.logger.InfoContext(ctx, "shutdown signal received")
                shutdownCtx, cancel := context.WithTimeout(
                    context.Background(), s.cfg.ShutdownTimeout)
                defer cancel()
                return s.srv.Shutdown(shutdownCtx)
            case err := <-errCh:
                return err
            }
        }
        ```
        
        Notes:
        
        - `gin.New()` not `gin.Default()` — `Default()` adds `Logger()` (text format, not slog) and `Recovery()` (no logger injection). We replace both.
        - `gin.SetMode(gin.ReleaseMode)` silences debug output. Production assumed.
        - `http.Server` with explicit timeouts. The default `nil` timeouts are a DoS waiting to happen.
        - Graceful shutdown: SIGINT/SIGTERM cancels the ctx → `Shutdown(shutdownCtx)` gives in-flight requests up to `ShutdownTimeout` to finish.
        
        ---
        
        ## Middleware ordering — the rule that actually matters
        
        ```
        RequestID    →   Recovery    →   Logger    →   CORS    →   Auth    →   Handler
           (1)            (2)              (3)            (4)         (5)
        ```
        
        1. **RequestID** is first so every subsequent middleware sees it.
        2. **Recovery** wraps everything after it. Order: a panic in CORS still gets caught.
        3. **Logger** sees the request_id and the recovered panic.
        4. **CORS** before Auth — OPTIONS preflight must return without auth.
        5. **Auth** is the last cross-cutting middleware. Per-route auth (admin-only) is mounted on a sub-router with extra middleware.
        
        ```go
        // Public routes — no auth
        api := r.Group("/api/v1")
        {
            api.POST("/auth/login", h.Login)
            api.GET("/healthz", h.Healthz)
        }
        
        // Authenticated routes
        authed := r.Group("/api/v1", middleware.Auth(authSvc))
        {
            authed.GET("/users/:id", h.GetUser)
            authed.POST("/users", h.CreateUser)
        }
        
        // Admin-only routes
        admin := r.Group("/api/v1/admin",
            middleware.Auth(authSvc),
            middleware.RequireRole("admin"))
        {
            admin.GET("/users", h.ListAllUsers)
        }
        ```
        
        ---
        
        ## Middleware examples
        
        ### `middleware/request_id.go`
        
        ```go
        package middleware
        
        import (
            "github.com/gin-gonic/gin"
            "github.com/google/uuid"
            "github.com/your-org/myservice/internal/obs"
        )
        
        func RequestID() gin.HandlerFunc {
            return func(c *gin.Context) {
                id := c.GetHeader("X-Request-ID")
                if id == "" {
                    id = uuid.Must(uuid.NewV7()).String()
                }
                c.Request = c.Request.WithContext(obs.WithRequestID(c.Request.Context(), id))
                c.Header("X-Request-ID", id)
                c.Next()
            }
        }
        ```
        
        ### `middleware/recovery.go`
        
        ```go
        package middleware
        
        import (
            "log/slog"
            "net/http"
            "runtime/debug"
        
            "github.com/gin-gonic/gin"
        )
        
        func Recovery(logger *slog.Logger) gin.HandlerFunc {
            return func(c *gin.Context) {
                defer func() {
                    if r := recover(); r != nil {
                        logger.ErrorContext(c.Request.Context(), "panic recovered",
                            slog.Any("panic", r),
                            slog.String("stack", string(debug.Stack())),
                        )
                        if !c.Writer.Written() {
                            c.JSON(http.StatusInternalServerError,
                                gin.H{"error": "internal_error"})
                        }
                        c.Abort()
                    }
                }()
                c.Next()
            }
        }
        ```
        
        ### `middleware/request_logging.go`
        
        ```go
        func RequestLogger(logger *slog.Logger) gin.HandlerFunc {
            return func(c *gin.Context) {
                start := time.Now()
                c.Next()
                logger.InfoContext(c.Request.Context(), "http request",
                    slog.String("method",  c.Request.Method),
                    slog.String("path",    c.Request.URL.Path),
                    slog.Int("status",     c.Writer.Status()),
                    slog.Int("bytes",      c.Writer.Size()),
                    slog.Duration("elapsed", time.Since(start)),
                    slog.String("ip",      c.ClientIP()),
                )
            }
        }
        ```
        
        The `sloglint` linter enforces typed attrs (`slog.String(...)`) over `slog.Any("path", ...)`. Keep the form.
        
        ### `middleware/cors.go`
        
        ```go
        func CORS() gin.HandlerFunc {
            return func(c *gin.Context) {
                c.Header("Access-Control-Allow-Origin",  "*")
                c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
                c.Header("Access-Control-Allow-Headers", "*")
                if c.Request.Method == http.MethodOptions {
                    c.AbortWithStatus(http.StatusNoContent)
                    return
                }
                c.Next()
            }
        }
        ```
        
        Note the explicit OPTIONS short-circuit — preflight must NOT go through Auth.
        
        ---
        
        ## Handlers — the canonical shape
        
        ```go
        package handlers
        
        import (
            "errors"
            "net/http"
        
            "github.com/gin-gonic/gin"
            "github.com/go-playground/validator/v10"
            "github.com/your-org/myservice/internal/domain"
            "github.com/your-org/myservice/internal/httperr"
            "github.com/your-org/myservice/internal/service"
        )
        
        type Handler struct {
            Users *service.UserService
        }
        
        func (h *Handler) Mount(r gin.IRouter) {
            api := r.Group("/api/v1")
            api.POST("/users", h.CreateUser)
            api.GET("/users/:id", h.GetUser)
        }
        
        type createUserReq struct {
            Email    string `json:"email"    binding:"required,email"`
            Username string `json:"username" binding:"required,alphanum,min=3,max=32"`
        }
        
        func (h *Handler) CreateUser(c *gin.Context) {
            var req createUserReq
            if err := c.ShouldBindJSON(&req); err != nil {
                writeBindingError(c, err)
                return
            }
        
            email, err := domain.NewEmail(req.Email)
            if err != nil {
                httperr.Write(c, err)
                return
            }
            username, err := domain.NewUsername(req.Username)
            if err != nil {
                httperr.Write(c, err)
                return
            }
        
            user, err := h.Users.Create(c.Request.Context(), email, username)
            if err != nil {
                httperr.Write(c, err)
                return
            }
            c.JSON(http.StatusCreated, user)
        }
        
        func writeBindingError(c *gin.Context, err error) {
            var vErr validator.ValidationErrors
            if errors.As(err, &vErr) {
                out := make(map[string]string, len(vErr))
                for _, fe := range vErr {
                    out[fe.Field()] = fe.Tag()
                }
                c.JSON(http.StatusBadRequest, gin.H{"errors": out})
                return
            }
            c.JSON(http.StatusBadRequest, gin.H{"error": "invalid_json"})
        }
        ```
        
        See `data-modeling.md` for the validator tag reference; see `error-handling.md` for the `httperr.Write` funnel.
        
        ---
        
        ## SSE streaming — the production pattern
        
        CLIProxyAPI streams OpenAI-compatible SSE for hundreds of concurrent clients. The pattern:
        
        ```go
        func (h *Handler) StreamChat(c *gin.Context) {
            ctx, cancel := context.WithCancel(c.Request.Context())
            defer cancel()
        
            // 1. Set SSE headers BEFORE writing any body
            c.Header("Content-Type",  "text/event-stream")
            c.Header("Cache-Control", "no-cache")
            c.Header("Connection",    "keep-alive")
            c.Header("X-Accel-Buffering", "no") // disable nginx buffering
        
            // 2. Obtain the flusher — REQUIRED for streaming
            flusher, ok := c.Writer.(http.Flusher)
            if !ok {
                httperr.Write(c, errors.New("streaming unsupported"))
                return
            }
        
            // 3. Pull chunks from upstream
            chunks, errs := h.svc.StreamCompletions(ctx, req)
        
            for {
                select {
                case <-ctx.Done():
                    return  // client disconnected, ctx cancelled
                case chunk, ok := <-chunks:
                    if !ok {
                        fmt.Fprint(c.Writer, "data: [DONE]\n\n")
                        flusher.Flush()
                        return
                    }
                    fmt.Fprintf(c.Writer, "data: %s\n\n", chunk)
                    flusher.Flush()
                case err := <-errs:
                    // Error mid-stream — emit as SSE event and bail
                    fmt.Fprintf(c.Writer, "event: error\ndata: %s\n\n", err.Error())
                    flusher.Flush()
                    return
                }
            }
        }
        ```
        
        Key facts:
        
        - **Headers MUST be set before the first `Write`.** Otherwise gin auto-sets `Content-Type: text/plain`.
        - **`c.Writer.(http.Flusher)` is the streaming primitive.** Without `flusher.Flush()`, the response is buffered and arrives as one blob at the end.
        - **Always respond to `<-ctx.Done()`.** A disconnected client must stop upstream work — otherwise you generate tokens for nothing.
        - **The trailing `\n\n` per event is wire-mandatory** for SSE parsing. Missing it = the client never sees the event.
        
        ---
        
        ## WebSocket upgrade
        
        ```go
        import "github.com/gorilla/websocket"  // still the canonical WS lib in 2026
        
        var upgrader = websocket.Upgrader{
            ReadBufferSize:  4096,
            WriteBufferSize: 4096,
            CheckOrigin: func(r *http.Request) bool {
                // tighten in production
                return true
            },
        }
        
        func (h *Handler) WebSocketEcho(c *gin.Context) {
            conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
            if err != nil {
                slog.ErrorContext(c.Request.Context(), "ws upgrade failed", slog.Any("err", err))
                return
            }
            defer conn.Close()
        
            for {
                mt, msg, err := conn.ReadMessage()
                if err != nil { return }
                if err := conn.WriteMessage(mt, msg); err != nil { return }
            }
        }
        ```
        
        For long-lived connections, use `conn.SetReadDeadline` + `SetPongHandler` for keepalive. CLIProxyAPI's `wsrelay` package is a reference implementation.
        
        ---
        
        ## Database wiring — pgx pool, injected, never global
        
        ```go
        package store
        
        import (
            "context"
            "fmt"
            "github.com/jackc/pgx/v5/pgxpool"
        )
        
        func NewPool(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
            cfg, err := pgxpool.ParseConfig(dsn)
            if err != nil {
                return nil, fmt.Errorf("parse dsn: %w", err)
            }
            cfg.MaxConns = 25
            cfg.MinConns = 5
            cfg.MaxConnLifetime = time.Hour
            cfg.MaxConnIdleTime = 30 * time.Minute
        
            pool, err := pgxpool.NewWithConfig(ctx, cfg)
            if err != nil {
                return nil, fmt.Errorf("connect: %w", err)
            }
            if err := pool.Ping(ctx); err != nil {
                pool.Close()
                return nil, fmt.Errorf("ping: %w", err)
            }
            return pool, nil
        }
        ```
        
        See `sqlc-pgx.md` for queries.
        
        ---
        
        ## Healthcheck
        
        ```go
        func (h *Handler) Healthz(c *gin.Context) {
            if err := h.pool.Ping(c.Request.Context()); err != nil {
                c.JSON(503, gin.H{"db": "down", "error": err.Error()})
                return
            }
            c.JSON(200, gin.H{"ok": true})
        }
        ```
        
        Mount BEFORE auth. Health checks must be unauthenticated.
        
        ---
        
        ## Testing the server
        
        ```go
        func TestCreateUser_returns_201_for_valid_input(t *testing.T) {
            // Given
            h := newTestHandler(t)
            r := gin.New()
            h.Mount(r)
        
            body := `{"email":"a@b.com","username":"alice"}`
            req := httptest.NewRequest("POST", "/api/v1/users", strings.NewReader(body))
            req.Header.Set("Content-Type", "application/json")
            rec := httptest.NewRecorder()
        
            // When
            r.ServeHTTP(rec, req)
        
            // Then
            require.Equal(t, http.StatusCreated, rec.Code)
            var got struct{ ID string `json:"id"` }
            require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
            require.NotEmpty(t, got.ID)
        }
        ```
        
        See `testing.md` for full patterns (testcontainers integration, table-driven, goleak).
        
        ---
        
        ## Sources
        
        - gin docs: https://gin-gonic.com/docs/
        - CLIProxyAPI (reference impl): https://github.com/router-for-me/CLIProxyAPI
        - pgx pool: https://pkg.go.dev/github.com/jackc/pgx/v5/pgxpool
        - SSE spec: https://html.spec.whatwg.org/multipage/server-sent-events.html
        - Go's `http.Server` graceful shutdown: https://pkg.go.dev/net/http#Server.Shutdown
        
      • bootstrap.md 8.8 KB
        # Bootstrap — Project Layout, Toolchain, Taskfile, CI
        
        What every new Go project gets in the first 60 seconds. Drop the script in `scripts/go/new-project.go` does all of this — this document explains *what* it produces and *why*.
        
        ## Toolchain pin
        
        `go.work` (monorepo) or just rely on `go.mod`'s `go 1.23` directive (single module). Go 1.21+ auto-downloads matching toolchain when the local `go` binary is older. **No `.tool-versions` / `asdf` / `mise` indirection required** unless your shop standardizes on it.
        
        ```bash
        # Confirm a working toolchain
        go env GOTOOLCHAIN   # should be "auto" or your pinned version
        go version           # ≥ 1.23
        ```
        
        ## Required global installs
        
        These are CLI tools, installed once per machine via `go install`:
        
        ```bash
        go install mvdan.cc/gofumpt@latest
        go install golang.org/x/tools/cmd/goimports@latest
        go install github.com/golangci/golangci-lint/cmd/golangci-lint@v2.0.0
        go install go.uber.org/nilaway/cmd/nilaway@latest
        go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
        go install github.com/pressly/goose/v3/cmd/goose@latest
        go install go.uber.org/mock/mockgen@latest
        go install github.com/go-task/task/v3/cmd/task@latest
        ```
        
        For Connect/protobuf projects, additionally:
        
        ```bash
        go install github.com/bufbuild/buf/cmd/buf@latest
        go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
        go install connectrpc.com/connect/cmd/protoc-gen-connect-go@latest
        ```
        
        ## Project layout — the canonical tree
        
        ```
        myservice/
        ├── go.mod
        ├── go.sum
        ├── Taskfile.yml               # task runner
        ├── .golangci.yml              # see golangci-strict.md
        ├── .editorconfig
        ├── .gitignore
        ├── README.md
        ├── AGENTS.md                  # agent-readable project facts
        ├── cmd/
        │   └── server/
        │       └── main.go            # ONLY: parse flags, call cmd.Execute(); ≤ 50 LOC
        ├── internal/                  # NEVER importable from outside this module
        │   ├── api/                   # transport layer (gin/connect routers)
        │   │   ├── server.go          # gin engine setup, route registration
        │   │   ├── middleware/
        │   │   │   ├── request_id.go
        │   │   │   ├── logging.go
        │   │   │   └── auth.go
        │   │   └── handlers/
        │   │       ├── users.go
        │   │       └── users_test.go
        │   ├── domain/                # parse-don't-validate types, smart constructors
        │   │   ├── user.go
        │   │   └── email.go
        │   ├── service/               # business logic, depends on domain only
        │   │   └── user_service.go
        │   ├── store/                 # persistence; sqlc-generated code lives here
        │   │   ├── sqlc/              # sqlc-generated, do not hand-edit
        │   │   ├── queries/           # *.sql files sqlc reads
        │   │   └── migrations/        # goose migrations
        │   ├── config/                # env-driven config (caarlos0/env)
        │   │   └── config.go
        │   └── obs/                   # observability: slog setup, otel, healthz
        │       └── logger.go
        ├── pkg/                       # exportable libraries — only if you publish
        │   └── …
        ├── proto/                     # *.proto definitions (Connect/gRPC projects)
        │   └── service.proto
        ├── gen/                       # generated code (Connect, OpenAPI)
        │   └── service/v1/
        │       ├── service.pb.go
        │       └── servicev1connect/
        ├── test/                      # cross-cutting test helpers, fixtures
        └── .github/workflows/ci.yml
        ```
        
        **Rules**:
        
        - `cmd/<binary>/main.go` is ≤ 50 LOC. Anything more lives in `internal/cmd/`.
        - `internal/` is **the** business code. Other modules cannot import it (Go compiler-enforced).
        - `pkg/` is for things you genuinely want third parties to import. Empty until proven otherwise.
        - No `utils/`, `helpers/`, `common/`, `shared/`. **REJECT.** Files are named after the concept they own.
        - One package per directory. One responsibility per package.
        
        ## `Taskfile.yml` — the entry point for every action
        
        `go-task/task` is the modern Make replacement. Cross-platform, YAML, fast.
        
        ```yaml
        version: '3'
        
        vars:
          BINARY: server
          PKG: ./cmd/server
        
        tasks:
          default:
            deps: [fmt, lint, test]
        
          fmt:
            desc: Format all Go files
            cmds:
              - gofumpt -w .
              - goimports -w -local "$(go list -m)" .
        
          lint:
            desc: Run all linters
            cmds:
              - golangci-lint run --timeout 5m ./...
              - nilaway -include-pkgs "$(go list -m)/..." ./...
        
          test:
            desc: Run tests with race detector
            cmds:
              - go test -race -shuffle=on -count=1 ./...
        
          test-cover:
            desc: Coverage report
            cmds:
              - go test -race -shuffle=on -count=1 -coverprofile=coverage.out ./...
              - go tool cover -html=coverage.out -o coverage.html
        
          build:
            desc: Build the binary
            cmds:
              - go build -trimpath -ldflags="-s -w" -o bin/{{.BINARY}} {{.PKG}}
        
          run:
            desc: Run the server locally
            deps: [build]
            cmds:
              - ./bin/{{.BINARY}}
        
          gen:
            desc: Run all code generators
            cmds:
              - task: gen:sqlc
              - task: gen:mocks
              - task: gen:proto
        
          gen:sqlc:
            cmds:
              - sqlc generate
            sources:
              - internal/store/queries/*.sql
              - internal/store/sqlc.yaml
            generates:
              - internal/store/sqlc/*.go
        
          gen:mocks:
            cmds:
              - go generate ./...
        
          gen:proto:
            cmds:
              - buf generate
            sources:
              - proto/**/*.proto
              - buf.yaml
              - buf.gen.yaml
        
          migrate:up:
            cmds:
              - goose -dir internal/store/migrations postgres "$DATABASE_URL" up
        
          migrate:down:
            cmds:
              - goose -dir internal/store/migrations postgres "$DATABASE_URL" down
        
          ci:
            desc: Everything CI does, locally
            deps: [fmt, lint, test, build]
        ```
        
        `task` (no args) runs format + lint + test in parallel where possible. `task ci` runs the full pipeline.
        
        ## `go.mod` template
        
        ```go
        module github.com/your-org/myservice
        
        go 1.23
        
        require (
            github.com/caarlos0/env/v11 v11.2.2
            github.com/gin-gonic/gin v1.10.1
            github.com/go-playground/validator/v10 v10.22.1
            github.com/google/uuid v1.6.0
            github.com/jackc/pgx/v5 v5.7.6
            golang.org/x/sync v0.18.0
        )
        ```
        
        Only direct deps listed; `go mod tidy` populates indirects.
        
        ## `.editorconfig`
        
        ```ini
        root = true
        
        [*]
        indent_style = tab
        indent_size = 4
        end_of_line = lf
        charset = utf-8
        trim_trailing_whitespace = true
        insert_final_newline = true
        
        [*.{yml,yaml,json,md}]
        indent_style = space
        indent_size = 2
        ```
        
        ## `.gitignore`
        
        ```gitignore
        bin/
        coverage.out
        coverage.html
        *.test
        *.prof
        
        # IDE
        .idea/
        .vscode/
        *.swp
        
        # Local env
        .env
        .env.local
        
        # Secrets
        *.pem
        *.key
        ```
        
        ## CI — minimal GitHub Actions
        
        `.github/workflows/ci.yml`:
        
        ```yaml
        name: ci
        on:
          pull_request:
          push:
            branches: [main]
        
        jobs:
          ci:
            runs-on: ubuntu-latest
            steps:
              - uses: actions/checkout@v4
              - uses: actions/setup-go@v5
                with:
                  go-version: '1.23'
                  cache: true
        
              - name: Install tools
                run: |
                  go install mvdan.cc/gofumpt@latest
                  go install github.com/golangci/golangci-lint/cmd/golangci-lint@v2.0.0
                  go install go.uber.org/nilaway/cmd/nilaway@latest
                  go install github.com/go-task/task/v3/cmd/task@latest
        
              - name: Format check
                run: gofumpt -l . | (! grep .)
        
              - name: Lint
                run: golangci-lint run --timeout 5m ./...
        
              - name: Nilaway
                run: nilaway ./...
        
              - name: Test
                run: go test -race -shuffle=on -count=1 ./...
        
              - name: Build
                run: go build -trimpath ./...
        ```
        
        The order matters: format → lint → nilaway → test → build. Fail fast on the cheap checks.
        
        ## `AGENTS.md` — agent-readable project facts
        
        Every new project gets an `AGENTS.md` at the root. The content is **machine-friendly**: short, declarative, no marketing prose. Example:
        
        ```markdown
        # AGENTS.md
        
        Go 1.23+ HTTP service for {one-line purpose}.
        
        ## Commands
        - `task` — fmt + lint + test
        - `task build` — produce ./bin/server
        - `task gen` — regenerate sqlc + mocks + proto
        
        ## Architecture
        - `cmd/server/main.go` — entrypoint, ≤50 LOC
        - `internal/api/` — gin handlers + middleware
        - `internal/domain/` — smart-constructor types, no I/O
        - `internal/store/sqlc/` — generated; never hand-edit
        
        ## Conventions
        - `slog` for all logs; never `log.*`, never `fmt.Println`
        - `context.Context` first arg for every public function
        - Errors wrapped with `%w`; check with `errors.Is/As`
        - 250 pure LOC ceiling per file — split before adding lines
        ```
        
        The skill's `cmd/new-project.go` writes this file with project-specific values filled in.
        
        ## Sources
        
        - Go modules reference: https://go.dev/ref/mod
        - go-task: https://taskfile.dev
        - golangci-lint v2: https://golangci-lint.run/docs/configuration/
        - Standard project layout debate: https://go.dev/doc/modules/layout (NOT `golang-standards/project-layout` — that repo is community, not official)
        
      • bubbletea-v2.md 11.3 KB
        # Bubbletea v2 — TUI with First-Class CJK / IME Support
        
        The TUI stack for 2026. Use **v2 RC**, not v1. If your users include Korean, Japanese, or Chinese speakers, v1 is broken — IME composition lands in the wrong cells. v2 fixes this. This document is the canonical setup.
        
        The reference implementation this document is distilled from: [`code-yeongyu/bubbletea-wm`](https://github.com/code-yeongyu/bubbletea-wm) — a floating window manager built specifically to nail down v2 + IME.
        
        ---
        
        ## Why v2 (not v1) — the IME story
        
        Bubbletea v1 manages cursor positioning in software ("virtual cursor"). It draws a `█` at the cursor position. The terminal's *real* cursor stays at `(0, 0)`.
        
        This breaks every CJK input method. IME candidate windows (the popup showing Hangul composition choices for Korean, kana → kanji for Japanese, and pinyin lookup for Chinese) anchor to the terminal's **real** cursor position. With v1, the candidate window appears at top-left while you are typing somewhere in the middle of the screen.
        
        Bubbletea v2 fixes this with two changes:
        
        1. **`tea.View{Cursor: *tea.Cursor}`** — your `View()` method returns a view that *includes* the desired cursor position. The framework moves the terminal's real cursor there.
        2. **`textarea.SetVirtualCursor(false)`** — textareas no longer draw their own `█`. They expose `.Cursor()` so you can read where they want the real cursor.
        
        Together: IME popups appear where the user is typing. As they should.
        
        ### Other v2 wins (incidental)
        
        - `tea.MouseClickMsg` / `MouseMotionMsg` / `MouseReleaseMsg` instead of one coarse `MouseMsg`.
        - Cleaner `View` struct with `AltScreen`, `MouseMode` fields instead of `tea.Cmd` setters.
        - Pluggable rendering pipeline; better performance under high message volume.
        
        ---
        
        ## `go.mod`
        
        ```go
        module github.com/your-org/mytui
        
        go 1.23
        
        require (
            charm.land/bubbletea/v2 v2.0.0-rc.2
            charm.land/bubbles/v2   v2.0.0-rc.1
            charm.land/lipgloss/v2  v2.0.0-beta.3
            github.com/mattn/go-runewidth v0.0.19
        )
        ```
        
        The packages live under `charm.land/` (NOT `github.com/charmbracelet/...`) for v2. This is the Charm team's deliberate import-path break to keep v2 separate from v1 until stable.
        
        ---
        
        ## Minimal app — the IME-correct skeleton
        
        ```go
        package main
        
        import (
            "fmt"
            "log"
        
            tea "charm.land/bubbletea/v2"
            "charm.land/bubbles/v2/textarea"
        )
        
        type model struct {
            width, height int
            ta            textarea.Model
        }
        
        func initial() model {
            ta := textarea.New()
            ta.Placeholder = "Type Korean / Japanese / Chinese here..."
            ta.SetWidth(60)
            ta.SetHeight(10)
            ta.SetVirtualCursor(false)  // ← THE LINE. Without this, IME breaks.
            ta.Focus()
            return model{ta: ta}
        }
        
        func (m model) Init() tea.Cmd { return textarea.Blink }
        
        func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
            switch msg := msg.(type) {
            case tea.WindowSizeMsg:
                m.width, m.height = msg.Width, msg.Height
            case tea.KeyPressMsg:
                if msg.String() == "ctrl+c" {
                    return m, tea.Quit
                }
            }
            var cmd tea.Cmd
            m.ta, cmd = m.ta.Update(msg)
            return m, cmd
        }
        
        func (m model) View() tea.View {
            var view tea.View
            view.AltScreen = true
            view.SetContent(m.ta.View())
        
            // ── THE OTHER LINE. Position the REAL cursor for IME. ──
            if cursor := m.ta.Cursor(); cursor != nil {
                view.Cursor = cursor
            }
            return view
        }
        
        func main() {
            if _, err := tea.NewProgram(initial(), tea.WithAltScreen()).Run(); err != nil {
                log.Fatal(err)
            }
            fmt.Println("bye")
        }
        ```
        
        The two lines that matter:
        
        1. `ta.SetVirtualCursor(false)` — disables the virtual `█`.
        2. `view.Cursor = cursor` (where `cursor = m.ta.Cursor()`) — exports the real cursor position to the framework.
        
        Without **both**, IME breaks.
        
        ---
        
        ## CJK width — go-runewidth, not `len()`
        
        Korean, Japanese, Chinese characters render as **two terminal cells** (wide characters per Unicode East Asian Width). Naive `len(string)` returns byte count, not display width. `utf8.RuneCountInString` returns rune count, also not display width.
        
        Use `github.com/mattn/go-runewidth`:
        
        ```go
        import "github.com/mattn/go-runewidth"
        
        func displayWidth(s string) int {
            return runewidth.StringWidth(s)
        }
        
        // Wide character occupies two cells; pad accordingly
        for _, r := range s {
            cell := string(r)
            w := runewidth.RuneWidth(r)
            canvas = append(canvas, cell)
            if w == 2 {
                canvas = append(canvas, "")  // placeholder for second cell
            }
        }
        ```
        
        `lipgloss/v2` uses `go-runewidth` internally — `lipgloss.Width("\u4e2d\u6587")` returns 4, not 2. **If you measure outside lipgloss, you must call runewidth directly.**
        
        ---
        
        ## Mouse — v2 has typed events
        
        ```go
        case tea.MouseClickMsg:
            // msg.X, msg.Y, msg.Button
            return m.handleClick(msg.X, msg.Y, msg.Button)
        
        case tea.MouseMotionMsg:
            return m.handleHover(msg.X, msg.Y)
        
        case tea.MouseReleaseMsg:
            return m.handleRelease(msg.X, msg.Y)
        ```
        
        Enable mouse via the `View`:
        
        ```go
        view.MouseMode = tea.MouseModeCellMotion  // or MouseModeAll
        ```
        
        `CellMotion` reports clicks + motion-while-button-pressed (drag). `MouseModeAll` reports motion always — heavier, only when you need hover.
        
        ---
        
        ## Components from `bubbles/v2`
        
        ```go
        import (
            "charm.land/bubbles/v2/textarea"
            "charm.land/bubbles/v2/textinput"
            "charm.land/bubbles/v2/spinner"
            "charm.land/bubbles/v2/viewport"
            "charm.land/bubbles/v2/list"
            "charm.land/bubbles/v2/table"
            "charm.land/bubbles/v2/help"
            "charm.land/bubbles/v2/key"
        )
        ```
        
        All v2 components support `SetVirtualCursor(false)` where they accept text input. Use it for every text input that users might type CJK into — and "might" should be assumed *yes*.
        
        ---
        
        ## Styling — `lipgloss/v2`
        
        ```go
        import "charm.land/lipgloss/v2"
        
        titleStyle := lipgloss.NewStyle().
            Bold(true).
            Foreground(lipgloss.Color("230")).
            Background(lipgloss.Color("62")).
            Padding(0, 1).
            Border(lipgloss.RoundedBorder()).
            BorderForeground(lipgloss.Color("63"))
        
        rendered := titleStyle.Render("\u4e2d\u6587")
        ```
        
        `lipgloss/v2` width and padding correctly account for CJK display width. v1 did too — this is not a v2-specific fix, just a reminder.
        
        ---
        
        ## Architecture pattern — Model–Update–View
        
        ```
        +--------------------------------------------+
        |  tea.Program runs the event loop           |
        |                                            |
        |  loop:                                     |
        |    msg <- queue                            |
        |    model, cmd = model.Update(msg)          |
        |    view = model.View()                     |
        |    render(view)                            |
        |    if cmd != nil: go run(cmd) -> queue     |
        +--------------------------------------------+
        ```
        
        Rules:
        
        - **Model is a value type, not a pointer.** Bubbletea calls `Update` with a value receiver and expects a new value returned. Pointer receivers cause subtle bugs where state mutation leaks across draws.
        - **`Update` is pure.** No I/O. No goroutines started inline. Any I/O returns a `tea.Cmd` — Bubbletea runs it in a goroutine and feeds the result back as a message.
        - **`View` is read-only.** It returns a `tea.View` without modifying state.
        - **`tea.Cmd` is `func() tea.Msg`.** It runs once, returns a message, exits. For repeating work, use `tea.Tick` or a self-resending command.
        
        ```go
        // One-shot command
        func loadData() tea.Cmd {
            return func() tea.Msg {
                data, err := fetch()
                if err != nil { return errMsg{err} }
                return dataLoadedMsg{data}
            }
        }
        
        // Periodic
        func tickEvery() tea.Cmd {
            return tea.Tick(time.Second, func(t time.Time) tea.Msg {
                return tickMsg{t}
            })
        }
        ```
        
        ---
        
        ## Splitting the model — sub-models
        
        ```go
        type model struct {
            list    list.Model
            input   textinput.Model
            spinner spinner.Model
        }
        
        func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
            var cmds []tea.Cmd
            var cmd tea.Cmd
        
            m.list, cmd = m.list.Update(msg)
            cmds = append(cmds, cmd)
        
            m.input, cmd = m.input.Update(msg)
            cmds = append(cmds, cmd)
        
            m.spinner, cmd = m.spinner.Update(msg)
            cmds = append(cmds, cmd)
        
            return m, tea.Batch(cmds...)
        }
        ```
        
        `tea.Batch` runs commands concurrently. The framework collects their results in the order they arrive.
        
        When the model exceeds 250 LOC, split by sub-model into separate files:
        
        ```
        internal/ui/
        ├── model.go          # root model orchestration
        ├── list.go           # list sub-model state + update + view
        ├── input.go          # input sub-model
        └── spinner.go        # spinner sub-model
        ```
        
        ---
        
        ## Testing TUI code — `teatest`
        
        ```go
        import "charm.land/bubbletea/v2/teatest"
        
        func TestModel_typing_cjk_keeps_cursor_in_position(t *testing.T) {
            // Given
            m := initial()
            tm := teatest.NewTestModel(t, m, teatest.WithInitialTermSize(80, 24))
        
            // When — simulate typing two CJK wide characters
            tm.Send(tea.KeyPressMsg{Code: '\u4e2d'})
            tm.Send(tea.KeyPressMsg{Code: '\u6587'})
        
            // Then
            out := tm.FinalOutput(t)
            require.Contains(t, string(out), "\u4e2d\u6587")
            // Cursor should be at column 4 (two wide chars = 4 cells)
            // ...
        }
        ```
        
        `teatest` lets you drive the model through synthetic messages and inspect the rendered output. Pair with `autogold` snapshots for full-view regression tests.
        
        ---
        
        ## Common antipatterns
        
        | Bad | Why | Good |
        |---|---|---|
        | `tea.Program` with `tea.WithoutSignals()` | Ctrl-C does not work | Default signal handling |
        | Pointer receivers on Model | Bubbletea expects value semantics | Value receivers, return new model |
        | `time.Sleep` inside `Update` | Blocks the event loop | `tea.Tick` or async `tea.Cmd` |
        | `fmt.Println` for debug | Corrupts the rendered output | `tea.Printf` for logging, or write to a file |
        | `len(s)` for CJK width | Off by 2x | `runewidth.StringWidth(s)` |
        | `Bubbletea v1` for an app with text input | Korean/Japanese IME breaks | v2 + `SetVirtualCursor(false)` |
        | Drawing your own `█` block cursor in v2 | Conflicts with `view.Cursor` | Let the terminal handle it |
        
        ---
        
        ## Performance — when v2 starts to crawl
        
        - **Reduce View frequency.** If the model changes 60 times/sec but the rendered view changes once/sec, gate redraws on a "dirty" flag.
        - **`viewport.Model` for scrollable content.** Avoid re-rendering thousands of lines on every keystroke.
        - **`Batch` your commands.** A series of synchronous `tea.Cmd` returns serializes; `tea.Batch` parallelizes.
        - **Profile with `tea.WithFPS(N)`** to cap repaint rate during development.
        
        ---
        
        ## When NOT to use Bubbletea
        
        - The app is one prompt + one answer. Use `huh` (also from Charm) — simpler, no Model–Update–View ceremony.
        - The app is a long-running daemon with occasional status output. Use `slog` to stderr and `tea.Program` only if interactivity becomes necessary.
        - The app must run as a non-tty subprocess (CI, redirected stdin). `tea.Program` requires a tty for input. Detect via `term.IsTerminal(int(os.Stdin.Fd()))` and fall back to a non-interactive path.
        
        ---
        
        ## Sources
        
        - bubbletea v2 RC: https://github.com/charmbracelet/bubbletea/tree/v2
        - bubbles v2: https://github.com/charmbracelet/bubbles/tree/v2
        - lipgloss v2: https://github.com/charmbracelet/lipgloss/tree/v2
        - bubbletea-wm (IME reference): https://github.com/code-yeongyu/bubbletea-wm
        - crush CLI (production IME impl): https://github.com/charmbracelet/crush
        - go-runewidth: https://github.com/mattn/go-runewidth
        - Unicode East Asian Width: https://www.unicode.org/reports/tr11/
        
      • cobra-stack.md 11.5 KB
        # CLI Stack — cobra + slog + caarlos0/env + signal handling
        
        The canonical Go CLI skeleton. `cobra` is the de facto framework — Kubernetes, Docker CLI, Helm, GitHub CLI, gh, Hugo all use it. Use it.
        
        ---
        
        ## Toolchain
        
        ```bash
        go install github.com/spf13/cobra-cli@latest
        cobra-cli init mytool
        cobra-cli add server
        cobra-cli add migrate
        ```
        
        `cobra-cli` scaffolds the `cmd/` package. Edit the result; do not regenerate.
        
        ---
        
        ## Layout
        
        ```
        mytool/
        ├── go.mod
        ├── main.go                  # ≤ 30 LOC, calls cmd.Execute
        ├── cmd/
        │   ├── root.go              # rootCmd, persistent flags, slog setup
        │   ├── server.go            # `mytool server` subcommand
        │   ├── migrate.go           # `mytool migrate` subcommand
        │   └── version.go           # `mytool version` — auto-injected version
        ├── internal/
        │   ├── config/
        │   └── server/
        └── Taskfile.yml
        ```
        
        ---
        
        ## `main.go`
        
        ```go
        package main
        
        import (
            "context"
            "log/slog"
            "os"
            "os/signal"
            "syscall"
        
            "github.com/your-org/mytool/cmd"
        )
        
        func main() {
            ctx, stop := signal.NotifyContext(context.Background(),
                syscall.SIGINT, syscall.SIGTERM)
            defer stop()
        
            if err := cmd.Execute(ctx); err != nil {
                slog.Error("fatal", slog.Any("err", err))
                os.Exit(1)
            }
        }
        ```
        
        `signal.NotifyContext` (Go 1.16+) gives every subcommand a ctx that cancels on Ctrl-C. Subcommands plumb the ctx into their workers.
        
        ---
        
        ## `cmd/root.go`
        
        ```go
        package cmd
        
        import (
            "context"
            "log/slog"
            "os"
        
            "github.com/spf13/cobra"
        )
        
        var (
            verbose    bool
            logFormat  string
            configPath string
        )
        
        var rootCmd = &cobra.Command{
            Use:   "mytool",
            Short: "Short description of mytool",
            Long:  `Long description, prose; cobra wraps it for --help.`,
            PersistentPreRunE: func(c *cobra.Command, args []string) error {
                return setupLogger()
            },
            SilenceUsage:  true,  // don't print --help on every error
            SilenceErrors: true,  // we log them ourselves in Execute
        }
        
        func init() {
            rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false,
                "enable debug logging")
            rootCmd.PersistentFlags().StringVar(&logFormat, "log-format", "text",
                "log format: text or json")
            rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "",
                "path to config file (optional)")
        }
        
        func Execute(ctx context.Context) error {
            return rootCmd.ExecuteContext(ctx)
        }
        
        func setupLogger() error {
            level := slog.LevelInfo
            if verbose { level = slog.LevelDebug }
            opts := &slog.HandlerOptions{Level: level}
        
            var h slog.Handler
            switch logFormat {
            case "json":
                h = slog.NewJSONHandler(os.Stderr, opts)
            case "text":
                h = slog.NewTextHandler(os.Stderr, opts)
            default:
                return fmt.Errorf("invalid log-format %q", logFormat)
            }
            slog.SetDefault(slog.New(h))
            return nil
        }
        ```
        
        Notes:
        
        - `RunE` / `PersistentPreRunE` (the `E` variants) return errors. Use these; never use `Run` (no error return, encourages `log.Fatal`).
        - `SilenceUsage: true` + `SilenceErrors: true` together: cobra stops printing the full `--help` on every command failure (the default behavior is rude in production scripts).
        - `ExecuteContext` (cobra 1.8+) plumbs the ctx into every subcommand's `cmd.Context()`.
        
        ---
        
        ## `cmd/server.go`
        
        ```go
        package cmd
        
        import (
            "log/slog"
        
            "github.com/spf13/cobra"
            "github.com/your-org/mytool/internal/server"
        )
        
        var (
            serverAddr string
        )
        
        var serverCmd = &cobra.Command{
            Use:   "server",
            Short: "Run the HTTP server",
            RunE: func(c *cobra.Command, args []string) error {
                ctx := c.Context()
                slog.InfoContext(ctx, "starting", slog.String("addr", serverAddr))
                return server.Run(ctx, serverAddr)
            },
        }
        
        func init() {
            serverCmd.Flags().StringVar(&serverAddr, "addr", ":8080",
                "listen address")
            rootCmd.AddCommand(serverCmd)
        }
        ```
        
        The subcommand is a thin shim — flags + log line + delegate to `internal/server`. Anything bigger violates the 250-LOC ceiling and belongs in `internal/`.
        
        ---
        
        ## Subcommands with arguments
        
        ```go
        var migrateUpCmd = &cobra.Command{
            Use:   "up [N]",
            Short: "Apply N migrations (default: all)",
            Args:  cobra.MaximumNArgs(1),
            RunE: func(c *cobra.Command, args []string) error {
                n := -1  // all
                if len(args) == 1 {
                    var err error
                    n, err = strconv.Atoi(args[0])
                    if err != nil {
                        return fmt.Errorf("invalid N: %w", err)
                    }
                }
                return migrate.Up(c.Context(), n)
            },
        }
        ```
        
        Use cobra's argument validators (`cobra.ExactArgs`, `cobra.MaximumNArgs`, `cobra.OnlyValidArgs`). They produce clean help text.
        
        ---
        
        ## Flag types — typed, not strings
        
        ```go
        // GOOD
        serverCmd.Flags().DurationVar(&timeout, "timeout", 30*time.Second, "request timeout")
        serverCmd.Flags().IntVar(&port, "port", 8080, "port")
        serverCmd.Flags().StringSliceVar(&hosts, "host", nil, "allowed hosts (repeatable)")
        
        // BAD — manual parsing
        serverCmd.Flags().StringVar(&timeoutStr, "timeout", "30s", "")
        // ...then later: time.ParseDuration(timeoutStr)
        ```
        
        `pflag` (cobra's flag lib) has typed variants for every common type. Use them; the parsing and error messages are free.
        
        ---
        
        ## Bind flags to env vars
        
        cobra + viper is overkill for env binding. Use `caarlos0/env/v11`:
        
        ```go
        type ServerOpts struct {
            Addr    string        `env:"ADDR"    envDefault:":8080"`
            Timeout time.Duration `env:"TIMEOUT" envDefault:"30s"`
        }
        
        var opts ServerOpts
        
        var serverCmd = &cobra.Command{
            Use: "server",
            PersistentPreRunE: func(c *cobra.Command, args []string) error {
                // 1. Parse env first.
                if err := env.Parse(&opts); err != nil { return err }
                // 2. Flags override env if explicitly set.
                if c.Flags().Changed("addr") {
                    opts.Addr, _ = c.Flags().GetString("addr")
                }
                return nil
            },
            RunE: func(c *cobra.Command, args []string) error {
                return server.Run(c.Context(), opts)
            },
        }
        
        func init() {
            serverCmd.Flags().String("addr", "", "listen address (env: ADDR)")
            serverCmd.Flags().Duration("timeout", 0, "request timeout (env: TIMEOUT)")
            rootCmd.AddCommand(serverCmd)
        }
        ```
        
        Precedence: **flag (if set) > env > default**. Document the env var in the flag usage string.
        
        ---
        
        ## Version subcommand — build-injected
        
        ```go
        // cmd/version.go
        package cmd
        
        import (
            "fmt"
            "runtime/debug"
        
            "github.com/spf13/cobra"
        )
        
        // Set by -ldflags at build time, falls back to debug.BuildInfo.
        var (
            version = ""
            commit  = ""
            date    = ""
        )
        
        var versionCmd = &cobra.Command{
            Use:   "version",
            Short: "Print version",
            Run: func(c *cobra.Command, args []string) {
                v, c2, d := resolveVersion()
                fmt.Printf("mytool %s (commit %s, built %s)\n", v, c2, d)
            },
        }
        
        func resolveVersion() (string, string, string) {
            if version != "" { return version, commit, date }
            info, ok := debug.ReadBuildInfo()
            if !ok { return "dev", "unknown", "unknown" }
        
            var vcs, hash, time string
            for _, s := range info.Settings {
                switch s.Key {
                case "vcs.revision": hash = s.Value
                case "vcs.time":     time = s.Value
                case "vcs":          vcs  = s.Value
                }
            }
            return info.Main.Version, hash, time + " (" + vcs + ")"
        }
        
        func init() { rootCmd.AddCommand(versionCmd) }
        ```
        
        Build with version injection:
        
        ```bash
        go build \
          -ldflags="-X 'github.com/your-org/mytool/cmd.version=v1.2.3' -X 'github.com/your-org/mytool/cmd.commit=$(git rev-parse --short HEAD)' -X 'github.com/your-org/mytool/cmd.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)'" \
          -o bin/mytool ./
        ```
        
        The `debug.BuildInfo` fallback means a `go install`'d binary also has version info — no manual `-ldflags` needed.
        
        ---
        
        ## Shell completions
        
        ```go
        var completionCmd = &cobra.Command{
            Use:                   "completion [bash|zsh|fish|powershell]",
            Short:                 "Generate shell completion",
            Args:                  cobra.ExactValidArgs(1),
            ValidArgs:             []string{"bash", "zsh", "fish", "powershell"},
            DisableFlagsInUseLine: true,
            RunE: func(c *cobra.Command, args []string) error {
                switch args[0] {
                case "bash":       return rootCmd.GenBashCompletionV2(os.Stdout, true)
                case "zsh":        return rootCmd.GenZshCompletion(os.Stdout)
                case "fish":       return rootCmd.GenFishCompletion(os.Stdout, true)
                case "powershell": return rootCmd.GenPowerShellCompletion(os.Stdout)
                }
                return nil
            },
        }
        
        func init() { rootCmd.AddCommand(completionCmd) }
        ```
        
        User:
        
        ```bash
        mytool completion zsh > "${fpath[1]}/_mytool"
        ```
        
        ---
        
        ## Interactive prompts — `huh` from charm
        
        For prompts/forms (`Are you sure?`, "Pick an environment", multi-field forms):
        
        ```go
        import "github.com/charmbracelet/huh"
        
        var confirm bool
        err := huh.NewConfirm().
            Title("Apply migrations to PRODUCTION?").
            Affirmative("Yes, do it").
            Negative("Abort").
            Value(&confirm).
            Run()
        ```
        
        `huh` replaces `survey` (which is no longer maintained). It composes with `lipgloss` for styling.
        
        ---
        
        ## Progress / spinners
        
        ```go
        import "github.com/charmbracelet/huh/spinner"
        
        err := spinner.New().Title("Fetching...").Action(func() {
            // long-running work
        }).Run()
        ```
        
        For determinate progress (downloads, batch processing), use `vbauerster/mpb/v8`:
        
        ```go
        import "github.com/vbauerster/mpb/v8"
        
        p := mpb.New(mpb.WithWidth(60))
        bar := p.AddBar(int64(total), /* decorators */)
        for i := 0; i < total; i++ {
            work()
            bar.Increment()
        }
        p.Wait()
        ```
        
        ---
        
        ## Output — JSON vs text
        
        Honor `--output json` for any CLI that scripts will parse:
        
        ```go
        var outputFmt string
        
        rootCmd.PersistentFlags().StringVar(&outputFmt, "output", "text",
            "output format: text or json")
        
        func render(v any) error {
            switch outputFmt {
            case "json":
                enc := json.NewEncoder(os.Stdout)
                enc.SetIndent("", "  ")
                return enc.Encode(v)
            case "text":
                return renderText(v)
            default:
                return fmt.Errorf("invalid --output %q", outputFmt)
            }
        }
        ```
        
        The `text` format uses `lipgloss` tables or `aquasecurity/table` for nicely-aligned columns. The `json` format is for `jq`-style piping.
        
        ---
        
        ## Error semantics
        
        - Return errors from `RunE`. Cobra catches them and the `Execute` wrapper logs + exits non-zero.
        - `os.Exit(1)` should appear **only in `main.go`**. Anywhere else means a subcommand cannot be tested.
        - For graceful early termination ("user cancelled"), return a sentinel and check it in `Execute`:
          ```go
          var ErrCancelled = errors.New("cancelled by user")
          // ... return ErrCancelled
          // in main:
          if errors.Is(err, cmd.ErrCancelled) { os.Exit(130) }  // 128 + SIGINT
          ```
        
        ---
        
        ## Testing CLI commands
        
        ```go
        func TestServerCmd_runs_with_default_addr(t *testing.T) {
            // Given
            buf := &bytes.Buffer{}
            rootCmd.SetOut(buf)
            rootCmd.SetErr(buf)
            rootCmd.SetArgs([]string{"server", "--addr", ":0"})
        
            // When
            ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
            defer cancel()
            err := rootCmd.ExecuteContext(ctx)
        
            // Then
            require.NoError(t, err)
            require.Contains(t, buf.String(), "starting")
        }
        ```
        
        `SetArgs` + `ExecuteContext` is the canonical pattern. Bind a ctx with a short deadline for tests that would otherwise block.
        
        ---
        
        ## Sources
        
        - cobra docs: https://github.com/spf13/cobra/blob/main/site/content/user_guide.md
        - pflag: https://github.com/spf13/pflag
        - huh: https://github.com/charmbracelet/huh
        - caarlos0/env: https://github.com/caarlos0/env
        - signal.NotifyContext: https://pkg.go.dev/os/signal#NotifyContext
        
      • concurrency.md 10.9 KB
        # Concurrency
        
        Goroutines, context, errgroup, channels, locks, and the discipline that keeps them from leaking. Go makes concurrency *easy to start* and *easy to get wrong*. This document is the boring rule set.
        
        ---
        
        ## The four non-negotiables
        
        1. **`ctx context.Context` is the first parameter of every public function that does I/O or can be cancelled.**
        2. **No goroutine without a shutdown path.** Every `go` keyword must answer "how does this stop?".
        3. **`-race` on every test run.** The `Taskfile.yml` and CI both enforce it.
        4. **`goleak` in `TestMain`** for every package that spawns goroutines. Catches leaks the race detector cannot.
        
        ---
        
        ## `context.Context` — the cancellation backbone
        
        ```go
        // GOOD — ctx as first param, propagated through
        func (s *UserService) Create(ctx context.Context, email Email) (User, error) {
            user, err := s.store.Insert(ctx, email)
            if err != nil {
                return User{}, fmt.Errorf("insert: %w", err)
            }
            if err := s.notifier.Welcome(ctx, user); err != nil {
                return User{}, fmt.Errorf("notify: %w", err)
            }
            return user, nil
        }
        
        // BAD — creates a fresh ctx, breaks request cancellation
        func (s *UserService) Create(email Email) (User, error) {
            ctx := context.Background()  // ← contextcheck linter rejects this
            // ...
        }
        ```
        
        The `contextcheck` linter (enabled in `golangci-strict.md`) refuses any function that has `ctx context.Context` available but uses `context.Background()` instead.
        
        ### `context.Value` — use sparingly
        
        ```go
        // Typed key — never use a bare string
        type ctxKey struct{ name string }
        var requestIDKey = ctxKey{"request_id"}
        
        func WithRequestID(ctx context.Context, id string) context.Context {
            return context.WithValue(ctx, requestIDKey, id)
        }
        
        func RequestID(ctx context.Context) string {
            v, _ := ctx.Value(requestIDKey).(string)
            return v
        }
        ```
        
        **Rules**:
        - Keys are unexported struct types, not strings. Prevents collisions across packages.
        - `context.Value` is for *request-scoped metadata* (request ID, auth subject, trace span), NEVER for application-scoped dependencies.
        - Dependencies (loggers, DB pools, config) go in your service struct, not in `context.Value`.
        
        ### `WithTimeout` / `WithCancel` — always pair with `defer cancel()`
        
        ```go
        ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
        defer cancel()  // ← MUST be deferred. fatcontext linter catches misses.
        
        if err := slow(ctx); err != nil { ... }
        ```
        
        Forgetting `defer cancel()` leaks a context goroutine until the parent expires — the `lostcancel` vet check catches it.
        
        ---
        
        ## `errgroup` — the structured concurrency primitive
        
        `golang.org/x/sync/errgroup` is Go's answer to Python's `asyncio.TaskGroup` or Rust's `JoinSet`. Use it instead of raw `go` for any group of related goroutines.
        
        ```go
        import "golang.org/x/sync/errgroup"
        
        func FetchAll(ctx context.Context, urls []string) ([][]byte, error) {
            g, ctx := errgroup.WithContext(ctx)
            g.SetLimit(8)  // concurrency cap — leave unbounded = production outage
        
            results := make([][]byte, len(urls))
            for i, u := range urls {
                g.Go(func() error {
                    body, err := fetch(ctx, u)
                    if err != nil {
                        return fmt.Errorf("fetch %s: %w", u, err)
                    }
                    results[i] = body
                    return nil
                })
            }
            if err := g.Wait(); err != nil {
                return nil, err
            }
            return results, nil
        }
        ```
        
        Properties:
        
        - `WithContext(parent)` returns a child ctx that gets cancelled on **first non-nil error**. All in-flight goroutines see `ctx.Done()` and bail.
        - `SetLimit(n)` blocks `g.Go(...)` when the in-flight count hits `n`. **Always set this.** Unbounded fan-out is how services die.
        - `g.Wait()` returns the **first** non-nil error. Others are dropped. If you need all errors, accumulate them manually:
          ```go
          var mu sync.Mutex
          var errs []error
          // inside g.Go:
          //   mu.Lock(); errs = append(errs, err); mu.Unlock()
          // after Wait, errors.Join(errs...)
          ```
        
        ---
        
        ## Goroutine leaks — `goleak`
        
        ```go
        package store_test
        
        import (
            "testing"
            "go.uber.org/goleak"
        )
        
        func TestMain(m *testing.M) {
            goleak.VerifyTestMain(m)
        }
        ```
        
        This single line at the top of `*_test.go` runs goleak's check after every test in the package. If a test leaks a goroutine, the run fails — pointing at which goroutine.
        
        **The bug it catches**: starting a goroutine in `setUp` and never joining it. Common in DB connection pools, background workers, ticker loops. The race detector does NOT catch this.
        
        If you have a known long-lived goroutine (a singleton background worker, a metrics exporter), use `goleak.IgnoreTopFunction`:
        
        ```go
        goleak.VerifyTestMain(m,
            goleak.IgnoreTopFunction("github.com/prometheus/client_golang/prometheus.(*Registry).Push"),
        )
        ```
        
        ---
        
        ## Channels — the rules that hold
        
        ### Direction
        
        ```go
        // GOOD — direction in signatures
        func produce(out chan<- Item)
        func consume(in <-chan Item)
        func pipeline(in <-chan Item, out chan<- Item)
        ```
        
        Direction restricts misuse. A consumer cannot close the producer's channel.
        
        ### Closing
        
        - **The sender closes.** Always. Never the receiver, never multiple senders.
        - **Multiple senders → use a `sync.WaitGroup` + one closer.**
        - **Closing a closed channel panics.** Closing a `nil` channel panics. Sending on a closed channel panics. Receiving from a closed channel returns zero value with `ok = false`.
        
        ```go
        // Canonical fan-in: multiple producers, one closer
        func fanIn(ctx context.Context, sources ...<-chan Item) <-chan Item {
            out := make(chan Item)
            var wg sync.WaitGroup
            wg.Add(len(sources))
            for _, src := range sources {
                go func() {
                    defer wg.Done()
                    for item := range src {
                        select {
                        case out <- item:
                        case <-ctx.Done():
                            return
                        }
                    }
                }()
            }
            go func() { wg.Wait(); close(out) }()
            return out
        }
        ```
        
        ### Selecting
        
        ```go
        select {
        case msg := <-incoming:
            handle(msg)
        case <-ctx.Done():
            return ctx.Err()
        case <-time.After(5 * time.Second):
            return ErrTimeout
        }
        ```
        
        - `time.After` allocates a timer each call — fine for occasional selects, **NOT for hot loops**. Use `time.NewTimer` + `timer.Reset` for repeat selects.
        - A `default:` case makes `select` non-blocking. Use deliberately, not by accident.
        
        ### Buffered vs unbuffered
        
        - **Unbuffered** (`make(chan T)`) = synchronous handoff. Sender blocks until receiver is ready. Use for *coordination*.
        - **Buffered** (`make(chan T, n)`) = asynchronous up to `n`. Use for *decoupling producer rate from consumer rate*.
        
        A buffered channel of size 1 acts as a **non-blocking signal**:
        
        ```go
        ready := make(chan struct{}, 1)
        // Producer
        select {
        case ready <- struct{}{}:  // signal once, non-blocking
        default:                    // already signaled, skip
        }
        // Consumer
        <-ready
        ```
        
        ---
        
        ## Locks — the pyramid
        
        ```
        Highest level (preferred)
          channels (message passing — "share memory by communicating")
          errgroup / wait group
        
          sync.RWMutex (many readers, occasional writer)
          sync.Mutex   (mutual exclusion)
        
          atomic.Int64 / atomic.Pointer  (single-word lock-free)
        
        Lowest level (rare)
          unsafe.Pointer + barriers  (custom lock-free; needs -race AND review)
        ```
        
        ### `sync.Mutex` — embed, don't expose
        
        ```go
        type Cache struct {
            mu    sync.RWMutex
            items map[string]Entry
        }
        
        func (c *Cache) Get(key string) (Entry, bool) {
            c.mu.RLock()
            defer c.mu.RUnlock()
            e, ok := c.items[key]
            return e, ok
        }
        
        func (c *Cache) Set(key string, e Entry) {
            c.mu.Lock()
            defer c.mu.Unlock()
            c.items[key] = e
        }
        ```
        
        - `sync.Mutex` is **not** copyable. The `copylocks` vet check catches `var c2 = c1` where `c1` has a mutex.
        - Always `defer mu.Unlock()` immediately after `Lock()`. Forgetting is the #1 deadlock cause.
        - Never call user code (callbacks, listener notifications) while holding the lock. Drop the lock, snapshot the data, release, then call out.
        
        ### `sync.OnceValue` / `sync.OnceFunc` (Go 1.21+)
        
        Replacement for `sync.Once` for typed lazy init:
        
        ```go
        var loadConfig = sync.OnceValue(func() Config {
            var cfg Config
            if err := env.Parse(&cfg); err != nil { panic(err) }
            return cfg
        })
        
        func handler() { cfg := loadConfig(); ... }
        ```
        
        Type-safe, no `sync.Once` + global variable boilerplate.
        
        ### Atomics — the typed API only
        
        ```go
        // Go 1.19+ — use the typed atomic.* family
        var counter atomic.Int64
        counter.Add(1)
        n := counter.Load()
        
        // NEVER — the old function-style is type-unsafe
        atomic.AddInt64(&counter, 1)  // ← rejected
        ```
        
        ---
        
        ## Time — inject a clock for testability
        
        ```go
        type Clock interface {
            Now() time.Time
        }
        
        type realClock struct{}
        func (realClock) Now() time.Time { return time.Now() }
        
        type Service struct {
            clock Clock
        }
        
        // Tests
        import "github.com/benbjohnson/clock"
        fake := clock.NewMock()
        fake.Set(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC))
        svc := &Service{clock: fake}
        ```
        
        **Never call `time.Now()` in domain or service code.** The `time` package becomes a hidden dependency — tests become flaky, retries become time-of-day-dependent, expirations cannot be tested.
        
        `time.Sleep` in production code is a code smell. Use:
        - `time.NewTicker` for periodic work (and a `<-ctx.Done()` exit).
        - `time.NewTimer` for one-shot delays.
        - `time.After` ONLY in select statements, ONLY in non-hot paths.
        
        ---
        
        ## Race detector — non-negotiable in CI
        
        ```bash
        go test -race -shuffle=on -count=1 ./...
        ```
        
        - `-race` instruments memory accesses; catches data races at runtime. ~10x slow-down — acceptable for tests, not production.
        - `-shuffle=on` randomizes test order; catches hidden ordering dependencies.
        - `-count=1` defeats the test cache. Without it, "passing" might mean "ran 3 weeks ago".
        
        If a test ONLY fails under `-race`, the bug is real. Don't disable the test; fix the race.
        
        ---
        
        ## Common antipatterns
        
        | Bad | Why | Good |
        |---|---|---|
        | `go func() { ... }()` with no `ctx` plumbing | Leaks on shutdown | `errgroup.WithContext` or pass ctx |
        | Bare `time.Sleep(d)` in production | Untestable, blocks | `time.NewTimer` + select with `ctx.Done()` |
        | Channel of `interface{}` | Loses type | Typed channel; use sealed interface if variants needed |
        | `sync.Mutex` in a struct passed by value | Locked copies, undefined behavior | Embed in pointer-receiver type; copylocks catches it |
        | Locking around an entire request handler | Serializes the whole API | Lock only the smallest critical section |
        | `for { select { ... } }` without `<-ctx.Done()` | Cannot stop | Add ctx case in every long-lived select |
        | `sync.WaitGroup.Add(1)` inside the goroutine | Race: Wait can return before Add | Add **before** `go` |
        
        ---
        
        ## Sources
        
        - Go memory model: https://go.dev/ref/mem
        - `errgroup` package: https://pkg.go.dev/golang.org/x/sync/errgroup
        - `goleak`: https://github.com/uber-go/goleak
        - "Go concurrency patterns" (Pike): https://go.dev/blog/pipelines
        - Sync.OnceValue blog: https://go.dev/blog/synctest (1.24+ note: `testing/synctest` for time-controlled tests is now experimental)
        
      • data-modeling.md 12 KB
        # Data Modeling — Three Layers of Validation
        
        Go has no Pydantic. Go has no Zod. **You do not need them**, but only if you wire three layers correctly. This document is the canonical pattern.
        
        ## The three layers
        
        ```
        ┌─────────────────────────────────────────────────────────────┐
        │                    HTTP / RPC / CLI                          │
        │  Raw bytes, strings, untrusted input                         │
        │                                                              │
        │   Layer 1: validator/v10 (struct tags)   ◄── parse-once     │
        │           OR protovalidate (proto)                           │
        │                                                              │
        └──────────────────────────┬──────────────────────────────────┘
                                   │  raw req → domain.X
                                   ▼
        ┌─────────────────────────────────────────────────────────────┐
        │                    Domain (internal/domain)                  │
        │                                                              │
        │   Layer 2: Smart constructors + unexported fields            │
        │           NewEmail(s) → (Email, error)                       │
        │           NewUserID(s) → (UserID, error)                     │
        │                                                              │
        │           Once inside this layer, NO further validation.     │
        │           The types prove correctness.                       │
        └──────────────────────────┬──────────────────────────────────┘
                                   │  domain.X (proven valid)
                                   ▼
        ┌─────────────────────────────────────────────────────────────┐
        │                    Storage (internal/store)                  │
        │                                                              │
        │   Layer 3: sqlc-generated row structs ↔ domain types         │
        │           Hand-written mappers, NOT struct tags              │
        └─────────────────────────────────────────────────────────────┘
        ```
        
        Each layer parses once, into the next layer's types. **A function in the domain layer should never receive a raw string and validate it.** If it does, the boundary above failed.
        
        ---
        
        ## Layer 1: HTTP boundary — `go-playground/validator/v10`
        
        ```go
        package handlers
        
        import (
            "github.com/gin-gonic/gin"
            "github.com/go-playground/validator/v10"
        )
        
        // CreateUserRequest is the wire format. Tags drive validation.
        type CreateUserRequest struct {
            Email    string `json:"email"    binding:"required,email"`
            Username string `json:"username" binding:"required,alphanum,min=3,max=32"`
            Age      int    `json:"age"      binding:"required,gte=13,lte=130"`
            Country  string `json:"country"  binding:"required,iso3166_1_alpha2"`
        }
        
        func (h *Handler) CreateUser(c *gin.Context) {
            var req CreateUserRequest
            if err := c.ShouldBindJSON(&req); err != nil {
                // validator returns ValidationErrors with field-by-field detail
                var vErr validator.ValidationErrors
                if errors.As(err, &vErr) {
                    c.JSON(400, gin.H{"errors": fieldErrors(vErr)})
                    return
                }
                c.JSON(400, gin.H{"error": "invalid json"})
                return
            }
        
            // Cross into domain — single point of failure
            email, err := domain.NewEmail(req.Email)
            if err != nil {
                c.JSON(400, gin.H{"error": err.Error()})
                return
            }
            username, err := domain.NewUsername(req.Username)
            if err != nil {
                c.JSON(400, gin.H{"error": err.Error()})
                return
            }
        
            user, err := h.svc.Create(c.Request.Context(), email, username, req.Age)
            if err != nil {
                h.writeServiceError(c, err)
                return
            }
            c.JSON(201, user)
        }
        
        func fieldErrors(vErr validator.ValidationErrors) map[string]string {
            out := make(map[string]string, len(vErr))
            for _, fe := range vErr {
                out[fe.Field()] = fe.Tag() + "(" + fe.Param() + ")"
            }
            return out
        }
        ```
        
        **Tag reference — the tags you actually use**:
        
        | Tag | Meaning |
        |---|---|
        | `required` | Non-zero value |
        | `omitempty` (json) | Skip if zero |
        | `min=N` / `max=N` | Length (strings/slices) or value (numbers) |
        | `gte=N` / `lte=N` / `gt=N` / `lt=N` | Numeric comparison |
        | `email` | RFC 5322-ish email |
        | `url` | Valid URL |
        | `uuid` / `uuid4` / `uuid7` | UUID format |
        | `alphanum` / `alpha` / `numeric` | Character class |
        | `iso3166_1_alpha2` | Country code (US, KR, JP) |
        | `iso4217` | Currency code (USD, KRW) |
        | `oneof=a b c` | Enum of literal values |
        | `dive` | Apply rules to each element of slice/map |
        | `eqfield=Field` | Cross-field equality (e.g., password confirm) |
        
        ### Custom validators — register at startup
        
        ```go
        func init() {
            if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
                _ = v.RegisterValidation("strongpassword", validateStrongPassword)
            }
        }
        
        func validateStrongPassword(fl validator.FieldLevel) bool {
            s := fl.Field().String()
            return len(s) >= 12 && hasUpper(s) && hasDigit(s) && hasSymbol(s)
        }
        ```
        
        Use sparingly. Most domain rules belong in smart constructors, not validators.
        
        ---
        
        ## Layer 2: Domain — smart constructors
        
        Covered in detail in `type-patterns.md`. Recap:
        
        ```go
        package domain
        
        type Username struct{ raw string }
        
        func NewUsername(s string) (Username, error) {
            s = strings.TrimSpace(s)
            if len(s) < 3 || len(s) > 32 {
                return Username{}, ErrInvalidUsername
            }
            if !isAlphanum(s) {
                return Username{}, ErrInvalidUsername
            }
            return Username{raw: s}, nil
        }
        
        func (u Username) String() string { return u.raw }
        ```
        
        **Rule**: every domain type that has invariants has:
        
        1. An unexported field holding the raw form.
        2. A `New<Type>(raw) (<Type>, error)` constructor as the sole entry point.
        3. A `String() string` for printing.
        4. `MarshalJSON` / `UnmarshalJSON` if it crosses a JSON boundary outside HTTP handlers (e.g., logging payloads, queue messages).
        5. Optionally: `Scan` and `Value` for `database/sql` interop (rare with sqlc).
        
        ---
        
        ## Layer 3: Storage — sqlc rows ↔ domain types
        
        sqlc generates row structs from `.sql` files. **Do not put validation tags on them.** Map between sqlc rows and domain types explicitly:
        
        ```go
        // internal/store/user_store.go
        package store
        
        import "myservice/internal/domain"
        
        func (s *UserStore) Get(ctx context.Context, id domain.UserID) (domain.User, error) {
            row, err := s.q.GetUser(ctx, string(id))
            if err != nil {
                return domain.User{}, err
            }
            return rowToUser(row)
        }
        
        func rowToUser(r sqlc.UserRow) (domain.User, error) {
            email, err := domain.NewEmail(r.Email)
            if err != nil {
                // DB invariant broken — this is a programmer error, not a user error
                return domain.User{}, fmt.Errorf("db invariant: invalid email for user %s: %w", r.ID, err)
            }
            username, err := domain.NewUsername(r.Username)
            if err != nil {
                return domain.User{}, fmt.Errorf("db invariant: invalid username: %w", err)
            }
            return domain.User{
                ID:       domain.UserID(r.ID),
                Email:    email,
                Username: username,
                Created:  r.CreatedAt,
            }, nil
        }
        ```
        
        The mapping is verbose. **That is the point.** Each field is a deliberate choice; refactors flag every site.
        
        ---
        
        ## Discriminated unions (sum types) at the boundary
        
        When a wire payload has variants (e.g., `{"type": "user.created", ...}` vs `{"type": "user.deleted", ...}`):
        
        ```go
        // Wire DTO with raw discriminator
        type EventDTO struct {
            Type    string          `json:"type"    binding:"required,oneof=created deleted updated"`
            Payload json.RawMessage `json:"payload" binding:"required"`
        }
        
        // Parse into the sealed domain type
        func ParseEvent(dto EventDTO) (event.Event, error) {
            switch dto.Type {
            case "created":
                var c event.Created
                if err := json.Unmarshal(dto.Payload, &c); err != nil {
                    return nil, fmt.Errorf("decode created: %w", err)
                }
                return c, nil
            case "deleted":
                var d event.Deleted
                if err := json.Unmarshal(dto.Payload, &d); err != nil {
                    return nil, fmt.Errorf("decode deleted: %w", err)
                }
                return d, nil
            case "updated":
                var u event.Updated
                if err := json.Unmarshal(dto.Payload, &u); err != nil {
                    return nil, fmt.Errorf("decode updated: %w", err)
                }
                return u, nil
            default:
                return nil, fmt.Errorf("unknown event type %q", dto.Type)
            }
        }
        ```
        
        The `exhaustive` linter on the switch + the `oneof` validation tag together cover both "unknown type" and "unhandled variant".
        
        ---
        
        ## Enums — typed string consts, not iota
        
        ```go
        // GOOD — string-based, JSON-serializes correctly, debuggable
        type Status string
        
        const (
            StatusPending Status = "pending"
            StatusActive  Status = "active"
            StatusClosed  Status = "closed"
        )
        
        func (s Status) IsValid() bool {
            switch s {
            case StatusPending, StatusActive, StatusClosed:
                return true
            }
            return false
        }
        
        func (s *Status) UnmarshalJSON(data []byte) error {
            var raw string
            if err := json.Unmarshal(data, &raw); err != nil { return err }
            parsed := Status(raw)
            if !parsed.IsValid() { return fmt.Errorf("invalid status %q", raw) }
            *s = parsed
            return nil
        }
        ```
        
        **Never use `iota` enums for anything that crosses a wire boundary.** They serialize as integers, which (a) breaks debuggability, (b) makes reordering enum values a silent breaking change.
        
        Use the validator tag `binding:"oneof=pending active closed"` to enforce at the HTTP boundary.
        
        ---
        
        ## Nullable fields — `*T` vs sentinel
        
        Three choices, in order of preference:
        
        1. **Sentinel zero value**: `Age int` with `0` meaning "unknown". Works when zero is genuinely unreachable as a valid value.
        2. **`sql.Null<T>`** for DB columns: `sql.NullString`, `sql.NullInt64`, `sql.NullTime`. sqlc generates these for nullable columns.
        3. **`*T`**: only when you need to distinguish "not provided" from "set to zero" in a JSON payload (PATCH semantics).
        
        ```go
        // PATCH payload — `*string` discriminates absent vs empty
        type UpdateUserRequest struct {
            Email    *string `json:"email,omitempty"`
            Username *string `json:"username,omitempty"`
        }
        ```
        
        Avoid `*T` in domain types — it bloats every consumer with nil checks. Keep `*T` at the boundary, unwrap on the way in.
        
        ---
        
        ## Common AI-generated antipatterns this rejects
        
        | Bad | Why | Good |
        |---|---|---|
        | `func handle(req map[string]any)` | No types, no validation | Define a struct, parse with `validator` |
        | `if email != "" { ... }` inside domain | Validation in the wrong layer | Make `email Email`, no check needed |
        | `type Status int` with `iota` for wire field | Silent breaking on reorder | `type Status string` with const literals |
        | Struct tags `json:"email,string"` (the `,string` coercion) | Magic coercion hides bad input | Strict parsing, fail-fast |
        | `json.Unmarshal` then range-check after | Two-step "validate after parse" | Use `validator` tags or custom `UnmarshalJSON` |
        | Reusing handler DTO as the domain type | Couples wire format to business logic | Two distinct types, explicit mapping |
        
        ---
        
        ## Sources
        
        - go-playground/validator: https://github.com/go-playground/validator
        - gin binding internals: https://github.com/gin-gonic/gin/blob/master/binding/json.go
        - Parse, don't validate: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
        - sqlc with custom types: https://docs.sqlc.dev/en/latest/howto/overrides.html
        
      • error-handling.md 10.1 KB
        # Error Handling
        
        Typed errors, wrap chains, `errors.Is` / `errors.As`, no panic in libraries, resource cleanup. Go errors look simple and are full of footguns. This document is the canonical set of moves.
        
        ---
        
        ## The five rules
        
        1. **Every error is wrapped on the way up, with `%w`, with context.** Never `return err` from a non-trivial site.
        2. **Compare with `errors.Is`, not `==`.** Wrap chains break `==`. The `errorlint` linter forbids `==` on errors.
        3. **Cast with `errors.As`, not type assertion.** Same reason.
        4. **`panic` is reserved for programmer errors.** Library code never panics on user input or environment failures. Use `(T, error)`.
        5. **Resources released via `defer` immediately after acquisition.** No "I'll add it later".
        
        ---
        
        ## Sentinel errors — for invariant programmatic checks
        
        ```go
        package domain
        
        import "errors"
        
        var (
            ErrInvalidEmail   = errors.New("domain: invalid email")
            ErrInvalidPhone   = errors.New("domain: invalid phone")
            ErrInvalidAge     = errors.New("domain: invalid age")
        )
        
        func NewEmail(s string) (Email, error) {
            if !emailRe.MatchString(s) {
                return Email{}, fmt.Errorf("email %q: %w", s, ErrInvalidEmail)
            }
            return Email{raw: strings.ToLower(s)}, nil
        }
        ```
        
        Caller branches on identity:
        
        ```go
        email, err := domain.NewEmail(input)
        if errors.Is(err, domain.ErrInvalidEmail) {
            return c.JSON(400, gin.H{"error": "email format"})
        }
        ```
        
        `errors.Is` walks the wrap chain. `err == domain.ErrInvalidEmail` would have failed because `fmt.Errorf` wrapped it.
        
        ---
        
        ## Typed errors — when you need structured data
        
        When callers need fields off the error (the offending value, the failing field name, the upstream HTTP status):
        
        ```go
        type ValidationError struct {
            Field   string
            Value   string
            Rule    string
        }
        
        func (e *ValidationError) Error() string {
            return fmt.Sprintf("validation: %s=%q failed %s", e.Field, e.Value, e.Rule)
        }
        
        // Optional: identity sentinel for errors.Is comparisons
        var ErrValidation = errors.New("validation")
        
        func (e *ValidationError) Is(target error) bool {
            return target == ErrValidation
        }
        ```
        
        Caller:
        
        ```go
        err := svc.Save(ctx, user)
        
        var vErr *ValidationError
        if errors.As(err, &vErr) {
            // vErr.Field, vErr.Rule are available
            c.JSON(400, gin.H{"field": vErr.Field, "rule": vErr.Rule})
            return
        }
        ```
        
        **`errors.As` requires a non-nil pointer-to-pointer.** Almost always the type is `*ConcreteError`. Forgetting the leading `*` is the most common bug here.
        
        ---
        
        ## Wrapping — `%w` is mandatory
        
        ```go
        // BAD — drops context
        return err
        
        // BAD — drops the error chain (errors.Is/As stops working)
        return fmt.Errorf("failed to save user: %v", err)
        
        // GOOD — preserves chain via %w
        return fmt.Errorf("save user %s: %w", userID, err)
        ```
        
        The `errorlint` linter catches `%v` where `%w` was meant. **Wrap once per layer**, with the minimum useful context:
        
        ```
        api/handler:  "create user request: %w"
           service:   "validate inputs: %w"
              domain: "email %q: %w"
        ```
        
        Each frame adds one fact, not a duplicate. The top-level error message reads as a path: `create user request: validate inputs: email "foo": domain: invalid email`.
        
        ### `errors.Join` — multiple errors at once
        
        ```go
        // Validate all fields, collect all errors
        var errs []error
        if _, err := NewEmail(req.Email); err != nil {
            errs = append(errs, fmt.Errorf("email: %w", err))
        }
        if _, err := NewUsername(req.Username); err != nil {
            errs = append(errs, fmt.Errorf("username: %w", err))
        }
        if len(errs) > 0 {
            return errors.Join(errs...)
        }
        ```
        
        `errors.Is` still walks each joined error. Use when reporting batch validation, not for "wrap two unrelated errors".
        
        ---
        
        ## Panics — when allowed, when banned
        
        **Banned**:
        
        - Anywhere a `(T, error)` could be returned.
        - Inside HTTP handlers (gin's `Recovery` middleware catches them, but you've already lost the error context).
        - Inside any goroutine that survives request lifetime.
        
        **Allowed** (with documentation):
        
        - Map literal init at package level: `var statusNames = map[Status]string{...}` followed by a `func init()` that panics if a const has no name. Catches the bug at startup, not runtime.
        - The `must*` convention for genuinely unrecoverable startup:
          ```go
          func MustParseURL(s string) *url.URL {
              u, err := url.Parse(s)
              if err != nil { panic(err) }
              return u
          }
          // Use only with literals known at compile time:
          var defaultAPI = MustParseURL("https://api.example.com")
          ```
        - `default:` case of an exhaustive sealed-interface switch — see `type-patterns.md`.
        
        The `revive` linter rule `error-return` will flag suspect panic sites; treat them as bugs.
        
        ---
        
        ## `defer` for resources — the only safe pattern
        
        ```go
        func writeReport(path string) (err error) {
            f, err := os.Create(path)
            if err != nil {
                return fmt.Errorf("create %s: %w", path, err)
            }
            defer func() {
                if cerr := f.Close(); cerr != nil && err == nil {
                    err = fmt.Errorf("close %s: %w", path, cerr)
                }
            }()
        
            if _, err := f.Write(data); err != nil {
                return fmt.Errorf("write %s: %w", path, err)
            }
            return nil
        }
        ```
        
        Key points:
        
        - `defer f.Close()` immediately after `os.Create` — never further down.
        - Named return `(err error)` so the deferred close can mutate it on close failure.
        - `bodyclose` linter catches missed `defer resp.Body.Close()` for HTTP responses.
        - `sqlclosecheck` linter catches missed `defer rows.Close()` for SQL.
        
        ### `errors.Join` for multi-stage cleanup
        
        ```go
        func process(path string) (err error) {
            f, err := os.Open(path)
            if err != nil { return err }
            defer func() {
                err = errors.Join(err, f.Close())
            }()
            // ... use f ...
            return nil
        }
        ```
        
        When both the main operation AND `Close` can fail, `errors.Join` reports both without dropping either.
        
        ---
        
        ## HTTP error responses — a single funnel
        
        Build one helper, route all handler errors through it:
        
        ```go
        package httperr
        
        type APIError struct {
            Status  int    `json:"-"`
            Code    string `json:"code"`
            Message string `json:"message"`
        }
        
        func (e *APIError) Error() string { return e.Code + ": " + e.Message }
        
        var (
            NotFound       = &APIError{Status: 404, Code: "not_found", Message: "resource not found"}
            Unauthorized   = &APIError{Status: 401, Code: "unauthorized", Message: "unauthorized"}
            BadRequest     = &APIError{Status: 400, Code: "bad_request", Message: "bad request"}
            Internal       = &APIError{Status: 500, Code: "internal", Message: "internal error"}
        )
        
        // Wrap a domain error into an API error.
        func From(err error) *APIError {
            if err == nil { return nil }
        
            var apiErr *APIError
            if errors.As(err, &apiErr) { return apiErr }
        
            switch {
            case errors.Is(err, domain.ErrInvalidEmail),
                 errors.Is(err, domain.ErrInvalidUsername):
                return &APIError{Status: 400, Code: "validation", Message: err.Error()}
            case errors.Is(err, ErrNotFound):
                return NotFound
            case errors.Is(err, ErrUnauthorized):
                return Unauthorized
            default:
                // unknown — log full chain, return generic
                slog.Error("unmapped error", slog.Any("err", err))
                return Internal
            }
        }
        
        func Write(c *gin.Context, err error) {
            apiErr := From(err)
            c.JSON(apiErr.Status, apiErr)
        }
        ```
        
        Handlers become trivial:
        
        ```go
        func (h *Handler) Create(c *gin.Context) {
            user, err := h.svc.Create(c.Request.Context(), req)
            if err != nil {
                httperr.Write(c, err)
                return
            }
            c.JSON(201, user)
        }
        ```
        
        ---
        
        ## errgroup — error propagation across goroutines
        
        ```go
        import "golang.org/x/sync/errgroup"
        
        func fetchAll(ctx context.Context, urls []string) ([][]byte, error) {
            g, ctx := errgroup.WithContext(ctx)
            g.SetLimit(8)  // concurrency cap
        
            results := make([][]byte, len(urls))
            for i, u := range urls {
                g.Go(func() error {
                    body, err := fetch(ctx, u)
                    if err != nil {
                        return fmt.Errorf("fetch %s: %w", u, err)
                    }
                    results[i] = body
                    return nil
                })
            }
        
            if err := g.Wait(); err != nil {
                return nil, err
            }
            return results, nil
        }
        ```
        
        - `errgroup.WithContext` cancels remaining tasks on first error.
        - `SetLimit` bounds concurrency.
        - First non-nil error is returned; others are discarded — by design.
        
        See `concurrency.md` for the full pattern.
        
        ---
        
        ## Logging errors — structured, once
        
        ```go
        slog.ErrorContext(ctx, "save user failed",
            slog.String("user_id", string(id)),
            slog.Any("err", err),   // %w chain is fully rendered
        )
        ```
        
        **Log once, at the outermost frame.** Logging at every wrap site produces five log lines for one error.
        
        The `sloglint` linter enforces `slog.Any("err", err)` over `slog.String("err", err.Error())` — the former preserves the chain when handlers walk the value.
        
        ---
        
        ## Antipatterns
        
        | Bad | Why | Good |
        |---|---|---|
        | `_ = err` | Silent ignore | Handle, log, or wrap |
        | `if err != nil { return err }` chained 10 deep without wrap | No path info | Add one fact per layer: `fmt.Errorf("step: %w", err)` |
        | `panic(err)` in HTTP handlers | Loses error chain, hits gin Recovery | `httperr.Write(c, err)` |
        | `err.Error() == "some string"` | Brittle, breaks on wrap | Define a sentinel, use `errors.Is` |
        | `if err == sql.ErrNoRows` | Breaks under wrap | `errors.Is(err, sql.ErrNoRows)` |
        | `catch-all log.Fatal(err)` in library code | Crashes the caller's process | Return error, let main decide |
        | Returning a typed nil pointer wrapped in error interface | Classic "nil != nil" bug | Return explicit `nil` for the error |
        
        The last bug deserves its own example:
        
        ```go
        // BUG — returns a non-nil error interface containing a nil concrete type
        func bad() error {
            var e *MyError = nil
            return e  // interface wraps nil pointer; errors == nil is FALSE
        }
        
        // Caller
        if err := bad(); err != nil {
            // ← entered, but err.(*MyError) is nil — surprise panic
        }
        ```
        
        Fix: return explicit `nil`, not a typed nil. The `nilnil` linter catches this in `(T, error)` returns.
        
        ---
        
        ## Sources
        
        - Go blog "Working with Errors in Go 1.13+": https://go.dev/blog/go1.13-errors
        - `errors.Join` (Go 1.20+): https://pkg.go.dev/errors#Join
        - errorlint: https://github.com/polyfloyd/go-errorlint
        - nilaway nil-interface check: https://github.com/uber-go/nilaway
        
      • golangci-strict.md 10 KB
        # Strict `.golangci.yml` (golangci-lint v2)
        
        The single source of truth for "is this Go code acceptable". Drop this in unmodified. **Every linter below is enabled deliberately — read the rationale before disabling one.**
        
        `golangci-lint` v2 changed config schema (top-level `version: "2"`). All v1 configs are incompatible. The block below is v2.
        
        ## `.golangci.yml`
        
        ```yaml
        version: "2"
        
        run:
          timeout: 5m
          tests: true
          modules-download-mode: readonly
        
        linters:
          default: none
          enable:
            # ── Correctness — bug catchers ───────────────────────────────
            - govet              # stdlib vet, includes shadow, fieldalignment, nilness
            - staticcheck        # SA1*-SA9* — the de facto Go correctness linter
            - errcheck           # unhandled errors. ZERO tolerance.
            - errorlint          # %w wrapping, errors.As vs type-assertion, errors.Is vs ==
            - nilerr             # `return nil` after `err != nil` — classic bug
            - nilnil             # returning `(nil, nil)` from a (*T, error) function
            - bodyclose          # http.Response.Body not closed
            - rowserrcheck       # sql.Rows.Err() not checked
            - sqlclosecheck      # sql.Rows / sql.Stmt not closed
            - contextcheck       # functions taking context.Context don't get context.Background()
            - fatcontext         # context.WithValue() in a loop — leaks
            - copyloopvar        # Go 1.22 loop-var capture — should now use the new semantics
            - intrange           # use `for i := range N` (Go 1.22+) instead of `for i := 0; i < N; i++`
            - usetesting         # use t.TempDir/t.Setenv over os.* in tests
            - testifylint        # require vs assert correctness, ObjectsAreEqual misuse
        
            # ── Style / readability — kept narrow to avoid bikeshedding ─
            - gofumpt            # stricter gofmt
            - goimports          # import grouping + local prefix
            - whitespace         # leading/trailing whitespace
            - misspell           # typos in comments and strings
            - unconvert          # redundant type conversions
            - unparam            # unused function parameters
            - ineffassign        # ineffective assignments
            - dupword            # duplicate words ("the the")
        
            # ── Architecture — file size, complexity, dead code ─────────
            - gocognit           # cognitive complexity per function (threshold 25)
            - gocyclo            # cyclomatic complexity per function (threshold 15)
            - funlen             # function length (90 lines, 60 statements)
            - lll                # line length 120
            - nestif             # excessive nesting depth (>4)
            - dupl               # duplicate code blocks
            - revive             # extensible replacement for golint; selected rules below
            - unused             # unused vars/funcs/types
        
            # ── Exhaustiveness — Go's weakest spot ──────────────────────
            - exhaustive         # type switch and enum-like const groups completeness
        
            # ── Security ────────────────────────────────────────────────
            - gosec              # CWE-aware security scanner
        
            # ── Logging ─────────────────────────────────────────────────
            - sloglint           # slog attr style + no slog.Any(); enforce structured logs
        
            # ── Performance ─────────────────────────────────────────────
            - perfsprint         # fmt.Sprintf where strconv suffices
            - prealloc           # slice prealloc when length is known
            - makezero           # make([]T, n) with non-zero n then append (the classic bug)
        
        linters-settings:
          errcheck:
            check-type-assertions: true
            check-blank: true     # `_ = err` is a violation
        
          govet:
            enable-all: true
            settings:
              shadow:
                strict: true
              fieldalignment:
                # On by default; this catches struct layouts wasting memory.
                # Disable per-file with //nolint:fieldalignment ONLY for boundary types
                # whose JSON tag order matters for OpenAPI doc stability.
        
          errorlint:
            errorf: true          # %w mandatory for wrapping
            asserts: true         # errors.As over type-assertion on `error`
            comparison: true      # errors.Is over ==
        
          gocognit:
            min-complexity: 25
        
          gocyclo:
            min-complexity: 15
        
          funlen:
            lines: 90
            statements: 60
            ignore-comments: true
        
          lll:
            line-length: 120
            tab-width: 4
        
          nestif:
            min-complexity: 4
        
          exhaustive:
            default-signifies-exhaustive: false
            check:
              - switch
              - map
        
          gosec:
            excludes:
              - G104        # handled by errcheck/errorlint
              - G304        # file path provided as input — too noisy for CLIs
        
          sloglint:
            no-mixed-args: true       # all attr or all key-value, never mixed
            kv-only: false
            attr-only: true           # force slog.String(...) form
            no-global: all            # disallow slog.Info; force a logger receiver
            context: scope            # require *Context variants where ctx is in scope
            static-msg: true          # msg must be a string literal (not fmt.Sprintf)
            no-raw-keys: true         # use slog.String("key", ...) not raw "key", "val"
            key-naming-case: snake
        
          testifylint:
            enable-all: true
            disable:
              - require-error          # We DO use assert.Error in table-driven loops
        
          revive:
            severity: warning
            rules:
              - name: var-naming
              - name: package-comments
              - name: exported
              - name: error-return
              - name: error-naming
              - name: errorf            # use fmt.Errorf instead of errors.New(fmt.Sprintf)
              - name: if-return
              - name: indent-error-flow
              - name: range-val-in-closure
              - name: redefines-builtin-id
              - name: superfluous-else
              - name: unhandled-error
                arguments:
                  - "fmt.Print.*"
                  - "fmt.Fprint.*"
        
          perfsprint:
            integer-format: true
            error-format: true
            bool-format: true
            string-format: true
        
          goimports:
            local-prefixes:
              - github.com/your-org
        
        issues:
          max-issues-per-linter: 0
          max-same-issues: 0
          exclude-rules:
            # Tests get a longer leash on funlen + lll
            - path: _test\.go
              linters:
                - funlen
                - lll
                - dupl
                - gosec
            # Generated code never lints
            - path: \.pb\.go$
              linters: [all]
            - path: \.connect\.go$
              linters: [all]
            - path: ^.*sqlc/.*\.sql\.go$
              linters: [all]
        
        formatters:
          enable:
            - gofumpt
            - goimports
        ```
        
        ## Per-linter rationale (why each is on)
        
        | Linter | What it catches | Why no compromise |
        |---|---|---|
        | `errcheck` (incl. `check-blank: true`) | `_ = err`, ignored errors from `Close()`, `Write()`, `json.Marshal()` | Silent error ignore is the #1 Go bug class. Banning `_ = err` forces a decision at every site. |
        | `errorlint` | `err == io.EOF` instead of `errors.Is(err, io.EOF)`; missing `%w` in `fmt.Errorf` | Once you wrap in middleware, `==` checks silently break. `errors.Is/As` is the only safe form. |
        | `nilerr` / `nilnil` | `return nil` after `err != nil`; `return nil, nil` from `(*T, error)` | Classic AI-generated bugs. Linter catches them mechanically. |
        | `bodyclose` | `defer resp.Body.Close()` missed | Single most common Go memory leak. |
        | `contextcheck` | `ctx := context.Background()` inside a function that received `ctx` | Breaks cancellation propagation — the entire reason ctx exists. |
        | `exhaustive` | `switch x.(type)` missing a sealed-interface variant | **Go's weakest type-system spot.** This linter is the closest thing to compiler-enforced exhaustiveness. |
        | `sloglint` | `slog.Info(...)` (global), mixed `Any`/typed attrs | Without this, structured logging silently degrades into string concatenation. |
        | `govet/shadow` strict | `err := ... ; if ... { err := ...; ... }` shadowing | Hides the real error from outer scope — extremely common. |
        | `govet/fieldalignment` | Struct field order wasting memory | Cheap correctness signal. Disable per-file when JSON tag order matters for OpenAPI. |
        | `copyloopvar` + `intrange` | Pre-1.22 loop-var capture and old `for i := 0; i < N; i++` | The language modernized; the lint enforces it. |
        | `usetesting` | `os.Setenv` / `os.Mkdir` in tests instead of `t.Setenv` / `t.TempDir` | Avoids test isolation bugs. |
        | `gocognit` / `gocyclo` / `funlen` | Functions exceeding cognitive thresholds | Direct architectural signal — same purpose as the 250 LOC ceiling, at function granularity. |
        | `gosec` | CWE patterns — SQL injection, weak crypto, path traversal | Production must pass this. |
        | `testifylint` | `assert.Equal` where `require.Equal` was meant; `ObjectsAreEqual` misuse | Subtle test-correctness bugs. |
        | `perfsprint` | `fmt.Sprintf("%d", n)` instead of `strconv.Itoa(n)` | 5–10x faster in tight loops, lints catch the lazy form. |
        
        ## `nolint` policy
        
        `//nolint:linter1,linter2 // <reason>` is permitted with **two hard rules**:
        
        1. **One linter at a time per directive.** No `//nolint:all`. No omitting the linter name.
        2. **A reason after `//` is mandatory.** "Generated code", "false positive — protobuf imports", "OpenAPI field order" are acceptable. "Ignore" is not.
        
        The skill auto-rejects `//nolint` without a reason. So does `revive` if you enable its `nolint` rule.
        
        ## CI gate
        
        ```bash
        gofumpt -l . | (! grep .)                          # format
        golangci-lint run --timeout 5m ./...                # everything above
        go vet -vettool=$(which fieldalignment) ./...       # extra check (also in govet)
        nilaway ./...                                       # nil-deref static analysis
        go test -race -shuffle=on -count=1 ./...            # races + ordering
        ```
        
        Any non-zero exit = the change does not ship.
        
        ## Sources
        
        - golangci-lint v2 docs: https://golangci-lint.run/docs/configuration/
        - staticcheck rules: https://staticcheck.dev/docs/checks
        - sloglint: https://github.com/go-simpler/sloglint
        - exhaustive: https://github.com/nishanths/exhaustive
        - nilaway: https://github.com/uber-go/nilaway
        
      • grpc-connect.md 10.3 KB
        # RPC — Connect-Go (default) + grpc-go (fallback) + protovalidate
        
        `connectrpc/connect-go` is the default. It is wire-compatible with gRPC, also speaks Connect protocol + gRPC-Web from browsers, and uses ordinary `net/http` so middleware (logging, auth, tracing) composes the same way as REST. Reach for raw `grpc-go` only when you need a gRPC-specific feature Connect lacks.
        
        ---
        
        ## When Connect vs grpc-go
        
        | Need | Use |
        |---|---|
        | Standard unary + server-streaming + client-streaming | **Connect** |
        | Browser client without `grpc-web` proxy | **Connect** (native gRPC-Web support) |
        | HTTP/1.1 fallback for hostile networks | **Connect** (gRPC requires HTTP/2 end-to-end) |
        | Server reflection for `grpcurl` | grpc-go (Connect has reflection too, but ecosystem smaller) |
        | Bidirectional streaming with frame-level control | grpc-go |
        | Strict gRPC environment (Envoy with gRPC filters, Istio strict mode) | grpc-go |
        
        **Default**: Connect. The default has been correct since 2024.
        
        ---
        
        ## Toolchain — Buf, not protoc
        
        ```bash
        go install github.com/bufbuild/buf/cmd/buf@latest
        go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
        go install connectrpc.com/connect/cmd/protoc-gen-connect-go@latest
        go install github.com/bufbuild/protovalidate/cmd/protoc-gen-go-vtproto@latest
        ```
        
        Buf replaces `protoc` for everything: linting, breaking-change detection, codegen, formatting. The `protoc` toolchain is dead-letter walking — every modern proto project uses Buf.
        
        ---
        
        ## Project layout
        
        ```
        proto/
          buf.yaml
          buf.gen.yaml
          buf.lock
          myservice/v1/
            user.proto
            auth.proto
        
        gen/
          myservice/v1/
            user.pb.go               # protoc-gen-go output
            auth.pb.go
            myservicev1connect/      # protoc-gen-connect-go output
              user.connect.go
              auth.connect.go
        ```
        
        **`gen/` is committed.** Generated code is part of the API contract; CI proves it is up-to-date.
        
        ---
        
        ## `buf.yaml`
        
        ```yaml
        version: v2
        modules:
          - path: proto
        lint:
          use:
            - STANDARD
        breaking:
          use:
            - FILE
        ```
        
        ## `buf.gen.yaml`
        
        ```yaml
        version: v2
        managed:
          enabled: true
          override:
            - file_option: go_package_prefix
              value: github.com/your-org/myservice/gen
        plugins:
          - remote: buf.build/protocolbuffers/go
            out: gen
            opt:
              - paths=source_relative
          - remote: buf.build/connectrpc/go
            out: gen
            opt:
              - paths=source_relative
          - remote: buf.build/bufbuild/validate-go
            out: gen
            opt:
              - paths=source_relative
        ```
        
        The `buf.build/...` plugin URIs use Buf's hosted remote registry — no local plugin installation needed.
        
        ## Taskfile target
        
        ```yaml
        gen:proto:
          cmds:
            - buf lint
            - buf format -w
            - buf generate
          sources:
            - proto/**/*.proto
            - buf.yaml
            - buf.gen.yaml
        ```
        
        Run `task gen:proto` after editing any `.proto`. CI runs `buf generate` then `git diff --exit-code` to catch stale generated code.
        
        ---
        
        ## A `.proto` with validation
        
        ```proto
        syntax = "proto3";
        
        package myservice.v1;
        
        import "buf/validate/validate.proto";
        
        option go_package = "github.com/your-org/myservice/gen/myservice/v1;myservicev1";
        
        service UserService {
          rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
          rpc GetUser(GetUserRequest)       returns (GetUserResponse);
          rpc StreamEvents(StreamEventsRequest) returns (stream Event);
        }
        
        message CreateUserRequest {
          string email    = 1 [(buf.validate.field).string.email = true];
          string username = 2 [
            (buf.validate.field).string.min_len = 3,
            (buf.validate.field).string.max_len = 32,
            (buf.validate.field).string.pattern = "^[a-zA-Z0-9_]+$"
          ];
          int32 age       = 3 [
            (buf.validate.field).int32.gte = 13,
            (buf.validate.field).int32.lte = 130
          ];
        }
        
        message CreateUserResponse {
          User user = 1;
        }
        
        message User {
          string id       = 1;
          string email    = 2;
          string username = 3;
          google.protobuf.Timestamp created_at = 4;
        }
        ```
        
        `protovalidate` replaces the abandoned `protoc-gen-validate` — it is the official Buf-backed successor as of 2024, supported by Connect's interceptor pipeline.
        
        ---
        
        ## Server — Connect
        
        ```go
        package main
        
        import (
            "context"
            "log/slog"
            "net/http"
        
            "connectrpc.com/connect"
            "buf.build/go/protovalidate"
            validateinterceptor "connectrpc.com/validate"
            "golang.org/x/net/http2"
            "golang.org/x/net/http2/h2c"
        
            myservicev1 "github.com/your-org/myservice/gen/myservice/v1"
            "github.com/your-org/myservice/gen/myservice/v1/myservicev1connect"
        )
        
        type UserServer struct {
            svc *UserService
        }
        
        func (s *UserServer) CreateUser(
            ctx context.Context,
            req *connect.Request[myservicev1.CreateUserRequest],
        ) (*connect.Response[myservicev1.CreateUserResponse], error) {
        
            // protovalidate already ran via the interceptor below.
            // req.Msg is guaranteed to satisfy the .proto constraints.
        
            user, err := s.svc.Create(ctx, req.Msg.Email, req.Msg.Username, req.Msg.Age)
            if err != nil {
                return nil, mapError(err)
            }
            return connect.NewResponse(&myservicev1.CreateUserResponse{
                User: userToProto(user),
            }), nil
        }
        
        func main() {
            validator, _ := protovalidate.New()
            interceptors := connect.WithInterceptors(
                loggingInterceptor(),
                validateinterceptor.NewInterceptor(validator),
            )
        
            mux := http.NewServeMux()
            mux.Handle(myservicev1connect.NewUserServiceHandler(
                &UserServer{svc: newUserService()},
                interceptors,
            ))
        
            // h2c lets the server speak HTTP/2 cleartext for gRPC clients.
            srv := &http.Server{
                Addr:    ":8080",
                Handler: h2c.NewHandler(mux, &http2.Server{}),
            }
            slog.Info("rpc server listening", slog.String("addr", srv.Addr))
            if err := srv.ListenAndServe(); err != nil { slog.Error("rpc", slog.Any("err", err)) }
        }
        ```
        
        The handler is **just an `http.Handler`** — mount it in the same `http.ServeMux` as your REST routes if you want one binary serving both.
        
        ---
        
        ## Error mapping — Connect codes
        
        ```go
        func mapError(err error) error {
            if err == nil { return nil }
        
            switch {
            case errors.Is(err, domain.ErrInvalidEmail),
                 errors.Is(err, domain.ErrInvalidUsername):
                return connect.NewError(connect.CodeInvalidArgument, err)
            case errors.Is(err, ErrNotFound):
                return connect.NewError(connect.CodeNotFound, err)
            case errors.Is(err, ErrUnauthorized):
                return connect.NewError(connect.CodeUnauthenticated, err)
            case errors.Is(err, ErrConflict):
                return connect.NewError(connect.CodeAlreadyExists, err)
            default:
                slog.Error("unmapped rpc error", slog.Any("err", err))
                return connect.NewError(connect.CodeInternal, errors.New("internal"))
            }
        }
        ```
        
        Connect codes map 1:1 to gRPC codes. Clients see canonical error semantics.
        
        ---
        
        ## Logging interceptor
        
        ```go
        func loggingInterceptor() connect.UnaryInterceptorFunc {
            return func(next connect.UnaryFunc) connect.UnaryFunc {
                return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
                    start := time.Now()
                    res, err := next(ctx, req)
                    attrs := []slog.Attr{
                        slog.String("proc",    req.Spec().Procedure),
                        slog.Duration("elapsed", time.Since(start)),
                    }
                    if err != nil {
                        attrs = append(attrs, slog.Any("err", err))
                        slog.LogAttrs(ctx, slog.LevelWarn, "rpc failed", attrs...)
                    } else {
                        slog.LogAttrs(ctx, slog.LevelInfo, "rpc ok", attrs...)
                    }
                    return res, err
                }
            }
        }
        ```
        
        For streaming, implement the full `connect.Interceptor` (`WrapStreamingClient`, `WrapStreamingHandler`). Pattern is identical.
        
        ---
        
        ## Server streaming
        
        ```go
        func (s *UserServer) StreamEvents(
            ctx context.Context,
            req *connect.Request[myservicev1.StreamEventsRequest],
            stream *connect.ServerStream[myservicev1.Event],
        ) error {
            events, errs := s.svc.Subscribe(ctx, req.Msg.UserId)
            for {
                select {
                case <-ctx.Done():
                    return ctx.Err()
                case e, ok := <-events:
                    if !ok { return nil }
                    if err := stream.Send(eventToProto(e)); err != nil {
                        return err
                    }
                case err := <-errs:
                    return connect.NewError(connect.CodeInternal, err)
                }
            }
        }
        ```
        
        Same shape as SSE in `backend-stack.md`. Connect handles HTTP/2 framing.
        
        ---
        
        ## Client
        
        ```go
        client := myservicev1connect.NewUserServiceClient(
            http.DefaultClient,
            "https://api.example.com",
            // Use connect.WithGRPC() if the server is grpc-go and you want strict gRPC framing.
            // Default is Connect protocol — works with Connect or gRPC servers transparently.
        )
        
        res, err := client.CreateUser(ctx, connect.NewRequest(&myservicev1.CreateUserRequest{
            Email:    "a@b.com",
            Username: "alice",
            Age:      30,
        }))
        if err != nil {
            var connectErr *connect.Error
            if errors.As(err, &connectErr) {
                slog.Error("rpc failed",
                    slog.String("code", connectErr.Code().String()),
                    slog.String("msg",  connectErr.Message()))
            }
            return err
        }
        slog.Info("created", slog.String("id", res.Msg.User.Id))
        ```
        
        ---
        
        ## When you genuinely need raw grpc-go
        
        ```go
        import "google.golang.org/grpc"
        
        lis, _ := net.Listen("tcp", ":8080")
        srv := grpc.NewServer(
            grpc.UnaryInterceptor(loggingUnaryInterceptor),
        )
        myservicev1.RegisterUserServiceServer(srv, &userServer{})
        _ = srv.Serve(lis)
        ```
        
        The codegen is from `protoc-gen-go-grpc` (different binary from `protoc-gen-connect-go`). You can codegen **both** in the same `buf.gen.yaml` and switch by importing the right package. Most teams pick one.
        
        ---
        
        ## When NOT to use RPC at all
        
        If your callers are all browsers, mobile apps, third-party developers, or the long tail of "things humans curl": **stay with REST + OpenAPI**. RPC's overhead is justified for service-to-service inside a single org. Outside that boundary, JSON over HTTP wins on debuggability.
        
        `oapi-codegen/oapi-codegen/v2` generates Go server stubs and clients from OpenAPI 3 — the REST equivalent of what Connect does for proto. Same parse-don't-validate boundary discipline, different wire format.
        
        ---
        
        ## Sources
        
        - Connect docs: https://connectrpc.com/docs/go/getting-started
        - Buf: https://buf.build/docs
        - protovalidate: https://github.com/bufbuild/protovalidate
        - "Why we replaced protoc with buf" (Buf blog): https://buf.build/blog
        - gRPC vs Connect comparison: https://connectrpc.com/docs/introduction
        
      • libraries.md 13.7 KB
        # Library Defaults — Full Decision Tree (Go 2026)
        
        The opinionated, in-production stack for 2026 Go. Every entry has a one-line rationale and a canonical snippet so the agent does not relearn each library's idioms.
        
        The biggest difference from Python/Rust/TypeScript: **Go has fewer "best" choices and more "boring" choices.** The standard library is the default; reach outside it only when the rationale below applies.
        
        ---
        
        ## HTTP framework — `gin` (default) or `chi` (minimalist) or `net/http` (no deps)
        
        The reality of 2026 Go: **`gin` runs ~48% of new Go API projects** (Go Developer Survey 2024 + crawls of new repos), with `gorilla/mux` (~17%, in maintenance), `echo` (~16%), and `fiber` (~11%) the remaining quarter. The skill picks gin not because it is technically superior — it is not — but because:
        
        1. The ecosystem (middleware, examples, SO answers) is largest.
        2. The CLIProxyAPI codebase, which this skill's `backend-stack.md` is distilled from, uses gin in production for OpenAI/Gemini/Claude proxying including SSE streaming and WebSocket upgrades. That is real reference code, not a toy.
        3. Gin's `Context` API is the closest thing Go has to a framework-blessed "request-scoped object", which makes middleware composition straightforward.
        
        ```go
        import "github.com/gin-gonic/gin"
        
        func main() {
            r := gin.New()
            r.Use(gin.Recovery(), middleware.RequestLogger(), middleware.RequestID())
            r.GET("/healthz", func(c *gin.Context) { c.JSON(200, gin.H{"ok": true}) })
            _ = r.Run(":8080")
        }
        ```
        
        **Pick `chi` instead** when:
        - You want `net/http`-compatible handlers (you do, eventually — chi is closer to stdlib).
        - The service is small and you do not need gin's binding helpers.
        
        **Pick `net/http` (stdlib) directly** when:
        - The service has fewer than 10 routes and zero auth complexity. Go 1.22's enhanced `ServeMux` (method+path patterns) eliminated 80% of the historical reason to use a framework.
        
        **Never use** `gorilla/mux` (effectively in maintenance), `fiber` (uses `fasthttp` which is **not stdlib-compatible**, so middleware ecosystem is split), or `echo` (smaller eco than gin, no real advantage today).
        
        See `backend-stack.md` for the gin canonical layout, middleware ordering, SSE, graceful shutdown, structured logging integration.
        
        ---
        
        ## RPC — `connectrpc/connect-go`
        
        The default RPC layer. **Use Connect, not raw grpc-go**, unless you have a measured reason.
        
        - Connect is wire-compatible with gRPC AND speaks HTTP/1.1 + HTTP/2 + Connect protocol. One server, three clients (gRPC, gRPC-Web, Connect-Web from browsers).
        - No `grpcurl` needed for debugging — `curl -H "Content-Type: application/json" -d ...` works.
        - Streaming, interceptors, deadlines, errors are first-class.
        - Buf toolchain (`buf generate`, `buf lint`, `buf breaking`) for codegen is dramatically nicer than `protoc`.
        
        ```go
        // Server
        mux := http.NewServeMux()
        mux.Handle(elizav1connect.NewElizaServiceHandler(&elizaServer{}))
        _ = http.ListenAndServe(":8080", h2c.NewHandler(mux, &http2.Server{}))
        
        // Client
        client := elizav1connect.NewElizaServiceClient(
            http.DefaultClient,
            "http://localhost:8080",
        )
        res, err := client.Say(ctx, connect.NewRequest(&elizav1.SayRequest{Sentence: "hi"}))
        ```
        
        **Use raw `grpc-go`** only when:
        - You need server-streaming-from-multiple-services with a single gRPC mux.
        - You are integrating with a strict gRPC-only environment (Envoy proxy with gRPC reflection, Istio strict-gRPC).
        
        See `grpc-connect.md`.
        
        ---
        
        ## Database — `pgx/v5` + `sqlc` + `goose`
        
        ```bash
        go get github.com/jackc/pgx/v5
        go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
        go install github.com/pressly/goose/v3/cmd/goose@latest
        ```
        
        - **`pgx/v5`** is faster, more type-safe, and has better PostgreSQL feature coverage than `database/sql + lib/pq`. Use the `pgxpool` package for connection pooling. Avoid `database/sql` driver mode — it loses pgx's batch, COPY, listen/notify.
        - **`sqlc`** generates type-safe Go from `.sql` files. Hand-written SQL with hand-written struct mapping is the #1 source of subtle DB bugs. sqlc eliminates the class.
        - **`goose`** for migrations — small, command-line first, no global state.
        
        **Never use** `gorm` (active record, slow, brings runtime reflection into hot paths, encourages N+1 queries). **Never use** `ent` (heavy, opinionated graph layer) unless you specifically want a graph-shaped data model.
        
        See `sqlc-pgx.md`.
        
        ---
        
        ## Validation — three layers, three tools
        
        Go has no Pydantic / Zod equivalent and **does not need one** — but only because you wire three layers properly:
        
        | Layer | Tool | Pattern |
        |---|---|---|
        | HTTP boundary (gin/chi/net/http) | `go-playground/validator/v10` via struct tags | `binding:"required,email,min=3"` |
        | RPC boundary (protobuf) | `bufbuild/protovalidate-go` | `(buf.validate.field).string.min_len = 3` in `.proto` |
        | Domain core | **Smart constructor + unexported fields** | `NewEmail(s) (Email, error)` returns a type whose fields cannot be set from outside |
        
        ```go
        // HTTP boundary
        type CreateUserReq struct {
            Email    string `json:"email" binding:"required,email"`
            Username string `json:"username" binding:"required,alphanum,min=3,max=32"`
        }
        
        // Domain — once a value is of type Email it is provably valid
        type Email struct{ raw string }
        func NewEmail(s string) (Email, error) {
            if !emailRegex.MatchString(s) { return Email{}, ErrInvalidEmail }
            return Email{raw: strings.ToLower(s)}, nil
        }
        func (e Email) String() string { return e.raw }
        ```
        
        The boundary parses raw input into the domain type **once**. Inside the domain, no further validation is permitted — the types prove it. This is parse-don't-validate adapted to Go.
        
        See `data-modeling.md` for the full pattern.
        
        ---
        
        ## Logging — `log/slog` (stdlib)
        
        ```go
        import "log/slog"
        
        logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
            Level:     slog.LevelInfo,
            AddSource: true,
        }))
        slog.SetDefault(logger)
        
        slog.InfoContext(ctx, "request handled",
            slog.String("path", r.URL.Path),
            slog.Int("status", 200),
            slog.Duration("elapsed", elapsed),
        )
        ```
        
        - **stdlib since 1.21**, stable since 1.23. Performance is on par with zerolog for structured output, and faster than logrus by a wide margin.
        - The `slog.Handler` interface is implemented by all major exporters (OpenTelemetry, Datadog, Honeycomb).
        - The skill bans `logrus`, `zap`, `zerolog` for new code. They are not bad — they are simply superseded. Existing projects on those keep them; new files use slog.
        
        Use the `sloglint` linter from `golangci-strict.md` to enforce attr style (`slog.String(...)` instead of `slog.Any(...)`).
        
        ---
        
        ## CLI — `cobra` + `pflag` + slog
        
        ```bash
        go install github.com/spf13/cobra-cli@latest
        cobra-cli init mytool
        cobra-cli add server
        ```
        
        `cobra` is the de facto Go CLI framework — Kubernetes, Docker CLI, Helm, GitHub CLI all use it. The companion `viper` for config-file-+-env-+-flag merging is **optional**: prefer `caarlos0/env/v11` for env-only configs (12-factor apps), reach for viper only when you genuinely need file-based config.
        
        See `cobra-stack.md`.
        
        ---
        
        ## TUI — `bubbletea v2` + `bubbles v2` + `lipgloss v2`
        
        Use **v2 RC** (`charm.land/bubbletea/v2`), not v1. The v2 model adds:
        
        - `tea.View{Cursor: *tea.Cursor, ...}` for real-cursor positioning.
        - `SetVirtualCursor(false)` on textareas — lets the terminal own the cursor, which is **required** for CJK IME (Korean Hangul composition, Japanese kana→kanji conversion, Chinese pinyin lookup).
        - Granular mouse events (`MouseClickMsg`, `MouseMotionMsg`, `MouseReleaseMsg`) instead of v1's coarse `MouseMsg`.
        
        This is not a preference. v1 has no way to position the IME candidate window correctly — Korean input shows up two cells to the left of where you typed, every time. **If your TUI accepts text input AND your users include CJK speakers, v1 is broken.**
        
        See `bubbletea-v2.md` for the full IME-correct skeleton.
        
        ---
        
        ## HTTP client — stdlib + `hashicorp/go-retryablehttp`
        
        Default: `net/http.Client` with a tuned `http.Transport`. The stdlib client is **already excellent** in 2026 — HTTP/2 by default, connection pooling, sane timeouts when configured.
        
        ```go
        client := &http.Client{
            Timeout: 30 * time.Second,
            Transport: &http.Transport{
                MaxIdleConns:        200,
                MaxIdleConnsPerHost: 40,
                IdleConnTimeout:     90 * time.Second,
                DisableCompression:  false,
                ForceAttemptHTTP2:   true,
            },
        }
        ```
        
        For retry/backoff, add `github.com/hashicorp/go-retryablehttp` — small, single-purpose, integrates as a wrapper.
        
        **Never use** `resty` (too much magic, hides headers, encourages wrong defaults). `req` is fine but adds dependency surface for marginal benefit over the stdlib + retry wrapper.
        
        ---
        
        ## JSON — stdlib (default), `goccy/go-json` (perf), `bytedance/sonic` (extreme perf)
        
        Stdlib `encoding/json` improved dramatically in Go 1.21+. **Use it.**
        
        Reach for `goccy/go-json` (~3x faster) only when you have measured a hot-path bottleneck:
        
        ```go
        import json "github.com/goccy/go-json"
        // drop-in replacement — same API
        ```
        
        Reach for `bytedance/sonic` (~5x faster, requires amd64/arm64) for production proxies with thousands of RPS of JSON traversal. CLIProxyAPI uses `tidwall/gjson` + `tidwall/sjson` for **partial-tree mutation without full unmarshal** — a different optimization, useful when you transform large payloads. See `backend-stack.md`.
        
        ---
        
        ## Concurrency primitives — stdlib only
        
        | Need | Use |
        |---|---|
        | Goroutine group with error propagation | `golang.org/x/sync/errgroup` |
        | Semaphore | `golang.org/x/sync/semaphore` |
        | Single-flight dedup | `golang.org/x/sync/singleflight` |
        | Lazy init | **`sync.OnceValue` / `sync.OnceFunc`** (Go 1.21+, replaces `sync.Once` for typed values) |
        | Atomic counter | `atomic.Int64` (Go 1.19+, typed atomics — don't use the old func-style) |
        | Channel-based fanout | `chan T` with `errgroup` for shutdown |
        
        The `x/sync` packages are stdlib-quality but live outside `std`. See `concurrency.md` for the discipline.
        
        ---
        
        ## Time — stdlib + `benbjohnson/clock` for tests
        
        ```go
        type Clock interface { Now() time.Time }
        // Production
        var realClock Clock = clockImpl{}
        // Test
        fake := clock.NewMock()
        fake.Set(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC))
        ```
        
        **Never call `time.Now()` directly inside domain code.** Inject a `Clock`. Tests become deterministic, no `time.Sleep` flakiness.
        
        ---
        
        ## IDs — `google/uuid` (UUID v4/v7) or `xid` (sortable short ID)
        
        ```go
        import "github.com/google/uuid"
        id := uuid.Must(uuid.NewV7())  // sortable, time-ordered, 128-bit
        ```
        
        UUID v7 is the modern default — sortable like v6, random like v4. Use v4 only when leaking creation time is a privacy concern.
        
        For short, URL-safe IDs (~12 bytes, sortable) use `rs/xid` — Kubernetes-style.
        
        ---
        
        ## Crypto — stdlib + `alecthomas/argon2id` for passwords
        
        Stdlib `crypto/*` for everything. For password hashing, **argon2id is the 2026 standard** — bcrypt is acceptable but argon2 is OWASP's recommendation since 2023.
        
        ```go
        import "github.com/alecthomas/argon2id"
        hash, err := argon2id.CreateHash("password", argon2id.DefaultParams)
        ```
        
        ---
        
        ## Data — `apache/arrow-go/v18` + `marcboeker/go-duckdb` + `gonum`
        
        Same philosophy as Python's "never pandas":
        
        | Need | Use |
        |---|---|
        | Tabular over CSV/Parquet/JSON | DuckDB-Go bindings — zero-copy Arrow integration |
        | In-memory frame | Arrow + custom code (Go has no pandas-equivalent and that's fine) |
        | Numerical | `gonum.org/v1/gonum` |
        | Stats | `gonum/stat` |
        
        Go's data-science story is intentionally thin. For heavy data work, write the pipeline in Polars/DuckDB (see `python/data-processing.md`), expose the result via Parquet or Arrow, consume from Go.
        
        ---
        
        ## Testing — stdlib + selective additions
        
        | Need | Use |
        |---|---|
        | Assertions | `stretchr/testify/require` (fail-fast) — `assert` only in table-driven loops |
        | Snapshots / golden | `hexops/autogold/v2` (auto-updates with `-update`) |
        | Property-based | `pgregory.net/rapid` (modern) or stdlib `testing/quick` |
        | Mocks | `go.uber.org/mock` (gomock successor) |
        | HTTP mocks | `h2non/gock` for outbound, stdlib `httptest` for inbound |
        | Integration containers | `testcontainers/testcontainers-go` |
        | Goroutine leak | `go.uber.org/goleak` |
        | Benchmarks | stdlib `testing.B` + `perf.dev/benchstat` |
        
        See `testing.md` for canonical patterns.
        
        ---
        
        ## Config — `caarlos0/env/v11`
        
        ```go
        type Config struct {
            Port        int           `env:"PORT" envDefault:"8080"`
            DatabaseURL string        `env:"DATABASE_URL,required"`
            Timeout     time.Duration `env:"TIMEOUT" envDefault:"30s"`
        }
        
        var cfg Config
        if err := env.Parse(&cfg); err != nil { log.Fatal(err) }
        ```
        
        Pure 12-factor. Defaults via struct tag, required marker, parsing for `time.Duration`, slices, maps. **Use viper only if you also need file-based config** — most services do not.
        
        ---
        
        ## Choosing an unfamiliar dependency — the checklist
        
        Before `go get`-ing anything new:
        
        1. Is it maintained? Latest tag within 12 months? Owner active?
        2. Does it expose stdlib-compatible types (`io.Reader`, `context.Context`, `http.Handler`)? If it invents its own `Connection` or `Request` type, that's a yellow flag.
        3. Does it use `init()` for side effects? **REJECT.** `init()` ruins testability.
        4. Does it call `log.Fatal` / `panic` outside of true programmer-error paths? **REJECT.**
        5. Does it have a `context.Context` first-arg convention? If not, **REJECT** — cancellation is non-negotiable.
        6. Does adding it overlap with something already in your `go.mod`? Pick one.
        
        ---
        
        ## Sources
        
        - 2024 Go Developer Survey: https://go.dev/blog/survey2024-h1-results
        - Connect-Go docs: https://connectrpc.com/docs/go/getting-started
        - sqlc: https://docs.sqlc.dev
        - bubbletea v2 IME: https://github.com/code-yeongyu/bubbletea-wm (reference for `SetVirtualCursor(false)` pattern)
        - CLIProxyAPI (gin + SSE + WebSocket in production): https://github.com/router-for-me/CLIProxyAPI
        - slog blog: https://go.dev/blog/slog
        
      • one-liners.md 5.5 KB
        # One-Liners and Disposable Scripts
        
        Production hygiene with throwaway ergonomics. Go scripts get the same strict lints, the same type discipline, the same 250 LOC ceiling. The difference: they live as single `.go` files invoked via `go run`, not as full modules.
        
        Python has PEP 723 + `uv run`. Rust has `rust-script`. **Go has `go run` directly** — no extra tooling needed.
        
        ---
        
        ## Pattern 1: Single-file `go run`
        
        A `.go` file with a `main` package, run directly:
        
        ```go
        //go:build ignore
        // fetch.go — fetch a URL and print body length.
        //
        // Usage:
        //   go run fetch.go <url>
        
        package main
        
        import (
            "fmt"
            "io"
            "log"
            "net/http"
            "os"
        )
        
        func main() {
            if len(os.Args) < 2 {
                log.Fatal("usage: go run fetch.go <url>")
            }
            resp, err := http.Get(os.Args[1])
            if err != nil { log.Fatal(err) }
            defer resp.Body.Close()
        
            body, err := io.ReadAll(resp.Body)
            if err != nil { log.Fatal(err) }
        
            fmt.Printf("%d bytes\n", len(body))
        }
        ```
        
        Run: `go run fetch.go https://example.com`.
        
        The `//go:build ignore` directive keeps this file out of `go build ./...` — it is a script, not part of the module. Without that line, every `.go` file in the package gets compiled into your binary.
        
        ---
        
        ## Pattern 2: Throwaway directory under `scripts/`
        
        ```
        myproject/
        ├── go.mod
        ├── internal/...
        └── scripts/
            ├── seed/
            │   └── main.go        # `go run ./scripts/seed`
            ├── migrate/
            │   └── main.go
            └── one-time-fix/
                └── main.go
        ```
        
        Each `scripts/<name>/main.go` is its own `main` package. Invoke as `go run ./scripts/seed/`. Dependencies are shared with the parent module — no separate `go.mod`.
        
        This is the right pattern when:
        
        - You need module deps (sqlc, pgx, your own internal packages).
        - You want IDE support, type-checking, test coverage.
        - The script lives alongside the project, runs in CI.
        
        ---
        
        ## Pattern 3: Inline `go run` from shell
        
        ```bash
        go run -mod=mod <(cat <<'EOF'
        package main
        import "fmt"
        func main() { fmt.Println("hello") }
        EOF
        )
        ```
        
        Rare, but useful for one-shot terminal experiments. The `<(...)` is process substitution; `go run -mod=mod` reads from stdin.
        
        ---
        
        ## Hard rules for scripts
        
        Even a 30-line script follows the philosophy:
        
        1. **Typed flags via `flag` or `pflag`**, not `os.Args` string parsing past 2 args.
           ```go
           var (
               url   = flag.String("url", "", "URL to fetch")
               limit = flag.Int("limit", 100, "max bytes")
           )
           flag.Parse()
           if *url == "" { log.Fatal("--url required") }
           ```
        
        2. **`context.Context` propagation** wherever I/O happens.
           ```go
           ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
           defer cancel()
           req, _ := http.NewRequestWithContext(ctx, "GET", *url, nil)
           ```
        
        3. **`log.Fatal` is fine in `main()`** of a script (programmer error / fatal path), but **never inside any function the script imports.** Library code returns errors.
        
        4. **Errors get wrapped.** Same rule as production code:
           ```go
           if err != nil { return fmt.Errorf("fetch %s: %w", *url, err) }
           ```
        
        5. **Resources released via `defer`.** No "I'll fix it later".
        
        6. **slog for output if it must be parseable.** `fmt.Println` for one-shot terminal output is fine.
        
        7. **No more than 250 pure LOC.** If it grows, it stops being a script and becomes a subcommand of your CLI tool.
        
        ---
        
        ## Pattern 4: Standalone tool with deps — temporary module
        
        Some scripts need deps the parent module does not have. Two options:
        
        ### Option A — script in its own tiny module
        
        ```bash
        mkdir /tmp/migrate-tool && cd $_
        go mod init scratch.local/migrate-tool
        go get github.com/pressly/goose/v3
        cat > main.go <<'EOF'
        package main
        import ... // use goose
        func main() { ... }
        EOF
        go run .
        ```
        
        Run, then delete `/tmp/migrate-tool`. Throwaway.
        
        ### Option B — `gorun` (community tool)
        
        ```bash
        go install github.com/erning/gorun@latest
        
        cat > script.go <<'EOF'
        //usr/bin/env gorun "$0" "$@"; exit
        // /// go.mod
        // module scratch
        // go 1.23
        // require github.com/spf13/cobra v1.8.0
        // ///
        
        package main
        ...
        EOF
        chmod +x script.go
        ./script.go
        ```
        
        `gorun` parses the inline `go.mod` block, materializes a temp module, runs the script. Niche tool — only if you want the executable-script experience.
        
        ---
        
        ## When a script becomes a CLI
        
        If your script needs:
        
        - More than one subcommand
        - Long-term storage of state
        - Help text more than a paragraph
        - Repeated invocations from CI
        
        ... promote it to a real CLI tool via `cobra` — see `cobra-stack.md`. The boundary is fuzzy; trust your judgment, but **a 500-line "script" is not a script.**
        
        ---
        
        ## Antipatterns
        
        | Bad | Why | Good |
        |---|---|---|
        | `os.Args[1]` indexing without length check | Panics on missing arg | `flag.Parse()` with explicit checks |
        | `log.Fatal` inside a function the script imports | Crashes caller's process | Return error |
        | `panic(err)` for expected failures | Same as above | `log.Fatal` in `main`, error return elsewhere |
        | Skipping `defer resp.Body.Close()` because "it's a script" | Leaks fd | Always close |
        | One 800-LOC `main.go` "to keep it simple" | Now harder to read than a real CLI | Promote to `cmd/<name>/` with subcommands |
        | `// TODO: handle error` | Production-grade hygiene means production-grade hygiene | Handle now or document why ignored |
        
        ---
        
        ## Sources
        
        - `go run` docs: https://pkg.go.dev/cmd/go#hdr-Compile_and_run_Go_program
        - `//go:build` constraints: https://pkg.go.dev/cmd/go#hdr-Build_constraints
        - `signal.NotifyContext`: https://pkg.go.dev/os/signal#NotifyContext
        - gorun: https://github.com/erning/gorun
        
      • README.md 7.4 KB
        
        # Go Programmer
        
        Production Go in 2026. **Boring on purpose, strict by tooling, illegal states unrepresentable by convention.**
        
        ## Philosophy
        
        Go gives you fewer type-system tools than Python, TypeScript, or Rust:
        
        - No sum types — only `interface{}` with type-switch.
        - No exhaustiveness check from the compiler — only the `exhaustive` linter.
        - No `Option<T>` — only `nil` and the eternal trap of "is this nil interface or nil concrete?".
        - No `Result<T, E>` — only `(T, error)`, no compiler enforcement of unwrapping.
        - No newtype that prevents primitive coercion — `type UserID string` is still implicitly convertible from a literal when used carelessly.
        
        **This is the whole point of the skill.** Where the language is weak, the linter bundle becomes the type checker, and code patterns become the type system. Treat `golangci-lint v2` with the configuration in `golangci-strict.md` as if it were `tsc --strict` or `basedpyright`. Treat `nilaway` and `go test -race` as if they were Miri.
        
        The skill enforces five non-negotiables:
        
        1. **Parse-don't-validate at every boundary.** HTTP/RPC/CLI/config gets parsed into a domain struct constructed only via `New*(...)` smart constructors. Once inside the domain, no further validation. See `data-modeling.md`.
        2. **`(T, error)` everywhere.** No panics in library code. No bare `_ = err`. Errors are wrapped with `%w` and asserted with `errors.Is` / `errors.As`. Typed error structs for anything a caller can branch on. See `error-handling.md`.
        3. **Sealed interfaces for variants.** Sum types via a sealed unexported method, dispatched through a `type switch`, with the `exhaustive` linter checking completeness. See `type-patterns.md`.
        4. **`context.Context` is the first parameter.** Always. No `context.Background()` inside leaf functions. No goroutine without context-driven shutdown. No `time.Now()` in domain code — inject a clock. See `concurrency.md`.
        5. **Generated, not hand-written, for external contracts.** `sqlc` for DB, `oapi-codegen` for OpenAPI servers and clients, `protoc-gen-go` + `protoc-gen-connect-go` for RPC. Hand-rolled marshalling is a regression. See `sqlc-pgx.md`, `grpc-connect.md`.
        
        ## Hard rules — tooling
        
        | Category | Use | Never |
        |---|---|---|
        | Go version | **1.23+** (range-over-func, iter package, slog stable) | <1.22 |
        | Module | `go modules` + `go work` for monorepos | dep, GOPATH layouts |
        | Format | **`gofumpt`** (stricter gofmt) + `goimports -local <module>` | bare `gofmt` |
        | Linter | **`golangci-lint v2`** with the strict bundle in `golangci-strict.md` | bare `go vet` |
        | Nil checker | **`nilaway`** (Uber, stable since 2024) in CI | hope |
        | Vet bundle | `go vet` + `fieldalignment` + `shadow` | "tests cover it" |
        | Tests | `go test -race -shuffle=on -count=1` | `-count` cache, no race |
        | Goroutine leaks | `go.uber.org/goleak` in `TestMain` | "looks fine" |
        | Mock | `go.uber.org/mock` (gomock successor) | hand-written stubs |
        | DB | `sqlc` + `jackc/pgx/v5` | `database/sql` + `gorm` |
        | HTTP framework | **`gin-gonic/gin`** (de facto, ~48% of Go API repos) — `go-chi/chi` for minimalist, `connectrpc/connect-go` for RPC | `echo` (smaller eco), `fiber` (fasthttp = non-stdlib), `gorilla/mux` (in maintenance mode) |
        | RPC | **`connectrpc/connect-go`** (gRPC-compatible, HTTP/1.1-friendly, browser-friendly) | hand-rolled `grpc-go` unless you specifically need bidi streaming features Connect lacks |
        | Validation | `go-playground/validator/v10` for HTTP boundary + `bufbuild/protovalidate-go` for proto + smart constructors for domain | ad-hoc `if len(s) == 0` chains |
        | Config | `caarlos0/env/v11` (struct-tag env) | `viper` unless you actually need file+env+flag merging |
        | Logging | **`log/slog`** (stdlib, Go 1.21+) | logrus, zap, zerolog (all superseded) |
        | CLI | `spf13/cobra` | hand-rolled `os.Args` parsing past 2 flags |
        | TUI | `charm.land/bubbletea/v2` + `bubbles/v2` + `lipgloss/v2` — see `bubbletea-v2.md` for CJK/IME | bubbletea v1 if you need IME |
        
        A single CI command should be the gate:
        
        ```bash
        gofumpt -l . && \
          golangci-lint run ./... && \
          nilaway ./... && \
          go test -race -shuffle=on -count=1 ./...
        ```
        
        If any of these fails, the change is not done. Period. The bundle is set up so a clean run actually means clean — see `golangci-strict.md` for the per-linter rationale and the deliberate `nolint:` policy.
        
        ## Hard rules — code
        
        Read these per-file references for the canonical patterns:
        
        - **Types & data** → `type-patterns.md`, `data-modeling.md` — branded named types, smart constructors with unexported fields, sealed interfaces as sum types.
        - **Errors** → `error-handling.md` — sentinel vs typed struct, `errors.Is/As`, `%w` wrapping, no panic in libraries, the `errorlint` ruleset.
        - **Concurrency** → `concurrency.md` — `context.Context` discipline, `errgroup`, `sync.OnceValue`, `goleak`, `-race`, channel selection rules.
        - **HTTP backend** → `backend-stack.md` — `gin` server skeleton, middleware ordering, SSE/streaming with `http.Flusher`, structured slog logging, graceful shutdown — distilled from the CLIProxyAPI codebase (a real proxy serving OpenAI/Gemini/Claude APIs).
        - **RPC** → `grpc-connect.md` — when to pick Connect vs grpc-go, codegen pipeline, protovalidate, streaming.
        - **DB** → `sqlc-pgx.md` — compile-time-safe SQL via sqlc + pgx connection pool + migrations via goose + testcontainers in CI.
        - **CLI** → `cobra-stack.md` — cobra layout, slog integration, graceful shutdown on signals, fang-style colored help.
        - **TUI** → `bubbletea-v2.md` — v2 model, `SetVirtualCursor(false)` + `tea.View{Cursor}` for CJK IME, why v1 was broken for Korean/Japanese/Chinese input.
        - **Testing** → `testing.md` — table-driven tests, `require` vs `assert`, `autogold` snapshots, `gopter` property tests, `testcontainers` for integration, `goleak` for goroutine leaks.
        - **Bootstrap** → `bootstrap.md` — `new-project.go` invocation, project layout (`cmd/`, `internal/`, `pkg/`), Taskfile, CI.
        - **Strict config** → `golangci-strict.md` — the canonical `.golangci.yml` with the full linter whitelist and per-linter rationale.
        - **One-liners** → `one-liners.md` — `go run` scripts with `//go:build ignore`, `gorun`-style invocation.
        
        ## The 250 pure LOC ceiling
        
        Same rule as Python/Rust/TS: a `.go` file whose pure LOC (non-blank, non-comment) exceeds 250 is architecturally broken. Go encourages many small files in a single package, so this is *more* natural here than elsewhere — split by responsibility, keep one cohesive type and its methods per file.
        
        The `cmd/server/main.go` is the most common violator. Refactor it: `main.go` only wires `os.Args` → `cmd.Execute()`. Anything else lives in `internal/`.
        
        ## Existing codebases — non-strict project
        
        When editing an existing `.go` file that doesn't follow these rules: **write new code in strict style, don't refactor existing code in the same change.** Use the `remove-ai-slops` skill for branch-scope cleanup.
        
        ## Activation
        
        This skill activates whenever you write or modify any `.go` file, `go.mod`, `go.sum`, `.golangci.yml`, `Taskfile.yml`, or any of the codegen specs (`*.proto`, `*.sql` next to `sqlc.yaml`, `openapi.yaml` next to `oapi-codegen.yaml`). Even one-off scripts get the strict treatment — that is what `//go:build ignore` + `go run` is for: production hygiene with throwaway ergonomics.
        
        The references contain the recipes. **Read them before writing code. Re-read them when the model drifts.** The post-write architectural review loop is non-negotiable.
        
      • sqlc-pgx.md 12.7 KB
        # Database Stack — sqlc + pgx + goose + testcontainers
        
        The canonical 2026 PostgreSQL stack. **Type-safe SQL with zero runtime reflection**, hot-path-friendly connection pooling, sane migrations, real Postgres in tests.
        
        If you came here from a `gorm` project: gorm is rejected. See "Why not gorm" at the end.
        
        ---
        
        ## Toolchain
        
        ```bash
        go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
        go install github.com/pressly/goose/v3/cmd/goose@latest
        ```
        
        ---
        
        ## Layout
        
        ```
        internal/store/
        ├── sqlc.yaml                 # sqlc config
        ├── schema.sql                # the cumulative DDL sqlc parses
        ├── queries/                  # one *.sql per resource
        │   ├── users.sql
        │   ├── orders.sql
        │   └── sessions.sql
        ├── sqlc/                     # GENERATED — do not hand-edit
        │   ├── db.go
        │   ├── models.go
        │   ├── users.sql.go
        │   ├── orders.sql.go
        │   └── sessions.sql.go
        ├── migrations/               # goose migrations, ordered
        │   ├── 20260101000001_create_users.sql
        │   └── 20260102000001_add_orders.sql
        ├── pool.go                   # pgxpool factory
        ├── user_store.go             # domain-facing wrapper around sqlc
        └── user_store_test.go        # testcontainers integration test
        ```
        
        ---
        
        ## `sqlc.yaml`
        
        ```yaml
        version: "2"
        sql:
          - engine: "postgresql"
            schema:  "schema.sql"
            queries: "queries"
            gen:
              go:
                package: "sqlc"
                out:     "sqlc"
                sql_package: "pgx/v5"
                emit_json_tags: false
                emit_prepared_queries: false
                emit_interface: true          # generates a Querier interface
                emit_exact_table_names: false
                emit_pointers_for_null_types: true
                emit_empty_slices: true
                overrides:
                  - db_type: "uuid"
                    go_type:
                      import: "github.com/google/uuid"
                      type:   "UUID"
                  - db_type: "timestamptz"
                    go_type:
                      import: "time"
                      type:   "Time"
        ```
        
        Key choices:
        
        - `sql_package: "pgx/v5"` — generated code uses pgx directly, not `database/sql`. Faster, type-safer.
        - `emit_interface: true` — generates a `Querier` interface. Lets stores accept either `*pgxpool.Pool` or `pgx.Tx` for transaction support.
        - `emit_pointers_for_null_types: true` — nullable columns become `*T`, not `sql.NullString`. Cleaner mapping to domain types.
        - `overrides` for `uuid` → `google/uuid.UUID` and `timestamptz` → `time.Time`.
        
        ---
        
        ## `schema.sql`
        
        ```sql
        -- internal/store/schema.sql
        -- The CUMULATIVE schema sqlc parses. Not migrations — the end state.
        -- Regenerate from a fresh DB via `pg_dump --schema-only`, or hand-maintain.
        
        CREATE TABLE users (
            id         UUID         PRIMARY KEY,
            email      TEXT         NOT NULL UNIQUE,
            username   TEXT         NOT NULL UNIQUE,
            created_at TIMESTAMPTZ  NOT NULL DEFAULT NOW()
        );
        
        CREATE INDEX idx_users_created_at ON users(created_at DESC);
        ```
        
        ---
        
        ## `queries/users.sql`
        
        ```sql
        -- name: GetUser :one
        SELECT id, email, username, created_at
        FROM users
        WHERE id = $1;
        
        -- name: ListUsers :many
        SELECT id, email, username, created_at
        FROM users
        ORDER BY created_at DESC
        LIMIT $1 OFFSET $2;
        
        -- name: CreateUser :one
        INSERT INTO users (id, email, username)
        VALUES ($1, $2, $3)
        RETURNING id, email, username, created_at;
        
        -- name: UpdateUserEmail :exec
        UPDATE users
        SET email = $2
        WHERE id = $1;
        
        -- name: DeleteUser :exec
        DELETE FROM users WHERE id = $1;
        ```
        
        sqlc directives:
        
        - `:one` — exactly one row; returns `(T, error)`. Returns `pgx.ErrNoRows` on miss.
        - `:many` — zero or more rows; returns `([]T, error)`.
        - `:exec` — no rows returned; returns `error`.
        - `:execrows` — returns `(int64, error)` with affected row count.
        - `:batchone` / `:batchmany` / `:batchexec` — pgx batch mode for bulk operations.
        
        Run `task gen:sqlc` (or `sqlc generate`). The generated file is committed; CI checks it is up-to-date.
        
        ---
        
        ## Generated code shape (`sqlc/users.sql.go`)
        
        ```go
        // GENERATED — do not edit
        type User struct {
            ID        uuid.UUID
            Email     string
            Username  string
            CreatedAt time.Time
        }
        
        const getUser = `-- name: GetUser :one
        SELECT id, email, username, created_at FROM users WHERE id = $1`
        
        func (q *Queries) GetUser(ctx context.Context, id uuid.UUID) (User, error) {
            row := q.db.QueryRow(ctx, getUser, id)
            var u User
            err := row.Scan(&u.ID, &u.Email, &u.Username, &u.CreatedAt)
            return u, err
        }
        ```
        
        Type-safe inputs, type-safe outputs, compile-time-checked column-to-field mapping. **A schema change that drops a column breaks compilation.** Hand-rolled SQL would have failed at runtime.
        
        ---
        
        ## `store/pool.go`
        
        ```go
        package store
        
        import (
            "context"
            "fmt"
            "time"
        
            "github.com/jackc/pgx/v5/pgxpool"
        )
        
        func NewPool(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
            cfg, err := pgxpool.ParseConfig(dsn)
            if err != nil { return nil, fmt.Errorf("parse dsn: %w", err) }
        
            cfg.MaxConns        = 25
            cfg.MinConns        = 5
            cfg.MaxConnLifetime = time.Hour
            cfg.MaxConnIdleTime = 30 * time.Minute
            cfg.HealthCheckPeriod = 1 * time.Minute
        
            pool, err := pgxpool.NewWithConfig(ctx, cfg)
            if err != nil { return nil, fmt.Errorf("connect: %w", err) }
        
            if err := pool.Ping(ctx); err != nil {
                pool.Close()
                return nil, fmt.Errorf("ping: %w", err)
            }
            return pool, nil
        }
        ```
        
        `pgxpool.Pool` is `Querier`-compatible (implements the interface sqlc generates). Same pool flows into sqlc queries unchanged.
        
        ---
        
        ## `store/user_store.go` — domain ↔ sqlc
        
        ```go
        package store
        
        import (
            "context"
            "errors"
            "fmt"
        
            "github.com/google/uuid"
            "github.com/jackc/pgx/v5"
            "github.com/jackc/pgx/v5/pgxpool"
        
            "github.com/your-org/myservice/internal/domain"
            "github.com/your-org/myservice/internal/store/sqlc"
        )
        
        type UserStore struct {
            q *sqlc.Queries
        }
        
        func NewUserStore(pool *pgxpool.Pool) *UserStore {
            return &UserStore{q: sqlc.New(pool)}
        }
        
        func (s *UserStore) Get(ctx context.Context, id domain.UserID) (domain.User, error) {
            row, err := s.q.GetUser(ctx, uuid.UUID(id))
            if err != nil {
                if errors.Is(err, pgx.ErrNoRows) {
                    return domain.User{}, domain.ErrUserNotFound
                }
                return domain.User{}, fmt.Errorf("get user %s: %w", id, err)
            }
            return rowToDomain(row)
        }
        
        func (s *UserStore) Create(ctx context.Context, u domain.User) (domain.User, error) {
            row, err := s.q.CreateUser(ctx, sqlc.CreateUserParams{
                ID:       uuid.UUID(u.ID),
                Email:    u.Email.String(),
                Username: u.Username.String(),
            })
            if err != nil {
                return domain.User{}, fmt.Errorf("create user: %w", err)
            }
            return rowToDomain(row)
        }
        
        func rowToDomain(r sqlc.User) (domain.User, error) {
            email, err := domain.NewEmail(r.Email)
            if err != nil {
                return domain.User{}, fmt.Errorf("db invariant: email %q: %w", r.Email, err)
            }
            username, err := domain.NewUsername(r.Username)
            if err != nil {
                return domain.User{}, fmt.Errorf("db invariant: username %q: %w", r.Username, err)
            }
            return domain.User{
                ID:        domain.UserID(r.ID),
                Email:     email,
                Username:  username,
                CreatedAt: r.CreatedAt,
            }, nil
        }
        ```
        
        The wrapping is verbose. **That is the point.** sqlc rows are storage representations; domain types are business representations. Mapping them explicitly is where invariants are enforced.
        
        `pgx.ErrNoRows` becomes `domain.ErrUserNotFound` — callers never see storage-level errors.
        
        ---
        
        ## Transactions — pgx.Tx satisfies the Querier interface
        
        ```go
        func (s *UserStore) CreateWithProfile(
            ctx context.Context,
            pool *pgxpool.Pool,
            u domain.User,
            p domain.Profile,
        ) error {
            tx, err := pool.Begin(ctx)
            if err != nil { return fmt.Errorf("begin: %w", err) }
            defer tx.Rollback(ctx)  // no-op if Commit succeeded
        
            q := s.q.WithTx(tx)  // sqlc.Queries bound to the tx
        
            if _, err := q.CreateUser(ctx, /* ... */); err != nil {
                return fmt.Errorf("create user: %w", err)
            }
            if _, err := q.CreateProfile(ctx, /* ... */); err != nil {
                return fmt.Errorf("create profile: %w", err)
            }
            return tx.Commit(ctx)
        }
        ```
        
        Pattern:
        
        - `defer tx.Rollback(ctx)` immediately after `Begin` — safe even after Commit (returns "tx closed", which we ignore via the unhandled return).
        - `q.WithTx(tx)` returns a `*Queries` bound to the tx.
        - Last line: `tx.Commit(ctx)`.
        
        For nested transactions across multiple stores, accept a `Querier` parameter:
        
        ```go
        func (s *UserStore) CreateTx(ctx context.Context, q sqlc.Querier, u domain.User) (domain.User, error) {
            // uses q instead of s.q — caller decides if it's pool or tx
        }
        ```
        
        ---
        
        ## Migrations — goose
        
        ```bash
        goose -dir internal/store/migrations create create_users sql
        ```
        
        ```sql
        -- migrations/20260101000001_create_users.sql
        -- +goose Up
        CREATE TABLE users (
            id UUID PRIMARY KEY,
            email TEXT NOT NULL UNIQUE,
            username TEXT NOT NULL UNIQUE,
            created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
        );
        
        -- +goose Down
        DROP TABLE users;
        ```
        
        Run:
        
        ```bash
        goose -dir internal/store/migrations postgres "$DATABASE_URL" up
        goose -dir internal/store/migrations postgres "$DATABASE_URL" status
        goose -dir internal/store/migrations postgres "$DATABASE_URL" down
        ```
        
        Rules:
        
        - One DDL change per migration. Never combine schema + data migrations in one file.
        - `Down` is real, not a stub. CI runs `up` → `down` → `up` on a fresh container to prove reversibility.
        - Migrations are append-only. Never edit a merged migration; add a new one.
        
        `goose` can run programmatically as well:
        
        ```go
        import "github.com/pressly/goose/v3"
        
        if err := goose.UpContext(ctx, db, "migrations"); err != nil { ... }
        ```
        
        Useful for tools that own their schema (CI runner, integration test setup).
        
        ---
        
        ## Integration tests — testcontainers
        
        ```go
        package store_test
        
        import (
            "context"
            "testing"
        
            "github.com/stretchr/testify/require"
            "github.com/testcontainers/testcontainers-go/modules/postgres"
        )
        
        func newTestDB(t *testing.T) *pgxpool.Pool {
            t.Helper()
            ctx := context.Background()
        
            pgC, err := postgres.Run(ctx,
                "postgres:16-alpine",
                postgres.WithDatabase("test"),
                postgres.WithUsername("test"),
                postgres.WithPassword("test"),
                postgres.BasicWaitStrategies(),
            )
            require.NoError(t, err)
            t.Cleanup(func() { _ = pgC.Terminate(ctx) })
        
            dsn, err := pgC.ConnectionString(ctx, "sslmode=disable")
            require.NoError(t, err)
        
            pool, err := store.NewPool(ctx, dsn)
            require.NoError(t, err)
            t.Cleanup(pool.Close)
        
            require.NoError(t, goose.UpContext(ctx, /* sql.DB from pool */, "../migrations"))
            return pool
        }
        
        func TestUserStore_Create_returns_new_user(t *testing.T) {
            // Given
            pool := newTestDB(t)
            s := store.NewUserStore(pool)
            ctx := context.Background()
        
            // When
            user, err := s.Create(ctx, domain.User{
                ID:       domain.UserID(uuid.Must(uuid.NewV7())),
                Email:    mustEmail("a@b.com"),
                Username: mustUsername("alice"),
            })
        
            // Then
            require.NoError(t, err)
            require.NotEmpty(t, user.ID)
        
            fetched, err := s.Get(ctx, user.ID)
            require.NoError(t, err)
            require.Equal(t, user.Email, fetched.Email)
        }
        ```
        
        testcontainers spins a real Postgres in Docker, runs migrations, hands you a pool. Tests are slow (~2s startup) but **real** — no fake that diverges from production.
        
        For test suites with many cases, share one container across tests in the same package via `TestMain`:
        
        ```go
        var testPool *pgxpool.Pool
        
        func TestMain(m *testing.M) {
            ctx := context.Background()
            pgC, _ := postgres.Run(ctx, "postgres:16-alpine", /* ... */)
            defer pgC.Terminate(ctx)
            dsn, _ := pgC.ConnectionString(ctx, "sslmode=disable")
            testPool, _ = store.NewPool(ctx, dsn)
            // run migrations once
            os.Exit(m.Run())
        }
        ```
        
        Each test then uses a transaction it rolls back at the end — fast and isolated.
        
        ---
        
        ## Why NOT gorm
        
        | Concern | gorm | sqlc + pgx |
        |---|---|---|
        | Type safety | runtime reflection; column-to-field via tags | compile-time-checked from SQL |
        | Performance | 2–5x slower than pgx | pgx is the fastest Go pg driver |
        | N+1 queries | encouraged by `Preload` API | explicit JOIN in `.sql` |
        | Migrations | AutoMigrate (unsafe in prod) | goose, explicit |
        | Debugging | "what query did it run?" requires logging | the query IS the source |
        | Cancellation | spotty ctx support | first-class |
        | Active development | Yes but with churn and breaking changes | sqlc is stable |
        
        Existing gorm projects: leave them. New code: sqlc + pgx.
        
        ---
        
        ## Sources
        
        - sqlc docs: https://docs.sqlc.dev
        - pgx: https://github.com/jackc/pgx
        - goose: https://github.com/pressly/goose
        - testcontainers-go: https://golang.testcontainers.org
        - pgx pool config: https://pkg.go.dev/github.com/jackc/pgx/v5/pgxpool#Config
        
      • testing.md 14.2 KB
        # Testing
        
        TDD shape, table-driven tests, `require` vs `assert`, snapshot tests, property-based tests, integration tests with testcontainers, goroutine-leak detection. The discipline in `programming/SKILL.md` (Given/When/Then, less mock the better, efficient AND accurate) — this document gives the Go-specific recipes.
        
        ---
        
        ## Tools
        
        | Need | Use |
        |---|---|
        | Assertions | `stretchr/testify/require` (and `assert` only inside table loops) |
        | Mocks | `go.uber.org/mock` (gomock successor) |
        | Goroutine leaks | `go.uber.org/goleak` |
        | Snapshots / golden | `hexops/autogold/v2` |
        | Property-based | `pgregory.net/rapid` |
        | HTTP mocks (outbound) | `h2non/gock` |
        | HTTP test server (inbound) | stdlib `net/http/httptest` |
        | Integration containers | `testcontainers/testcontainers-go` |
        | TUI | `charm.land/bubbletea/v2/teatest` |
        | Bench tooling | stdlib `testing.B` + `perf.dev/benchstat` |
        
        ---
        
        ## Test naming — Given / When / Then in the name
        
        ```go
        // ──── PATTERN ────
        // Test_<Subject>_<Outcome>_when_<Condition>
        //   OR
        // Test_<Subject>_<Action>_<ExpectedOutcome>
        
        func Test_Email_NewEmail_lowercases_input(t *testing.T)
        func Test_Email_NewEmail_rejects_input_without_at_sign(t *testing.T)
        func Test_UserService_Create_persists_user_when_inputs_valid(t *testing.T)
        func Test_UserService_Create_returns_validation_error_when_email_invalid(t *testing.T)
        ```
        
        A test name should answer "what behavior is this asserting?" without reading the body. Names that need a comment to explain them are misnamed.
        
        ---
        
        ## Single test — explicit Given/When/Then
        
        ```go
        func Test_Email_NewEmail_rejects_input_without_at_sign(t *testing.T) {
            // Given
            raw := "not-an-email"
        
            // When
            _, err := domain.NewEmail(raw)
        
            // Then
            require.Error(t, err)
            require.ErrorIs(t, err, domain.ErrInvalidEmail)
        }
        ```
        
        `require.*` fails the test immediately on miss. Use `require` for preconditions and primary assertions. Use `assert.*` only inside table-driven loops where you want all cases to report.
        
        ---
        
        ## Table-driven tests
        
        ```go
        func Test_Email_NewEmail(t *testing.T) {
            tests := []struct {
                name    string
                input   string
                want    string
                wantErr error
            }{
                {"lowercases", "ALICE@example.com", "alice@example.com", nil},
                {"trims whitespace", "  bob@example.com  ", "bob@example.com", nil},
                {"rejects missing @", "no-at-sign", "", domain.ErrInvalidEmail},
                {"rejects empty", "", "", domain.ErrInvalidEmail},
                {"rejects too long", strings.Repeat("a", 256) + "@e.com", "", domain.ErrInvalidEmail},
            }
            for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
                    // When
                    got, err := domain.NewEmail(tt.input)
        
                    // Then
                    if tt.wantErr != nil {
                        require.ErrorIs(t, err, tt.wantErr)
                        return
                    }
                    require.NoError(t, err)
                    assert.Equal(t, tt.want, got.String())
                })
            }
        }
        ```
        
        Rules:
        
        - One **scenario** per row, not one **assertion** per row.
        - Subtest names are sentences in lowercase; `t.Run(tt.name, ...)` makes them filterable: `go test -run Test_Email_NewEmail/rejects_missing_@`.
        - The loop body itself is Given/When/Then in shape.
        - For Go 1.22+, the loop var capture works correctly without the `tt := tt` shadow line — the `copyloopvar` linter enforces the new style.
        
        ---
        
        ## Less mocks — the priority order
        
        In Go specifically:
        
        1. **Real implementation.** Domain types, pure functions, value objects — instantiate them. They are fast.
        2. **In-memory fake** that satisfies the interface. Has its own test suite proving behavioral parity with the real impl.
        3. **`httptest.Server`** for HTTP collaborators (real wire, no internet).
        4. **`testcontainers`** for stateful collaborators (Postgres, Redis, S3-compatible, Kafka).
        5. **gomock** ONLY for: clocks, randomness, third-party SaaS with no sandbox.
        
        ### Example: an in-memory fake
        
        ```go
        // Real interface
        type UserRepo interface {
            Save(ctx context.Context, u domain.User) error
            Get(ctx context.Context, id domain.UserID) (domain.User, error)
        }
        
        // In-memory fake — production-quality, tested separately
        type FakeUserRepo struct {
            mu    sync.RWMutex
            users map[domain.UserID]domain.User
        }
        
        func NewFakeUserRepo() *FakeUserRepo {
            return &FakeUserRepo{users: map[domain.UserID]domain.User{}}
        }
        
        func (r *FakeUserRepo) Save(ctx context.Context, u domain.User) error {
            r.mu.Lock(); defer r.mu.Unlock()
            r.users[u.ID] = u
            return nil
        }
        
        func (r *FakeUserRepo) Get(ctx context.Context, id domain.UserID) (domain.User, error) {
            r.mu.RLock(); defer r.mu.RUnlock()
            u, ok := r.users[id]
            if !ok { return domain.User{}, domain.ErrUserNotFound }
            return u, nil
        }
        ```
        
        The fake has the same observable behavior as the real one. Tests against `FakeUserRepo` survive when the production repo's internals change. Tests against a gomock stub of `UserRepo` break.
        
        **A test passing against a fake AND a test passing against the real impl is the gold standard.** Run the same test suite twice — once with the fake, once with testcontainers. The fakes earn their keep when the suites diverge.
        
        ### Example: gomock for the unmockable
        
        ```go
        //go:generate mockgen -source=clock.go -destination=mocks/clock_mock.go -package=mocks
        
        type Clock interface {
            Now() time.Time
        }
        
        // In a test:
        ctrl := gomock.NewController(t)
        clock := mocks.NewMockClock(ctrl)
        clock.EXPECT().Now().Return(fixedTime).AnyTimes()
        ```
        
        Mock the narrowest seam. Never mock `UserRepo` if a fake suffices.
        
        ---
        
        ## E2E scenario tests
        
        ```go
        //go:build e2e
        
        func Test_E2E_user_can_signup_then_login(t *testing.T) {
            // Given — full server in a goroutine
            ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
            defer cancel()
        
            pool := newTestDB(t)              // testcontainers Postgres
            server := startServer(t, pool)    // real gin engine on a random port
            defer server.Close()
        
            client := server.Client()
        
            // When — sign up
            resp, err := client.Post(server.URL+"/api/v1/users",
                "application/json",
                strings.NewReader(`{"email":"a@b.com","username":"alice","password":"PassWord!23"}`),
            )
            require.NoError(t, err)
            require.Equal(t, 201, resp.StatusCode)
        
            // When — log in
            resp, err = client.Post(server.URL+"/api/v1/auth/login",
                "application/json",
                strings.NewReader(`{"email":"a@b.com","password":"PassWord!23"}`),
            )
            require.NoError(t, err)
            require.Equal(t, 200, resp.StatusCode)
        
            var body struct{ Token string `json:"token"` }
            require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
            require.NotEmpty(t, body.Token)
        
            // Then — token works on protected endpoint
            req, _ := http.NewRequestWithContext(ctx, "GET", server.URL+"/api/v1/me", nil)
            req.Header.Set("Authorization", "Bearer "+body.Token)
            resp, err = client.Do(req)
            require.NoError(t, err)
            require.Equal(t, 200, resp.StatusCode)
        }
        ```
        
        Patterns:
        
        - `//go:build e2e` build tag separates slow E2E from fast unit tests. Run with `go test -tags=e2e ./...`.
        - One narrative per test: "user can sign up then log in". One `Test_E2E_*` per user-visible outcome.
        - Real DB via testcontainers, real gin engine, real HTTP. **No mocks.** The point is to catch integration bugs.
        - Bounded context — every E2E gets a `context.WithTimeout` so failures don't hang CI.
        
        ---
        
        ## Goroutine leak detection
        
        ```go
        package mypkg
        
        import (
            "testing"
            "go.uber.org/goleak"
        )
        
        func TestMain(m *testing.M) {
            goleak.VerifyTestMain(m,
                goleak.IgnoreTopFunction("github.com/prometheus/client_golang/prometheus.(*Registry)..."),
            )
        }
        ```
        
        One line at the top of every package that spawns goroutines. Catches the bug class the race detector cannot.
        
        ---
        
        ## Snapshot / golden tests — `autogold`
        
        ```go
        import "github.com/hexops/autogold/v2"
        
        func Test_RenderHelp_matches_snapshot(t *testing.T) {
            // Given
            cmd := newRootCmd()
        
            // When
            out := captureOutput(t, func() { _ = cmd.Help() })
        
            // Then
            autogold.ExpectFile(t, out)
        }
        ```
        
        First run: `go test -update ./...` writes `testdata/Test_RenderHelp.golden`. Future runs compare; failures show a diff. Re-approve intentional changes with `-update`.
        
        **Use snapshots for STRUCTURE, not BEHAVIOR.** Good targets:
        
        - CLI `--help` output
        - JSON response shape
        - Generated SQL queries
        - Rendered prompts (assert the structure, not exact wording — see SKILL.md prompt-test rule)
        
        Bad targets: a function's return value where you should `require.Equal` on the actual structure.
        
        ---
        
        ## Property-based tests — `rapid`
        
        ```go
        import "pgregory.net/rapid"
        
        func Test_Email_NewEmail_then_String_roundtrips(t *testing.T) {
            rapid.Check(t, func(t *rapid.T) {
                // Given — generate valid emails
                local  := rapid.StringMatching(`[a-z]{3,10}`).Draw(t, "local")
                domain := rapid.StringMatching(`[a-z]{3,10}\.com`).Draw(t, "domain")
                raw    := local + "@" + domain
        
                // When
                e, err := domain.NewEmail(raw)
                require.NoError(t, err)
        
                // Then — round-trip property
                e2, err := domain.NewEmail(e.String())
                require.NoError(t, err)
                require.Equal(t, e, e2)
            })
        }
        ```
        
        `rapid` shrinks failing cases to minimal counterexamples. Use for:
        
        - Round-trips (parse → serialize → parse).
        - Algebraic properties (sort produces ordered, dedup is idempotent, JSON marshal/unmarshal is involutive).
        - Invariants under random input (validator never panics, serializer never produces invalid UTF-8).
        
        ---
        
        ## HTTP testing — `httptest`
        
        ### Server side
        
        ```go
        func Test_GetUser_returns_user_for_existing_id(t *testing.T) {
            // Given
            svc := newSvcWithFake(t)
            r := gin.New()
            h := &Handler{Users: svc}
            h.Mount(r)
        
            req := httptest.NewRequest("GET", "/api/v1/users/u-1", nil)
            rec := httptest.NewRecorder()
        
            // When
            r.ServeHTTP(rec, req)
        
            // Then
            require.Equal(t, 200, rec.Code)
            var body domain.User
            require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
            require.Equal(t, "u-1", string(body.ID))
        }
        ```
        
        ### Client side — `httptest.NewServer`
        
        ```go
        func Test_Client_retries_on_500(t *testing.T) {
            // Given — fake upstream
            var calls int
            srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                calls++
                if calls < 3 {
                    w.WriteHeader(500)
                    return
                }
                w.WriteHeader(200)
                _, _ = w.Write([]byte(`{"ok":true}`))
            }))
            defer srv.Close()
        
            client := myclient.New(srv.URL)
        
            // When
            err := client.DoSomething(context.Background())
        
            // Then
            require.NoError(t, err)
            require.Equal(t, 3, calls)
        }
        ```
        
        `httptest.NewServer` spins a real HTTP server on a random port. The fake handler implements the upstream contract. Test the client against the contract, not the implementation.
        
        ---
        
        ## Determinism — the cardinal rules
        
        - **No `time.Sleep` in tests.** If you need delay, you need a Clock injection.
        - **`go test -shuffle=on`** in every CI run.
        - **`go test -count=1`** to defeat the cache.
        - **Subscribe to the event, do not poll for it.** Channels, callbacks, `t.Cleanup` over polling.
        - **`t.Parallel()`** for tests that share no state. Speeds up large suites by 4-8x.
        
        A test that fails 1-in-10 runs is a bug, not flake. The race detector + `-shuffle=on` + ordering hygiene catches >95% of "flake".
        
        ---
        
        ## Benchmarks — `testing.B` + `benchstat`
        
        ```go
        func Benchmark_NewEmail(b *testing.B) {
            for b.Loop() {  // Go 1.24+ idiom, replaces `for i := 0; i < b.N; i++`
                _, _ = domain.NewEmail("alice@example.com")
            }
        }
        ```
        
        Run:
        
        ```bash
        go test -bench=. -count=10 -benchmem ./... | tee bench.txt
        benchstat bench.txt   # statistical comparison
        ```
        
        Always `-count=10` for stable means. `-benchmem` reports allocations. A 5%-slower benchmark in one run is noise; 10 runs + benchstat tells you what is real.
        
        To compare before/after a change:
        
        ```bash
        git stash
        go test -bench=. -count=10 ./... > before.txt
        git stash pop
        go test -bench=. -count=10 ./... > after.txt
        benchstat before.txt after.txt
        ```
        
        ---
        
        ## Coverage — the right target
        
        Run:
        
        ```bash
        go test -race -shuffle=on -coverprofile=cover.out ./...
        go tool cover -html=cover.out -o cover.html
        ```
        
        **Aim for 80%+ on `internal/domain` and `internal/service`.** Boundary code (handlers, store mappers) is exercised by integration tests, where line coverage understates what is actually verified. Do not chase 100% — the last 5% is usually error paths that need fault-injection to hit.
        
        The `golangci-lint` config does not enforce a minimum — coverage as a CI gate becomes a goal-displacement metric. Treat it as feedback, not requirement.
        
        ---
        
        ## TUI testing — `teatest`
        
        ```go
        import teatest "charm.land/bubbletea/v2/teatest"
        
        func Test_Counter_increments_on_space(t *testing.T) {
            // Given
            tm := teatest.NewTestModel(t, initial(), teatest.WithInitialTermSize(80, 24))
        
            // When
            tm.Send(tea.KeyPressMsg{Code: ' '})
        
            // Then
            final := tm.FinalModel(t).(model)
            require.Equal(t, 1, final.count)
        }
        ```
        
        For full-view regression, snapshot the rendered output via `autogold`.
        
        ---
        
        ## Antipatterns the skill rejects
        
        | Bad | Why | Good |
        |---|---|---|
        | `if got != want { t.Errorf("expected %v got %v", want, got) }` | Reinvents `require.Equal` | Use testify |
        | `time.Sleep(100 * time.Millisecond)` after triggering async work | Flake | Subscribe to completion signal, bounded await |
        | `t.Skip(...)` to silence a known failure | Buries the bug | Fix or open an issue; never silently skip |
        | One mega-test asserting 12 things | First failure hides next 11 | Split by `Then` |
        | Snapshot-everything | Locks formatting, not behavior | Snapshots for structure, asserts for values |
        | Mock every collaborator | Test asserts implementation, not behavior | Real or fake, never mock everything |
        | Test calls private function via `_test.go` in same package only | Couples test to implementation | Test through the public surface |
        
        ---
        
        ## Sources
        
        - testify: https://github.com/stretchr/testify
        - goleak: https://github.com/uber-go/goleak
        - autogold: https://github.com/hexops/autogold
        - rapid: https://pkg.go.dev/pgregory.net/rapid
        - testcontainers-go: https://golang.testcontainers.org
        - benchstat: https://pkg.go.dev/golang.org/x/perf/cmd/benchstat
        - "Go test naming conventions" (Dave Cheney): https://dave.cheney.net/practical-go/presentations/qcon-china.html
        
      • type-patterns.md 9.5 KB
        # Type Patterns
        
        How to use Go's *limited* type system to catch bugs at compile time. Go gives you fewer tools than Python/TS/Rust — this document covers the four patterns that buy back most of the safety.
        
        The four patterns:
        
        1. **Named types** for branding primitives (the Go answer to `NewType` / branded TS).
        2. **Smart constructors with unexported fields** for parse-don't-validate.
        3. **Sealed interfaces** for sum types, with `type switch` + `exhaustive` linter.
        4. **Generics with constraints** for bounded polymorphism (1.18+).
        
        ---
        
        ## 1. Named types — distinct primitives
        
        Same underlying type, different meaning. The Go type checker prevents *implicit* mixing — but explicit conversion is always possible. Treat this as a contract enforced at boundaries.
        
        ```go
        package domain
        
        type UserID string
        type OrderID string
        type EmailRaw string  // raw, unvalidated string from input
        
        func GetUser(id UserID) User { /* ... */ }
        
        uid := UserID("u-123")
        oid := OrderID("o-456")
        
        GetUser(uid)              // ✅ OK
        GetUser(oid)              // ❌ cannot use oid (type OrderID) as UserID
        GetUser("u-123")          // ❌ untyped string literal — Go DOES catch this
        GetUser(UserID("u-123"))  // ✅ explicit conversion — accept it
        ```
        
        **Use when**: IDs, opaque tokens, foreign keys, units that share a base primitive.
        
        **Reality check**: Go does NOT prevent `UserID(orderIDAsString)`. The defense is **smart constructors** for everything beyond an internal identifier. Use named types for cheap brand-only protection; combine with constructors for protection that actually holds.
        
        ### Time-of-day units
        
        ```go
        type Milliseconds int64
        type Seconds      int64
        
        func (ms Milliseconds) ToSeconds() Seconds {
            return Seconds(ms / 1000)
        }
        ```
        
        No implicit `Milliseconds + Seconds`. The compiler refuses. Convert explicitly.
        
        ---
        
        ## 2. Smart constructors with unexported fields — the Go answer to Pydantic/Zod
        
        The single most important pattern in this document. **Go has no Pydantic. It has this.**
        
        ```go
        package domain
        
        import (
            "errors"
            "regexp"
            "strings"
        )
        
        var (
            ErrInvalidEmail = errors.New("invalid email")
            emailRe         = regexp.MustCompile(`^[^@\s]+@[^@\s]+\.[^@\s]+$`)
        )
        
        // Email is a parsed, lowercased, valid email address.
        // The zero value is invalid; construct via NewEmail.
        type Email struct {
            raw string  // unexported — cannot be set from outside the package
        }
        
        func NewEmail(s string) (Email, error) {
            s = strings.TrimSpace(strings.ToLower(s))
            if !emailRe.MatchString(s) {
                return Email{}, ErrInvalidEmail
            }
            return Email{raw: s}, nil
        }
        
        // String implements fmt.Stringer for printing.
        func (e Email) String() string { return e.raw }
        
        // MarshalJSON keeps the wire format unchanged.
        func (e Email) MarshalJSON() ([]byte, error) {
            return []byte(`"` + e.raw + `"`), nil
        }
        
        // UnmarshalJSON is the parsing boundary — strict mode.
        func (e *Email) UnmarshalJSON(data []byte) error {
            if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
                return ErrInvalidEmail
            }
            parsed, err := NewEmail(string(data[1 : len(data)-1]))
            if err != nil {
                return err
            }
            *e = parsed
            return nil
        }
        ```
        
        **Why this works**:
        
        - `Email{raw: "anything"}` from outside the `domain` package is a compile error — `raw` is unexported.
        - The only way to obtain a non-zero `Email` is `NewEmail(...)`, which validates.
        - `UnmarshalJSON` routes wire input through the same constructor — boundary parsing is automatic.
        - Once a function signature has `email Email`, the caller has *proven* it is valid. No internal `if email == ""` checks.
        
        **Use for every domain value that has invariants**: emails, URLs, phone numbers, currency amounts, percentages, semver versions, IDs with format constraints, time ranges, anything you currently validate in three places.
        
        ### The "zero value problem"
        
        Go's zero value (`Email{}`) is reachable. The mitigation is documentation + a `IsValid()` method when needed:
        
        ```go
        func (e Email) IsZero() bool { return e.raw == "" }
        ```
        
        Or accept it: receivers that take `Email` should *never* receive a zero-value `Email` in correct code. Tests verify it.
        
        ---
        
        ## 3. Sealed interfaces — sum types in Go
        
        Go has no sum types. The closest thing: an interface with an **unexported method** that only types in the same package can satisfy, dispatched via `type switch`, with the `exhaustive` linter ensuring completeness.
        
        ```go
        package event
        
        // Event is a closed sum: Created | Updated | Deleted.
        // The sealed() method is unexported so external packages cannot add variants.
        type Event interface {
            sealed()
            OccurredAt() time.Time
        }
        
        type Created struct {
            UserID    UserID
            Email     Email
            Timestamp time.Time
        }
        func (Created) sealed()                    {}
        func (e Created) OccurredAt() time.Time    { return e.Timestamp }
        
        type Updated struct {
            UserID    UserID
            Changes   map[string]any
            Timestamp time.Time
        }
        func (Updated) sealed()                    {}
        func (e Updated) OccurredAt() time.Time    { return e.Timestamp }
        
        type Deleted struct {
            UserID    UserID
            Reason    string
            Timestamp time.Time
        }
        func (Deleted) sealed()                    {}
        func (e Deleted) OccurredAt() time.Time    { return e.Timestamp }
        ```
        
        Consumer code:
        
        ```go
        func Render(e event.Event) string {
            switch v := e.(type) {
            case event.Created:
                return fmt.Sprintf("created %s with %s", v.UserID, v.Email)
            case event.Updated:
                return fmt.Sprintf("updated %s: %v", v.UserID, v.Changes)
            case event.Deleted:
                return fmt.Sprintf("deleted %s (reason: %s)", v.UserID, v.Reason)
            default:
                panic(fmt.Sprintf("unhandled event variant: %T", v))
            }
        }
        ```
        
        The `panic` in `default` is the Go equivalent of TS's `assertNever` or Python's `assert_never`. It is only reachable if a new variant is added without updating the switch.
        
        ### The `exhaustive` linter — your compiler
        
        ```yaml
        # .golangci.yml
        linters:
          enable: [exhaustive]
        linters-settings:
          exhaustive:
            check:
              - switch
              - map
            default-signifies-exhaustive: false
        ```
        
        Now adding `event.Suspended` without updating `Render` is a **lint error**. This is the closest thing Go has to Rust's match exhaustiveness check. **Treat it as compulsory.**
        
        ### Sealed interface gotchas
        
        - The method MUST be unexported (`sealed()`, not `Sealed()`). Otherwise other packages can implement it.
        - `type switch` with `*Created` vs `Created` matters — pick value receivers and value cases, or pointer receivers and pointer cases. **Mixing them causes silent miss.**
        - `interface{}` is not a sealed type. Anything implementing zero methods satisfies it. Sealed interfaces have at least the `sealed()` method.
        
        ---
        
        ## 4. Generics with constraints — bounded polymorphism
        
        Go 1.18+. Use for genuinely generic algorithms; **do not** use for "I want this to accept anything".
        
        ```go
        import "cmp"
        
        // Ordered constraint includes all ordered types (int, float, string, …).
        func Max[T cmp.Ordered](a, b T) T {
            if a > b { return a }
            return b
        }
        
        // Custom constraint
        type Stringer interface {
            String() string
        }
        
        func Join[T Stringer](items []T, sep string) string {
            parts := make([]string, len(items))
            for i, item := range items {
                parts[i] = item.String()
            }
            return strings.Join(parts, sep)
        }
        ```
        
        The `cmp.Ordered` (Go 1.21+), `cmp.Compare`, and `slices`/`maps` packages cover the common cases without you writing constraints.
        
        ### When NOT to use generics
        
        - "I want to accept multiple types, so I'll make it generic." Use an **interface** instead. Generics are for parametric polymorphism (same code, different types). Interfaces are for behavioral polymorphism (different code behind a contract).
        - "I want to return `any`." Use a sealed interface and a `type switch`. `any` returns are anti-patterns past public APIs.
        
        ---
        
        ## 5. Type assertions — the controlled escape hatch
        
        ```go
        // Bad — panics on failure
        e := evt.(event.Created)
        
        // Good — comma-ok form, always
        if e, ok := evt.(event.Created); ok {
            // use e
        }
        
        // Use errors.As for error chains
        var pgErr *pgconn.PgError
        if errors.As(err, &pgErr) {
            // pgErr is the wrapped pg error
        }
        ```
        
        **The `errcheck` and `errorlint` linters reject bare type assertions on `error` values.** Use `errors.As`. See `error-handling.md`.
        
        ---
        
        ## 6. Pointers vs values — the only durable rule
        
        You will see endless debates. The rule that holds up:
        
        - **If a type has a mutex, never copy it.** Use `*T` everywhere.
        - **If a type is large (> 64 bytes) and read-only, pass by value or pointer is a measured choice.** Default to pointer for "large" things.
        - **Receivers must be consistent.** All methods on `T` either take `T` or `*T`. Don't mix. The `staticcheck` linter catches mixed-receiver bugs.
        - **`nil` pointer = absence. Zero value = "not set yet".** Choose ONE convention per type. Document it.
        
        ---
        
        ## 7. `any` / `interface{}` — when it is acceptable
        
        Almost never in domain code. Acceptable cases:
        
        - JSON parsing of genuinely heterogeneous payloads (and even then, prefer `json.RawMessage` + targeted parsing).
        - `fmt.Sprintf` arguments (variadic `any` is unavoidable here).
        - Generic container internals before the user-facing API.
        
        The skill rejects `any` in handler signatures, service signatures, store signatures. If you find yourself writing `func Handle(payload any) error`, you have a sealed-interface waiting to happen.
        
        ---
        
        ## Sources
        
        - "Parse, don't validate" — Alexis King: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
        - exhaustive linter: https://github.com/nishanths/exhaustive
        - Generics constraints: https://go.dev/blog/intro-generics
        - cmp.Ordered: https://pkg.go.dev/cmp
        
    • python
      • async-anyio.md 14.2 KB
        # AnyIO Reference: Replacing asyncio Idioms
        
        > **Skill mandate**: `import asyncio` is BANNED. Use `import anyio` exclusively.
        > This reference targets AnyIO 4.x (2026 Python projects).
        
        ---
        
        ## 1. Task Groups (The Core Primitive)
        
        AnyIO uses **structured concurrency** via task groups. A task group is an async context manager that guarantees all child tasks finish before the block exits.
        
        ### `start_soon` — fire-and-forget
        
        ```python
        import anyio
        
        async def worker(n: int) -> None:
            await anyio.sleep(1)
            print(f"task {n} done")
        
        async def main() -> None:
            async with anyio.create_task_group() as tg:
                for i in range(3):
                    tg.start_soon(worker, i)
            print("all tasks finished")
        
        anyio.run(main)
        ```
        
        **Signature**: `tg.start_soon(func, *args, name=None)`
        - `func` must be a **coroutine function** (not a coroutine object).
        - `name` is optional, for introspection/debugging.
        - No return value; exceptions propagate as `ExceptionGroup` on exit.
        
        ### `start` — wait for ready signal
        
        Use when a task must initialize before the caller proceeds (e.g., starting a server and then connecting to it).
        
        ```python
        from anyio import TASK_STATUS_IGNORED, create_task_group, run
        from anyio.abc import TaskStatus
        
        async def start_server(port: int, *, task_status: TaskStatus[None] = TASK_STATUS_IGNORED) -> None:
            listener = await anyio.create_tcp_listener(local_host="127.0.0.1", local_port=port)
            task_status.started()  # unblocks tg.start()
            await listener.serve(handler)
        
        async def main() -> None:
            async with create_task_group() as tg:
                await tg.start(start_server, 8080)  # blocks until task_status.started()
                # server is guaranteed ready here
                async with await anyio.connect_tcp("127.0.0.1", 8080) as client:
                    ...
        
        run(main)
        ```
        
        **Rule of thumb**:
        - Use `start_soon` when you don't need to know when the task is ready.
        - Use `start` when the task must signal readiness before you continue.
        
        ### `create_task` — retrieving return values (AnyIO 4.14+)
        
        ```python
        async def add(x: int, y: int) -> int:
            return x + y
        
        async def main() -> None:
            async with anyio.create_task_group() as tg:
                handle = tg.create_task(add(2, 4))
                result = await handle  # == 6
                print(handle.return_value)  # also 6
        
        anyio.run(main)
        ```
        
        **Signature**: `tg.create_task(coro, *, name=None, context=None) -> TaskHandle[T]`
        - Returns a `TaskHandle` you can `await` for the result.
        - If the task raises, awaiting raises `TaskFailed` (or `TaskCancelled`).
        - This is the canonical replacement for `asyncio.gather` when you need results.
        
        ---
        
        ## 2. asyncio → anyio Cheat Sheet
        
        | asyncio | anyio | Notes |
        |---------|-------|-------|
        | `asyncio.gather(a, b, c)` | `tg.create_task(a); tg.create_task(b); tg.create_task(c); results = [await h for h in handles]` | No direct gather; structured concurrency requires explicit task group scope. For fire-and-forget, use `tg.start_soon`. |
        | `asyncio.create_task(coro)` | `tg.start_soon(func, *args)` or `tg.create_task(coro)` | `start_soon` takes a coroutine **function** + args. `create_task` takes a coroutine **object** and returns a handle. |
        | `asyncio.sleep(n)` | `anyio.sleep(n)` | Identical semantics. |
        | `asyncio.wait_for(coro, timeout)` | `with anyio.fail_after(timeout): await coro` | Raises `TimeoutError`. Use `move_on_after` for silent timeout. |
        | `asyncio.Event()` | `anyio.Event()` | AnyIO events are **not reusable**; create a new one instead of `.clear()`. |
        | `asyncio.Lock()` | `anyio.Lock()` | Use `async with lock:`. Pass `fast_acquire=True` if performance-critical. |
        | `asyncio.Semaphore(n)` | `anyio.Semaphore(n)` | Same. Pass `fast_acquire=True` if performance-critical. |
        | `asyncio.Condition()` | `anyio.Condition()` | Same semantics. |
        | `asyncio.run(main())` | `anyio.run(main)` | Backend-agnostic entry point. |
        | `asyncio.Queue(maxsize=N)` | `anyio.create_memory_object_stream[T](max_buffer_size=N)` | Returns `(send_stream, receive_stream)`. Supports `async for` on receive end. |
        | `asyncio.to_thread(fn, *args)` | `anyio.to_thread.run_sync(fn, *args)` | Supports `abandon_on_cancel=True` and custom `limiter`. |
        | `asyncio.run_coroutine_threadsafe(coro, loop)` | `anyio.from_thread.run(func, *args)` | Call async code from a worker thread. |
        | `loop.call_soon_threadsafe(callback)` | `anyio.from_thread.run_sync(func, *args)` | Call sync code in event loop thread from worker thread, **with return value**. |
        | `asyncio.shield(coro)` | `with anyio.CancelScope(shield=True): ...` | AnyIO shielding does not orphan tasks. |
        | `asyncio.timeout(delay)` | `with anyio.fail_after(delay): ...` | AnyIO uses level cancellation, not edge cancellation. |
        | `asyncio.CancelledError` | `anyio.get_cancelled_exc_class()` | Use this to catch cancellation portably across backends. |
        
        ---
        
        ## 3. Cancellation & CancelScope
        
        AnyIO uses **level cancellation** (inspired by Trio), not asyncio's **edge cancellation**.
        
        - **Edge cancellation** (asyncio): A `CancelledError` is injected once. If caught and not re-raised, the task keeps running.
        - **Level cancellation** (anyio): As long as a task is inside an effectively cancelled scope, every yield point raises a new cancellation exception.
        
        ### Basic CancelScope
        
        ```python
        from anyio import CancelScope, create_task_group, get_cancelled_exc_class, sleep, run
        
        async def worker() -> None:
            try:
                await sleep(10)
            except get_cancelled_exc_class():
                print("cancelled!")
                raise  # ALWAYS re-raise cancellation exceptions
        
        async def main() -> None:
            async with create_task_group() as tg:
                tg.start_soon(worker)
                await sleep(0.1)
                tg.cancel_scope.cancel()  # cancels all children
        
        run(main)
        ```
        
        ### Shielding
        
        Shield a block from external cancellation. Essential for cleanup.
        
        ```python
        from anyio import CancelScope, create_task_group, sleep, run
        
        async def main() -> None:
            async with create_task_group() as tg:
                with CancelScope(shield=True):
                    tg.start_soon(some_task)
                    tg.cancel_scope.cancel()  # shielded block is protected
                    await sleep(1)  # this still runs
        
        run(main)
        ```
        
        **Combine with timeouts for graceful shutdown**:
        
        ```python
        from anyio import CancelScope, move_on_after
        
        async def do_something(resource) -> None:
            try:
                await run_async_stuff()
            except BaseException:
                # Allow up to 10s for cleanup, then move on
                with move_on_after(10, shield=True):
                    await resource.aclose()
                raise
        ```
        
        ### Structured Concurrency Guarantee
        
        A task group contains its own `CancelScope`. If any child task raises an exception:
        1. The task group's cancel scope is cancelled.
        2. All other child tasks receive cancellation.
        3. The task group waits for all children to finish.
        4. The original exception (wrapped in `ExceptionGroup` if multiple) is re-raised.
        
        ---
        
        ## 4. Timeouts
        
        Two context managers. Both create a `CancelScope` internally.
        
        ### `fail_after` — raises on timeout
        
        ```python
        from anyio import fail_after, sleep, run
        
        async def main() -> None:
            try:
                with fail_after(5) as scope:
                    await sleep(10)
            except TimeoutError:
                print("timed out")
                print(scope.cancelled_caught)  # True
        
        run(main)
        ```
        
        ### `move_on_after` — silent timeout
        
        ```python
        from anyio import move_on_after, sleep, run
        
        async def main() -> None:
            with move_on_after(5) as scope:
                await sleep(10)
                print("this never prints")
        
            print("exited scope, cancelled =", scope.cancelled_caught)
        
        run(main)
        ```
        
        ### Combined with shielding
        
        ```python
        from anyio import move_on_after
        
        # Give cleanup 10 seconds, but don't let outer cancellation interrupt it
        with move_on_after(10, shield=True):
            await resource.aclose()
        ```
        
        ---
        
        ## 5. Memory Object Streams (Queue Replacement)
        
        Replaces `asyncio.Queue` with a safer, typed, structured-concurrency-friendly construct.
        
        ```python
        from anyio import create_task_group, create_memory_object_stream, run
        from anyio.streams.memory import MemoryObjectReceiveStream
        
        async def consumer(stream: MemoryObjectReceiveStream[str]) -> None:
            async with stream:  # closes receive end on exit
                async for item in stream:
                    print("received", item)
        
        async def main() -> None:
            # Type-annotated stream creation (AnyIO 4+ syntax)
            send_stream, receive_stream = create_memory_object_stream[str](max_buffer_size=10)
        
            async with create_task_group() as tg:
                tg.start_soon(consumer, receive_stream)
                async with send_stream:
                    for i in range(5):
                        await send_stream.send(f"item {i}")
                # send_stream closed → consumer's async for loop exits naturally
        
        run(main)
        ```
        
        **Key differences from `asyncio.Queue`**:
        - **Bounded by default**: `max_buffer_size=0` means send blocks until a receiver is ready.
        - **Cloneable**: Each producer/consumer can close its own clone. The stream only ends when **all** clones of one end are closed.
        - **Async iterable**: `async for item in receive_stream:` works out of the box.
        - **Type-safe**: Generic `create_memory_object_stream[T]()`.
        - **Synchronous close**: Both `close()` and `async with` work.
        
        ---
        
        ## 6. Backend Selection
        
        AnyIO is backend-agnostic. Code written against AnyIO APIs runs on both asyncio and Trio.
        
        ```python
        import anyio
        
        async def main() -> None:
            print("running on", anyio.current_async_library())
            await anyio.sleep(1)
        
        # Default backend (asyncio)
        anyio.run(main)
        
        # Explicit backend
        anyio.run(main, backend="trio")
        anyio.run(main, backend="asyncio", backend_options={"debug": True})
        ```
        
        **Library design rule**: Never hardcode a backend. Let the application choose via `anyio.run()`. Libraries should only import `anyio` and avoid backend-specific APIs.
        
        ---
        
        ## 7. Compatibility with asyncio-only libraries
        
        ### Using asyncio libraries under the asyncio backend
        
        If a third-party library exposes only an asyncio interface (returns asyncio coroutine objects), it works directly under the asyncio backend because AnyIO runs on top of asyncio's event loop:
        
        ```python
        import anyio
        import some_asyncio_only_lib  # returns asyncio.Future/coroutine objects
        
        async def main() -> None:
            # This works because under the asyncio backend, await passes through
            result = await some_asyncio_only_lib.fetch_data()
        
        anyio.run(main, backend="asyncio")
        ```
        
        **Important**: This only works on the `asyncio` backend. On the `trio` backend, asyncio-native objects will not work.
        
        ### When you MUST use asyncio APIs
        
        Some APIs have no AnyIO equivalent and require direct event loop access:
        
        | Scenario | asyncio API | AnyIO approach |
        |----------|-------------|----------------|
        | Signal handlers | `loop.add_signal_handler()` | `anyio.open_signal_receiver()` |
        | Custom protocols | `asyncio.Protocol` | Use AnyIO streams / sockets |
        | Direct Future manipulation | `asyncio.Future` | Avoid; use AnyIO primitives |
        | Eager task factories | `asyncio.eager_task_factory` | Experimental in AnyIO; avoid |
        
        If you absolutely need the running loop:
        
        ```python
        import asyncio
        
        async def main() -> None:
            loop = asyncio.get_running_loop()
            # ... do something loop-specific ...
            # WARNING: this breaks backend-agnosticism
        
        anyio.run(main, backend="asyncio")
        ```
        
        **Best practice**: Wrap asyncio-only code in a backend-agnostic facade, and document that the feature requires the asyncio backend.
        
        ---
        
        ## 8. Idiomatic Code Snippets
        
        ### Snippet 1: Parallel HTTP requests with timeout and cleanup
        
        ```python
        import anyio
        
        async def fetch(url: str) -> bytes:
            await anyio.sleep(0.5)  # simulate
            return b"data"
        
        async def main() -> None:
            urls = ["a", "b", "c"]
            async with anyio.create_task_group() as tg:
                with anyio.move_on_after(5):
                    for url in urls:
                        tg.start_soon(fetch, url)
                # All tasks are cancelled on timeout; task group waits for cleanup
        
        anyio.run(main)
        ```
        
        ### Snippet 2: Producer-consumer with memory object stream
        
        ```python
        import anyio
        from anyio.streams.memory import MemoryObjectReceiveStream
        
        async def producer(send_stream: anyio.streams.memory.MemoryObjectSendStream[int]) -> None:
            async with send_stream:
                for i in range(100):
                    await send_stream.send(i)
        
        async def consumer(receive_stream: MemoryObjectReceiveStream[int]) -> None:
            async with receive_stream:
                async for item in receive_stream:
                    print(f"consumed {item}")
        
        async def main() -> None:
            send, receive = anyio.create_memory_object_stream[int](max_buffer_size=5)
            async with anyio.create_task_group() as tg:
                tg.start_soon(producer, send)
                tg.start_soon(consumer, receive)
        
        anyio.run(main)
        ```
        
        ### Snippet 3: Calling sync code from async
        
        ```python
        import time
        import anyio
        
        async def main() -> None:
            # Run blocking function in worker thread
            result = await anyio.to_thread.run_sync(time.sleep, 2)
            print("done")
        
        anyio.run(main)
        ```
        
        ### Snippet 4: Calling async code from a worker thread
        
        ```python
        import anyio
        
        def blocking_callback() -> None:
            # Inside a worker thread, call back into the event loop
            anyio.from_thread.run(anyio.sleep, 1)
            anyio.from_thread.run_sync(print, "hello from thread")
        
        async def main() -> None:
            await anyio.to_thread.run_sync(blocking_callback)
        
        anyio.run(main)
        ```
        
        ### Snippet 5: Graceful shutdown with shielded cleanup
        
        ```python
        import anyio
        
        async def worker() -> None:
            try:
                await anyio.sleep_forever()
            except anyio.get_cancelled_exc_class():
                with anyio.CancelScope(shield=True):
                    await anyio.sleep(0.5)  # cleanup
                    print("cleaned up")
                raise
        
        async def main() -> None:
            async with anyio.create_task_group() as tg:
                tg.start_soon(worker)
                await anyio.sleep(1)
                tg.cancel_scope.cancel()
        
        anyio.run(main)
        ```
        
        ---
        
        ## Sources
        
        - AnyIO Documentation (stable): https://anyio.readthedocs.io/en/stable/
        - AnyIO GitHub (HEAD `cb245dba`): https://github.com/agronholm/anyio
        - Task Groups: https://anyio.readthedocs.io/en/stable/tasks.html
        - Cancellation & Timeouts: https://anyio.readthedocs.io/en/stable/cancellation.html
        - Streams: https://anyio.readthedocs.io/en/stable/streams.html
        - Synchronization: https://anyio.readthedocs.io/en/stable/synchronization.html
        - Threads: https://anyio.readthedocs.io/en/stable/threads.html
        - Basics / Backends: https://anyio.readthedocs.io/en/stable/basics.html
        - Design Rationale (why asyncio is problematic): https://anyio.readthedocs.io/en/stable/why.html
        
      • data-modeling.md 6.6 KB
        # Data Modeling
        
        Which container to use, how to structure data, and why frozen is the default.
        
        ---
        
        ## Decision flowchart
        
        ```
        Is it a fixed set of named constants?
          YES → StrEnum / IntEnum
          NO ↓
        Is it just branding a primitive (int, str, float)?
          YES → NewType("X", base)
          NO ↓
        Is it an interface / contract ("this thing can do X")?
          ├─ Shape only, no shared code → Protocol
          └─ Shared method implementation needed → ABC
          NO ↓
        Does the data cross a trust boundary (user input, API, file, external DB)?
          YES → pydantic.BaseModel (frozen=True) — validates + serializes
          NO ↓
        Is it a dict shape needed for JSON compat / **kwargs typing?
          YES → TypedDict
          NO ↓
        Is it structured data with named fields?
          YES → @dataclass(frozen=True, slots=True)
          NO ↓
        Is it a tuple with positional semantics (x, y coords / DB row)?
          YES → NamedTuple
          NO → you probably don't need a new type
        ```
        
        ---
        
        ## Container reference
        
        ### @dataclass — internal value object
        
        The default for structured data inside your codebase. Zero overhead, no framework coupling.
        
        ```python
        from dataclasses import dataclass
        from typing import NewType
        
        UserId = NewType("UserId", int)
        
        @dataclass(frozen=True, slots=True)
        class User:
            id: UserId
            name: str
            email: str
        
        @dataclass(frozen=True, slots=True)
        class Point:
            x: float
            y: float
        ```
        
        Always `frozen=True, slots=True`. Mutable only when mutation is the documented purpose — opt out with `# noqa: MUTABLE_OK`.
        
        ### Pydantic BaseModel — trust boundary guardian
        
        Use when data enters or leaves your system. Validates at construction, serializes to JSON, generates OpenAPI schema.
        
        ```python
        from pydantic import BaseModel, ConfigDict, EmailStr
        
        class CreateUserRequest(BaseModel):
            model_config = ConfigDict(frozen=True)
        
            name: str
            email: EmailStr
            age: int
        
        class UserResponse(BaseModel):
            model_config = ConfigDict(frozen=True)
        
            id: int
            name: str
            email: str
        ```
        
        **The one rule**: data crosses a trust boundary → Pydantic. Everything else → dataclass.
        Never use Pydantic for internal-only data just because it's convenient. The validation cost is real.
        
        ### TypedDict — dict that knows its shape
        
        Use when the value must stay a `dict` at runtime — JSON blobs, `**kwargs`, third-party APIs expecting dicts.
        
        ```python
        from typing import TypedDict, NotRequired
        
        class Headers(TypedDict):
            content_type: str
            authorization: NotRequired[str]
        
        def make_request(url: str, headers: Headers) -> None: ...
        
        make_request("https://api.example.com", {"content_type": "application/json"})
        ```
        
        ### Protocol — structural interface
        
        "Anything that has method X" — no inheritance required.
        
        ```python
        from typing import Protocol
        
        class Renderable(Protocol):
            def render(self) -> str: ...
        
        class Saveable(Protocol):
            async def save(self) -> None: ...
        
        @dataclass(frozen=True, slots=True)
        class MarkdownDoc:
            content: str
            def render(self) -> str:
                return self.content
        
        def publish(doc: Renderable) -> None:
            print(doc.render())  # MarkdownDoc works — no inheritance needed
        ```
        
        Default to Protocol for interfaces. ABC only when you need shared method implementations.
        
        ### ABC — interface with shared code
        
        Only when Protocol isn't enough.
        
        ```python
        from abc import ABC, abstractmethod
        
        class BaseRepository(ABC):
            @abstractmethod
            async def get(self, id: int) -> Model | None: ...
        
            @abstractmethod
            async def save(self, model: Model) -> None: ...
        
            async def get_or_raise(self, id: int) -> Model:
                result = await self.get(id)
                if result is None:
                    msg = f"{type(self).__name__}: id {id} not found"
                    raise LookupError(msg)
                return result
        ```
        
        ### NamedTuple — positional + named (rare)
        
        Only when you need tuple protocol (unpacking, indexing).
        
        ```python
        from typing import NamedTuple
        
        class Coordinate(NamedTuple):
            x: float
            y: float
        
        x, y = Coordinate(1.0, 2.0)  # tuple unpacking
        ```
        
        99% of the time, `@dataclass(frozen=True, slots=True)` is better.
        
        ---
        
        ## Quick lookup
        
        | Situation | Use | Why |
        |---|---|---|
        | User input, API request/response | `Pydantic BaseModel` | Validation, JSON schema, serialization |
        | DB row ↔ Python (ORM) | SQLAlchemy `Mapped[]` model | ORM integration, async session |
        | Internal value object | `@dataclass(frozen=True, slots=True)` | Zero overhead, no validation needed |
        | Multiple outcomes from function | Union of frozen dataclasses | Distinct types for `match` |
        | Dict shape for JSON / `**kwargs` | `TypedDict` | Stays a dict at runtime |
        | Fixed constants | `StrEnum` / `IntEnum` | Exhaustive match, no typos |
        | Distinct primitive | `NewType("X", int)` | Zero runtime cost, type-level only |
        | Contract / capability | `Protocol` | Structural typing, no inheritance |
        | Contract + shared impl | `ABC` | When Protocol isn't enough |
        
        ---
        
        ## Comparison matrix
        
        | Feature | dataclass | Pydantic | TypedDict | Protocol | NamedTuple | NewType | Enum |
        |---|---|---|---|---|---|---|---|
        | Validation | - | ✓ | - | - | - | - | - |
        | JSON serialization | manual | built-in | native dict | - | - | - | `.value` |
        | Immutable | frozen=True | frozen=True | - (dict) | N/A | always | N/A | always |
        | Runtime cost | ~zero | validation | zero | zero | ~zero | zero | ~zero |
        | `match` support | ✓ | ✓ | - | - | ✓ | - | ✓ |
        | `slots` support | ✓ | - | - | - | - | - | - |
        
        ---
        
        ## Parse, don't validate
        
        Validate at the boundary. Inside the boundary, types are proof of validity.
        
        ```python
        # BAD — validate then pass raw data
        def process_email(email: str) -> None:
            if "@" not in email:
                raise ValueError("invalid email")
            # still a raw str everywhere downstream
        
        # GOOD — parse into typed value at boundary
        from typing import NewType
        
        Email = NewType("Email", str)
        
        def parse_email(raw: str) -> Email:
            if "@" not in raw or "." not in raw.split("@")[1]:
                msg = f"invalid email: {raw}"
                raise ValueError(msg)
            return Email(raw.lower().strip())
        
        # Downstream only sees Email, never raw str
        def send_welcome(email: Email) -> None: ...
        ```
        
        With Pydantic this happens automatically — `EmailStr` is already a parsed type. Once constructed, `.email` is always valid. No re-validation needed.
        
        ---
        
        ## Sources
        
        - Python docs: [dataclasses](https://docs.python.org/3/library/dataclasses.html)
        - Pydantic v2: [docs.pydantic.dev](https://docs.pydantic.dev/latest/)
        - Python docs: [typing — Protocol](https://docs.python.org/3/library/typing.html#typing.Protocol)
        - Python docs: [typing — TypedDict](https://docs.python.org/3/library/typing.html#typing.TypedDict)
        - Alexis King: [Parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)
        
      • data-processing.md 4.2 KB
        # Data Processing — Polars + DuckDB
        
        ## The rule
        
        NEVER pandas. Polars (with numpy) plus DuckDB. Pandas is 10-50x slower, has weaker types, and the modern Python data ecosystem has moved on.
        
        ## Quick decision tree
        
        | Operation | Use | Why |
        |---|---|---|
        | `.csv` / `.parquet` / `.json` direct query | DuckDB | Zero memory load, SQL ergonomics |
        | `.duckdb` file | DuckDB | Native format |
        | Filter (any size) | Polars | 128x faster than DuckDB for filtering |
        | Sort | Polars | 12x faster |
        | Multi-table join | DuckDB | 3x faster, more join types |
        | Heavy GROUP BY aggregation | DuckDB | 4x faster on large datasets |
        | Window function | Polars | 3-5x faster |
        | Pivot / melt / string ops | Polars | 2x faster |
        | Larger than RAM | Polars streaming or DuckDB out-of-core | Both handle OOM |
        | Mixed pipeline | Hybrid (zero-copy via Arrow) | Use each tool's strengths |
        
        For the deep version (per-operation benchmarks, OOM strategies, full execution templates), load the **`data-scientist`** skill - it lives in this same skill set and is the source of truth for performance numbers.
        
        ## Standard imports
        
        ```python
        import numpy as np
        import polars as pl
        import duckdb
        ```
        
        ## DuckDB direct file query (zero memory load)
        
        ```python
        result = duckdb.sql("""
            SELECT category, SUM(amount) AS total
            FROM 'data.csv'
            WHERE date >= '2026-01-01'
            GROUP BY category
            ORDER BY total DESC
        """).pl()  # zero-copy → Polars DataFrame
        ```
        
        `.pl()` returns Polars; `.df()` would return pandas - never use `.df()`.
        
        ## Polars lazy pipeline
        
        ```python
        result = (
            pl.scan_csv("data.csv")              # lazy, no read yet
            .filter(pl.col("amount") > 1000)
            .filter(pl.col("status") == "active")
            .sort("amount", descending=True)
            .head(100)
            .collect()                            # execute optimised plan
        )
        ```
        
        `scan_*` over `read_*` for files; `lazy()` then `collect()` for in-memory frames. Polars optimises the entire plan before execution (predicate pushdown, projection pushdown, common subexpression elimination).
        
        ## Streaming for OOM data
        
        ```python
        result = (
            pl.scan_csv("huge.csv")
            .filter(pl.col("active"))
            .group_by("category")
            .agg([
                pl.len().alias("count"),
                pl.sum("amount").alias("total"),
            ])
            .collect(streaming=True)
        )
        ```
        
        ## Hybrid pipeline (most realistic shape)
        
        ```python
        # Phase 1: DuckDB for the join (3x faster)
        joined = duckdb.sql("""
            SELECT o.*, c.region, p.category
            FROM 'orders.parquet' o
            JOIN 'customers.parquet' c ON o.customer_id = c.id
            JOIN 'products.parquet'  p ON o.product_id  = p.id
        """).pl()
        
        # Phase 2: Polars for filtering and transformation (128x + 2x faster)
        processed = (
            joined
            .filter(pl.col("amount") > 100)
            .with_columns([
                (pl.col("amount") * 1.1).alias("amount_with_tax"),
            ])
        )
        
        # Phase 3: DuckDB for final aggregation (4x faster) - register Polars frame by name
        duckdb.register("processed", processed)
        final = duckdb.sql("""
            SELECT region, category, SUM(amount_with_tax) AS revenue
            FROM processed
            GROUP BY region, category
            ORDER BY revenue DESC
        """).pl()
        ```
        
        ## Type safety with Polars
        
        Polars supports schema overrides at read time, and `.cast()` for explicit conversion. Avoid implicit coercion in hot paths.
        
        ```python
        schema = {"id": pl.Int64, "amount": pl.Float64, "date": pl.Date}
        df = pl.read_csv("data.csv", schema_overrides=schema)
        ```
        
        basedpyright understands `polars-stubs`, which ship with polars itself. No extra type stubs to install.
        
        ## Things you might miss from pandas (and how to do them in Polars)
        
        | pandas | polars |
        |---|---|
        | `df.iloc[5]` | `df.row(5)` (named tuple) or `df[5]` (single-row frame) |
        | `df.loc[df["x"] > 5]` | `df.filter(pl.col("x") > 5)` |
        | `df["x"].apply(fn)` | `df["x"].map_elements(fn)` (slow path) or use native expressions |
        | `df.merge(...)` | `df.join(other, on="key")` |
        | `df.groupby(...).agg(...)` | `df.group_by(...).agg(...)` |
        | `pd.read_csv(...).dtypes` | `pl.read_csv(...).schema` |
        | `df.to_dict("records")` | `df.to_dicts()` |
        
        ## Sources
        
        - Polars docs: <https://docs.pola.rs>
        - DuckDB Python API: <https://duckdb.org/docs/api/python/overview>
        - Cross-reference - this skill set's `data-scientist` skill (load it for the deep version)
        
      • error-handling.md 6 KB
        # Error Handling
        
        Typed errors, exhaustive matching, union returns, and resource safety.
        
        ---
        
        ## Typed errors — no bare strings
        
        Error types carry structured data. Pattern matching works. Callers know exactly what can go wrong.
        
        ```python
        from dataclasses import dataclass
        from typing import NewType
        
        UserId = NewType("UserId", int)
        
        @dataclass(frozen=True, slots=True)
        class UserNotFoundError(Exception):
            user_id: UserId
        
            def __str__(self) -> str:  # REQUIRED — see note below
                return f"user {self.user_id} not found"
        
        @dataclass(frozen=True, slots=True)
        class PermissionDeniedError(Exception):
            user_id: UserId
            required_role: str
        
            def __str__(self) -> str:
                return f"user {self.user_id} needs role {self.required_role}"
        ```
        
        **`__str__` is mandatory** on dataclass exceptions. `@dataclass` replaces `Exception.__init__`, so `self.args` is always `()`. Without `__str__`, `str(e)` returns an empty string and logging/monitoring breaks.
        
        ```python
        # BAD
        raise ValueError("user not found")
        raise ValueError("permission denied")
        
        # GOOD
        raise UserNotFoundError(user_id=uid)
        raise PermissionDeniedError(user_id=uid, required_role="admin")
        ```
        
        ---
        
        ## Union returns — expected failures without exceptions
        
        For failures that are **expected** (not found, validation error, permission denied), return a union instead of raising. Exceptions are for **unexpected** failures (network down, OOM, corrupted data).
        
        ### Define the outcome types
        
        ```python
        @dataclass(frozen=True, slots=True)
        class User:
            id: UserId
            name: str
        
        @dataclass(frozen=True, slots=True)
        class UserNotFound:
            id: UserId
        
        @dataclass(frozen=True, slots=True)
        class PermissionDenied:
            id: UserId
            reason: str
        
        type GetUserResult = User | UserNotFound | PermissionDenied
        ```
        
        ### Handle exhaustively
        
        ```python
        from typing import assert_never
        
        def handle_result(result: GetUserResult) -> str:
            match result:
                case User(name=name):
                    return f"Found: {name}"
                case UserNotFound(id=uid):
                    return f"No user with id {uid}"
                case PermissionDenied(reason=reason):
                    return f"Denied: {reason}"
                case _ as unreachable:
                    assert_never(unreachable)
        ```
        
        `assert_never` in the default case: if you add a new variant to `GetUserResult` without handling it here, the type checker errors. No silent fall-through.
        
        ### When to use which
        
        **The heuristic**: caller is 1-2 levels away and MUST handle it → union return. Error should propagate up many layers to a boundary → exception.
        
        | Scenario | Pattern | Why |
        |---|---|---|
        | Repository → service (caller handles it) | Union return (`User \| UserNotFound`) | Caller is right there, must handle both |
        | Validation at boundary (parsing input) | Exception (typed, with fields) | Propagates up to HTTP/CLI handler |
        | Infrastructure failure (network, OOM) | Exception | Can't handle locally, must propagate |
        | Service → service (deep internal) | Exception (typed) | Union boilerplate across many layers is worse than exceptions |
        | HTTP handler → response | Catch exceptions, convert to response | Boundary code catches and translates |
        
        **Practical tradeoff**: union returns are safest (type checker forces handling) but create boilerplate when every caller in a chain must `match`. If the error would just propagate through 3+ layers unchanged, use a typed exception instead.
        
        ---
        
        ## Exhaustive match — every match needs a default
        
        Every `match` statement ends with `case _: assert_never(x)`. No exceptions.
        
        ```python
        from enum import StrEnum
        from typing import assert_never
        
        class Status(StrEnum):
            PENDING = "pending"
            ACTIVE = "active"
            DELETED = "deleted"
        
        def describe(status: Status) -> str:
            match status:
                case Status.PENDING:
                    return "waiting"
                case Status.ACTIVE:
                    return "live"
                case Status.DELETED:
                    return "gone"
                case _ as unreachable:
                    assert_never(unreachable)
        ```
        
        Add a new enum member? The type checker tells you every `match` that needs updating.
        
        ---
        
        ## Context managers — resource safety
        
        If it has `.close()`, `.shutdown()`, `.disconnect()`, or `.release()`, wrap it in `with`.
        
        ```python
        # BAD
        f = open("data.txt")
        data = f.read()
        f.close()  # forgotten? leaked
        
        # GOOD
        from pathlib import Path
        
        data = Path("data.txt").read_text()
        ```
        
        ### Async resources
        
        ```python
        import httpx
        
        async def fetch_users() -> list[User]:
            async with httpx.AsyncClient() as client:
                response = await client.get("https://api.example.com/users")
                response.raise_for_status()
                return [User(**u) for u in response.json()]
        ```
        
        ### Custom context manager
        
        ```python
        from contextlib import asynccontextmanager
        from collections.abc import AsyncIterator
        
        @asynccontextmanager
        async def managed_connection(url: str) -> AsyncIterator[Connection]:
            conn = await connect(url)
            try:
                yield conn
            finally:
                await conn.close()
        
        async with managed_connection("postgres://...") as conn:
            await conn.execute("SELECT 1")
        # conn is closed here, guaranteed
        ```
        
        ---
        
        ## Exception hierarchy — when you do raise
        
        Keep exception hierarchies shallow and specific.
        
        ```python
        class AppError(Exception):
            """Base for all application errors."""
        
        @dataclass(frozen=True, slots=True)
        class NotFoundError(AppError):
            entity: str
            id: int
        
            def __str__(self) -> str:
                return f"{self.entity} {self.id} not found"
        
        @dataclass(frozen=True, slots=True)
        class ConflictError(AppError):
            entity: str
            field: str
            value: str
        
            def __str__(self) -> str:
                return f"{self.entity}.{self.field} = {self.value!r} already exists"
        ```
        
        Callers catch `AppError` at the boundary, or specific subtypes where they can do something useful.
        
        ---
        
        ## Sources
        
        - Python docs: [typing — assert_never](https://docs.python.org/3/library/typing.html#typing.assert_never)
        - Python docs: [contextlib](https://docs.python.org/3/library/contextlib.html)
        - Python docs: [match statement](https://docs.python.org/3/reference/compound_stmts.html#the-match-statement)
        
      • fastapi-stack.md 9.2 KB
        # FastAPI + SQLAlchemy 2.x async + Postgres + Pydantic v2
        
        The canonical web API stack. Async end-to-end, type-safe end-to-end, OpenAPI-generated end-to-end.
        
        ## Project layout
        
        ```
        myapi/
        ├── pyproject.toml
        ├── alembic.ini
        ├── migrations/
        │   └── env.py
        ├── src/
        │   └── myapi/
        │       ├── __init__.py
        │       ├── main.py            # FastAPI app + lifespan
        │       ├── config.py          # pydantic-settings
        │       ├── db.py              # engine, session factory, dependency
        │       ├── models.py          # SQLAlchemy declarative models
        │       ├── schemas.py         # Pydantic request/response models
        │       └── routers/
        │           ├── __init__.py
        │           └── users.py
        └── tests/
            ├── conftest.py
            └── test_users.py
        ```
        
        ## Dependencies
        
        ```bash
        uv add fastapi 'sqlalchemy[asyncio]>=2.0' asyncpg 'pydantic[email]>=2' pydantic-settings 'uvicorn[standard]' orjson
        uv add --dev httpx pytest alembic
        ```
        
        `orjson` is mandatory: set `default_response_class=ORJSONResponse` on the FastAPI app. Pydantic-typed responses bypass it (Pydantic v2's `model_dump_json` is already Rust-backed); raw `dict` / `list` returns are accelerated. For SSE / NDJSON streams, call `orjson.dumps(...)` per chunk inside `StreamingResponse`. See `orjson-stack.md` for the decision tree, flag reference, and benchmarks.
        
        ## Configuration (`config.py`)
        
        ```python
        from functools import lru_cache
        
        from pydantic import Field, PostgresDsn
        from pydantic_settings import BaseSettings, SettingsConfigDict
        
        
        class Settings(BaseSettings):
            model_config = SettingsConfigDict(env_file=".env", env_prefix="MYAPI_")
        
            database_url: PostgresDsn
            debug: bool = False
            cors_origins: list[str] = Field(default_factory=list)
        
        
        @lru_cache
        def get_settings() -> Settings:
            return Settings()  # type: ignore[call-arg]  # pydantic populates from env
        ```
        
        Wait — that comment violates the no-excuse rule. Use proper field defaults instead. Real version:
        
        ```python
        class Settings(BaseSettings):
            model_config = SettingsConfigDict(env_file=".env", env_prefix="MYAPI_")
            database_url: PostgresDsn
            debug: bool = False
            cors_origins: list[str] = Field(default_factory=list)
        ```
        
        Construct via `Settings(_env_file=".env")` if needed in tests; in production it reads from env.
        
        ## Database (`db.py`)
        
        ```python
        from collections.abc import AsyncIterator
        from typing import Annotated
        
        from fastapi import Depends
        from sqlalchemy.ext.asyncio import (
            AsyncEngine,
            AsyncSession,
            async_sessionmaker,
            create_async_engine,
        )
        
        from myapi.config import get_settings
        
        
        def make_engine() -> AsyncEngine:
            settings = get_settings()
            return create_async_engine(
                str(settings.database_url),
                echo=settings.debug,
                pool_pre_ping=True,
            )
        
        
        _engine = make_engine()
        _SessionFactory = async_sessionmaker(_engine, expire_on_commit=False)
        
        
        async def get_session() -> AsyncIterator[AsyncSession]:
            async with _SessionFactory() as session:
                yield session
        
        
        SessionDep = Annotated[AsyncSession, Depends(get_session)]
        ```
        
        `expire_on_commit=False` is essential for FastAPI - otherwise attribute access after commit triggers an implicit refresh and errors out under async.
        
        ## Models (`models.py`)
        
        ```python
        from datetime import datetime, UTC
        from sqlalchemy import DateTime, String, func
        from sqlalchemy.orm import (
            DeclarativeBase,
            Mapped,
            MappedAsDataclass,
            mapped_column,
        )
        
        
        class Base(MappedAsDataclass, DeclarativeBase):
            pass
        
        
        class User(Base):
            __tablename__ = "users"
        
            id: Mapped[int] = mapped_column(primary_key=True, init=False)
            email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
            name: Mapped[str] = mapped_column(String(100))
            created_at: Mapped[datetime] = mapped_column(
                DateTime(timezone=True),
                server_default=func.now(),
                init=False,
            )
        ```
        
        `MappedAsDataclass` makes `User(email=..., name=...)` work as a real dataclass constructor. `init=False` excludes the auto-generated columns (`id`, `created_at`) from `__init__`.
        
        ## Schemas (`schemas.py`)
        
        ```python
        from datetime import datetime
        from pydantic import BaseModel, ConfigDict, EmailStr
        
        
        class UserCreate(BaseModel):
            email: EmailStr
            name: str
        
        
        class UserRead(BaseModel):
            model_config = ConfigDict(from_attributes=True)  # SQLAlchemy → Pydantic
        
            id: int
            email: EmailStr
            name: str
            created_at: datetime
        ```
        
        Always have a separate `*Create` (input) and `*Read` (output) model. Never expose your ORM model as the API model.
        
        ## Routers (`routers/users.py`)
        
        ```python
        from fastapi import APIRouter, HTTPException, status
        from sqlalchemy import select
        
        from myapi.db import SessionDep
        from myapi.models import User
        from myapi.schemas import UserCreate, UserRead
        
        router = APIRouter(prefix="/users", tags=["users"])
        
        
        @router.post("", response_model=UserRead, status_code=status.HTTP_201_CREATED)
        async def create_user(payload: UserCreate, session: SessionDep) -> User:
            user = User(email=payload.email, name=payload.name)
            session.add(user)
            await session.commit()
            await session.refresh(user)
            return user
        
        
        @router.get("/{user_id}", response_model=UserRead)
        async def get_user(user_id: int, session: SessionDep) -> User:
            result = await session.execute(select(User).where(User.id == user_id))
            user = result.scalar_one_or_none()
            if user is None:
                raise HTTPException(status.HTTP_404_NOT_FOUND, "User not found")
            return user
        
        
        @router.get("", response_model=list[UserRead])
        async def list_users(session: SessionDep, limit: int = 100) -> list[User]:
            result = await session.execute(select(User).limit(limit))
            return list(result.scalars().all())
        ```
        
        ## Application (`main.py`)
        
        ```python
        from contextlib import asynccontextmanager
        from collections.abc import AsyncIterator
        
        from fastapi import FastAPI
        
        from myapi.config import get_settings
        from myapi.routers import users
        
        
        @asynccontextmanager
        async def lifespan(_: FastAPI) -> AsyncIterator[None]:
            # Startup: warm up engine pool, run migrations check, etc.
            yield
            # Shutdown: close engine
            from myapi.db import _engine
            await _engine.dispose()
        
        
        def create_app() -> FastAPI:
            settings = get_settings()
            app = FastAPI(
                title="My API",
                debug=settings.debug,
                lifespan=lifespan,
            )
            app.include_router(users.router)
            return app
        
        
        app = create_app()
        ```
        
        Run with:
        
        ```bash
        uv run uvicorn myapi.main:app --host 0.0.0.0 --port 8000 --reload
        ```
        
        ## Migrations (Alembic + async)
        
        ```bash
        uv run alembic init -t async migrations
        ```
        
        In `migrations/env.py` replace the `target_metadata` line:
        
        ```python
        from myapi.models import Base
        target_metadata = Base.metadata
        ```
        
        Set `sqlalchemy.url` in `alembic.ini` to your async URL or override via `env.py`:
        
        ```python
        from myapi.config import get_settings
        config.set_main_option("sqlalchemy.url", str(get_settings().database_url))
        ```
        
        Generate and apply:
        
        ```bash
        uv run alembic revision --autogenerate -m "create users"
        uv run alembic upgrade head
        ```
        
        ## Tests (`tests/test_users.py`)
        
        ```python
        import pytest
        from httpx import ASGITransport, AsyncClient
        
        from myapi.main import app
        
        
        @pytest.mark.anyio
        async def test_create_and_get_user() -> None:
            async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
                create_response = await client.post(
                    "/users",
                    json={"email": "alice@example.com", "name": "Alice"},
                )
                assert create_response.status_code == 201
                user_id = create_response.json()["id"]
        
                get_response = await client.get(f"/users/{user_id}")
                assert get_response.status_code == 200
                assert get_response.json()["email"] == "alice@example.com"
        ```
        
        For database-backed tests, run a Postgres container in CI (`testcontainers-python` or `docker-compose`) and apply migrations against a test schema. SQLite-as-test-db breaks once you use Postgres-specific types (`JSONB`, `tsvector`, arrays).
        
        ## Common pitfalls
        
        | Pitfall | Fix |
        |---|---|
        | `MissingGreenlet` exception when accessing relationships after commit | `expire_on_commit=False` on the session factory |
        | Connection pool exhausted under load | Set `pool_size`, `max_overflow` in `create_async_engine` |
        | Pydantic v1 syntax (`from pydantic import ...; class X(BaseModel): class Config: orm_mode = True`) | v2 uses `model_config = ConfigDict(from_attributes=True)` |
        | Returning ORM objects without `response_model` | FastAPI serialises with `from_attributes=True` automatically; declare `response_model` so OpenAPI is correct |
        | `await session.execute(...)` returning Sequence | Wrap with `list(result.scalars().all())` to satisfy strict types |
        | `func.now()` returning naive datetime | Use `DateTime(timezone=True)` and `created_at: Mapped[datetime]` with `UTC`-aware default |
        
        ## Sources
        
        - FastAPI: <https://fastapi.tiangolo.com>
        - SQLAlchemy 2.x async: <https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html>
        - SQLAlchemy MappedAsDataclass: <https://docs.sqlalchemy.org/en/20/orm/dataclasses.html>
        - asyncpg: <https://magicstack.github.io/asyncpg/current/>
        - Pydantic v2 migration: <https://docs.pydantic.dev/latest/migration/>
        - Alembic async: <https://alembic.sqlalchemy.org/en/latest/cookbook.html#using-asyncio-with-alembic>
        
      • httpx2-optimization.md 12 KB
        # httpx2 — Production Defaults
        
        > **Source**: [pydantic/httpx2](https://github.com/pydantic/httpx2) — next-generation HTTP client for Python 3, continuation of HTTPX under Pydantic stewardship.
        >
        > **Rule**: Every network request MUST use `httpx2`. **ALL optimizations below are ON by default** — HTTP/2, brotli+zstd, tuned connection pool, fine-grained timeouts, transport retries, TCP_NODELAY. This is the baseline, not a stretch goal. A bare `httpx2.AsyncClient()` is a bug.
        
        ---
        
        ## 1. Installation — all extras, always
        
        ```toml
        # pyproject.toml
        dependencies = [
            "httpx2[http2,brotli,zstd]",
        ]
        ```
        
        | Extra | What it enables | Why it's mandatory |
        |-------|----------------|--------------------|
        | `http2` | HTTP/2 multiplexing via `h2` | Single TCP connection handles concurrent requests; eliminates head-of-line blocking |
        | `brotli` | Brotli content decoding (`br`) | ~20% smaller payloads than gzip for text/JSON |
        | `zstd` | Zstandard content decoding | Faster decompression than brotli at similar ratios; stdlib in Python ≥ 3.14 |
        | `socks` | SOCKS5 proxy support via `socksio` | Install only if you route through SOCKS proxies |
        
        All three core extras (`http2,brotli,zstd`) are non-negotiable. Omitting any is leaving performance on the table.
        
        ---
        
        ## 2. The canonical defaults — ALL ON
        
        These are not "optimizations to consider". These are **the correct defaults** that every httpx2 client must use.
        
        ```python
        import socket
        import httpx2
        
        # ── These are the STANDARD values. Use them verbatim. ──
        
        LIMITS = httpx2.Limits(
            max_connections=200,           # library default 100 is too conservative
            max_keepalive_connections=40,  # library default 20 wastes reconnects
            keepalive_expiry=30.0,         # library default 5s kills warm connections too fast
        )
        
        TIMEOUT = httpx2.Timeout(
            connect=5.0,    # TCP + TLS handshake budget
            read=30.0,      # time to receive a response chunk
            write=10.0,     # time to send a request chunk
            pool=10.0,      # time to acquire a connection from pool
        )
        
        SOCKET_OPTIONS: list[tuple[int, int, int]] = [
            (socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),   # disable Nagle — no 40ms delay
        ]
        ```
        
        ### Why each knob is set this way
        
        | Setting | Library default | Our default | Why |
        |---------|----------------|-------------|-----|
        | `http2` | `False` | **`True`** | HTTP/2 multiplexing is strictly superior for any modern API |
        | `max_connections` | `100` | `200` | Headroom for fan-out; prevents pool exhaustion under load |
        | `max_keepalive_connections` | `20` | `40` | Keeps warm connections alive; fewer TLS handshakes |
        | `keepalive_expiry` | `5.0s` | `30.0s` | 5s is too aggressive — kills connections between burst requests |
        | `Timeout(5.0)` uniform | `5.0` all | Split | Uniform 5s is too tight for reads, too loose for connects |
        | `read` timeout | `5.0` | `30.0` | Slow APIs and streaming need breathing room |
        | `pool` timeout | `5.0` | `10.0` | Explicit — hitting this means `max_connections` needs raising |
        | `TCP_NODELAY` | off | **on** | Eliminates Nagle's 40ms coalescing delay for small payloads |
        | `retries` | `0` | `3` | Retries on `ConnectError`/`ConnectTimeout` only — safe and resilient |
        | `follow_redirects` | `False` | **`True`** | Most APIs redirect; failing on 3xx is wrong default behavior |
        
        ---
        
        ## 3. Factory functions — the ONE correct way to create clients
        
        Copy this into your project. This is the canonical pattern.
        
        ```python
        """httpx2 client factory. Always use create_client() / create_async_client()."""
        
        from __future__ import annotations
        
        import socket
        import typing
        
        import httpx2
        
        _LIMITS = httpx2.Limits(
            max_connections=200,
            max_keepalive_connections=40,
            keepalive_expiry=30.0,
        )
        
        _TIMEOUT = httpx2.Timeout(
            connect=5.0,
            read=30.0,
            write=10.0,
            pool=10.0,
        )
        
        _SOCKET_OPTIONS: list[tuple[int, int, int]] = [
            (socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),
        ]
        
        
        def create_async_client(
            *,
            base_url: str = "",
            http2: bool = True,
            retries: int = 3,
            limits: httpx2.Limits = _LIMITS,
            timeout: httpx2.Timeout = _TIMEOUT,
            headers: dict[str, str] | None = None,
            event_hooks: dict[str, list[typing.Callable[..., typing.Any]]] | None = None,
            **kwargs: typing.Any,
        ) -> httpx2.AsyncClient:
            transport = httpx2.AsyncHTTPTransport(
                http2=http2,
                retries=retries,
                limits=limits,
                socket_options=_SOCKET_OPTIONS,
            )
            return httpx2.AsyncClient(
                transport=transport,
                timeout=timeout,
                base_url=base_url,
                headers=headers or {},
                event_hooks=event_hooks or {},
                follow_redirects=True,
                **kwargs,
            )
        
        
        def create_client(
            *,
            base_url: str = "",
            http2: bool = True,
            retries: int = 3,
            limits: httpx2.Limits = _LIMITS,
            timeout: httpx2.Timeout = _TIMEOUT,
            headers: dict[str, str] | None = None,
            event_hooks: dict[str, list[typing.Callable[..., typing.Any]]] | None = None,
            **kwargs: typing.Any,
        ) -> httpx2.Client:
            transport = httpx2.HTTPTransport(
                http2=http2,
                retries=retries,
                limits=limits,
                socket_options=_SOCKET_OPTIONS,
            )
            return httpx2.Client(
                transport=transport,
                timeout=timeout,
                base_url=base_url,
                headers=headers or {},
                event_hooks=event_hooks or {},
                follow_redirects=True,
                **kwargs,
            )
        ```
        
        Usage:
        
        ```python
        # Async — the common case
        async with create_async_client(base_url="https://api.example.com") as client:
            r = await client.get("/users")
        
        # Sync
        with create_client() as client:
            r = client.get("https://api.example.com/health")
        ```
        
        **If you are NOT using this factory pattern, you are doing it wrong.** A bare `httpx2.AsyncClient()` leaves HTTP/2 off, retries off, TCP_NODELAY off, keepalive too short, and timeouts too uniform.
        
        ---
        
        ## 4. Special case overrides
        
        The factory defaults cover 95% of use cases. Override only when you have a specific reason:
        
        | Scenario | Override |
        |----------|----------|
        | LLM streaming endpoints | `timeout=httpx2.Timeout(connect=10.0, read=None, write=10.0, pool=10.0)` — no read timeout on streaming |
        | Single-host API with low concurrency | `limits=httpx2.Limits(max_connections=50, max_keepalive_connections=20, keepalive_expiry=60.0)` |
        | Ephemeral short-lived requests | `keepalive_expiry=5.0` — don't hold connections |
        | Unix domain sockets | `httpx2.AsyncHTTPTransport(uds="/path/to/socket", ...)` |
        | mTLS / client certs | Pass `verify=ssl_ctx` with `ctx.load_cert_chain(certfile=...)` |
        | SOCKS proxy | `httpx2[socks]`, `proxy="socks5://..."` |
        
        ---
        
        ## 5. Event hooks — always wire observability
        
        This is not optional. Every production client should log requests.
        
        ```python
        import time
        import logging
        
        logger = logging.getLogger(__name__)
        
        async def log_request(request: httpx2.Request) -> None:
            request.extensions["request_start"] = time.perf_counter()
        
        async def log_response(response: httpx2.Response) -> None:
            start = response.request.extensions.get("request_start", 0)
            elapsed = time.perf_counter() - start
            logger.info(
                "HTTP %s %s → %d (%.3fs, %s)",
                response.request.method,
                response.request.url,
                response.status_code,
                elapsed,
                response.http_version,
            )
        
        # Sync versions for Client
        def log_request_sync(request: httpx2.Request) -> None:
            request.extensions["request_start"] = time.perf_counter()
        
        def log_response_sync(response: httpx2.Response) -> None:
            start = response.request.extensions.get("request_start", 0)
            elapsed = time.perf_counter() - start
            logger.info(
                "HTTP %s %s → %d (%.3fs, %s)",
                response.request.method,
                response.request.url,
                response.status_code,
                elapsed,
                response.http_version,
            )
        ```
        
        For auto `raise_for_status()`:
        
        ```python
        async def raise_on_error(response: httpx2.Response) -> None:
            response.raise_for_status()
        ```
        
        ---
        
        ## 6. Verification script — confirm your setup is fully optimized
        
        Run this against your target endpoint to **verify** (not decide) that all optimizations are active:
        
        ```python
        """Verify httpx2 is fully optimized against a target endpoint."""
        
        from __future__ import annotations
        
        import socket
        import time
        
        import anyio
        import httpx2
        
        
        TARGET_URL = "https://api.example.com/health"
        ITERATIONS = 30
        
        
        async def bench(label: str, client: httpx2.AsyncClient, url: str, n: int) -> float:
            for _ in range(3):  # warmup
                await client.get(url)
            start = time.perf_counter()
            for _ in range(n):
                r = await client.get(url)
                assert r.status_code == 200
            elapsed = time.perf_counter() - start
            avg_ms = (elapsed / n) * 1000
            print(f"  {label}: {avg_ms:.1f}ms avg ({n} reqs in {elapsed:.2f}s)")
            return avg_ms
        
        
        async def main() -> None:
            results: dict[str, float] = {}
        
            # BAD: bare defaults (this is what we're proving is worse)
            async with httpx2.AsyncClient() as c:
                results["BAD-bare-defaults"] = await bench("BAD-bare-defaults", c, TARGET_URL, ITERATIONS)
        
            # GOOD: full production defaults (this is what we always use)
            limits = httpx2.Limits(max_connections=200, max_keepalive_connections=40, keepalive_expiry=30.0)
            timeout = httpx2.Timeout(connect=5.0, read=30.0, write=10.0, pool=10.0)
            transport = httpx2.AsyncHTTPTransport(
                http2=True, retries=3, limits=limits,
                socket_options=[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)],
            )
            async with httpx2.AsyncClient(transport=transport, timeout=timeout, follow_redirects=True) as c:
                results["GOOD-full-production"] = await bench("GOOD-full-production", c, TARGET_URL, ITERATIONS)
        
            print("\n--- Proof ---")
            baseline = results["BAD-bare-defaults"]
            for label, avg in results.items():
                delta = ((avg - baseline) / baseline) * 100
                print(f"  {label}: {avg:.1f}ms ({delta:+.1f}% vs bare)")
        
        
        if __name__ == "__main__":
            anyio.run(main)
        ```
        
        ---
        
        ## 7. Quick reference — all knobs
        
        ### `httpx2.AsyncClient` / `httpx2.Client`
        
        | Parameter | Type | Library Default | **Our Default** |
        |-----------|------|-----------------|-----------------|
        | `http1` | `bool` | `True` | `True` |
        | `http2` | `bool` | `False` | **`True`** |
        | `verify` | `ssl.SSLContext \| str \| bool` | `True` | `True` |
        | `cert` | `CertTypes \| None` | `None` | `None` |
        | `proxy` | `str \| Proxy \| None` | `None` | `None` |
        | `mounts` | `dict[str, Transport]` | `None` | `None` |
        | `timeout` | `Timeout \| float \| None` | `Timeout(5.0)` | **Split: 5/30/10/10** |
        | `limits` | `Limits` | `Limits(100, 20, 5.0)` | **`Limits(200, 40, 30.0)`** |
        | `follow_redirects` | `bool` | `False` | **`True`** |
        | `max_redirects` | `int` | `20` | `20` |
        | `event_hooks` | `dict` | `{}` | **Wire logging** |
        | `base_url` | `str` | `""` | Set for single-API clients |
        | `trust_env` | `bool` | `True` | `True` |
        | `default_encoding` | `str \| Callable` | `"utf-8"` | `"utf-8"` |
        
        ### `httpx2.AsyncHTTPTransport` / `httpx2.HTTPTransport`
        
        | Parameter | Type | Library Default | **Our Default** |
        |-----------|------|-----------------|-----------------|
        | `http1` | `bool` | `True` | `True` |
        | `http2` | `bool` | `False` | **`True`** |
        | `retries` | `int` | `0` | **`3`** |
        | `limits` | `Limits` | `Limits(100, 20, 5.0)` | **`Limits(200, 40, 30.0)`** |
        | `uds` | `str \| None` | `None` | `None` |
        | `local_address` | `str \| None` | `None` | `None` |
        | `socket_options` | `Iterable[SOCKET_OPTION]` | `None` | **`[TCP_NODELAY]`** |
        | `proxy` | `str \| Proxy \| None` | `None` | `None` |
        
        ### `httpx2.Timeout`
        
        | Parameter | Library Default | **Our Default** |
        |-----------|-----------------|-----------------|
        | `connect` | `5.0` | `5.0` |
        | `read` | `5.0` | **`30.0`** |
        | `write` | `5.0` | **`10.0`** |
        | `pool` | `5.0` | **`10.0`** |
        
        ### `httpx2.Limits`
        
        | Parameter | Library Default | **Our Default** |
        |-----------|-----------------|-----------------|
        | `max_connections` | `100` | **`200`** |
        | `max_keepalive_connections` | `20` | **`40`** |
        | `keepalive_expiry` | `5.0` | **`30.0`** |
        
        ### Async backend (httpcore2)
        
        httpcore2 uses `anyio` by default (works with both asyncio and trio). No extra config needed if you're already on the anyio stack. For trio, install `httpcore2[trio]`.
        
      • libraries.md 10.6 KB
        # Library Defaults — Decision Tree
        
        For each domain, the canonical 2026 choice, why, and the canonical usage snippet. The skill enforces these unless the project's `pyproject.toml` explicitly says otherwise.
        
        ## CLI — typer
        
        `typer` builds a CLI from type-annotated function signatures. argparse needs 5x the code; click ignores type annotations; fire is magic that breaks at scale.
        
        ```python
        import typer
        from rich import print as rprint
        
        app = typer.Typer()
        
        @app.command()
        def greet(name: str, count: int = 1, shout: bool = False) -> None:
            """Print a greeting `count` times."""
            message = f"Hello, {name}!" if not shout else f"HELLO, {name.upper()}!"
            for _ in range(count):
                rprint(message)
        
        if __name__ == "__main__":
            app()
        ```
        
        For a single-function script, `typer.run(main)` skips the `Typer()` boilerplate. Subcommands use `@app.command()`.
        
        ## Terminal output — rich
        
        `rich` produces tables, progress bars, syntax highlighting, traceback rendering. Use it for any structured output. Plain `print` is acceptable for non-interactive log lines (and even those are usually better via `rich.console.Console(stderr=True).log(...)`).
        
        ```python
        from rich.console import Console
        from rich.table import Table
        
        console = Console()
        
        table = Table(title="Users")
        table.add_column("ID", style="cyan")
        table.add_column("Name", style="magenta")
        table.add_row("1", "Alice")
        console.print(table)
        
        # Rich tracebacks (call once at process start)
        from rich.traceback import install
        install(show_locals=True)
        ```
        
        ## HTTP client — [httpx2](https://github.com/pydantic/httpx2)
        
        Next-generation HTTP client under Pydantic stewardship. Sync and async in one library, HTTP/2 native, brotli + zstd content decoding, real type stubs. Replaces `requests` (sync only), `aiohttp` (async only), and the original `httpx`.
        
        **Install**: `httpx2[http2,brotli,zstd]` — always include all three extras, no exceptions.
        
        **A bare `httpx2.AsyncClient()` / `httpx2.Client()` is a bug.** Always use the factory pattern from `references/httpx2-optimization.md` with ALL optimizations enabled by default:
        
        ```python
        import socket
        import httpx2
        
        # ── Production defaults — ALL ON, always. ──
        _LIMITS = httpx2.Limits(max_connections=200, max_keepalive_connections=40, keepalive_expiry=30.0)
        _TIMEOUT = httpx2.Timeout(connect=5.0, read=30.0, write=10.0, pool=10.0)
        _SOCKET_OPTS: list[tuple[int, int, int]] = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]
        
        # Async (the common case)
        transport = httpx2.AsyncHTTPTransport(http2=True, retries=3, limits=_LIMITS, socket_options=_SOCKET_OPTS)
        async with httpx2.AsyncClient(transport=transport, timeout=_TIMEOUT, follow_redirects=True) as client:
            response = await client.get("https://api.example.com/users")
            response.raise_for_status()
            users = response.json()
        
        # Sync
        transport = httpx2.HTTPTransport(http2=True, retries=3, limits=_LIMITS, socket_options=_SOCKET_OPTS)
        with httpx2.Client(transport=transport, timeout=_TIMEOUT, follow_redirects=True) as client:
            response = client.get("https://api.example.com/users")
            response.raise_for_status()
            users = response.json()
        ```
        
        See `references/httpx2-optimization.md` for the full factory functions (`create_client()` / `create_async_client()`), event hooks, and the rationale behind every setting. **Load that reference whenever you write ANY network code.**
        
        ## JSON — stdlib `json` (default) or `orjson` (hot paths)
        
        Stdlib `json` is fine for cold paths and configs. **Reach for `orjson` when JSON is in the hot path** — cache layers, queue payloads, streaming responses, structured logs, FastAPI endpoints returning raw `dict` / `list`.
        
        ```python
        import orjson
        
        # orjson.dumps returns bytes, not str
        raw: bytes = orjson.dumps(
            payload,
            option=orjson.OPT_NAIVE_UTC | orjson.OPT_UTC_Z | orjson.OPT_SERIALIZE_DATACLASS,
        )
        ```
        
        **Critical 2026 fact**: with Pydantic v2, `model.model_dump_json()` is backed by pydantic-core (Rust) and is faster than `orjson + default=` bridge for Pydantic-shaped responses. **Use `model_dump_json()` for Pydantic; orjson for everything else.**
        
        For FastAPI: `app = FastAPI(default_response_class=ORJSONResponse)`. Pydantic-typed responses bypass it (and that's correct — Pydantic's path is faster). Raw `dict`/`list` returns go through orjson.
        
        See `references/orjson-stack.md` for the full decision tree, option flag reference, FastAPI integration, Redis/queue/logging patterns, and the `model_dump_json()` vs orjson benchmark.
        
        ## Validation — pydantic v2
        
        Pydantic v2's core is in Rust (~10x faster than v1). It is the de-facto boundary validator. Use it for:
        
        - HTTP request/response models (FastAPI uses pydantic natively)
        - Config files (env vars via `pydantic-settings`)
        - Anything entering the program from outside
        
        ```python
        from pydantic import BaseModel, Field, EmailStr, field_validator
        
        class User(BaseModel):
            id: int = Field(ge=1)
            email: EmailStr
            name: str = Field(min_length=1, max_length=100)
            age: int | None = Field(default=None, ge=0, le=150)
        
            @field_validator("name")
            @classmethod
            def name_no_digits(cls, v: str) -> str:
                if any(c.isdigit() for c in v):
                    raise ValueError("name cannot contain digits")
                return v
        
        # Inside the program, use the validated instance with confidence
        user = User.model_validate({"id": 1, "email": "a@b.com", "name": "Alice"})
        print(user.model_dump_json(indent=2))
        ```
        
        `@dataclass` is fine for purely internal records (no validation needed). For anything crossing a process boundary, use Pydantic.
        
        ## Async — anyio
        
        Full reference: [async-anyio.md](async-anyio.md). The summary:
        
        ```python
        import anyio
        
        async def fetch(url: str) -> str:
            await anyio.sleep(0.1)
            return url
        
        async def main() -> None:
            async with anyio.create_task_group() as tg:
                for url in ["a", "b", "c"]:
                    tg.start_soon(fetch, url)
        
        anyio.run(main)
        ```
        
        Never `import asyncio` directly. The third-party libraries you call are free to use asyncio internally.
        
        ## Web framework — fastapi
        
        Type-hint-driven HTTP framework. Pydantic models become OpenAPI schemas automatically.
        
        ```python
        from fastapi import FastAPI
        from pydantic import BaseModel
        
        app = FastAPI()
        
        class CreateUser(BaseModel):
            name: str
            email: str
        
        class User(BaseModel):
            id: int
            name: str
            email: str
        
        @app.post("/users", response_model=User)
        async def create_user(payload: CreateUser) -> User:
            return User(id=1, **payload.model_dump())
        ```
        
        Full stack with database: [fastapi-stack.md](fastapi-stack.md).
        
        ## ORM — sqlalchemy 2.x async
        
        SQLAlchemy 2.x finally has a real async API. Use the modern declarative `MappedAsDataclass` style with type annotations.
        
        ```python
        from sqlalchemy import String
        from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
        from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, MappedAsDataclass
        
        class Base(MappedAsDataclass, DeclarativeBase):
            pass
        
        class User(Base):
            __tablename__ = "users"
            id: Mapped[int] = mapped_column(primary_key=True, init=False)
            name: Mapped[str] = mapped_column(String(100))
            email: Mapped[str] = mapped_column(String(255), unique=True)
        
        engine = create_async_engine("postgresql+asyncpg://localhost/myapp")
        SessionFactory = async_sessionmaker(engine, expire_on_commit=False)
        ```
        
        Full pattern with FastAPI integration: [fastapi-stack.md](fastapi-stack.md).
        
        ## Database — postgres + asyncpg
        
        For new applications, default to Postgres. SQLite for tests is fine; SQLite for production is not.
        
        asyncpg is the fastest Python Postgres driver, native to SQLAlchemy 2.x async, native to FastAPI's lifespan model. URL: `postgresql+asyncpg://user:pass@host:5432/db`.
        
        For migrations, use Alembic with `[alembic.context]` configured to use the async engine. Single-step:
        
        ```bash
        uv add alembic
        uv run alembic init -t async migrations
        ```
        
        ## TUI — textual
        
        Textual builds rich, mouse-aware, mobile-style TUIs on the rich rendering engine. See [textual-tui.md](textual-tui.md).
        
        ## AI agents — pydantic-ai
        
        The agent framework from the Pydantic team. Type-strict, structured outputs are first-class, model-agnostic. See [pydantic-ai.md](pydantic-ai.md).
        
        ## DataFrames — polars + numpy
        
        Polars is 10-50x faster than pandas, has a real type system, and supports lazy evaluation. Numpy stays in the toolbox for arrays. See [data-processing.md](data-processing.md).
        
        ## OLAP / SQL — duckdb
        
        DuckDB is the SQL engine for analytical workloads. Query CSV/Parquet/JSON files directly without loading into memory; perform joins and aggregations 3-4x faster than Polars; zero-copy interchange with Polars via Arrow. See [data-processing.md](data-processing.md).
        
        ## Tests — pytest
        
        Plain `unittest` is fine for stdlib; everything else uses pytest. Conventions:
        
        - File names `test_*.py`, function names `test_*`.
        - Fixtures via `@pytest.fixture`. Async fixtures are anyio-aware (`@pytest.fixture` on an async function works under `pytest-anyio` which is bundled with anyio).
        - Parametrise with `@pytest.mark.parametrize`.
        - Mark async tests with `@pytest.mark.anyio` (provided by anyio's pytest plugin).
        
        ```python
        import pytest
        import anyio
        
        @pytest.fixture
        def sample_user() -> dict[str, str]:
            return {"name": "Alice", "email": "a@b.com"}
        
        @pytest.mark.parametrize("count,expected", [(1, "Hello"), (2, "Hello, Hello")])
        def test_greet(count: int, expected: str) -> None:
            result = ", ".join(["Hello"] * count)
            assert result == expected
        
        @pytest.mark.anyio
        async def test_async_fetch() -> None:
            await anyio.sleep(0)
            assert True
        ```
        
        `pyproject.toml`:
        
        ```toml
        [tool.pytest.ini_options]
        minversion = "8.0"
        testpaths = ["tests"]
        addopts = ["-ra", "--strict-config", "--strict-markers"]
        ```
        
        ## Settings / config — pydantic-settings
        
        Loads env vars and `.env` files into a Pydantic model. Replaces ad-hoc `os.environ.get(...)` everywhere.
        
        ```python
        from pydantic import Field
        from pydantic_settings import BaseSettings, SettingsConfigDict
        
        class Settings(BaseSettings):
            model_config = SettingsConfigDict(env_file=".env", env_prefix="MYAPP_")
        
            database_url: str
            api_key: str = Field(min_length=1)
            debug: bool = False
        
        settings = Settings()  # loads at import time; raises if any required var is missing
        ```
        
        ## Logging — stdlib logging + rich handler
        
        Stdlib `logging` is fine; it gets a face-lift from `rich.logging.RichHandler`.
        
        ```python
        import logging
        from rich.logging import RichHandler
        
        logging.basicConfig(
            level=logging.INFO,
            format="%(message)s",
            datefmt="[%X]",
            handlers=[RichHandler(rich_tracebacks=True, show_path=False)],
        )
        log = logging.getLogger(__name__)
        log.info("ready")
        ```
        
        For structured logging in production, swap to `structlog` (separate dep). Don't roll your own.
        
      • one-liners.md 7.8 KB
        # One-liner Scripts (PEP 723 + uv)
        
        Self-contained Python scripts with declared dependencies, run with no environment setup. The combination eliminates the historical reason to write small tools in Go or Bash.
        
        **Rule: EVERY `.py` script — even throwaway — MUST use PEP 723 inline metadata with the usage comment block.** No venv, no requirements.txt, no setup.py. The script IS the environment spec.
        
        ## The two patterns
        
        ### Pattern 1: inline `uv run` invocation
        
        ```bash
        uv run --with httpx2 --with rich python -c "
        import httpx2
        from rich import print
        print(httpx2.get('https://api.github.com').json())
        "
        ```
        
        Use for terminal one-shots that you don't want to save. `--with PKG` may be repeated.
        
        ### Pattern 2: PEP 723 script with shebang (THE CANONICAL PATTERN)
        
        A regular `.py` file with metadata in a comment block. uv reads the metadata, materialises a disposable venv (cached), and runs the script.
        
        ```python
        #!/usr/bin/env -S uv run --script
        # /// script
        # requires-python = ">=3.13"
        # dependencies = [
        #     "httpx2[http2,brotli,zstd]",
        #     "rich",
        # ]
        # ///
        
        # ─── How to run ───
        # 1. Install uv (if not installed):
        #      curl -LsSf https://astral.sh/uv/install.sh | sh
        # 2. Run directly (no venv, no pip install needed):
        #      uv run my_script.py
        # 3. Or make executable and run:
        #      chmod +x my_script.py && ./my_script.py
        # ──────────────────
        
        from __future__ import annotations
        
        import httpx2
        from rich import print as rprint
        
        
        def main() -> None:
            with httpx2.Client(http2=True, follow_redirects=True) as client:
                resp = client.get("https://api.github.com")
                resp.raise_for_status()
                rprint(resp.json())
        
        
        if __name__ == "__main__":
            main()
        ```
        
        ### Mandatory elements
        
        Every PEP 723 script MUST include these, in order:
        
        1. **Shebang**: `#!/usr/bin/env -S uv run --script`
        2. **PEP 723 metadata block**: `# /// script` ... `# ///` with `requires-python` and `dependencies`
        3. **Usage comment block**: How to install uv + how to run the script. Copy the template above verbatim.
        4. **`from __future__ import annotations`**: Always first import.
        5. **`if __name__ == "__main__": main()`**: Entry point guard.
        
        ### The usage comment block (NON-NEGOTIABLE)
        
        ```python
        # ─── How to run ───
        # 1. Install uv (if not installed):
        #      curl -LsSf https://astral.sh/uv/install.sh | sh
        # 2. Run directly (no venv, no pip install needed):
        #      uv run <SCRIPT_NAME>.py [ARGS]
        # 3. Or make executable and run:
        #      chmod +x <SCRIPT_NAME>.py && ./<SCRIPT_NAME>.py
        # ──────────────────
        ```
        
        Replace `<SCRIPT_NAME>` with the actual filename. Add argument descriptions if the script takes CLI args. This block goes immediately after the `# ///` closing line, before any imports.
        
        **Why mandatory**: Anyone who receives this script — colleague, CI, future you — must know how to run it without reading docs. The comment IS the docs.
        
        ## Template generator
        
        Use `scripts/new-script.py` to scaffold a new PEP 723 script with all boilerplate pre-filled:
        
        ```bash
        # Generate to temp directory (default)
        uv run scripts/new-script.py my_tool
        
        # Generate to specific path
        uv run scripts/new-script.py my_tool --output ./scripts/my_tool.py
        
        # With extra dependencies
        uv run scripts/new-script.py my_tool --deps "polars" "duckdb" "rich"
        ```
        
        ## Common dependency sets
        
        | Use case | Dependencies line |
        |---|---|
        | API client | `"httpx2[http2,brotli,zstd]"` |
        | Data processing | `"polars"`, `"duckdb"` |
        | CLI tool | `"typer"`, `"rich"` |
        | Web scraping | `"httpx2[http2,brotli,zstd]"`, `"selectolax"` |
        | File watcher | `"watchfiles"` |
        | JSON pretty | `"rich"` |
        | AI / LLM | `"pydantic-ai"`, `"httpx2[http2,brotli,zstd]"` |
        
        ## Real-world examples
        
        ### Fetch + print JSON
        
        ```python
        #!/usr/bin/env -S uv run --script
        # /// script
        # requires-python = ">=3.13"
        # dependencies = [
        #     "httpx2[http2,brotli,zstd]",
        #     "rich",
        # ]
        # ///
        
        # ─── How to run ───
        # 1. Install uv: curl -LsSf https://astral.sh/uv/install.sh | sh
        # 2. Run: uv run fetch_json.py https://api.github.com/repos/pydantic/httpx2
        # ──────────────────
        
        from __future__ import annotations
        
        import sys
        
        import httpx2
        from rich import print as rprint
        
        
        def main() -> None:
            url = sys.argv[1] if len(sys.argv) > 1 else "https://api.github.com"
            with httpx2.Client(http2=True, follow_redirects=True) as client:
                resp = client.get(url)
                resp.raise_for_status()
                rprint(resp.json())
        
        
        if __name__ == "__main__":
            main()
        ```
        
        ### CSV → Parquet conversion
        
        ```python
        #!/usr/bin/env -S uv run --script
        # /// script
        # requires-python = ">=3.13"
        # dependencies = [
        #     "polars",
        #     "typer",
        #     "rich",
        # ]
        # ///
        
        # ─── How to run ───
        # 1. Install uv: curl -LsSf https://astral.sh/uv/install.sh | sh
        # 2. Run: uv run csv2parquet.py input.csv output.parquet
        # ──────────────────
        
        from __future__ import annotations
        
        from pathlib import Path
        
        import polars as pl
        import typer
        from rich import print as rprint
        
        
        def main(input_path: Path, output_path: Path | None = None) -> None:
            """Convert CSV to Parquet."""
            out = output_path or input_path.with_suffix(".parquet")
            df = pl.read_csv(input_path)
            df.write_parquet(out)
            rprint(f"[green]✓[/green] {input_path} → {out} ({len(df)} rows)")
        
        
        if __name__ == "__main__":
            typer.run(main)
        ```
        
        ### Quick benchmark
        
        ```python
        #!/usr/bin/env -S uv run --script
        # /// script
        # requires-python = ">=3.13"
        # dependencies = [
        #     "httpx2[http2,brotli,zstd]",
        #     "rich",
        #     "anyio",
        # ]
        # ///
        
        # ─── How to run ───
        # 1. Install uv: curl -LsSf https://astral.sh/uv/install.sh | sh
        # 2. Run: uv run bench.py https://api.example.com/health 50
        # ──────────────────
        
        from __future__ import annotations
        
        import socket
        import sys
        import time
        
        import anyio
        import httpx2
        from rich import print as rprint
        
        
        async def main() -> None:
            url = sys.argv[1] if len(sys.argv) > 1 else "https://api.github.com"
            n = int(sys.argv[2]) if len(sys.argv) > 2 else 20
        
            limits = httpx2.Limits(max_connections=200, max_keepalive_connections=40, keepalive_expiry=30.0)
            timeout = httpx2.Timeout(connect=5.0, read=30.0, write=10.0, pool=10.0)
            transport = httpx2.AsyncHTTPTransport(
                http2=True, retries=3, limits=limits,
                socket_options=[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)],
            )
        
            async with httpx2.AsyncClient(transport=transport, timeout=timeout, follow_redirects=True) as client:
                # warmup
                for _ in range(3):
                    await client.get(url)
        
                start = time.perf_counter()
                for _ in range(n):
                    r = await client.get(url)
                    assert r.status_code == 200
                elapsed = time.perf_counter() - start
        
            avg_ms = (elapsed / n) * 1000
            rprint(f"[bold]{url}[/bold]: {avg_ms:.1f}ms avg over {n} requests ({elapsed:.2f}s total, {r.http_version})")
        
        
        if __name__ == "__main__":
            anyio.run(main)
        ```
        
        ## Anti-patterns
        
        | ❌ Don't | ✅ Do |
        |---|---|
        | `pip install httpx2 && python script.py` | `uv run script.py` |
        | `requirements.txt` alongside script | PEP 723 inline metadata |
        | `python -m venv .venv && ...` | `uv run --script` handles it |
        | Script without usage comment | Always include the "How to run" block |
        | `import asyncio; asyncio.run(main())` | `import anyio; anyio.run(main)` |
        | Bare `httpx2.AsyncClient()` | Full production defaults (see `references/httpx2-optimization.md`) |
        
        ## Sources
        
        - PEP 723 - Inline script metadata: <https://peps.python.org/pep-0723/>
        - uv `run --script` docs: <https://docs.astral.sh/uv/guides/scripts/>
        - Original article: <https://www.cottongeeks.com/articles/2025-06-24-fun-with-uv-and-pep-723>
        - Simon Willison on one-shot Python tools: <https://simonwillison.net/2024/Dec/19/one-shot-python-tools/>
        
      • orjson-stack.md 13.3 KB
        # orjson — When to Use, How to Integrate
        
        `orjson` is the fastest JSON library on PyPI — written in Rust, 6–11× faster than stdlib `json` on serialization, 1.5–4× faster on deserialization. It also supports types the stdlib refuses to serialize: `datetime`, `date`, `UUID`, `numpy` arrays, `dataclass`, Pydantic models (via a small bridge).
        
        This document covers the production patterns. **Not every project needs orjson.** The decision tree is in §1.
        
        ---
        
        ## 1. Decision tree — should you adopt orjson?
        
        ```
        Are you serializing/deserializing JSON in a hot path?
          ├─ NO  → stdlib `json` is fine. Stop here.
          └─ YES ↓
        
        Is the project FastAPI?
          ├─ YES ↓
          │
          │   Is your response body fully described by a Pydantic v2 model?
          │     ├─ YES → Use FastAPI's default JSON response (uses Pydantic's
          │     │       Rust-backed serializer; orjson saves nothing in this path).
          │     │       Adopt orjson only for *non-Pydantic* responses below.
          │     └─ NO  → Use `ORJSONResponse` for endpoints that return dicts,
          │             lists, or arbitrary structures.
          │
          └─ NOT FastAPI ↓
        
        Are you serializing Pydantic v2 models repeatedly?
          ├─ YES → Use `model.model_dump_json()` directly — backed by pydantic-core
          │        (Rust), within ~10% of orjson on the same payload, and respects
          │        every Pydantic feature (computed fields, aliases, validators).
          └─ NO  ↓
        
        Are you serializing dicts / lists / dataclasses / datetime / UUID?
          ├─ YES → orjson is the right answer.
          └─ NO  → stdlib `json`.
        ```
        
        **The crucial 2026 fact**: with Pydantic v2's `model_dump_json()`, **Pydantic-shaped responses no longer need orjson**. Adopt orjson where you are still going through `dict` / `list` / `dataclass`.
        
        ---
        
        ## 2. Install
        
        ```toml
        # pyproject.toml
        dependencies = [
            "orjson>=3.10",
        ]
        ```
        
        orjson wheels are published for every major CPython version and platform (macOS, Linux glibc/musl, Windows, ARM64). No compilation step on install.
        
        ---
        
        ## 3. Basic usage
        
        ```python
        import orjson
        
        # Serialization — returns bytes, not str
        raw: bytes = orjson.dumps({"hello": "world", "ts": datetime.now(UTC)})
        
        # Deserialization
        data = orjson.loads(raw)
        ```
        
        Two things to internalize:
        
        1. **`orjson.dumps` returns `bytes`**, not `str`. Stdlib `json.dumps` returns `str`. This is by design — most JSON destinations (sockets, files in binary mode, HTTP bodies) want bytes anyway, and skipping the encode/decode round trip is part of the speedup.
        2. **No `indent` arg.** orjson supports `OPT_INDENT_2` (and only 2-space indent) via flags. If you need other indentation, use stdlib `json`.
        
        ---
        
        ## 4. The option flags you actually use
        
        ```python
        import orjson
        
        orjson.dumps(
            payload,
            option=(
                orjson.OPT_NAIVE_UTC          # treat naive datetimes as UTC (recommended)
                | orjson.OPT_UTC_Z            # render UTC as "...Z" instead of "+00:00"
                | orjson.OPT_SERIALIZE_NUMPY  # serialize numpy arrays natively
                | orjson.OPT_SERIALIZE_DATACLASS  # serialize @dataclass instances
                | orjson.OPT_NON_STR_KEYS     # allow int / UUID / datetime dict keys
                # | orjson.OPT_SORT_KEYS      # only when you need deterministic output
                # | orjson.OPT_INDENT_2       # only for human-readable output (slower)
            ),
        )
        ```
        
        Each flag is opt-in for a reason — orjson defaults to spec-strict JSON.
        
        The flag combination above is a sensible "production default" for application code. The `OPT_NAIVE_UTC | OPT_UTC_Z` pair is especially important: it produces RFC 3339 timestamps that every parser on earth accepts.
        
        ---
        
        ## 5. orjson + FastAPI
        
        ### 5.1 The legacy pattern: `ORJSONResponse`
        
        ```python
        from fastapi import FastAPI
        from fastapi.responses import ORJSONResponse
        
        app = FastAPI(default_response_class=ORJSONResponse)
        
        @app.get("/items")
        async def get_items() -> dict[str, list[dict[str, int]]]:
            return {"items": [{"id": i, "qty": i * 2} for i in range(1000)]}
        ```
        
        `default_response_class=ORJSONResponse` swaps the global JSON encoder for orjson. **This affects only the response body serialization**, not request parsing — for request parsing, FastAPI still uses Pydantic.
        
        ### 5.2 The 2026 reality — Pydantic v2 vs orjson
        
        With FastAPI 0.100+ on Pydantic v2:
        
        - If your response is annotated with a Pydantic model, FastAPI calls `model_dump_json()` directly. **orjson is bypassed** even with `default_response_class=ORJSONResponse`, because the Pydantic serializer is already Rust-backed.
        - If your response is a raw `dict` / `list` / Python object, `ORJSONResponse` does kick in and saves real time.
        
        The benchmark in `tiangolo/fastapi#11728` (Apr 2024) showed `model_dump_json()` is ~10–15% faster than `ORJSONResponse + model_dump()` for Pydantic-shaped responses. The shape of the data matters; on mixed-shape APIs, keep `ORJSONResponse` as the default and trust Pydantic's path for typed responses.
        
        ### 5.3 Recommended setup
        
        ```python
        from fastapi import FastAPI
        from fastapi.responses import ORJSONResponse
        
        app = FastAPI(
            default_response_class=ORJSONResponse,  # benefits dict/list returns
            # Pydantic-typed returns automatically use pydantic-core serialization
        )
        ```
        
        **Do NOT** wrap Pydantic models manually:
        
        ```python
        # BAD — defeats Pydantic's optimized path
        @app.get("/users/{id}", response_class=ORJSONResponse)
        async def get_user(id: int) -> ORJSONResponse:
            user = await fetch_user(id)
            return ORJSONResponse(content=user.model_dump())  # extra dict trip
        
        # GOOD — let FastAPI serialize the model
        @app.get("/users/{id}")
        async def get_user(id: int) -> User:
            return await fetch_user(id)
        ```
        
        ### 5.4 Streaming responses
        
        `ORJSONResponse` does not stream — it buffers the whole response. For SSE, NDJSON, or chunked JSON, use `StreamingResponse` and call `orjson.dumps` per chunk:
        
        ```python
        from fastapi.responses import StreamingResponse
        import orjson
        
        async def ndjson_stream():
            async for row in fetch_rows():
                yield orjson.dumps(row) + b"\n"
        
        @app.get("/export")
        async def export():
            return StreamingResponse(ndjson_stream(), media_type="application/x-ndjson")
        ```
        
        This is where orjson shines — per-chunk serialization in a tight loop, zero buffering.
        
        ---
        
        ## 6. orjson + Pydantic v2 (no FastAPI)
        
        When you have a Pydantic model and want orjson's output for non-FastAPI contexts:
        
        ```python
        from pydantic import BaseModel
        import orjson
        
        class User(BaseModel):
            id: int
            email: str
            created: datetime
        
        user = User(id=1, email="a@b.com", created=datetime.now(UTC))
        
        # Option A — Pydantic's built-in Rust serializer (USE THIS by default)
        raw: bytes = user.model_dump_json().encode()
        # 2026: ~1.2× faster than orjson on the same payload, supports
        # every Pydantic feature (aliases, computed fields, json_schema_extra, etc.)
        
        # Option B — orjson bridge for cases Pydantic does not cover
        raw: bytes = orjson.dumps(
            user,
            default=lambda obj: obj.model_dump() if isinstance(obj, BaseModel) else None,
        )
        # Useful when serializing nested non-Pydantic structures that contain
        # BaseModels — e.g. a list of dicts that each may contain a BaseModel.
        ```
        
        For routine "serialize one Pydantic model to JSON", `model_dump_json()` wins on speed AND feature parity. Reach for orjson only at the *container* level (a dict of mixed types).
        
        ### Custom `default=` callback — the universal extension point
        
        ```python
        import orjson
        from decimal import Decimal
        from pydantic import BaseModel
        
        def _default(obj):
            if isinstance(obj, BaseModel):
                return obj.model_dump()
            if isinstance(obj, Decimal):
                return str(obj)
            if isinstance(obj, set):
                return list(obj)
            raise TypeError(f"orjson: cannot serialize {type(obj).__name__}")
        
        orjson.dumps(payload, default=_default, option=orjson.OPT_NAIVE_UTC | orjson.OPT_UTC_Z)
        ```
        
        The `default=` callback runs once per unrecognized type, then orjson caches the path. Performance impact on subsequent calls is negligible.
        
        ---
        
        ## 7. Caching, queues, logging — the prime orjson use cases
        
        These are where orjson pays off most clearly because there is no Pydantic in the loop:
        
        ### Redis cache
        
        ```python
        import orjson
        import redis.asyncio as redis
        
        r = redis.from_url("redis://localhost")
        
        async def set_cache(key: str, value: dict) -> None:
            await r.set(key, orjson.dumps(value), ex=3600)
        
        async def get_cache(key: str) -> dict | None:
            raw = await r.get(key)
            return orjson.loads(raw) if raw else None
        ```
        
        `orjson` over stdlib `json` here saves ~5–10× on the serialize step for typical cache payloads. Multiply by request rate.
        
        ### Task queue payloads (Celery, RQ, dramatiq)
        
        ```python
        # Celery custom serializer
        from kombu.serialization import register
        import orjson
        
        def _orjson_dumps(obj):
            return orjson.dumps(obj, option=orjson.OPT_NAIVE_UTC | orjson.OPT_UTC_Z).decode()
        
        def _orjson_loads(s):
            return orjson.loads(s)
        
        register("orjson", _orjson_dumps, _orjson_loads,
                 content_type="application/x-orjson",
                 content_encoding="utf-8")
        ```
        
        Same speedup, applied to every task payload encode/decode.
        
        ### Structured logging (structlog, custom slog)
        
        ```python
        import structlog
        import orjson
        
        structlog.configure(
            processors=[
                structlog.processors.TimeStamper(fmt="iso"),
                structlog.processors.add_log_level,
                structlog.processors.JSONRenderer(serializer=orjson.dumps),
            ],
        )
        ```
        
        structlog's `JSONRenderer` accepts any callable; orjson is the obvious default. Logging hot paths benefit dramatically — every log line at info level becomes ~5× cheaper to render.
        
        ---
        
        ## 8. Gotchas
        
        ### `orjson.dumps` returns bytes, not str
        
        ```python
        # BAD — concatenating bytes and str
        log.info("payload: " + orjson.dumps(data))   # TypeError
        
        # GOOD
        log.info("payload: %s", orjson.dumps(data).decode())
        # or
        log.info("payload: %s", orjson.dumps(data))  # let the formatter handle it
        ```
        
        ### No `cls=` argument for custom encoders
        
        orjson uses `default=` only. If you have a custom `JSONEncoder` subclass from stdlib `json`, port its `default()` method to a `default=` callable.
        
        ### Subclasses of `dict` / `list` are NOT serialized as their parent
        
        ```python
        class StrictDict(dict): ...
        d = StrictDict({"k": "v"})
        
        import json
        json.dumps(d)              # OK — stdlib walks subclasses
        orjson.dumps(d)            # TypeError — orjson is strict by design
        orjson.dumps(d, option=orjson.OPT_PASSTHROUGH_SUBCLASS)  # then route via default=
        ```
        
        Set `OPT_PASSTHROUGH_SUBCLASS` and handle the subclass in `default=`. The design discourages accidental subclass usage that breaks elsewhere.
        
        ### `int` overflow
        
        orjson refuses to encode integers larger than 2⁵³ - 1 by default (the IEEE-754 double-precision safe-integer limit — what JavaScript can round-trip). For larger ints, opt in:
        
        ```python
        orjson.dumps(huge_int, option=orjson.OPT_STRICT_INTEGER)  # error
        orjson.dumps(huge_int)  # default — int is encoded as JSON number
        # JavaScript clients lose precision past 2^53; consider sending as string
        ```
        
        This is more spec-strict than stdlib `json`, which silently emits ints of any size.
        
        ### Timezone-naive datetimes
        
        By default, orjson treats naive `datetime` as the system local timezone — almost never what you want. **Always set `OPT_NAIVE_UTC`** to treat naive datetimes as UTC, or use timezone-aware datetimes (which is the better long-term habit).
        
        ---
        
        ## 9. Benchmark — should I actually adopt this?
        
        The numbers below are 2024–2026 averages from `tiangolo/fastapi#11728` and orjson's own benchmark suite, on Python 3.13, modern x86_64:
        
        | Payload | stdlib `json` | `orjson` | `model_dump_json()` (Pydantic v2) |
        |---|---|---|---|
        | Small dict (100 fields) | 1.0× | **8×** | n/a |
        | List of 10k dicts | 1.0× | **11×** | n/a |
        | Pydantic model with 20 fields | 1.0× (after `model_dump()`) | 5× (with `default=` bridge) | **6×** |
        | Datetime-heavy payload | 1.0× (after manual ISO conv) | **9×** | 6× |
        | numpy array (1M floats) | impossible without manual conv | **20×** vs json+tolist | n/a |
        
        The takeaways:
        
        - For raw dict/list/datetime, **orjson is dramatically faster**.
        - For Pydantic models, **`model_dump_json()` is already faster than orjson+bridge**.
        - For numpy, orjson is the only sane choice.
        
        In production, the actual measured win on a FastAPI app with mixed payloads is typically 5–15% reduction in p99 latency. Worth the one-line `default_response_class=ORJSONResponse` switch.
        
        ---
        
        ## 10. When NOT to adopt orjson
        
        - The codebase is small, JSON is not a bottleneck, and you have no measured perf concern.
        - You depend on stdlib `json`'s `cls=` arg or its lax tolerance for non-spec input (NaN, Infinity, comments).
        - You need pretty-printed JSON with custom indent — orjson only supports 2-space indent via the flag.
        - You need pure-Python portability (e.g., MicroPython, no-wheel platforms) — orjson is a compiled Rust extension.
        
        If the choice is "add a dependency that does 5–10× the speed on serialization for free", the answer is almost always yes. The "almost" is in the bullets above.
        
        ---
        
        ## Sources
        
        - orjson: https://github.com/ijl/orjson
        - Pydantic v2 `model_dump_json`: https://docs.pydantic.dev/latest/concepts/serialization/#modelmodel_dump_json
        - FastAPI `ORJSONResponse`: https://fastapi.tiangolo.com/advanced/custom-response/#use-orjsonresponse
        - "FastAPI + orjson vs Pydantic v2" benchmark: https://github.com/fastapi/fastapi/discussions/11728
        - structlog JSON rendering: https://www.structlog.org/en/stable/api.html#structlog.processors.JSONRenderer
        
      • pydantic-ai.md 8.2 KB
        # PydanticAI Reference (v1.x, 2026)
        
        > Canonical patterns for wiring PydanticAI agents. Target: production usage, late-2025 / 2026.
        > Source: [ai.pydantic.dev](https://ai.pydantic.dev) and [pydantic/pydantic-ai@`cad9569`](https://github.com/pydantic/pydantic-ai/blob/cad956910079737ea0886b50cef15777208f92e6).
        
        ---
        
        ## 1. Agent Constructor
        
        ```python
        from pydantic_ai import Agent
        
        agent = Agent(
            'openai:gpt-5.5',               # model (str | Model | None)
            output_type=MyOutputModel,       # structured output type; default=str
            instructions='You are a...',     # static or callable instructions
            system_prompt='Be concise.',     # static system prompt(s)
            deps_type=MyDeps,                # dependency type for type-checking only
            name='my-agent',                 # optional, inferred from var name if omitted
            retries=1,                       # default retries for tools + output validation
            output_retries=None,             # override retries for output validation only
            tools=[my_tool],                 # list of Tool objects or plain functions
            defer_model_check=False,         # set True to skip env-var check at init time
            end_strategy='early',            # 'early' | 'graceful' | 'exhaustive'
        )
        ```
        
        **Breaking change (v1.88.0)**: `result_type` was renamed to `output_type`. Use `output_type`.
        
        ---
        
        ## 2. Model Strings
        
        Format: `provider:model-name`. The framework infers the provider from the prefix.
        
        | Provider prefix | Example |
        |---|---|
        | `openai:` | `'openai:gpt-5.5'`, `'openai:gpt-4o'` |
        | `anthropic:` | `'anthropic:claude-sonnet-4-6'`, `'anthropic:claude-opus-4-1'` |
        | `google-gla:` | `'google-gla:gemini-3-flash-preview'` |
        | `google-vertex:` | `'google-vertex:gemini-3-pro-preview'` |
        | `bedrock:` | `'bedrock:anthropic.claude-sonnet-4-6'` |
        | `xai:` / `grok:` | `'xai:grok-3'`, `'grok:grok-3-fast'` |
        | `deepseek:` | `'deepseek:deepseek-chat'` |
        | `cohere:` | `'cohere:command-r-08-2024'` |
        | `gateway/...` | `'gateway/openai:gpt-5.5'` (PydanticAI Gateway) |
        
        Model can also be omitted at construction and passed per-run: `agent.run(prompt, model='openai:gpt-5.5')`.
        
        ---
        
        ## 3. Tools
        
        ### Decorator syntax
        
        ```python
        from pydantic_ai import Agent, RunContext
        
        agent = Agent('openai:gpt-5.5', deps_type=str)
        
        @agent.tool                    # default: receives RunContext as first arg
        async def greet(ctx: RunContext[str], name: str) -> str:
            return f"Hello {ctx.deps}, {name}!"
        
        @agent.tool_plain              # no context needed
        async def roll_dice(sides: int) -> int:
            import random
            return random.randint(1, sides)
        ```
        
        ### `RunContext[Deps]`
        
        First parameter of `@agent.tool` functions. Carries:
        
        - `ctx.deps` — the dependency instance
        - `ctx.model` — the model being used
        - `ctx.usage` — token usage so far
        - `ctx.messages` — conversation history
        - `ctx.retry` / `ctx.max_retries` — current retry count
        - `ctx.agent` — the running agent instance
        
        Use `@agent.tool_plain` when the tool does **not** need any of the above.
        
        ---
        
        ## 4. Structured Output
        
        Pass a Pydantic `BaseModel` (or `bool`, `int`, `list[str]`, etc.) as `output_type`. The result is accessed via `.output`.
        
        ```python
        from pydantic import BaseModel
        from pydantic_ai import Agent
        
        class City(BaseModel):
            name: str
            country: str
            population_millions: float
        
        agent = Agent('openai:gpt-5.5', output_type=City)
        result = agent.run_sync('Tell me about Tokyo')
        print(result.output)            # City(name='Tokyo', country='Japan', ...)
        print(result.output.name)       # 'Tokyo'
        ```
        
        **Note**: `result.data` was renamed; the canonical accessor is `result.output`.
        
        ---
        
        ## 5. Async vs Sync
        
        | Method | Mode | Returns |
        |---|---|---|
        | `await agent.run(prompt, ...)` | async | `AgentRunResult[OutputDataT]` |
        | `agent.run_sync(prompt, ...)` | sync | `AgentRunResult[OutputDataT]` |
        | `async with agent.run_stream(prompt, ...) as response:` | async streaming | `StreamedRunResult` |
        
        ```python
        # Sync
        result = agent.run_sync('What is the capital of Italy?')
        print(result.output)
        
        # Async
        result = await agent.run('What is the capital of France?')
        print(result.output)
        
        # Streaming
        async with agent.run_stream('What is the capital of the UK?') as response:
            async for text in response.stream_text():
                print(text, end='')
            # After streaming finishes:
            print(response.output)
        ```
        
        `run_sync()` is a convenience wrapper over `loop.run_until_complete(self.run(...))`. Do not use it inside an active async context.
        
        ---
        
        ## 6. Dependencies
        
        Use a `@dataclass` container, pass the **type** to `deps_type`, and pass an **instance** to `deps` at run time.
        
        ```python
        from dataclasses import dataclass
        import httpx
        from pydantic_ai import Agent, RunContext
        
        @dataclass
        class Deps:
            api_key: str
            http_client: httpx.AsyncClient
        
        agent = Agent(
            'openai:gpt-5.5',
            deps_type=Deps,
        )
        
        @agent.tool
        async def fetch_data(ctx: RunContext[Deps], endpoint: str) -> str:
            r = await ctx.deps.http_client.get(
                endpoint,
                headers={'Authorization': f'Bearer {ctx.deps.api_key}'},
            )
            r.raise_for_status()
            return r.text
        
        async def main():
            async with httpx.AsyncClient() as client:
                deps = Deps(api_key='sk-...', http_client=client)
                result = await agent.run('Get /users', deps=deps)
                print(result.output)
        ```
        
        ---
        
        ## 7. Error Types & Retrying from a Tool
        
        ```python
        from pydantic_ai import Agent, ModelRetry, UnexpectedModelBehavior, capture_run_messages
        
        agent = Agent('openai:gpt-5.5', retries=3)
        
        @agent.tool_plain
        def calc_volume(size: int) -> int:
            if size == 42:
                return size ** 3
            raise ModelRetry('Please try again with size 42.')
        
        with capture_run_messages() as messages:
            try:
                result = agent.run_sync('Get the volume of a box with size 6.')
            except UnexpectedModelBehavior as e:
                print('Error:', e)          # "Tool 'calc_volume' exceeded max retries count of 3"
                print('Cause:', e.__cause__)  # ModelRetry('Please try again...')
                print('Messages:', messages)
        ```
        
        - **`ModelRetry`** — raise from a tool, output validator, or capability hook to ask the model to retry.
        - **`UnexpectedModelBehavior`** — raised when the retry limit is exceeded or the model API returns an unrecoverable error.
        - **`capture_run_messages()`** — context manager that records all messages exchanged during a run for debugging.
        
        ---
        
        ## 8. Logfire Integration
        
        One-line setup if the `logfire` extra is installed (included in the default `pydantic-ai` package):
        
        ```python
        import logfire
        
        logfire.configure()               # reads token from .logfire directory
        logfire.instrument_pydantic_ai()  # auto-traces all agent runs
        ```
        
        Alternatively, set `instrument=True` on the agent:
        
        ```python
        agent = Agent('openai:gpt-5.5', instrument=True)
        ```
        
        ---
        
        ## 9. Minimal Complete Snippets
        
        ### (a) Basic agent with structured output
        
        ```python
        from pydantic import BaseModel
        from pydantic_ai import Agent
        
        class City(BaseModel):
            name: str
            country: str
        
        agent = Agent('openai:gpt-5.5', output_type=City)
        result = agent.run_sync('Tell me about Paris')
        print(result.output)   # City(name='Paris', country='France')
        ```
        
        ### (b) Agent with tools and dependencies
        
        ```python
        from dataclasses import dataclass
        from pydantic_ai import Agent, RunContext
        
        @dataclass
        class Deps:
            api_key: str
        
        agent = Agent('openai:gpt-5.5', deps_type=Deps)
        
        @agent.tool
        async def get_secret(ctx: RunContext[Deps], code: str) -> str:
            if code == '1234':
                return f'secret-for-{ctx.deps.api_key}'
            return 'wrong code'
        
        result = agent.run_sync('My code is 1234', deps=Deps(api_key='sk-abc'))
        print(result.output)
        ```
        
        ### (c) Async streaming
        
        ```python
        import anyio
        from pydantic_ai import Agent
        
        agent = Agent('openai:gpt-5.5')
        
        async def main() -> None:
            async with agent.run_stream('Write a haiku about Python') as response:
                async for text in response.stream_text():
                    print(text, end='')
                print('\n---')
                print('Final:', response.output)
        
        anyio.run(main)
        ```
        
        ---
        
        ## Version Notes
        
        - **V1** reached API stability in September 2025. Breaking changes are reserved for V2 (earliest April 2026).
        - **v1.88.0** renamed `result_type` → `output_type` and `result_tool_name` / `result_tool_description` were removed. Use `output_type`.
        - The canonical accessor for run results is `result.output` (not `result.data`).
        
      • pyproject-strict.md 9 KB
        # Strict pyproject.toml (basedpyright + ruff + uv)
        
        The canonical "super strict but sane" config for modern Python projects. Copy-paste, then add your own dependencies.
        
        ## Bootstrap
        
        ```bash
        # Application
        uv init --app myproject
        cd myproject
        
        # Library (publishable to PyPI)
        uv init --lib mylibrary
        cd mylibrary
        
        # Add dev tools
        uv add --dev basedpyright ruff pytest
        ```
        
        `uv init` creates `pyproject.toml`, `.python-version`, and `src/` layout. Replace its `pyproject.toml` `[tool.*]` sections with the block below.
        
        ## The full pyproject.toml
        
        ```toml
        [project]
        name = "myproject"
        version = "0.1.0"
        description = "..."
        readme = "README.md"
        requires-python = ">=3.13"
        dependencies = []
        
        [dependency-groups]
        dev = [
            "basedpyright>=1.21",
            "ruff>=0.8",
            "pytest>=8",
            "pytest-cov>=5",
        ]
        
        # ─────────────────────────────────────────────────────────────────
        # basedpyright - typeCheckingMode = "all" sets every report flag to error
        # Source: https://docs.basedpyright.com/latest/configuration/config-files/
        # ─────────────────────────────────────────────────────────────────
        [tool.basedpyright]
        typeCheckingMode = "all"
        pythonVersion = "3.13"
        pythonPlatform = "All"          # default in basedpyright; explicit for clarity
        include = ["src", "tests"]
        exclude = ["**/__pycache__", "**/.venv", "**/build", "**/dist"]
        
        # Strict enforcement extras (most are already "error" under "all" mode,
        # but listing them explicitly documents the intent)
        reportUnusedCallResult = "warning"       # flag ignored return values
        reportUnnecessaryTypeIgnoreComment = "error"  # stale type: ignore comments must die
        reportUnusedVariable = "error"           # unused variables are errors
        reportMissingParameterType = "error"     # every parameter must have a type
        reportMissingReturnType = "error"        # every function must declare its return type
        reportPrivateUsage = "error"             # respect _private convention
        
        # Optional: gradual adoption baseline
        # baselineFile = "./.basedpyright/baseline.json"
        
        # ─────────────────────────────────────────────────────────────────
        # ruff - select = ["ALL"] enables every rule, then we ignore the
        # small set that conflicts with the formatter or is not useful.
        # Source: https://docs.astral.sh/ruff/linter/#rule-selection
        # ─────────────────────────────────────────────────────────────────
        [tool.ruff]
        target-version = "py313"
        line-length = 88                # ruff/black default; 100 or 120 also fine
        src = ["src", "tests"]
        
        [tool.ruff.lint]
        select = ["ALL"]
        ignore = [
            # Formatter conflicts (ruff itself tells you to ignore these)
            "COM812",   # missing trailing comma
            "ISC001",   # implicit string concat
            # Docstyle conflicts (pick D211 over D203, D212 over D213)
            "D203",
            "D213",
            # Project-specific noise
            "CPY001",   # missing copyright notice
            "FBT001",   # boolean positional arg in def
            "FBT002",   # boolean positional default in def
            "TD002",    # missing TODO author
            "TD003",    # missing TODO link
            "FIX002",   # line contains TODO (TODOs are allowed)
        ]
        fixable = ["ALL"]
        unfixable = []
        
        [tool.ruff.lint.per-file-ignores]
        "tests/**/*.py" = [
            "S101",     # `assert` is the entire point of pytest
            "ARG",      # unused args (fixtures appear unused)
            "PLR2004",  # magic numbers in test data
            "SLF001",   # tests need access to private members
            "D",        # docstrings not required in tests
        ]
        "scripts/**/*.py" = [
            "T201",     # `print` allowed in scripts
            "INP001",   # implicit namespace package
        ]
        
        [tool.ruff.lint.pydocstyle]
        convention = "google"  # or "numpy" / "pep257"
        
        [tool.ruff.lint.flake8-bugbear]
        # typer / fastapi rely on call-as-default for parameter metadata.
        # Without this, ruff B008 ("function call in default") fires on every typer/fastapi route.
        extend-immutable-calls = [
            "typer.Argument",
            "typer.Option",
            "fastapi.Depends",
            "fastapi.Query",
            "fastapi.Path",
            "fastapi.Body",
            "fastapi.Header",
            "fastapi.Cookie",
            "fastapi.File",
            "fastapi.Form",
        ]
        
        [tool.ruff.format]
        quote-style = "double"
        indent-style = "space"
        docstring-code-format = true
        docstring-code-line-length = "dynamic"
        
        # ─────────────────────────────────────────────────────────────────
        # pytest
        # ─────────────────────────────────────────────────────────────────
        [tool.pytest.ini_options]
        minversion = "8.0"
        testpaths = ["tests"]
        addopts = [
            "-ra",
            "--strict-config",
            "--strict-markers",
        ]
        filterwarnings = ["error"]
        
        # ─────────────────────────────────────────────────────────────────
        # coverage
        # ─────────────────────────────────────────────────────────────────
        [tool.coverage.run]
        source = ["src"]
        branch = true
        
        [tool.coverage.report]
        exclude_lines = [
            "pragma: no cover",
            "if TYPE_CHECKING:",
            "if typing.TYPE_CHECKING:",
            "raise NotImplementedError",
            "@(abc\\.)?abstractmethod",
        ]
        ```
        
        ## Why these settings
        
        ### basedpyright `typeCheckingMode = "all"`
        
        basedpyright's modes, strictest first:
        
        | Mode | Behavior |
        |---|---|
        | `"all"` | Every diagnostic at `error` |
        | `"recommended"` | Same rules; less severe ones at `warning`; `failOnWarnings = true` makes CI still fail |
        | `"strict"` | pyright's strict mode |
        | `"standard"` | Default |
        | `"basic"` / `"off"` | Loose / disabled |
        
        `"all"` enables basedpyright-exclusive rules pyright lacks: `reportImplicitOverride`, `reportImplicitStringConcatenation`, `reportIncompatibleUnannotatedOverride`, `reportUnannotatedClassAttribute`. No need to opt-in to additional flags.
        
        `pythonPlatform = "All"` is basedpyright's default (better than pyright's host-OS default) - it errors on platform-specific imports that fail on other OSes.
        
        ### ruff `select = ["ALL"]`
        
        The official docs say *"Use ALL with discretion. Enabling ALL will implicitly enable new rules whenever you upgrade."* For a strict skill that is the intended behavior - every new ruff rule should be considered an error until you justify ignoring it.
        
        The minimal ignore set:
        
        | Rule | Reason |
        |---|---|
        | `COM812`, `ISC001` | Conflict with `ruff format` (ruff itself documents this) |
        | `D203` vs `D211`, `D213` vs `D212` | Mutually-exclusive docstring conventions; pick the modern one |
        | `CPY001` | Most projects don't need a copyright header on every file |
        | `FBT001`, `FBT002` | Boolean flags are ergonomic for CLI/typer; ban makes typer awkward |
        | `TD002`, `TD003`, `FIX002` | TODOs without a JIRA link are fine in solo / internal code |
        
        `ANN101` and `ANN102` were **removed in ruff 0.8.0** (Nov 2024). Do NOT include them in `ignore` - ruff errors on unknown rule codes.
        
        `per-file-ignores` for `tests/**` is the standard pattern from real-world repos like `community-of-python/auto-typing-final` and `Preston-Landers/concurrent-log-handler`.
        
        ## CI gate
        
        ```bash
        # In CI, fail on any violation:
        uv run basedpyright
        uv run ruff check
        uv run ruff format --check
        uv run pytest
        ```
        
        A single `make ci` target combining the four works fine.
        
        ## Enforcement summary
        
        The config above, combined with `scripts/check-no-excuse-rules.py`, enforces:
        
        | What | How |
        |---|---|
        | Exhaustive match | basedpyright `all` mode + `assert_never` |
        | No `Any` | basedpyright `all` mode + script `cast-any` rule |
        | Ignored return values | `reportUnusedCallResult = "warning"` |
        | Immutable default | Script `mutable-dataclass` + `missing-slots` rules |
        | No null surprise | basedpyright strict `None` analysis |
        | Constants are const | basedpyright catches `Final` reassignment |
        | Unused variables | `reportUnusedVariable = "error"` |
        
        ## Sources
        
        - basedpyright modes: <https://docs.basedpyright.com/latest/configuration/config-files/#type-check-diagnostics-settings>
        - basedpyright `"all"` vs `"recommended"`: <https://docs.basedpyright.com/latest/configuration/config-files/#recommended-and-all>
        - basedpyright better defaults: <https://docs.basedpyright.com/latest/benefits-over-pyright/better-defaults/>
        - ruff rule selection: <https://docs.astral.sh/ruff/linter/#rule-selection>
        - ruff ANN101/ANN102 removed: <https://github.com/astral-sh/ruff/pull/14384>
        - Real-world ALL config: <https://github.com/community-of-python/auto-typing-final/blob/main/pyproject.toml>
        - PEP 735 dependency-groups: <https://peps.python.org/pep-0735/>
        
      • README.md 15.4 KB
        
        # Python Programmer
        
        Modern Python. Type-strict, stack-first, async-correct.
        
        ## Philosophy
        
        The type checker is your compiler. Make illegal states unrepresentable. Parse at boundaries. Own resources explicitly. Every function has a contract; the type system enforces it.
        
        ## Hard rules
        
        These are deliberate project choices. Violations are always wrong, not "style preferences".
        
        ### Tooling
        
        | Category | Use | Never |
        |---|---|---|
        | Package manager | `uv` | pip, poetry, conda, pipenv |
        | Type checker | `basedpyright` (`typeCheckingMode = "all"`) | pyright, mypy |
        | Linter + formatter | `ruff` (`select = ["ALL"]`) | flake8, black, isort, autopep8 |
        | Async runtime | `anyio` | `import asyncio` |
        | Data | `polars` + `duckdb` + `numpy` | pandas |
        | Web framework | FastAPI + Pydantic v2 | Flask, Django REST |
        | ORM | SQLAlchemy 2.x async | Django ORM, Tortoise |
        | HTTP client | [`httpx2`](https://github.com/pydantic/httpx2) | requests, aiohttp, httpx |
        | Testing | `pytest` | unittest |
        | CLI | `typer` + `rich` | argparse, click, fire |
        
        ### The iron list
        
        1. **Frozen by default** — `@dataclass(frozen=True, slots=True)`. Pydantic: `model_config = ConfigDict(frozen=True)`. Mutable only when mutation is the documented purpose.
        2. **NewType for distinct IDs** — `UserId = NewType("UserId", int)`. Never pass raw `int` where a branded type exists.
        3. **`match` only for variants, `if` only for booleans** — **NEVER** use `if/elif/else` to discriminate on type (`isinstance`), enum value, or literal variant. `match/case` is mandatory for these — non-negotiable. **ALWAYS** end with `case unreachable: assert_never(unreachable)` — bare `case _: pass` and `case _: raise ValueError` are banned (they silently swallow new variants). `if/else` is fine only for boolean expressions, range checks, and predicate calls that aren't variant discrimination. See "Why `if/elif` on variants is banned" below for examples.
        4. **Protocol over ABC** — `typing.Protocol` for interfaces. ABC only when you need shared method implementation.
        5. **No raw dicts in signatures** — params and returns use `TypedDict`, `dataclass`, or Pydantic model. Internal scratch dicts are fine.
        6. **Parse, don't validate** — constructors produce typed objects or raise. Never pass unvalidated data deeper into the call stack.
        7. **Typed errors** — error types are dataclasses or exceptions with typed fields. Never `raise ValueError("something")` with a bare string. Use union returns when the caller is within 1-2 call levels and must handle the outcome (repository → service). Use exceptions when the error should propagate up many layers to a boundary handler (service → HTTP handler).
        8. **Final for constants** — module-level constants use `Final`. Mutable module globals are a code smell.
        9. **Explicit None** — annotate `-> X | None`. Never return `None` from a function whose signature omits it.
        10. **Context managers for resources** — files, DB connections, HTTP clients, locks. No manual `.close()`.
        11. **No Any, no object** — both are banned as type annotations. `object` erases all structural information (zero callable attributes, zero narrowing). Use `Protocol` (structural typing), `TypeVar` (generic pass-through), explicit union (known variants), or `TypedDict` (dict shapes).
        12. **No cast** — `cast()` is banned. Redesign the types.
        13. **No type: ignore** — fix the type error. The checker is right; you are wrong.
        14. **No broad except** — `except Exception` and `except BaseException` are banned. Catch the **specific** exception you expect. A broad catch swallows bugs you need to see — `KeyError`, `AttributeError`, `TypeError` all vanish silently. If you genuinely need a catch-all at a top-level boundary (CLI entry, HTTP handler), use `# noqa: BROAD_EXCEPT_OK` and log + re-raise.
        
        ### Typing and safety
        
        - `basedpyright` in `typeCheckingMode = "all"`. Every public function has full annotations. Internal helpers: annotate return type; parameter types may be inferred.
        - `ruff` with `select = ["ALL"]`. Override specific rules per project in `pyproject.toml`, never globally disable the strict baseline.
        - Every new function must have a `docstring` unless its name + signature makes it completely obvious (e.g. `def full_name(first: str, last: str) -> str:`).
        - Use `X | Y` union syntax (PEP 604), never `Union[X, Y]` or `Optional[X]`.
        
        ### Why `object` is banned
        
        `object` pretends to be safe ("it's the top type!") but gives **zero** narrowing and **zero** attributes. Even `Any` is more honest — it admits the boundary is untyped.
        
        ```python
        # BANNED
        def process(data: object) -> object: ...
        def store(items: list[object]) -> None: ...
        results: dict[str, object] = {}
        
        # GOOD — Protocol for structural typing
        class Serializable(Protocol):
            def serialize(self) -> bytes: ...
        def process(data: Serializable) -> ProcessResult: ...
        
        # GOOD — TypeVar for generic pass-through
        def identity[T](x: T) -> T: ...
        def first[T](items: Sequence[T]) -> T: ...
        
        # GOOD — explicit union for known variants
        def parse(raw: str | bytes) -> Document: ...
        ```
        
        ### Why `if/elif` on variants is banned
        
        `if/elif/else` chains on type, enum, or literal values lose compile-time exhaustiveness. When a new variant is added, nothing warns you. `match/case` + `assert_never` does.
        
        ```python
        # BANNED — if/elif for type discrimination
        if isinstance(event, Click):
            handle_click(event.x, event.y)
        elif isinstance(event, Scroll):
            handle_scroll(event.delta)
        else:
            raise ValueError(f"Unknown: {event}")  # runtime bomb
        
        # BANNED — if/elif for enum discrimination
        if status == Status.PENDING:
            start_review()
        elif status == Status.ACTIVE:
            continue_processing()
        elif status == Status.CLOSED:
            archive()
        
        # BANNED — non-exhaustive match (swallows new variants)
        match event:
            case Click(x, y): handle_click(x, y)
            case _: pass
        
        # GOOD — exhaustive match with assert_never
        match event:
            case Click(x=x, y=y):
                handle_click(x, y)
            case Scroll(delta=delta):
                handle_scroll(delta)
            case unreachable:
                assert_never(unreachable)
        
        # GOOD — enum match
        match status:
            case Status.PENDING: start_review()
            case Status.ACTIVE:  continue_processing()
            case Status.CLOSED:  archive()
            case unreachable:    assert_never(unreachable)
        ```
        
        `if/else` is fine for boolean conditions and range checks — things that aren't variant discrimination:
        
        ```python
        # FINE — boolean, not variant
        if age >= 18:
            grant_access()
        else:
            deny_access()
        ```
        
        ### Why broad `except` is banned
        
        `except Exception` catches **every** non-system exception — `KeyError`, `TypeError`, `AttributeError`, `ValueError` all vanish. You lose the stack trace that would have told you exactly what went wrong. The fix is always to name the exception you expect.
        
        ```python
        # BANNED — swallows bugs
        try:
            result = api.fetch(url)
        except Exception as e:
            logger.error(e)
            return None
        
        # BANNED — catch-and-ignore
        try:
            parse(data)
        except Exception:
            pass
        
        # GOOD — catch what you expect
        try:
            result = api.fetch(url)
        except httpx.HTTPStatusError as e:
            logger.error("API %d: %s", e.response.status_code, e.request.url)
            return None
        except httpx.ConnectError:
            raise ServiceUnavailableError(service="api") from None
        
        # GOOD — top-level boundary (only place broad catch is acceptable)
        def main() -> int:  # noqa: BROAD_EXCEPT_OK
            try:
                return run()
            except Exception:
                logger.exception("unhandled error")
                return 1
        ```
        
        ### Async
        
        - `import asyncio` is **BANNED**. Use `import anyio`.
        - For background tasks, use `anyio.create_task_group`. Never fire-and-forget with `asyncio.create_task`.
        - For concurrency gates, use `anyio.CapacityLimiter` (not `asyncio.Semaphore`).
        - Load `async-anyio.md` when writing async code for the full pattern library.
        
        ### Data modeling — which container, when
        
        All model fields carry type annotations. No `Any`, no untyped dicts in public APIs.
        Use `polars` + `duckdb` for data. pandas is never the right answer in this stack.
        
        | Situation | Use |
        |---|---|
        | User input, API request/response | `Pydantic BaseModel (frozen=True)` |
        | Internal value object (no I/O) | `@dataclass(frozen=True, slots=True)` |
        | Function with multiple outcomes | Union of frozen dataclasses + `match` |
        | Dict shape for JSON compat / `**kwargs` | `TypedDict` |
        | Fixed constants | `StrEnum` / `IntEnum` |
        | Distinct primitive (UserId vs MovieId) | `NewType` |
        | Contract / capability | `Protocol` |
        | Contract + shared implementation | `ABC` |
        | ORM model (SQLAlchemy) | `Mapped[]` — inherently mutable, `# noqa: MUTABLE_OK` |
        | Config from env vars | `pydantic-settings BaseSettings` |
        
        **The one rule**: data crosses trust boundary → Pydantic. Everything else → dataclass.
        
        Load `data-modeling.md` for the full decision flowchart and comparison matrix.
        
        ### When frozen=True does not apply
        
        - **ORM models** — SQLAlchemy `Mapped[]` requires mutation. Use `# noqa: MUTABLE_OK`.
        - **Builder / accumulator** — object exists to be mutated (counter, buffer, state machine). Docstring must explain why.
        - **Pydantic Settings** — tests override fields. Mutable is acceptable.
        
        If you need `# noqa: MUTABLE_OK`, the class docstring must say why mutation is required.
        
        ### Libraries
        
        Canonical defaults (override only if `pyproject.toml` explicitly picks something else):
        
        | Domain | Library | Reason |
        |---|---|---|
        | CLI | `typer` | Type-annotated CLI from function sigs |
        | Pretty output | `rich` | Tables, progress, tracebacks, markdown |
        | HTTP client | [`httpx2`](https://github.com/pydantic/httpx2) | Next-gen HTTP client (Pydantic stewardship), HTTP/2, brotli+zstd. Always `httpx2[http2,brotli,zstd]`. See `httpx2-optimization.md` |
        | Validation | `pydantic` v2 | Fast native validator, JSON Schema |
        | Web API | `fastapi` | Async, Pydantic-native, OpenAPI |
        | ORM | `sqlalchemy` 2.x async | `Mapped[]` types, async sessions |
        | DB driver (Postgres) | `asyncpg` (via SQLAlchemy) | Fastest PG driver |
        | AI agents | `pydantic-ai` | Typed deps, structured output |
        | TUI | `textual` | Rich-based, CSS layout, widgets |
        | Logging | `rich.logging.RichHandler` | Pretty; swap to `structlog` in prod |
        
        ## pyproject.toml — the one true config
        
        Scaffold a new project with all strict defaults pre-configured:
        
        ```bash
        uv run ../../scripts/python/new-project.py myproject
        uv run ../../scripts/python/new-project.py myproject --path ./workspace
        uv run ../../scripts/python/new-project.py myproject --lib   # publishable library
        ```
        
        Creates via `uv init`, then injects basedpyright `typeCheckingMode = "all"` + ruff `select = ["ALL"]` + pytest strict. Cross-platform (macOS, Linux, Windows).
        
        For manual setup: `uv init --app myproject`, then load `pyproject-strict.md`.
        
        ## PEP 723 — inline script metadata (mandatory for ALL scripts)
        
        Every `.py` script — even throwaway — MUST use PEP 723 inline metadata with the `# ─── How to run ───` comment block. No venv, no `requirements.txt`. The script IS the environment spec. A script without the usage comment block is incomplete.
        
        Scaffold with: `uv run ../../scripts/python/new-script.py <name> --deps "httpx2[http2,brotli,zstd]"` (writes to temp dir by default, `--output` for specific path).
        
        Load `one-liners.md` for full patterns, examples, and anti-patterns.
        
        ## Reference loading
        
        Load on demand — not all at once.
        
        | Need | Load |
        |---|---|
        | Full pyproject.toml config | `pyproject-strict.md` |
        | Type patterns (NewType, Final, enums, narrowing) | `type-patterns.md` |
        | Data modeling (container choice, frozen, parse-don't-validate) | `data-modeling.md` |
        | Error handling (typed errors, union returns, exhaustive match) | `error-handling.md` |
        | Async patterns (anyio) | `async-anyio.md` |
        | Data processing (polars / duckdb) | `data-processing.md` |
        | FastAPI + SQLAlchemy stack | `fastapi-stack.md` |
        | Library decision tree | `libraries.md` |
        | **httpx2 optimization** (MUST load for any network code) | `httpx2-optimization.md` |
        | **orjson** (when JSON is in the hot path; FastAPI/Pydantic v2 integration) | `orjson-stack.md` |
        | One-liner scripts (PEP 723) | `one-liners.md` |
        | PydanticAI agents | `pydantic-ai.md` |
        | Textual TUI | `textual-tui.md` |
        
        ## httpx2 — mandatory for ALL network requests
        
        Every outgoing HTTP call MUST use [`httpx2`](https://github.com/pydantic/httpx2) (`httpx2[http2,brotli,zstd]`). Never `requests`, never `aiohttp`, never the original `httpx`.
        
        **ALL optimizations are ON by default — not optional, not progressive, not "nice to have".** A bare `httpx2.AsyncClient()` is a bug — treat it like a lint violation. The correct way is the factory pattern in `httpx2-optimization.md` with: HTTP/2 enabled, tuned connection pool (200/40/30s), split timeouts (5/30/10/10), transport retries (3), TCP_NODELAY, follow_redirects, and event hooks for observability.
        
        When writing or reviewing ANY network code, **ALWAYS load `httpx2-optimization.md`** and use the factory pattern verbatim. No exceptions.
        
        ## No-excuse audit
        
        Violations caught by `../../scripts/python/check-no-excuse-rules.py`. Run after every edit session.
        
        | Rule ID | Catches | Opt-out |
        |---|---|---|
        | `cast-any` | `cast(Any, ...)` | None — redesign types |
        | `type-ignore` | `# type: ignore` | None — fix the type |
        | `pyright-ignore` | `# pyright: ignore` | None — fix the type |
        | `bare-except` | `except:` with no class | None — name the exception |
        | `silent-except` | `except X: pass` / `except X: ...` | None — handle or re-raise |
        | `no-asyncio` | `import asyncio` | `# noqa: ANYIO_OK` |
        | `no-pandas` | `import pandas` | `# noqa: PANDAS_OK` |
        | `mutable-dataclass` | `@dataclass` without `frozen=True` | `# noqa: MUTABLE_OK` |
        | `missing-slots` | `@dataclass` without `slots=True` | `# noqa: SLOTS_OK` |
        | `raw-dict-return` | `-> dict` in function return type | `# noqa: DICT_OK` |
        | `missing-assert-never` | `match` block without `assert_never` default | `# noqa: MATCH_OK` |
        | `generic-exception` | `raise ValueError("...")` / `raise TypeError("...")` with bare string | `# noqa: GENERIC_ERR_OK` |
        | `no-object` | `object` used as type annotation (param, return, generic arg) | `# noqa: OBJECT_OK` |
        | `if-elif-on-variant` | `if isinstance()`/`if x == Enum.V` chain that should be `match/case` | `# noqa: IF_VARIANT_OK` |
        | `oversized-module` | File exceeds 250 pure LOC (non-blank, non-comment) | `# noqa: SIZE_OK` |
        | `broad-except` | `except Exception` / `except BaseException` (too broad) | `# noqa: BROAD_EXCEPT_OK` |
        
        Fix every violation before declaring work done. basedpyright + ruff strict config catches the rest.
        
        ## In tests
        
        Tests are strict too, with these exceptions (already configured in `pyproject.toml` per-file-ignores):
        
        | In tests you may | Why |
        |---|---|
        | Use `assert` | That's how pytest works (`S101` ignored) |
        | Use magic numbers | Test data (`PLR2004` ignored) |
        | Access `_private` members | Testing internals (`SLF001` ignored) |
        | Skip docstrings | Test names are the docs (`D` ignored) |
        | Have unused function args | Fixtures (`ARG` ignored) |
        
        Tests still follow the iron list — frozen dataclasses, typed errors, exhaustive match. If test fixtures need mutable state, use `# noqa: MUTABLE_OK` on the fixture class.
        
        ## Existing codebases
        
        When editing an existing file that doesn't follow these rules: **write new code in strict style, don't refactor existing code in the same change.** Mixing feature work with style migration makes reviews harder and bugs likelier.
        
        ## Activation
        
        This skill activates whenever you are writing or modifying any `.py` file. Even one-off scripts get the strict treatment — that is the whole point of PEP 723 + uv: production hygiene with throwaway ergonomics.
        
      • textual-tui.md 5.1 KB
        # Textual TUI
        
        Textual builds rich, mouse-aware, scrollable, mobile-style TUIs on top of `rich`. Replaces curses, urwid, blessed.
        
        ## Install
        
        ```bash
        uv add textual
        uv add --dev textual-dev   # textual console + run --dev for hot reload
        ```
        
        ## Minimal app
        
        ```python
        from textual.app import App, ComposeResult
        from textual.widgets import Header, Footer, Button, Label
        from textual.containers import Vertical
        
        
        class CounterApp(App[None]):
            """A trivial counter app."""
        
            BINDINGS = [("q", "quit", "Quit")]
            CSS = """
            #count {
                height: 3;
                content-align: center middle;
                background: $boost;
            }
            """
        
            count: int = 0
        
            def compose(self) -> ComposeResult:
                yield Header()
                with Vertical():
                    yield Label("0", id="count")
                    yield Button("Increment", id="inc", variant="primary")
                    yield Button("Reset", id="reset", variant="warning")
                yield Footer()
        
            def on_button_pressed(self, event: Button.Pressed) -> None:
                if event.button.id == "inc":
                    self.count += 1
                elif event.button.id == "reset":
                    self.count = 0
                self.query_one("#count", Label).update(str(self.count))
        
        
        if __name__ == "__main__":
            CounterApp().run()
        ```
        
        Run:
        
        ```bash
        uv run python counter.py
        ```
        
        For hot reload during development:
        
        ```bash
        uv run textual run --dev counter.py
        ```
        
        ## Reactive attributes
        
        Textual's `reactive()` descriptor turns a class attribute into something that watches assignments and re-renders automatically. Replaces the manual `query_one` + `update` dance.
        
        ```python
        from textual.app import App, ComposeResult
        from textual.reactive import reactive
        from textual.widgets import Label
        
        
        class CountWidget(Label):
            count: reactive[int] = reactive(0)
        
            def render(self) -> str:
                return f"Count: {self.count}"
        
        
        class CounterApp(App[None]):
            def compose(self) -> ComposeResult:
                yield CountWidget()
        
            def on_key(self, event) -> None:
                if event.key == "space":
                    self.query_one(CountWidget).count += 1
        ```
        
        `reactive()` triggers `render()` (or `watch_<attr>` and `validate_<attr>` callbacks if defined). Use `recompose=True` if you need to call `compose()` again on change.
        
        ## Async work — workers
        
        NEVER block the event loop. For network/disk/CPU work, use `@work` (creates a worker) or `run_worker`.
        
        ```python
        import httpx
        from textual.app import App, ComposeResult
        from textual.widgets import Input, Static
        from textual.work import work
        
        
        class FetchApp(App[None]):
            def compose(self) -> ComposeResult:
                yield Input(placeholder="URL", id="url")
                yield Static(id="result")
        
            @work(exclusive=True)
            async def fetch(self, url: str) -> None:
                async with httpx.AsyncClient(timeout=10.0) as client:
                    response = await client.get(url)
                self.query_one("#result", Static).update(f"{response.status_code} - {len(response.text)} bytes")
        
            def on_input_submitted(self, event: Input.Submitted) -> None:
                self.fetch(event.value)
        ```
        
        `exclusive=True` cancels the previous worker if the user submits a new URL before the first finishes. Workers integrate with Textual's lifecycle - they're cancelled when the app exits.
        
        `@work` is asyncio-flavoured under the hood. That is fine - it does not violate the no-asyncio rule because you are calling Textual's API, not importing asyncio yourself. Inside the worker body, use `httpx.AsyncClient` and other anyio-friendly libraries.
        
        ## Action handlers
        
        Bind keys to method calls via `BINDINGS` and `action_*` methods.
        
        ```python
        class App(App):
            BINDINGS = [
                ("ctrl+s", "save", "Save"),
                ("ctrl+r", "reload", "Reload"),
            ]
        
            def action_save(self) -> None:
                # Called on ctrl+s
                ...
        
            def action_reload(self) -> None:
                ...
        ```
        
        Bindings can also include the `priority=True` flag to fire before children get a chance.
        
        ## CSS
        
        Textual's CSS supports selectors, variables (`$primary`, `$boost`), animations. Inline via `CSS = "..."` or external via `CSS_PATH = "app.tcss"`.
        
        ```css
        Screen {
            background: $surface;
            color: $text;
            layout: vertical;
        }
        
        #sidebar {
            width: 30;
            background: $boost;
        }
        
        Button.danger {
            background: $error;
        }
        ```
        
        Reload with `r` in dev mode (`textual run --dev`).
        
        ## Testing
        
        ```python
        import pytest
        from myapp import CounterApp
        
        
        @pytest.mark.anyio
        async def test_counter_increments() -> None:
            app = CounterApp()
            async with app.run_test() as pilot:
                await pilot.click("#inc")
                await pilot.click("#inc")
                assert app.count == 2
        ```
        
        `pilot.click(selector)`, `pilot.press("q")`, `pilot.pause()` for waiting on the next frame.
        
        ## When NOT to use Textual
        
        | Need | Use |
        |---|---|
        | One-off CLI with structured output | typer + rich |
        | Progress bar in a script | rich.progress |
        | Tabular display of query results | rich.table |
        | Full-screen app with state, input, mouse | Textual |
        
        A pretty CLI is not a TUI. Reach for Textual when the user expects to navigate a UI, not when you want colours.
        
        ## Sources
        
        - Textual docs: <https://textual.textualize.io>
        - Textual tutorial: <https://textual.textualize.io/tutorial/>
        - API reference: <https://textual.textualize.io/api/>
        
      • type-patterns.md 4.1 KB
        # Type Patterns
        
        How to use Python's type system to catch bugs at check time, not runtime.
        
        ---
        
        ## NewType — distinct primitives
        
        Same runtime type, different meaning. The type checker prevents mixing.
        
        ```python
        from typing import NewType
        
        UserId = NewType("UserId", int)
        MovieId = NewType("MovieId", int)
        Email = NewType("Email", str)
        Seconds = NewType("Seconds", float)
        Milliseconds = NewType("Milliseconds", float)
        
        def get_user(user_id: UserId) -> User: ...
        def get_movie(movie_id: MovieId) -> Movie: ...
        def sleep(duration: Seconds) -> None: ...
        
        uid = UserId(42)
        mid = MovieId(42)
        
        get_user(uid)   # OK
        get_user(mid)   # type error: MovieId is not UserId
        get_user(42)    # type error: int is not UserId
        sleep(Milliseconds(100.0))  # type error
        ```
        
        **Use when**: IDs, indices, keys, units of measurement — any pair where swapping is a bug.
        **Skip when**: ephemeral local math where branding adds noise with zero safety gain.
        
        ---
        
        ## Final — constants are const
        
        Module-level constants declare their intent. Reassignment is a type error.
        
        ```python
        from typing import Final
        
        MAX_RETRIES: Final = 3
        API_BASE_URL: Final = "https://api.example.com"
        DEFAULT_TIMEOUT: Final = 30.0
        
        MAX_RETRIES = 5  # type error: cannot assign to Final
        ```
        
        If it changes at runtime, it's not a constant — make it a function parameter or config field.
        
        ---
        
        ## TypeAlias — name complex types
        
        If a union or generic appears more than once, give it a name.
        
        ```python
        # Python 3.12+
        type JsonValue = str | int | float | bool | None | list["JsonValue"] | dict[str, "JsonValue"]
        type Headers = dict[str, str]
        type Middleware = Callable[[Request], Awaitable[Response]]
        
        # Pre-3.12
        from typing import TypeAlias
        
        JsonValue: TypeAlias = str | int | float | bool | None | list["JsonValue"] | dict[str, "JsonValue"]
        ```
        
        ---
        
        ## StrEnum / IntEnum — closed sets
        
        Any fixed set of known values. No string literals scattered through code.
        
        ```python
        from enum import StrEnum, IntEnum, unique
        
        @unique
        class Role(StrEnum):
            ADMIN = "admin"
            USER = "user"
            GUEST = "guest"
        
        @unique
        class HttpStatus(IntEnum):
            OK = 200
            NOT_FOUND = 404
            INTERNAL_ERROR = 500
        
        # BAD
        def check_role(role: str) -> bool: ...
        
        # GOOD
        def check_role(role: Role) -> bool: ...
        ```
        
        `StrEnum` when values serialize as strings (API, DB). `IntEnum` for numeric codes. Plain `Enum` for pure labels.
        
        ---
        
        ## Type narrowing — let the checker follow your logic
        
        `isinstance`, `is None`, and `match` narrow types automatically. Use them instead of `cast`.
        
        ```python
        def process(value: str | int | None) -> str:
            if value is None:
                return "nothing"
            # checker knows: str | int
        
            if isinstance(value, str):
                return value.upper()
            # checker knows: int
        
            return str(value * 2)
        ```
        
        ### TypeGuard for custom narrowing
        
        ```python
        from typing import TypeGuard
        
        def is_valid_email(value: str) -> TypeGuard[Email]:
            return "@" in value and "." in value.split("@")[1]
        
        def send(addr: str) -> None:
            if not is_valid_email(addr):
                raise ValueError(addr)
            # checker knows: addr is Email
            deliver(addr)
        ```
        
        ### TypeIs (Python 3.13+) — the strict version
        
        `TypeIs` is stricter than `TypeGuard` — it narrows in both `if` and `else` branches.
        
        ```python
        from typing import TypeIs
        
        def is_str(value: str | int) -> TypeIs[str]:
            return isinstance(value, str)
        
        def handle(v: str | int) -> None:
            if is_str(v):
                print(v.upper())   # checker knows: str
            else:
                print(v + 1)       # checker knows: int
        ```
        
        ---
        
        ## Union syntax
        
        Always `X | Y`. Never `Union[X, Y]` or `Optional[X]`.
        
        ```python
        # BAD
        from typing import Union, Optional
        def f(x: Optional[int]) -> Union[str, int]: ...
        
        # GOOD
        def f(x: int | None) -> str | int: ...
        ```
        
        ---
        
        ## Sources
        
        - Python docs: [typing — NewType](https://docs.python.org/3/library/typing.html#newtype)
        - Python docs: [typing — Final](https://docs.python.org/3/library/typing.html#typing.Final)
        - Python docs: [typing — TypeGuard](https://docs.python.org/3/library/typing.html#typing.TypeGuard)
        - PEP 604: [Union syntax X | Y](https://peps.python.org/pep-0604/)
        - PEP 742: [TypeIs](https://peps.python.org/pep-0742/)
        
    • rust
      • api-design.md 6.5 KB
        # Public API Design
        
        What a caller sees: names, conversions, traits, attributes, visibility, and rustdoc. Every rule here exists so a call site reads correctly without opening the implementation.
        
        ## Naming says cost and ownership
        
        | Prefix / form | Meaning | Example |
        |---|---|---|
        | `as_x(&self) -> &X` | Borrowed view, no allocation | `str::as_bytes`, `PathBuf::as_path` |
        | `to_x(&self) -> X` | Builds a new owned value (may allocate or compute) | `str::to_lowercase`, `Path::to_path_buf` |
        | `into_x(self) -> X` | Consumes `self`, reuses its buffers | `String::into_bytes`, `Vec::into_boxed_slice` |
        | `x(&self)` | Plain accessor, no `get_` prefix | `len()`, `name()`, `user_id()` |
        | `get(&self, key)` | Fallible lookup that returns `Option` | `HashMap::get`, `slice::get` |
        | `iter()` / `iter_mut()` / `IntoIterator` | Borrowing, mutable, and consuming iteration | implement the ones the type actually needs |
        | `is_x()` / `has_x()` / `can_x()` | `bool` query, named for the positive state | `is_empty()`, `has_children()` |
        
        Types, traits, and enum variants are `UpperCamelCase` with acronyms as one word (`HttpClient`, `Json`, not `HTTPClient`); functions, modules, and locals `snake_case`; constants and statics `SCREAMING_SNAKE_CASE`; simple generics `T`, `E`, `K`, `V`, lifetimes short (`'a`, `'de`). Return `impl Iterator<Item = T>` instead of a named iterator struct unless callers need to name the type.
        
        ## Conversions
        
        | Conversion | Trait | Rule |
        |---|---|---|
        | Always succeeds, no invariant can break | `From<T>` | Implement `From`; callers get `Into` for free. Never implement `Into` directly. |
        | Can fail | `TryFrom<T>` | `type Error` is a real error type, never `()` or `String`. |
        | Parses text | `FromStr` | Routes through the same checked constructor as `TryFrom`. |
        | Cheap borrowed view for a generic parameter | `AsRef<T>` | Accept `impl AsRef<Path>` only where callers really pass several types; a plain `&Path` is simpler otherwise. |
        
        Validated types expose `TryFrom` / `FromStr` only; an infallible `From` into them bypasses the invariant ([type-state.md](type-state.md)). Units convert through named methods (`to_feet()`), never `From`, so the conversion is visible.
        
        Generic parameters that only convert (`fn new(name: impl Into<String>)`) hide an allocation at the call site; take `&str` or `String` explicitly unless the constructor is called with both often.
        
        ## Trait design
        
        - **Generic by default, `dyn` for heterogeneity.** `fn run<R: Renderer>(r: &R)` monomorphizes and inlines; `&dyn Renderer` or `Box<dyn Renderer>` is for a collection of mixed implementors or a deliberate code-size trade. `dyn` needs indirection, not necessarily a heap allocation: `&dyn Trait` borrows.
        - **Associated type vs generic parameter.** One natural output per implementor (`Iterator::Item`, a parser's `Output`) is an associated type. A generic parameter (`From<T>`) is for traits a type implements several times.
        - **Minimal required methods.** Require the few methods that carry the contract; provide the rest as default methods built on them. A default method must be correct for every implementor, not merely convenient.
        - **Blanket impls are a semver commitment.** A public `impl<T: Display> MyTrait for T` forbids downstream crates, and your own future versions, from writing a more specific impl. Add one only when it is permanently right for every `T`.
        - **Orphan rule.** You cannot implement a foreign trait for a foreign type. Wrap the type in a local newtype and implement the trait on the wrapper; do not add `Deref` to the inner type to make it convenient.
        - **Closed sets are sealed.** A public trait whose implementations only you may add uses the sealed-trait pattern ([type-state.md](type-state.md#sealed-traits)).
        - **Bounds live on impls, not structs.** `struct Cache<K, V> { .. }` stays unbounded; `impl<K: Hash + Eq, V> Cache<K, V>` carries the bounds, so every mention of `Cache` does not repeat them.
        - **Callables take the weakest bound.** `FnOnce` if called once, `FnMut` if called repeatedly with mutation, `Fn` for shared repeated calls. Return a closure as `impl Fn(..)`; box it (`Box<dyn Fn(..)>`) only to store closures of different types together.
        
        ## Attributes that protect callers
        
        - **`#[must_use]`** on builder methods that return `Self` (a dropped builder silently loses configuration), on pure constructors of values whose drop is a bug, and on functions whose whole point is the return value. `Result` is already `must_use`; do not blanket-annotate every function.
        - **`#[non_exhaustive]`** on public enums and structs that will grow (error enums, config structs, event types). Downstream matches get a required `_` arm and your next variant is not a breaking change. Keep a public enum exhaustive only when its closed set is part of the contract (`Ordering`). `exhaustive_enums` / `exhaustive_structs` in [cargo-strict.md](cargo-strict.md) flag the choice.
        
        ## Visibility
        
        Start private. Widen one step at a time, only for a caller that exists: private -> `pub(super)` (sibling modules) -> `pub(crate)` -> `pub`. The `unreachable_pub` lint catches `pub` items no one outside the crate can reach. Shape the public surface with deliberate re-exports (`pub use crate::parse::Parser;` in `lib.rs`), never `pub use module::*`, which exports whatever a later edit adds.
        
        ## Rustdoc sections
        
        Every public item has a doc comment (`missing_docs`). Public fallible, panicking, or `unsafe` functions carry the section a caller needs:
        
        ```rust
        /// Parses a `key=value` line.
        ///
        /// # Errors
        ///
        /// Returns [`ParseError::MissingSeparator`] when the line has no `=`.
        ///
        /// # Examples
        ///
        /// ```
        /// # use my_crate::{parse_pair, ParseError};
        /// # fn main() -> Result<(), ParseError> {
        /// let (key, value) = parse_pair("port=8080")?;
        /// assert_eq!((key, value), ("port", "8080"));
        /// # Ok(())
        /// # }
        /// ```
        pub fn parse_pair(line: &str) -> Result<(&str, &str), ParseError> {
            line.split_once('=').ok_or(ParseError::MissingSeparator)
        }
        ```
        
        - `# Errors` for every public `Result`-returning function (`missing_errors_doc`), `# Panics` for every reachable panic (`missing_panics_doc`), `# Safety` for every public `unsafe fn` or `unsafe trait`, listing each obligation the caller takes on.
        - Examples use `?`, never `unwrap`; lines starting with `# ` compile but stay hidden, which is where imports and the `fn main() -> Result` wrapper go.
        - Link types with intra-doc links (``[`ParseError`]``). `RUSTDOCFLAGS="-D warnings" cargo doc` fails on a broken link, and `cargo test --doc` runs every example.
        - A crate's front page can be its README: `#![doc = include_str!("../README.md")]` makes the README's code blocks doctests too.
        
      • async-tokio.md 12.6 KB
        # Async with Tokio
        
        Structured concurrency, cancellation, blocking-work isolation, channel selection. The patterns the agent should reach for by default.
        
        ## Runtime selection
        
        ```rust
        // Default for services and CLIs that do real work: multi-thread, one worker per core
        #[tokio::main]
        async fn main() -> anyhow::Result<()> { ... }
        
        // For tiny CLIs or wasm where you measured single-thread is enough
        #[tokio::main(flavor = "current_thread")]
        async fn main() -> anyhow::Result<()> { ... }
        ```
        
        Keep the default worker count. Set `worker_threads` only for a measured reason or a deployment constraint (a container CPU quota the runtime cannot see, a desktop tool sharing the machine). CPU-heavy work does not get more workers; it leaves the runtime (see Blocking work).
        
        ## Spawning
        
        `tokio::spawn` returns a `JoinHandle<T>`. The future runs to completion even if the handle is dropped (detached). To enforce structured concurrency, use `JoinSet`:
        
        ```rust
        use tokio::task::JoinSet;
        
        let mut set = JoinSet::new();
        for url in urls {
            let client = client.clone();
            set.spawn(async move { fetch(&client, &url).await });
        }
        
        let mut results = Vec::new();
        while let Some(joined) = set.join_next().await {
            match joined {
                Ok(Ok(body)) => results.push(body),
                Ok(Err(error)) => tracing::warn!(%error, "fetch failed"),
                Err(panicked) if panicked.is_panic() => {
                    tracing::error!(?panicked, "worker panicked");
                    // Choose: re-raise, or continue with degraded result set.
                }
                Err(other) => tracing::error!(?other, "worker join error"),
            }
        }
        ```
        
        `JoinSet`:
        - Knows when all spawned tasks finish.
        - Dropping the set aborts every still-running task.
        - Lets you handle failures one by one rather than all-or-nothing.
        
        For a fixed set of independent futures, `join!` waits for all; for fallible ones, `try_join!` returns on the first `Err`:
        
        ```rust
        let (a, b, c) = tokio::try_join!(load_a(), load_b(), load_c())?;
        ```
        
        On that first error `try_join!` drops the other futures mid-flight. Dropping is cancellation, not rollback: side effects they already started stay started (see Cancellation).
        
        For first-of-many, `select!`:
        
        ```rust
        tokio::select! {
            biased;  // bias to top-to-bottom checking when ordering matters
            _ = shutdown.recv() => {
                tracing::info!("shutdown signal");
                return Ok(());
            }
            request = listener.accept() => {
                handle_request(request?).await?;
            }
        }
        ```
        
        Without `biased`, branches are polled in random order each iteration (good for fairness). Use `biased` only when you need deterministic priority (shutdown signal first, etc).
        
        ## Cancellation
        
        A future is cancelled when it is dropped (e.g., the `select!` arm wins another branch). **Always think: if this future is dropped mid-await, what state is left behind?**
        
        Cancel-safe futures (you can drop them without losing data):
        - `recv()` on channels, `accept()` on listeners
        - `changed()` / `wait_for` on `watch::Receiver`
        - `AsyncReadExt::read` / `read_buf`: bytes land in your buffer only when the call completes
        
        Cancel-unsafe futures (dropping mid-way loses data or leaves partial state):
        - `read_exact`, `read_to_end`, `read_to_string`: bytes already read are gone
        - `write_all` / `write_all_buf`: an unknown prefix was already written
        - Custom futures that perform side effects before suspending
        
        Tokio documents cancel safety per method; check it for every future used as a `select!` branch in a loop. When an operation is cancel-unsafe, keep its progress outside the future (a buffer that lives across loop iterations) or move it into its own task. If your own function is cancel-unsafe, document it in a rustdoc `# Cancel Safety` section.
        
        Cooperative cancellation of a whole task tree uses `tokio_util::sync::CancellationToken`: tasks watch the token and exit at a point they choose, so cleanup runs.
        
        ```rust
        use tokio_util::sync::CancellationToken;
        
        let token = CancellationToken::new();
        let child = token.child_token();
        tokio::spawn(async move {
            tokio::select! {
                _ = child.cancelled() => { /* clean up */ }
                result = work() => { /* normal */ }
            }
        });
        // later
        token.cancel();
        ```
        
        Pass child tokens down the call tree so the whole tree can be cancelled together.
        
        ## Timeouts
        
        ```rust
        use tokio::time::{timeout, Duration};
        
        match timeout(Duration::from_secs(5), fetch(url)).await {
            Ok(Ok(body)) => Ok(body),
            Ok(Err(error)) => Err(error.into()),
            Err(_elapsed) => Err(anyhow::anyhow!("timed out fetching {url}")),
        }
        ```
        
        Set timeouts on every external I/O boundary. Defaults of "wait forever" are bugs.
        
        ## Blocking work
        
        NEVER block inside an async task. Symptoms: deadlock, every future stalled, latency cliffs.
        
        Heavy CPU or sync I/O → `spawn_blocking`:
        
        ```rust
        let result = tokio::task::spawn_blocking(|| {
            // CPU-bound: parsing, hashing, image processing
            // Or sync I/O: rusqlite, OS APIs without async wrappers
            expensive_pure_computation()
        }).await?;
        ```
        
        Sustained CPU parallelism → a dedicated pool (`rayon`), not tokio's blocking pool, which is sized for many short blocking calls. The same rule covers the blocking std APIs that look harmless inside `async fn`: `std::thread::sleep` (use `tokio::time::sleep`), `std::fs::*` (use `tokio::fs`, or batch it into one `spawn_blocking`), and `blocking_recv` / `blocking_lock` (they panic inside a runtime).
        
        ## Channels
        
        | Need | Use |
        |---|---|
        | 1-many producers → 1 consumer, async | `tokio::sync::mpsc::channel(cap)` |
        | Same as above, both sync + async | `flume::bounded(cap)` |
        | 1 → many fan-out, latest-value semantics | `tokio::sync::watch::channel(initial)` |
        | 1 → many fan-out, queued | `tokio::sync::broadcast::channel(cap)` |
        | One-shot reply | `tokio::sync::oneshot::channel()` |
        | Backpressure-driven stream of items | `tokio::sync::mpsc::Receiver` + `ReceiverStream` |
        
        Mpsc pattern:
        
        ```rust
        let (tx, mut rx) = tokio::sync::mpsc::channel::<Job>(256);
        
        tokio::spawn(async move {
            while let Some(job) = rx.recv().await {
                if let Err(error) = process(job).await {
                    tracing::warn!(%error, "job failed");
                }
            }
            tracing::info!("queue closed, shutting down worker");
        });
        
        tx.send(Job { ... }).await?;  // blocks if full, applies backpressure
        ```
        
        Always bound channels. Unbounded channels are a memory leak waiting to happen.
        
        ## Streams
        
        `futures::Stream` is the async analogue of `Iterator`. Use it for paginated fetches, long-poll responses, file lines.
        
        ```rust
        use futures::stream::{StreamExt, TryStreamExt};
        
        let urls: Vec<String> = ...;
        let bodies: Vec<String> = futures::stream::iter(urls)
            .map(|url| async move { fetch(&url).await })
            .buffer_unordered(8)  // up to 8 in flight
            .try_collect()
            .await?;
        ```
        
        `buffer_unordered(n)` is the throttle. Use it instead of spawning N tasks manually.
        
        For producing a stream from a channel:
        
        ```rust
        use tokio_stream::wrappers::ReceiverStream;
        
        let (tx, rx) = tokio::sync::mpsc::channel::<Event>(64);
        let stream = ReceiverStream::new(rx);
        serve_sse(stream).await
        ```
        
        ## Graceful shutdown
        
        ```rust
        use tokio::signal;
        
        /// Resolves on Ctrl-C or SIGTERM. A signal source that cannot be installed is
        /// logged and never fires, so the other source still works.
        async fn shutdown_signal() {
            let ctrl_c = async {
                if let Err(error) = signal::ctrl_c().await {
                    tracing::error!(%error, "cannot listen for ctrl-c");
                    std::future::pending::<()>().await;
                }
            };
            #[cfg(unix)]
            let terminate = async {
                match signal::unix::signal(signal::unix::SignalKind::terminate()) {
                    Ok(mut sigterm) => { sigterm.recv().await; }
                    Err(error) => {
                        tracing::error!(%error, "cannot listen for SIGTERM");
                        std::future::pending::<()>().await;
                    }
                }
            };
            #[cfg(not(unix))]
            let terminate = std::future::pending::<()>();
        
            tokio::select! {
                () = ctrl_c => {},
                () = terminate => {},
            }
            tracing::info!("shutdown signal received");
        }
        
        #[tokio::main]
        async fn main() -> anyhow::Result<()> {
            let token = CancellationToken::new();
            let server = tokio::spawn(run_server(token.child_token()));
            shutdown_signal().await;
            token.cancel();
            match tokio::time::timeout(Duration::from_secs(10), server).await {
                Ok(joined) => joined??, // JoinError (panic) and the server's own error both surface
                Err(_elapsed) => tracing::warn!("server did not stop within 10s"),
            }
            Ok(())
        }
        ```
        
        Pattern: catch signal → cancel a token shared with the server → server's `select!` arms see the cancel and exit cleanly → wait with a timeout so a hung worker can't deadlock shutdown.
        
        ## Concurrency primitives
        
        - `tokio::sync::Mutex` — async mutex. Use for state shared between async tasks. **Do not hold across `.await` without thinking** (you'll serialize the whole system).
        - `tokio::sync::RwLock` — async read-write lock. Same caveat.
        - `std::sync::Mutex` / `parking_lot::Mutex` — sync mutex for a critical section that never spans an `.await` (see [concurrency.md](concurrency.md) for choosing between them).
        - Nothing guard-like lives across an `.await`: not a sync lock guard, not a `watch` borrow ([concurrency.md](concurrency.md)), not a `span.enter()` guard. Instrument the future instead: `fut.instrument(span).await`.
        - `tokio::sync::Semaphore` — bound concurrent operations. Perfect for "max 10 in-flight HTTP requests" or "max 3 DB writers".
        
        ```rust
        let sem = Arc::new(tokio::sync::Semaphore::new(10));
        let mut set = JoinSet::new();
        for url in urls {
            let permit = Arc::clone(&sem).acquire_owned().await?;
            set.spawn(async move {
                let _permit = permit;  // released on task end
                fetch(&url).await
            });
        }
        ```
        
        ## Common mistakes
        
        1. **Holding a sync mutex across `.await`.** Compiles and runs, deadlocks at scale. Solution: refactor to release before await, or use `tokio::sync::Mutex`.
        2. **Forgetting `?` on `JoinHandle`.** A panicked task returns `Err(JoinError)`; if you `.await` and ignore, panics are silently swallowed.
        3. **`tokio::spawn` instead of `JoinSet`.** Detached tasks survive past their parent, causing leaks. Default to `JoinSet` for structured concurrency.
        4. **Unbounded channels.** Always set a capacity.
        5. **`block_on` inside an async context.** Causes deadlock under `current_thread` runtime, performance cliff under `multi_thread`.
        6. **CPU-heavy work in async fn.** Move to `spawn_blocking` or `rayon`.
        7. **No timeout on external I/O.** Every `await` that touches the network or filesystem needs `tokio::time::timeout` wrapping.
        
        ## Testing async code
        
        ```rust
        #[tokio::test]
        async fn fetches_and_parses() {
            let server = wiremock::MockServer::start().await;
            wiremock::Mock::given(wiremock::matchers::method("GET"))
                .respond_with(wiremock::ResponseTemplate::new(200).set_body_string("{\"id\":1}"))
                .mount(&server)
                .await;
        
            let result = my_client::fetch(&server.uri()).await.unwrap();
            assert_eq!(result.id, 1);
        }
        
        #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
        async fn parallel_work() { ... }
        ```
        
        `current_thread` is the default test runtime; it chooses a scheduler, it does not make a test deterministic. Wait on the exact event (a channel message, a `Notify`, a `watch` change) or on paused virtual time below, never on a real `sleep`.
        
        For time-sensitive tests, advance virtual time:
        
        ```rust
        #[tokio::test(start_paused = true)]
        async fn time_travel() {
            let start = tokio::time::Instant::now();
            tokio::time::sleep(Duration::from_secs(3600)).await;
            assert!(start.elapsed() >= Duration::from_secs(3600));
            // Real wallclock elapsed: ~0ms.
        }
        ```
        
        ## Async traits and async closures
        
        `async fn` in traits is stable (1.75) and dispatches statically:
        
        ```rust
        trait Store {
            async fn get(&self, key: &str) -> Result<Option<Vec<u8>>, StoreError>;
        }
        ```
        
        Two limits: callers cannot require the returned future to be `Send` (spawning a generic `S: Store` call fails), and the trait is not `dyn`-compatible. For `Send` futures, declare the method as `fn get(&self, key: &str) -> impl Future<Output = ...> + Send;`. For `dyn Store`, box the future (`Pin<Box<dyn Future<Output = T> + Send + '_>>`) or use `async-trait`; pick `dyn` only for runtime heterogeneity.
        
        A parameter that is an async callback borrowing from its caller takes `F: AsyncFn(&Request) -> Response` (1.85, `std::ops::AsyncFn`), not `F: Fn(&Request) -> Fut`: the `Fut` form cannot name a future that borrows its argument.
        
        ## When NOT to use async
        
        - Single-threaded CPU-heavy code that does no I/O — plain `fn` + `rayon` is simpler and often faster.
        - Trivial scripts that do one HTTP call — `ureq` (sync) is simpler.
        - FFI heavy code where the FFI side is sync.
        
        Async pays off when you have many concurrent I/O operations or need cancellation as a first-class primitive.
        
      • axum-stack.md 14.2 KB
        # axum + sqlx + tracing + tower — HTTP API Stack
        
        The canonical production HTTP service in Rust 2026.
        
        ## Cargo.toml dependencies
        
        ```toml
        [dependencies]
        axum = { version = "0.8", features = ["macros", "tracing", "ws", "multipart"] }
        tokio = { version = "1", features = ["full"] }
        tower = "0.5"
        tower-http = { version = "0.6", features = [
            "trace", "compression-gzip", "compression-br",
            "timeout", "cors", "request-id", "sensitive-headers",
            "limit", "set-header",
        ] }
        
        # Errors / observability
        anyhow = "1"
        thiserror = "2"
        tracing = "0.1"
        tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
        color-eyre = "0.6"
        
        # Database
        sqlx = { version = "0.8", features = [
            "runtime-tokio-rustls", "postgres", "uuid", "macros",
            "migrate", "json",
        ] }
        
        # Serialization / validation
        serde = { version = "1", features = ["derive"] }
        serde_json = "1"
        validator = { version = "0.18", features = ["derive"] }
        
        # Types
        uuid = { version = "1", features = ["v4", "v7", "serde"] }
        jiff = { version = "0.1", features = ["serde"] }
        
        # Config
        config = { version = "0.14", features = ["toml", "yaml"] }
        secrecy = { version = "0.10", features = ["serde"] }
        
        # OpenAPI (optional but recommended)
        utoipa = { version = "5", features = ["axum_extras", "uuid", "chrono"] }
        utoipa-axum = "0.1"
        utoipa-swagger-ui = { version = "8", features = ["axum"] }
        ```
        
        ## Project structure
        
        ```
        src/
          main.rs            # binary entry
          lib.rs             # re-exports + app builder
          config.rs          # Settings type + loader
          state.rs           # AppState (shared via Arc)
          routes/
            mod.rs           # Router::new() composition
            health.rs
            users.rs
          middleware/
            mod.rs
            auth.rs
            request_id.rs
          models/
            mod.rs
            user.rs
          error.rs           # AppError + IntoResponse impl
          db/
            mod.rs
            migrations/
        migrations/          # sqlx migrations
        ```
        
        ## Error type
        
        ```rust
        // src/error.rs
        use axum::{http::StatusCode, response::{IntoResponse, Response}, Json};
        use serde_json::json;
        
        #[derive(Debug, thiserror::Error)]
        pub enum AppError {
            #[error("not found")]
            NotFound,
            #[error("unauthorized")]
            Unauthorized,
            #[error("validation: {0}")]
            Validation(String),
            #[error("conflict: {0}")]
            Conflict(String),
            #[error("internal")]
            Internal(#[from] anyhow::Error),
            #[error("database")]
            Database(#[from] sqlx::Error),
        }
        
        impl IntoResponse for AppError {
            fn into_response(self) -> Response {
                let (status, code, message) = match &self {
                    AppError::NotFound => (StatusCode::NOT_FOUND, "not_found", self.to_string()),
                    AppError::Unauthorized => (StatusCode::UNAUTHORIZED, "unauthorized", "unauthorized".into()),
                    AppError::Validation(m) => (StatusCode::UNPROCESSABLE_ENTITY, "validation", m.clone()),
                    AppError::Conflict(m) => (StatusCode::CONFLICT, "conflict", m.clone()),
                    AppError::Database(e) => {
                        tracing::error!(error = ?e, "database error");
                        (StatusCode::INTERNAL_SERVER_ERROR, "database", "internal".into())
                    }
                    AppError::Internal(e) => {
                        tracing::error!(error = ?e, "internal error");
                        (StatusCode::INTERNAL_SERVER_ERROR, "internal", "internal".into())
                    }
                };
                (status, Json(json!({"error": {"code": code, "message": message}}))).into_response()
            }
        }
        
        pub type AppResult<T> = std::result::Result<T, AppError>;
        ```
        
        Pattern: business errors return `AppResult<T>`; the `IntoResponse` impl translates them to HTTP. `sqlx::Error` and `anyhow::Error` auto-convert via `From`. Internal-bucket errors are logged but never leak their `Debug` representation to clients.
        
        ## AppState
        
        ```rust
        // src/state.rs
        use std::sync::Arc;
        use sqlx::PgPool;
        
        #[derive(Clone)]
        pub struct AppState {
            pub db: PgPool,
            pub config: Arc<crate::config::Settings>,
            pub http: reqwest::Client,
        }
        
        impl AppState {
            pub async fn new(config: crate::config::Settings) -> anyhow::Result<Self> {
                let db = sqlx::postgres::PgPoolOptions::new()
                    .max_connections(config.db.max_connections)
                    .acquire_timeout(std::time::Duration::from_secs(3))
                    .connect(config.db.url.expose_secret())
                    .await?;
                sqlx::migrate!("./migrations").run(&db).await?;
                let http = reqwest::Client::builder()
                    .timeout(std::time::Duration::from_secs(15))
                    .user_agent(concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")))
                    .build()?;
                Ok(Self { db, config: Arc::new(config), http })
            }
        }
        ```
        
        ## Route handler
        
        ```rust
        // src/routes/users.rs
        use axum::{
            extract::{Path, State},
            Json,
        };
        use serde::{Deserialize, Serialize};
        use uuid::Uuid;
        use validator::Validate;
        
        use crate::{error::{AppError, AppResult}, state::AppState};
        
        #[derive(Debug, Deserialize, Validate)]
        pub struct CreateUser {
            #[validate(email)]
            pub email: String,
            #[validate(length(min = 1, max = 100))]
            pub name: String,
        }
        
        #[derive(Debug, Serialize, sqlx::FromRow)]
        pub struct User {
            pub id: Uuid,
            pub email: String,
            pub name: String,
            pub created_at: jiff::Timestamp,
        }
        
        #[tracing::instrument(skip(state, body))]
        pub async fn create_user(
            State(state): State<AppState>,
            Json(body): Json<CreateUser>,
        ) -> AppResult<(axum::http::StatusCode, Json<User>)> {
            body.validate().map_err(|e| AppError::Validation(e.to_string()))?;
        
            let id = Uuid::now_v7();
            let user = sqlx::query_as!(
                User,
                r#"INSERT INTO users (id, email, name, created_at)
                   VALUES ($1, $2, $3, NOW())
                   RETURNING id, email, name, created_at as "created_at: jiff::Timestamp""#,
                id, body.email, body.name
            )
            .fetch_one(&state.db)
            .await
            .map_err(|e| match &e {
                sqlx::Error::Database(db) if db.code().as_deref() == Some("23505") =>
                    AppError::Conflict("email already exists".into()),
                _ => AppError::Database(e),
            })?;
        
            Ok((axum::http::StatusCode::CREATED, Json(user)))
        }
        
        #[tracing::instrument(skip(state))]
        pub async fn get_user(
            State(state): State<AppState>,
            Path(id): Path<Uuid>,
        ) -> AppResult<Json<User>> {
            sqlx::query_as!(
                User,
                r#"SELECT id, email, name, created_at as "created_at: jiff::Timestamp"
                   FROM users WHERE id = $1"#,
                id
            )
            .fetch_optional(&state.db)
            .await?
            .map(Json)
            .ok_or(AppError::NotFound)
        }
        ```
        
        ## Router assembly
        
        ```rust
        // src/routes/mod.rs
        use axum::{routing::{get, post}, Router};
        use tower_http::{
            compression::CompressionLayer,
            cors::CorsLayer,
            request_id::{MakeRequestUuid, PropagateRequestIdLayer, SetRequestIdLayer},
            sensitive_headers::SetSensitiveHeadersLayer,
            timeout::TimeoutLayer,
            trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer},
        };
        use std::time::Duration;
        use crate::state::AppState;
        
        mod health;
        mod users;
        
        pub fn router(state: AppState) -> Router {
            let api = Router::new()
                .route("/health", get(health::handler))
                .route("/users", post(users::create_user))
                .route("/users/:id", get(users::get_user))
                .with_state(state);
        
            Router::new()
                .nest("/api/v1", api)
                .layer(
                    tower::ServiceBuilder::new()
                        .layer(SetSensitiveHeadersLayer::new([
                            axum::http::header::AUTHORIZATION,
                            axum::http::header::COOKIE,
                        ]))
                        .layer(SetRequestIdLayer::x_request_id(MakeRequestUuid))
                        .layer(
                            TraceLayer::new_for_http()
                                .make_span_with(DefaultMakeSpan::new().include_headers(false))
                                .on_response(DefaultOnResponse::new().latency_unit(tower_http::LatencyUnit::Millis)),
                        )
                        .layer(PropagateRequestIdLayer::x_request_id())
                        .layer(TimeoutLayer::new(Duration::from_secs(30)))
                        .layer(CompressionLayer::new())
                        .layer(CorsLayer::permissive()), // tighten in production
                )
        }
        ```
        
        Order matters: outermost layer wraps the request first. Trace before timeout so timeouts get logged. Compression after trace so trace sees the original body size.
        
        ## Main + graceful shutdown
        
        ```rust
        // src/main.rs
        use my_app::{config::Settings, routes, state::AppState};
        
        #[tokio::main]
        async fn main() -> color_eyre::Result<()> {
            color_eyre::install()?;
            init_tracing();
        
            let config = Settings::load()?;
            let state = AppState::new(config.clone()).await?;
            let app = routes::router(state);
        
            let listener = tokio::net::TcpListener::bind(&config.bind).await?;
            tracing::info!(addr = %config.bind, "listening");
        
            axum::serve(listener, app)
                .with_graceful_shutdown(shutdown_signal())
                .await?;
            Ok(())
        }
        
        fn init_tracing() {
            use tracing_subscriber::{fmt, EnvFilter};
            let filter = EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| EnvFilter::new("info,sqlx=warn,hyper=warn,tower_http=info"));
            fmt().with_env_filter(filter).with_target(false).json().init();
        }
        
        // shutdown_signal(): the Ctrl-C / SIGTERM future from async-tokio.md#graceful-shutdown
        ```
        
        ## Middleware: bearer auth example
        
        ```rust
        // src/middleware/auth.rs
        use axum::{
            extract::{Request, State},
            http::header::AUTHORIZATION,
            middleware::Next,
            response::Response,
        };
        use crate::{error::AppError, state::AppState};
        
        #[derive(Clone, Debug)]
        pub struct AuthUser { pub id: uuid::Uuid }
        
        pub async fn require_auth(
            State(state): State<AppState>,
            mut request: Request,
            next: Next,
        ) -> Result<Response, AppError> {
            let token = request.headers()
                .get(AUTHORIZATION)
                .and_then(|v| v.to_str().ok())
                .and_then(|s| s.strip_prefix("Bearer "))
                .ok_or(AppError::Unauthorized)?;
        
            let claims = verify_jwt(token, &state.config.jwt_secret)?;
            request.extensions_mut().insert(AuthUser { id: claims.sub });
            Ok(next.run(request).await)
        }
        ```
        
        Apply with `.route_layer(middleware::from_fn_with_state(state.clone(), require_auth))` on the subroutes that need it.
        
        ## Testing handlers
        
        ```rust
        // tests/users.rs
        #[tokio::test]
        async fn creates_user() {
            let pool = test_db().await;          // helper that spins up a transactional DB
            let state = AppState::new_test(pool).await.unwrap();
            let app = my_app::routes::router(state);
        
            let request = axum::http::Request::builder()
                .uri("/api/v1/users")
                .method("POST")
                .header("content-type", "application/json")
                .body(axum::body::Body::from(
                    serde_json::to_vec(&serde_json::json!({"email": "a@b.com", "name": "A"})).unwrap()
                )).unwrap();
        
            let response = tower::ServiceExt::oneshot(app, request).await.unwrap();
            assert_eq!(response.status(), 201);
            let bytes = axum::body::to_bytes(response.into_body(), 1 << 20).await.unwrap();
            let user: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
            assert_eq!(user["email"], "a@b.com");
        }
        ```
        
        `tower::ServiceExt::oneshot` calls the router directly without binding a socket. Tests run in parallel without port collisions.
        
        ## Config
        
        ```rust
        // src/config.rs
        use secrecy::{Secret, ExposeSecret};
        
        #[derive(Debug, Clone, serde::Deserialize)]
        pub struct Settings {
            pub bind: String,
            pub db: Database,
            pub jwt_secret: Secret<String>,
        }
        
        #[derive(Debug, Clone, serde::Deserialize)]
        pub struct Database {
            pub url: Secret<String>,
            pub max_connections: u32,
        }
        
        impl Settings {
            pub fn load() -> anyhow::Result<Self> {
                let cfg = config::Config::builder()
                    .add_source(config::File::with_name("config/default").required(false))
                    .add_source(config::File::with_name(&format!(
                        "config/{}", std::env::var("APP_ENV").unwrap_or_else(|_| "dev".into())
                    )).required(false))
                    .add_source(config::Environment::with_prefix("APP").separator("__"))
                    .build()?;
                Ok(cfg.try_deserialize()?)
            }
        }
        ```
        
        `Secret<T>` from the `secrecy` crate hides the value in `Debug`/`Display` to prevent accidental log leakage. Access via `.expose_secret()` only where needed.
        
        ## OpenAPI (optional)
        
        Add `utoipa` derive macros on your DTOs and handlers, mount Swagger UI at `/swagger-ui`:
        
        ```rust
        use utoipa::OpenApi;
        use utoipa_axum::router::OpenApiRouter;
        use utoipa_swagger_ui::SwaggerUi;
        
        #[derive(OpenApi)]
        #[openapi(
            paths(routes::users::create_user, routes::users::get_user),
            components(schemas(routes::users::User, routes::users::CreateUser))
        )]
        struct ApiDoc;
        
        let (router, api) = OpenApiRouter::with_openapi(ApiDoc::openapi())
            .routes(utoipa_axum::routes!(routes::users::create_user, routes::users::get_user))
            .split_for_parts();
        
        let app = router.merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", api));
        ```
        
        ## Production checklist
        
        - Bind to `0.0.0.0` in containers, `127.0.0.1` for local-only services.
        - Set `RUST_LOG=info,sqlx=warn` (or use `EnvFilter` defaults as shown).
        - Send logs to stdout in JSON. Ingest via Vector / Fluent Bit / Loki.
        - Run migrations on startup (`sqlx::migrate!` block). Fail fast on schema mismatch.
        - Health endpoint **must hit the DB** (so load balancers know if the pool is dead).
        - Add `tower::limit::RateLimitLayer` or token-bucket middleware for public endpoints.
        - Set `tower_http::limit::RequestBodyLimitLayer` to bound request size.
        - Compress with brotli + gzip via `CompressionLayer`.
        - Tighten CORS, do not ship `CorsLayer::permissive()` to production.
        - Strip sensitive headers from traces via `SetSensitiveHeadersLayer`.
        - Set up SIGTERM-driven `with_graceful_shutdown` so deploys roll without dropping requests.
        - Containerize with `cargo chef` for incremental Docker builds.
        
        ## Common mistakes
        
        1. **Forgetting `error_for_status()?` on outbound `reqwest`** — 4xx silently succeeds.
        2. **Returning `Result<T, sqlx::Error>` from handlers** — leak DB details to clients. Always go through `AppError`.
        3. **`Json<T>` extractor before validation** — invalid JSON returns axum's default 422 with no body shape. Wrap in a `ValidatedJson<T>` extractor that runs `validator` and returns `AppError`.
        4. **Holding DB connections across `.await` on slow external calls** — exhausts the pool. Acquire late, release early.
        5. **Skipping `tracing::instrument`** on handlers — losing per-request span correlation.
        6. **No `RequestBodyLimitLayer`** — DoS surface. Default axum has no limit.
        
      • cargo-strict.md 10 KB
        # Cargo Strict Configuration
        
        The exact knobs every new Rust project gets. Drop these in unmodified.
        
        ## `rust-toolchain.toml`
        
        ```toml
        [toolchain]
        channel = "stable"
        components = ["rustfmt", "clippy", "rust-src"]
        profile = "default"
        ```
        
        Pin nightly separately when miri runs:
        
        ```bash
        rustup install nightly
        rustup component add miri rust-src --toolchain nightly
        ```
        
        ## `Cargo.toml` — `[lints]` section
        
        ```toml
        [lints.rust]
        unsafe_op_in_unsafe_fn = "deny"
        missing_docs = "warn"
        missing_debug_implementations = "warn"
        unreachable_pub = "warn"
        unused_must_use = "deny"
        elided_lifetimes_in_paths = "warn"
        non_ascii_idents = "deny"
        trivial_numeric_casts = "warn"
        unused_lifetimes = "warn"
        single_use_lifetimes = "warn"
        # every custom cfg is declared here; a typo like cfg(feture = "x") fails the build
        unexpected_cfgs = { level = "deny", check-cfg = ["cfg(loom)"] }
        
        [lints.clippy]
        # Groups
        all = { level = "deny", priority = -1 }
        pedantic = { level = "warn", priority = -1 }
        nursery = { level = "warn", priority = -1 }
        cargo = { level = "warn", priority = -1 }
        
        # Hard denies - turn warnings into errors for sharp tools
        undocumented_unsafe_blocks = "deny"
        multiple_unsafe_ops_per_block = "deny"
        unwrap_used = "deny"
        expect_used = "deny"
        panic = "deny"
        todo = "deny"
        unimplemented = "deny"
        unreachable = "deny"
        indexing_slicing = "deny"
        mem_forget = "deny"
        arithmetic_side_effects = "warn"
        cast_possible_truncation = "warn"
        cast_possible_wrap = "warn"
        cast_precision_loss = "warn"
        cast_sign_loss = "warn"
        as_underscore = "deny"
        as_ptr_cast_mut = "deny"
        ptr_as_ptr = "warn"
        borrow_as_ptr = "warn"
        fn_to_numeric_cast_any = "deny"
        clone_on_ref_ptr = "warn"
        mutex_atomic = "warn"
        rc_buffer = "warn"
        rc_mutex = "warn"
        exit = "warn"
        allow_attributes = "deny"                 # silence with #[expect(lint, reason)], never #[allow]
        allow_attributes_without_reason = "deny"
        let_underscore_must_use = "deny"          # `let _ = fallible()` drops the error
        dbg_macro = "warn"
        print_stderr = "warn"
        print_stdout = "warn"
        use_debug = "warn"
        
        # Stylistic relaxations (project-wide opinions only)
        module_name_repetitions = "allow"
        must_use_candidate = "allow"
        
        # Restriction lints - opt-in soundness rails
        mod_module_files = "warn"      # prefer foo.rs over foo/mod.rs
        empty_drop = "warn"
        empty_structs_with_brackets = "warn"
        empty_enum = "warn"
        exhaustive_enums = "warn"      # public enums should consider #[non_exhaustive]
        exhaustive_structs = "warn"
        ```
        
        The `priority = -1` trick: group-level levels are weak; specific lints below them win. This lets us deny `unwrap_used` while still allowing `pedantic` group warnings instead of denies.
        
        A lint is silenced only with `#[expect(clippy::lint_name, reason = "...")]` on the smallest item that needs it. Unlike `#[allow]`, `#[expect]` fails the build once the lint stops firing, so stale exceptions cannot pile up; `allow_attributes` and `allow_attributes_without_reason` enforce both halves. `missing_errors_doc` / `missing_panics_doc` stay on (pedantic): public fallible APIs document their `# Errors`.
        
        ## `Cargo.toml` — release profile
        
        ```toml
        [profile.release]
        opt-level = 3
        lto = "fat"
        codegen-units = 1
        strip = "symbols"
        panic = "abort"        # smaller, faster - if you need unwinding (FFI catch), set "unwind"
        debug = "line-tables-only"
        
        [profile.dev]
        opt-level = 0
        debug = true
        incremental = true
        codegen-units = 256
        split-debuginfo = "unpacked"
        
        # A profile for miri - opt-level 1 keeps simulation bearable while still
        # exercising real codegen patterns. miri ignores most profile keys but reads
        # overflow-checks.
        [profile.miri]
        inherits = "test"
        opt-level = 1
        overflow-checks = true
        ```
        
        `-C target-cpu=native` and PGO are deployment decisions, not defaults: a `native` binary faults on older CPUs, and PGO only pays with a representative training workload. Apply either only with a before/after measurement.
        
        ## `Cargo.toml` — workspace level
        
        ```toml
        [workspace]
        resolver = "3"
        
        [workspace.package]
        edition = "2024"
        rust-version = "1.85"   # the 2024 edition floor; raise only when a needed feature lands
        license = "Apache-2.0 OR MIT"
        
        [workspace.lints]
        # Then in each member crate:
        # [lints]
        # workspace = true
        
        [workspace.dependencies]
        # one version per dependency; members write `serde = { workspace = true }`
        ```
        
        ## Features and build scripts
        
        - **Features are additive.** Enabling a feature never removes an API or changes another feature's behavior, and `--all-features` always builds. Optional dependencies use `dep:` (`serde = ["dep:serde"]`) so no implicit feature leaks. Truly exclusive backends fail loudly: `#[cfg(all(feature = "a", feature = "b"))] compile_error!("features `a` and `b` are mutually exclusive");`.
        - **`build.rs` is deterministic.** Declare every input with `cargo::rerun-if-changed=` / `cargo::rerun-if-env-changed=`, write only under `OUT_DIR`, and never touch the network. A build script that reads undeclared inputs produces stale builds that pass locally and fail in CI.
        
        ## `rustfmt.toml`
        
        ```toml
        edition = "2024"
        max_width = 100
        imports_granularity = "Module"
        group_imports = "StdExternalCrate"
        reorder_imports = true
        reorder_modules = true
        newline_style = "Unix"
        use_field_init_shorthand = true
        use_try_shorthand = true
        unstable_features = false
        ```
        
        Most options come from stable rustfmt. `imports_granularity` and `group_imports` are nightly-only but ignored cleanly on stable; CI runs `cargo +nightly fmt --check` for the import grouping.
        
        ## `clippy.toml`
        
        ```toml
        # Reduce cognitive load thresholds.
        cognitive-complexity-threshold = 25
        type-complexity-threshold = 250
        too-many-arguments-threshold = 3   # SKILL.md Smell 2: more than 3 parameters is a smell
        too-many-lines-threshold = 100
        
        # msrv - keeps clippy from suggesting features past our MSRV
        msrv = "1.85"
        
        # Avoid `panic` lint complaining about derived Debug impls calling unreachable_unchecked etc.
        allow-unwrap-in-tests = true
        allow-expect-in-tests = true
        allow-panic-in-tests = true
        allow-dbg-in-tests = true
        allow-print-in-tests = true
        
        # At most 4 single-character bindings in scope
        single-char-binding-names-threshold = 4
        ```
        
        ## `deny.toml` (cargo-deny)
        
        ```toml
        [advisories]
        db-path = "~/.cargo/advisory-db"
        db-urls = ["https://github.com/rustsec/advisory-db"]
        yanked = "deny"
        ignore = []
        
        [licenses]
        allow = [
            "MIT",
            "Apache-2.0",
            "Apache-2.0 WITH LLVM-exception",
            "BSD-2-Clause",
            "BSD-3-Clause",
            "ISC",
            "Unicode-DFS-2016",
            "Unicode-3.0",
            "Zlib",
            "MPL-2.0",
            "CC0-1.0",
        ]
        confidence-threshold = 0.93
        exceptions = []
        
        [bans]
        multiple-versions = "warn"
        wildcards = "deny"
        highlight = "all"
        deny = [
            # Pin out unmaintained alternatives
            { name = "async-std", reason = "use tokio" },
            { name = "actix-web", reason = "use axum" },
            { name = "chrono", reason = "use jiff" },
        ]
        
        [sources]
        unknown-registry = "deny"
        unknown-git = "warn"
        allow-registry = ["https://github.com/rust-lang/crates.io-index"]
        ```
        
        ## CI Workflow (`.github/workflows/ci.yml`)
        
        ```yaml
        name: ci
        on:
          push:
            branches: [main]
          pull_request:
        
        env:
          CARGO_TERM_COLOR: always
          RUST_BACKTRACE: 1
        
        jobs:
          fmt:
            runs-on: ubuntu-latest
            steps:
              - uses: actions/checkout@v4
              - uses: dtolnay/rust-toolchain@nightly
                with:
                  components: rustfmt
              - run: cargo +nightly fmt --all -- --check
        
          clippy:
            runs-on: ubuntu-latest
            steps:
              - uses: actions/checkout@v4
              - uses: dtolnay/rust-toolchain@stable
                with:
                  components: clippy
              - uses: Swatinem/rust-cache@v2
              - run: cargo clippy --all-targets --all-features --workspace -- -D warnings
        
          test:
            runs-on: ubuntu-latest
            steps:
              - uses: actions/checkout@v4
              - uses: dtolnay/rust-toolchain@stable
              - uses: Swatinem/rust-cache@v2
              - uses: taiki-e/install-action@nextest
              - run: cargo nextest run --all-targets --all-features --workspace
              - run: cargo test --doc --all-features --workspace   # nextest does not run doctests
        
          doc:
            runs-on: ubuntu-latest
            steps:
              - uses: actions/checkout@v4
              - uses: dtolnay/rust-toolchain@stable
              - uses: Swatinem/rust-cache@v2
              - env:
                  RUSTDOCFLAGS: "-D warnings"   # broken intra-doc links fail the build
                run: cargo doc --no-deps --all-features --workspace
        
          msrv:
            runs-on: ubuntu-latest
            steps:
              - uses: actions/checkout@v4
              - uses: dtolnay/rust-toolchain@1.85   # = rust-version
              - uses: Swatinem/rust-cache@v2
              - run: cargo check --all-features --workspace
        
          miri:
            runs-on: ubuntu-latest
            steps:
              - uses: actions/checkout@v4
              - uses: dtolnay/rust-toolchain@nightly
                with:
                  components: miri, rust-src
              - uses: Swatinem/rust-cache@v2
              - uses: taiki-e/install-action@nextest
              - env:
                  MIRIFLAGS: "-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check"
                run: cargo +nightly miri nextest run --all-features --workspace
              - env:
                  MIRIFLAGS: "-Zmiri-tree-borrows -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check"
                run: cargo +nightly miri nextest run --all-features --workspace
        
          machete:
            runs-on: ubuntu-latest
            steps:
              - uses: actions/checkout@v4
              - uses: dtolnay/rust-toolchain@stable
              - uses: bnjbvr/cargo-machete@main
        
          deny:
            runs-on: ubuntu-latest
            steps:
              - uses: actions/checkout@v4
              - uses: EmbarkStudios/cargo-deny-action@v2
                with:
                  command: check all
        
          audit:
            runs-on: ubuntu-latest
            steps:
              - uses: actions/checkout@v4
              - uses: rustsec/audit-check@v1
                with:
                  token: ${{ secrets.GITHUB_TOKEN }}
        ```
        
        ## Project bootstrap
        
        ```bash
        cargo new --bin my-app --edition 2024
        cd my-app
        cargo install cargo-nextest cargo-machete cargo-deny cargo-edit cargo-watch
        rustup install nightly
        rustup component add miri --toolchain nightly
        # drop the configs above
        git add . && git commit -m "chore: bootstrap strict toolchain"
        ```
        
        After every change:
        
        ```bash
        cargo fmt --all -- --check && \
        cargo clippy --all-targets --all-features -- -D warnings && \
        cargo nextest run && cargo test --doc && \
        cargo +nightly miri nextest run    # only if unsafe is involved
        ```
        
      • clap-stack.md 12.1 KB
        # CLI Stack — clap + color-eyre + tracing + indicatif + dialoguer
        
        The default for any new CLI tool. Strict typing on arguments, beautiful errors, progress feedback, interactive prompts when needed.
        
        ## Cargo.toml
        
        ```toml
        [package]
        name = "mytool"
        version = "0.1.0"
        edition = "2024"
        
        [dependencies]
        clap = { version = "4", features = ["derive", "env", "wrap_help", "color", "unicode"] }
        clap_complete = "4"
        color-eyre = "0.6"
        tracing = "0.1"
        tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
        anyhow = "1"
        indicatif = { version = "0.17", features = ["tokio"] }
        dialoguer = { version = "0.11", features = ["fuzzy-select"] }
        console = "0.15"
        tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs", "process", "signal"] }
        
        [profile.release]
        opt-level = 3
        lto = "fat"
        codegen-units = 1
        strip = "symbols"
        panic = "abort"
        ```
        
        ## Command structure
        
        ```rust
        // src/cli.rs
        use clap::{Parser, Subcommand, ValueEnum};
        use std::path::PathBuf;
        
        #[derive(Debug, Parser)]
        #[command(
            name = "mytool",
            author,
            version,
            about = "A short description",
            long_about = "A longer description that appears in --help",
            arg_required_else_help = true,
        )]
        pub struct Cli {
            /// Configuration file path
            #[arg(short, long, env = "MYTOOL_CONFIG", default_value = "config.toml", global = true)]
            pub config: PathBuf,
        
            /// Increase verbosity (-v info, -vv debug, -vvv trace)
            #[arg(short, long, action = clap::ArgAction::Count, global = true)]
            pub verbose: u8,
        
            /// Suppress all non-error output
            #[arg(short, long, global = true, conflicts_with = "verbose")]
            pub quiet: bool,
        
            /// Force colored output even when stdout is not a terminal
            #[arg(long, global = true, value_enum, default_value_t = ColorChoice::Auto)]
            pub color: ColorChoice,
        
            /// Output format
            #[arg(short, long, global = true, value_enum, default_value_t = OutputFormat::Pretty)]
            pub format: OutputFormat,
        
            #[command(subcommand)]
            pub command: Command,
        }
        
        #[derive(Debug, Clone, ValueEnum)]
        pub enum ColorChoice { Auto, Always, Never }
        
        #[derive(Debug, Clone, ValueEnum)]
        pub enum OutputFormat { Pretty, Json, Plain }
        
        #[derive(Debug, Subcommand)]
        pub enum Command {
            /// Build the thing
            Build(BuildArgs),
            /// Watch and rebuild
            Watch(WatchArgs),
            /// Generate shell completions
            Completions { #[arg(value_enum)] shell: clap_complete::Shell },
        }
        
        #[derive(Debug, clap::Args)]
        pub struct BuildArgs {
            /// Target directory
            #[arg(short, long, default_value = "target")]
            pub target: PathBuf,
            /// Build mode
            #[arg(short, long, value_enum, default_value_t = Mode::Release)]
            pub mode: Mode,
            /// Specific files to build (default: all)
            pub files: Vec<PathBuf>,
        }
        
        #[derive(Debug, clap::Args)]
        pub struct WatchArgs {
            /// Glob pattern to watch
            #[arg(short, long, default_value = "**/*.rs")]
            pub pattern: String,
        }
        
        #[derive(Debug, Clone, ValueEnum)]
        pub enum Mode { Debug, Release }
        ```
        
        Key clap derive patterns:
        
        - `env = "VAR"` — falls back to env var if flag not given.
        - `global = true` — flag inherits to subcommands.
        - `arg_required_else_help = true` — running with no args prints help instead of erroring.
        - `value_enum` on an enum — case-insensitive parsing + auto-completion.
        - `action = clap::ArgAction::Count` — `-v` is 1, `-vv` is 2, etc.
        - `conflicts_with` — incompatible flags.
        
        ## Main + tracing init
        
        ```rust
        // src/main.rs
        use clap::Parser;
        use mytool::cli::{Cli, Command, ColorChoice};
        use tracing::Level;
        use tracing_subscriber::EnvFilter;
        
        fn main() -> color_eyre::Result<()> {
            color_eyre::install()?;
            let cli = Cli::parse();
            init_tracing(&cli);
        
            if matches!(cli.color, ColorChoice::Always) {
                console::set_colors_enabled(true);
            } else if matches!(cli.color, ColorChoice::Never) {
                console::set_colors_enabled(false);
            }
        
            match cli.command {
                Command::Build(args) => mytool::commands::build::run(&cli, args),
                Command::Watch(args) => mytool::commands::watch::run(&cli, args),
                Command::Completions { shell } => {
                    let mut cmd = <Cli as clap::CommandFactory>::command();
                    clap_complete::generate(shell, &mut cmd, "mytool", &mut std::io::stdout());
                    Ok(())
                }
            }
        }
        
        fn init_tracing(cli: &Cli) {
            let level = if cli.quiet {
                Level::ERROR
            } else {
                match cli.verbose {
                    0 => Level::WARN,
                    1 => Level::INFO,
                    2 => Level::DEBUG,
                    _ => Level::TRACE,
                }
            };
            let filter = EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| EnvFilter::new(format!("mytool={level}")));
            tracing_subscriber::fmt()
                .with_env_filter(filter)
                .with_target(false)
                .without_time()
                .compact()
                .with_writer(std::io::stderr)
                .init();
        }
        ```
        
        Tracing on a CLI:
        - **Write to stderr.** stdout is for the tool's actual output (which the user might pipe). Logs and progress bars go to stderr.
        - **Verbosity from `-v`, not from `RUST_LOG`.** Users expect `-v` on a CLI; `RUST_LOG` is a developer escape hatch (kept, but secondary).
        
        ## Progress bars — `indicatif`
        
        ```rust
        use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
        use std::time::Duration;
        
        let mp = MultiProgress::new();
        let pb = mp.add(ProgressBar::new(files.len() as u64));
        pb.set_style(
            ProgressStyle::with_template(
                "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} ({eta}) {msg}"
            )?
            .progress_chars("=>-")
        );
        
        for file in files {
            pb.set_message(file.display().to_string());
            process(&file)?;
            pb.inc(1);
        }
        pb.finish_with_message("done");
        ```
        
        For unbounded operations:
        
        ```rust
        let spinner = ProgressBar::new_spinner();
        spinner.enable_steady_tick(Duration::from_millis(80));
        spinner.set_message("connecting…");
        let result = connect().await?;
        spinner.finish_and_clear();
        ```
        
        With multiple parallel tasks:
        
        ```rust
        let mp = MultiProgress::new();
        let bars: Vec<_> = (0..workers).map(|i| {
            let pb = mp.add(ProgressBar::new(unit));
            pb.set_style(ProgressStyle::with_template("worker {prefix}: {pos}/{len}")?);
            pb.set_prefix(i.to_string());
            pb
        }).collect();
        ```
        
        `MultiProgress` keeps bars stacked and redraws cleanly even with concurrent updates from multiple tasks.
        
        When stdout is not a terminal, indicatif silently disables animation. Force on/off with `pb.set_draw_target(ProgressDrawTarget::stdout())` / `hidden()`.
        
        ## Interactive prompts — `dialoguer`
        
        ```rust
        use dialoguer::{theme::ColorfulTheme, Confirm, Input, Password, Select, FuzzySelect, MultiSelect};
        
        let name: String = Input::with_theme(&ColorfulTheme::default())
            .with_prompt("Project name")
            .validate_with(|input: &String| -> Result<(), &str> {
                if input.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_') {
                    Ok(())
                } else {
                    Err("alphanumeric, dash, underscore only")
                }
            })
            .interact_text()?;
        
        let secret = Password::with_theme(&ColorfulTheme::default())
            .with_prompt("API key")
            .with_confirmation("Repeat", "passwords don't match")
            .interact()?;
        
        let go: bool = Confirm::with_theme(&ColorfulTheme::default())
            .with_prompt(format!("Delete {}? This cannot be undone.", path.display()))
            .default(false)
            .interact()?;
        if !go { return Ok(()); }
        
        let items = ["yes", "no", "maybe"];
        let idx = Select::with_theme(&ColorfulTheme::default())
            .with_prompt("Pick one")
            .items(&items)
            .default(0)
            .interact()?;
        
        let picks = MultiSelect::with_theme(&ColorfulTheme::default())
            .with_prompt("Toggle features")
            .items(&["alpha", "beta", "gamma"])
            .defaults(&[true, false, false])
            .interact()?;
        ```
        
        Detect non-TTY before prompting:
        
        ```rust
        if !console::user_attended() {
            return Err(anyhow::anyhow!("input required but stdin is not a terminal"));
        }
        ```
        
        For automated tests, expose a `--non-interactive` flag and gate all prompts behind it.
        
        ## Structured output
        
        ```rust
        use std::io::Write as _;
        
        // `println!` panics when stdout is closed (`mytool | head -1`); writing to a
        // locked handle returns the error instead, so a broken pipe exits cleanly.
        let mut out = std::io::stdout().lock();
        match cli.format {
            OutputFormat::Json => {
                serde_json::to_writer(&mut out, &result)?;
                writeln!(out)?;
            }
            OutputFormat::Plain => {
                for row in &result.rows {
                    writeln!(out, "{}\t{}\t{}", row.a, row.b, row.c)?;
                }
            }
            OutputFormat::Pretty => {
                use console::{style, Term};
                let term = Term::stdout();
                for row in &result.rows {
                    term.write_line(&format!(
                        "{} {} {}",
                        style(&row.a).green(),
                        style(&row.b).yellow(),
                        style(&row.c).dim(),
                    ))?;
                }
            }
        }
        ```
        
        Always offer `--format json` for piping into `jq`, scripts, and other tools.
        
        ## Shell completions
        
        Already shown in the `Completions` subcommand above. Distribute completions by adding to the install script:
        
        ```bash
        mytool completions bash > /etc/bash_completion.d/mytool
        mytool completions fish > ~/.config/fish/completions/mytool.fish
        mytool completions zsh  > "${fpath[1]}/_mytool"
        ```
        
        ## Signal handling
        
        ```rust
        // In an async CLI command
        use tokio::signal::ctrl_c;
        
        tokio::select! {
            _ = ctrl_c() => {
                tracing::warn!("interrupted, cleaning up");
                cleanup().await?;
                std::process::exit(130);  // standard exit code for SIGINT
            }
            result = long_running_task() => {
                result
            }
        }
        ```
        
        For sync CLIs, install a one-shot handler with `ctrlc` crate:
        
        ```rust
        let interrupted = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
        let i = interrupted.clone();
        ctrlc::set_handler(move || i.store(true, std::sync::atomic::Ordering::SeqCst))?;
        
        while !interrupted.load(std::sync::atomic::Ordering::Relaxed) {
            do_step()?;
        }
        ```
        
        ## Error reporting with color-eyre
        
        ```rust
        fn main() -> color_eyre::Result<()> {
            color_eyre::config::HookBuilder::default()
                .display_env_section(false)        // hide SPANTRACE/BACKTRACE env hints by default
                .display_location_section(false)   // hide file:line section
                .panic_section("If this is a bug, please report at https://github.com/me/mytool/issues")
                .install()?;
            real_main()
        }
        ```
        
        Errors with `.wrap_err("...")` from `eyre::WrapErr` (compatible with anyhow's `.context`) show as a numbered chain. `RUST_BACKTRACE=1` shows the full trace; `RUST_SPANTRACE=1` shows tracing spans where the error fired.
        
        ## Distribution
        
        - Add `cargo dist init` for prebuilt binary release pipeline (cross-platform tarballs + installers).
        - Publish to Homebrew tap, AUR, scoop, Chocolatey via dist.
        - Sign Linux binaries with `cosign` if your audience is enterprise.
        - Build single static binary on Linux with `--target x86_64-unknown-linux-musl` (or `aarch64-unknown-linux-musl`).
        - For wasm-runnable CLIs (`wasi-cli`), add `--target wasm32-wasip1`.
        
        ## Common mistakes
        
        1. **Mixing stdout and stderr.** Tool output goes to stdout; logs and progress go to stderr.
        2. **No `--non-interactive` flag.** Interactive prompts block automation.
        3. **Printing colored output unconditionally.** Honor `NO_COLOR` env var, detect TTY with `console::user_attended()`.
        4. **`println!` for errors.** Use `tracing::error!` so logs go to stderr automatically and respect verbosity.
        5. **`unwrap()` on `Cli::parse()`.** clap returns clean errors with `--help` text; `parse()` exits on its own.
        6. **Long subcommand handlers in `main.rs`.** Split into `src/commands/<name>.rs` per command.
        7. **Missing exit code semantics.** Use `std::process::exit(1)` (general error), `2` (usage), `130` (SIGINT) appropriately. Or return `Result` and let main map.
        
        ## Testing CLIs
        
        ```rust
        // tests/cli.rs
        use assert_cmd::Command;
        use predicates::prelude::*;
        
        #[test]
        fn shows_help() {
            Command::cargo_bin("mytool").unwrap()
                .arg("--help")
                .assert()
                .success()
                .stdout(predicate::str::contains("Usage:"));
        }
        
        #[test]
        fn rejects_unknown_subcommand() {
            Command::cargo_bin("mytool").unwrap()
                .arg("nope")
                .assert()
                .failure()
                .stderr(predicate::str::contains("unrecognized subcommand"));
        }
        ```
        
        `assert_cmd` builds the binary once per test run and gives a fluent assertion API.
        
      • concurrency.md 14.3 KB
        # Concurrency Primitives
        
        Locks, atomics, channels, and the loom model checker. The decision tree that keeps the agent out of soundness trouble.
        
        ## The pyramid
        
        ```
        Highest level          tokio::sync::mpsc / broadcast / watch
                               (message passing — default for new code)
        
                               Arc<Mutex<T>> / Arc<RwLock<T>>
                               (shared mutable state — common, easy to get right)
        
                               std::sync / parking_lot::{Mutex, RwLock, Condvar}
                               (sync locks for critical sections without .await)
        
                               Atomics (AtomicUsize, AtomicBool, AtomicPtr)
                               (single-word lock-free state)
        
        Lowest level           UnsafeCell + unsafe + loom + miri
                               (custom lock-free / wait-free primitives)
        ```
        
        **Always start at the top.** Drop a level only when you have measured a real bottleneck.
        
        ## Decision tree
        
        ```
        Need to share state between tasks?
        ├── State is configuration (read-only after start)
        │   └── Arc<Config>  (no lock needed)
        ├── State is a queue of work
        │   └── tokio::sync::mpsc::channel(cap)
        ├── State is "latest value" published to many readers
        │   └── tokio::sync::watch::channel(initial)
        ├── State is broadcast (every consumer sees every value)
        │   └── tokio::sync::broadcast::channel(cap)
        ├── State is request-response within one task tree
        │   └── tokio::sync::oneshot::channel()
        ├── State is a counter
        │   └── AtomicU64 (or AtomicUsize)
        ├── State is a flag / set-once
        │   └── AtomicBool / OnceLock<T> / OnceCell<T>
        ├── State needs mutation across many tasks/threads, cheap critical sections
        │   ├── async context           → tokio::sync::Mutex<T>
        │   └── sync context (no .await held) → std::sync::Mutex<T> / parking_lot::Mutex<T>
        ├── State needs mutation, many readers, few writers
        │   ├── async context           → tokio::sync::RwLock<T>
        │   └── sync context            → std::sync::RwLock<T> / parking_lot::RwLock<T>
        └── State is a custom lock-free primitive (channels, hazard pointers)
            └── UnsafeCell + atomics + loom-tested + miri-tested + a co-author
        ```
        
        ## Atomics — when and how
        
        Use atomics for:
        - Counters incremented from many threads (`AtomicU64`).
        - Single-shot flags (`AtomicBool`).
        - Pointer publication (`AtomicPtr<T>`).
        
        ### Memory orderings
        
        ```rust
        use std::sync::atomic::{AtomicUsize, Ordering};
        
        let c = AtomicUsize::new(0);
        
        // Just need a count, no synchronization with other data
        c.fetch_add(1, Ordering::Relaxed);
        
        // Reading a counter that was incremented from elsewhere
        let n = c.load(Ordering::Relaxed);
        ```
        
        | Ordering | When |
        |---|---|
        | `Relaxed` | Standalone counters, no other memory needs to be synchronized. |
        | `Acquire` (loads) / `Release` (stores) | Publish/consume pattern: you write some data then release a flag, readers acquire the flag then read the data. |
        | `AcqRel` | RMW that both reads-and-publishes (e.g., `fetch_add` on a sequence number). |
        | `SeqCst` | Total ordering across all `SeqCst` ops. Strongest, slowest. Use when in doubt and switch to a weaker ordering after testing under loom. |
        
        **Default to `SeqCst` if unsure.** Performance difference is usually negligible. Going weaker requires loom.
        
        ### Publish-then-load pattern
        
        Write data, then `store(true, Release)` a flag; a reader that `load(Acquire)`s `true` is guaranteed to see the data written before the store. Do not hand-roll it around a `static mut` (the 2024 edition denies references to one, and every access is `unsafe`): `OnceLock<T>` is exactly this pattern behind a safe API.
        
        ```rust
        static CONFIG: std::sync::OnceLock<Config> = std::sync::OnceLock::new();
        
        // Producer, once:  CONFIG.set(cfg) returns Err(cfg) if already set.
        // Consumer:        CONFIG.get() is Some only after the publishing set completed.
        ```
        
        ## Std vs parking_lot vs tokio for locks
        
        | | std::sync::Mutex | parking_lot::Mutex | tokio::sync::Mutex |
        |---|---|---|---|
        | Speed | Fast (futex-based since 1.62) | Fast (adaptive spinning, smaller) | Slower (await-aware) |
        | Poisoning | Yes (`PoisonError`) | No | No |
        | Hold across `.await` | Dangerous (deadlock under current-thread runtime) | Dangerous | Safe |
        | Drop guard releases | Yes | Yes | Yes |
        | RAII | Yes (`MutexGuard`) | Yes | Yes |
        | Const constructor | Yes (since 1.63) | Yes | No |
        | Async | No | No | Yes |
        
        **Rule of thumb:**
        
        - Short critical section, no await inside → `std::sync::Mutex`, or `parking_lot::Mutex` when the crate already depends on it or you want no poisoning. Neither is universally faster; measure before switching for speed.
        - Shared state held across `.await` → `tokio::sync::Mutex`.
        - Static init / app config → `OnceLock` or `LazyLock`.
        - A `PoisonError` from `std` means another thread panicked mid-update; propagate it or recover the data deliberately with `into_inner()`, never `unwrap()` it away.
        
        ### Common deadlock — async + sync mutex
        
        ```rust
        let m = parking_lot::Mutex::new(0u64);
        let mut guard = m.lock();
        something_async().await;  // WRONG: guard is held across await
        *guard += 1;
        ```
        
        Under `current_thread` runtime this deadlocks (the future suspends while holding the lock; another future on the same thread tries to acquire, blocks the executor). Under `multi_thread` it works but serializes the system.
        
        Fix:
        
        ```rust
        {
            let mut guard = m.lock();
            *guard += 1;
        }  // guard released
        something_async().await;
        ```
        
        Or switch to `tokio::sync::Mutex` whose guard is `Send` across awaits.
        
        ## Channels
        
        ### Mpsc — the workhorse
        
        ```rust
        let (tx, mut rx) = tokio::sync::mpsc::channel::<Job>(256);
        
        tokio::spawn(async move {
            while let Some(job) = rx.recv().await {
                process(job).await;
            }
        });
        
        tx.send(Job::new()).await?;  // backpressure: awaits if full
        ```
        
        Capacity is the backpressure budget. **Never `unbounded_channel()`** unless you have a hard upper bound elsewhere; otherwise it is a slow-leak memory bomb.
        
        ### Watch — latest-value pubsub
        
        ```rust
        let (tx, mut rx) = tokio::sync::watch::channel(Config::default());
        
        // Producer:
        tx.send(new_config)?;
        
        // Consumer:
        loop {
            rx.changed().await?;
            let cfg = rx.borrow_and_update().clone(); // release the read lock before any .await
            apply(&cfg).await;
        }
        ```
        
        Receivers see only the latest value (older updates are dropped). Perfect for config reload, leadership changes, "current time" propagation. The `borrow()` guard blocks the sender while held; never keep it across an `.await`.
        
        ### Broadcast — fanout queue
        
        ```rust
        let (tx, _) = tokio::sync::broadcast::channel::<Event>(1024);
        let mut rx1 = tx.subscribe();
        let mut rx2 = tx.subscribe();
        
        while let Ok(event) = rx1.recv().await {
            // ...
        }
        ```
        
        Each subscriber has its own buffer. If a subscriber falls behind by more than the buffer size, it gets `RecvError::Lagged(n)` and skips messages. Decide explicitly: log + continue, or drop the subscriber and reconnect.
        
        ### Oneshot — single value
        
        ```rust
        let (tx, rx) = tokio::sync::oneshot::channel::<Response>();
        worker.send(Request { reply: tx }).await?;
        let response = rx.await?;
        ```
        
        The standard request/response pattern over an actor.
        
        ## Semaphores
        
        Bound concurrent operations:
        
        ```rust
        let sem = Arc::new(tokio::sync::Semaphore::new(10));
        let mut set = tokio::task::JoinSet::new();
        
        for task in tasks {
            let permit = Arc::clone(&sem).acquire_owned().await?;
            set.spawn(async move {
                let _hold = permit;       // released when task exits
                process(task).await
            });
        }
        ```
        
        Use cases:
        - "Max 10 outbound HTTP requests in flight."
        - "Max 3 DB connections doing writes."
        - "Max N tokio tasks running heavy CPU."
        
        A semaphore with `permits=1` is a mutex. Use the actual `Mutex` for that — clearer intent.
        
        ## Arc and Rc
        
        `Arc<T>` for cross-thread shared ownership, `Rc<T>` for single-thread (never spans threads).
        
        ```rust
        let shared = Arc::new(BigData::new());
        let mut set = tokio::task::JoinSet::new();
        for _ in 0..workers {
            let s = Arc::clone(&shared);
            set.spawn(async move { use_data(&s).await });
        }
        ```
        
        `Arc::clone(&s)` is just a reference-count increment; the data is not copied. Write it as `Arc::clone(&x)`, never `x.clone()` (`clone_on_ref_ptr`), so a deep clone never hides behind the same spelling.
        
        **Do not clone in hot loops** if you can pass a reference. `&Arc<T>` is fine to pass; only call `Arc::clone` when you need to move ownership across a thread/task boundary.
        
        `Weak<T>` for back-references in graphs / parent pointers to avoid cycles.
        
        ## Once-init primitives
        
        ```rust
        use std::sync::LazyLock;
        
        // Lazy initialization of an infallible value, computed on first read
        static WORD_RE: LazyLock<regex::Regex> = LazyLock::new(|| {
            #[expect(clippy::expect_used, reason = "literal pattern; covered by a unit test")]
            regex::Regex::new(r"\w+").expect("valid regex literal")
        });
        
        #[tokio::main]
        async fn main() -> anyhow::Result<()> {
            // Fallible setup happens in main, where `?` reports it; pass the result down
            // as a parameter instead of reaching for a global.
            let config = Config::load_from_env()?;
            let pool = sqlx::PgPool::connect(&config.database_url).await?;
            run(&config, &pool).await
        }
        ```
        
        `LazyLock` runs its closure on first access, where no caller can handle an error, so it holds only values that cannot fail at runtime. `OnceLock` and `LazyLock` are in `std::sync` (1.70 / 1.80); avoid `once_cell` and `lazy_static!` for new code.
        
        ## Loom — model-checking lock-free code
        
        When `unsafe` participates in a concurrent algorithm, miri's single-thread model is insufficient. Loom exhaustively explores thread interleavings.
        
        `Cargo.toml`:
        
        ```toml
        [target.'cfg(loom)'.dev-dependencies]
        loom = "0.7"
        ```
        
        In code, switch between real and loom primitives:
        
        ```rust
        #[cfg(loom)]
        use loom::sync::atomic::{AtomicUsize, Ordering};
        #[cfg(not(loom))]
        use std::sync::atomic::{AtomicUsize, Ordering};
        
        #[cfg(loom)]
        use loom::sync::Arc;
        #[cfg(not(loom))]
        use std::sync::Arc;
        ```
        
        Write a test:
        
        ```rust
        #[cfg(loom)]
        mod loom_tests {
            use super::*;
            use loom::thread;
        
            #[test]
            fn concurrent_push_pop_preserves_order() {
                loom::model(|| {
                    let queue = Arc::new(MyQueue::new());
                    let q1 = Arc::clone(&queue);
                    let q2 = Arc::clone(&queue);
                    let h1 = thread::spawn(move || q1.push(1));
                    let h2 = thread::spawn(move || q2.pop());
                    h1.join().unwrap();
                    h2.join().unwrap();
                    // Assert the invariant: queue is in a coherent state.
                });
            }
        }
        ```
        
        Run:
        
        ```bash
        RUSTFLAGS="--cfg loom" cargo test --release -- --test-threads 1
        ```
        
        Loom systematically explores the thread interleavings its model allows (bounded by `LOOM_MAX_PREEMPTIONS`), including those a real scheduler would rarely produce, and replays a failing schedule deterministically. It is strong evidence within that bound, not a proof over unbounded executions.
        
        ### Loom's limits
        
        - Slow. Each `loom::model` invocation explores many schedules; keep tests tiny (2-3 threads, a few operations each).
        - Single-machine only. Doesn't model distributed systems.
        - Doesn't catch UB inside `unsafe` blocks the way miri does. **Run both: miri for memory safety, loom for thread schedules.**
        - Doesn't handle `tokio` directly. Loom replaces stdlib's sync primitives; tokio's are independent.
        
        ## Scoped threads and per-thread state
        
        For a fixed number of short-lived threads that borrow local data, `std::thread::scope` joins every thread before it returns, so no `Arc` or `'static` bound is needed:
        
        ```rust
        let (left, right) = data.split_at(data.len() / 2);
        let (a, b) = std::thread::scope(|s| {
            let a = s.spawn(|| checksum(left));
            let b = s.spawn(|| checksum(right));
            (a.join(), b.join())
        });
        // a and b are Result<_, panic payload>: a panicked thread surfaces here
        ```
        
        For data-parallel work over a collection, `rayon`'s `par_iter()` is simpler. Per-thread scratch state (a reusable buffer, a per-thread RNG) is `thread_local!` with `Cell` / `RefCell`, never a `static mut`:
        
        ```rust
        thread_local! {
            static SCRATCH: std::cell::RefCell<Vec<u8>> = const { std::cell::RefCell::new(Vec::new()) };
        }
        SCRATCH.with_borrow_mut(|buf| { buf.clear(); encode_into(buf, msg) });
        ```
        
        ## Send and Sync — what they mean
        
        - `T: Send` — `T` can be moved between threads safely.
        - `T: Sync` — `&T` can be shared between threads safely.
        
        These are auto-derived for composite types if all components implement them. Manual `unsafe impl Send/Sync` is required only for raw pointer types and FFI handles.
        
        ```rust
        struct MyHandle { raw: *mut FfiObject }
        
        // SAFETY: FfiObject's documented contract states that move-between-threads
        // is safe as long as concurrent use is externally synchronized. We do not
        // implement Sync because the FFI object is single-threaded once obtained.
        unsafe impl Send for MyHandle {}
        // Do NOT impl Sync — the FFI is not thread-safe.
        ```
        
        When the compiler complains that "T: Send is not satisfied", the cause is usually a raw pointer, an `Rc` (not `Arc`), or a `RefCell` (use `Mutex`).
        
        ## Common mistakes
        
        1. **Holding a `std::sync::Mutex` guard across `.await`.** Compiles, deadlocks at runtime under `current_thread`.
        2. **`Arc::clone` in a tight loop.** Refcount bump is cheap but not free; pass `&Arc<T>` when possible.
        3. **`Mutex<HashMap<K, V>>` for hot reads.** Switch to `RwLock` or `Arc<dashmap::DashMap>`.
        4. **Atomic operations with `Ordering::Relaxed` for happens-before publication.** You need `Release`/`Acquire`. Run under loom to be sure.
        5. **Unbounded channels.** Always set capacity. If you "know it won't backlog", you don't, and it will.
        6. **Spawning detached tokio tasks for fire-and-forget cleanup.** Use `JoinSet` so panics surface.
        7. **`std::mem::transmute` to fake `Send`/`Sync`.** Use `unsafe impl` with a SAFETY comment instead. Transmute breaks Stacked Borrows and miri.
        8. **Locking order inversion across two mutexes.** Always acquire in a globally consistent order. For more than three locks, switch to a single mutex around a struct.
        
        ## When to escape to lock-free
        
        You should reach for atomics + `UnsafeCell` only when:
        1. The hot path is **measured** to be bottlenecked on lock contention.
        2. There is no existing library (crossbeam, atomic-queue, hazardous) that solves your problem.
        3. You can write loom tests that pass.
        4. You can write miri tests that pass.
        5. You have at least one other engineer who can review the algorithm.
        
        Practically all "I want to write a lock-free queue" projects fail (3) or (4). When in doubt, take the lock and move on.
        
      • libraries.md 17.4 KB
        # Library Defaults — Full Decision Tree
        
        The opinionated, audited-in-prod stack for 2026 Rust. Every entry has a one-line rationale and a canonical code snippet so the agent does not have to relearn each library's idioms.
        
        ## Async runtime — `tokio`
        
        The default. Use `tokio` for new work. Multi-thread runtime with the default worker count unless you have a measured reason otherwise ([async-tokio.md](async-tokio.md)).
        
        ```rust
        #[tokio::main]
        async fn main() -> anyhow::Result<()> {
            tracing_subscriber::fmt::init();
            run().await
        }
        ```
        
        Avoid:
        - `async-std` — unmaintained, last release ages ago. crates.io download counts are misleading because of historical inertia.
        - `smol` — fine for embedded-ish niches; outside that, the ecosystem is on tokio.
        - Mixing runtimes in one binary. Pick one and stay.
        
        ## Errors — `anyhow` (apps) + `thiserror` (libs)
        
        Application boundaries get `anyhow::Error` with `.context("...")` at every layer that adds meaning. Libraries expose `#[derive(thiserror::Error)]` enums with `#[non_exhaustive]`.
        
        ```rust
        // Application code
        use anyhow::Context as _;
        
        pub async fn load_config(path: &Path) -> anyhow::Result<Config> {
            let text = tokio::fs::read_to_string(path)
                .await
                .with_context(|| format!("reading config from {}", path.display()))?;
            let cfg: Config = toml::from_str(&text)
                .with_context(|| format!("parsing config at {}", path.display()))?;
            Ok(cfg)
        }
        
        // Library code
        #[derive(Debug, thiserror::Error)]
        #[non_exhaustive]
        pub enum ParseError {
            #[error("expected {expected}, found {found} at position {position}")]
            Mismatch { expected: &'static str, found: String, position: usize },
            #[error("unexpected end of input after {context}")]
            UnexpectedEof { context: &'static str },
            #[error(transparent)]
            Io(#[from] std::io::Error),
        }
        ```
        
        `#[non_exhaustive]` on enums prevents downstream `match` from breaking when you add variants. `#[error(transparent)]` on a wrapper variant forwards Display + cause to the inner error. Keep the cause chain: a variant that wraps another error marks it `#[from]` or `#[source]` so `anyhow`'s `{error:#}` and reporters print every layer. Error messages are lowercase with no trailing period, because they get composed into `context: cause` chains.
        
        ## CLI — `clap` with derive
        
        ```rust
        use clap::{Parser, Subcommand};
        
        #[derive(Parser, Debug)]
        #[command(author, version, about, long_about = None)]
        struct Cli {
            /// Path to the config file
            #[arg(short, long, env = "MYAPP_CONFIG", default_value = "config.toml")]
            config: PathBuf,
        
            /// Enable verbose output (-v, -vv, -vvv)
            #[arg(short, long, action = clap::ArgAction::Count)]
            verbose: u8,
        
            #[command(subcommand)]
            command: Command,
        }
        
        #[derive(Subcommand, Debug)]
        enum Command {
            /// Run the server
            Serve {
                #[arg(short, long, default_value_t = 8080)]
                port: u16,
            },
            /// Migrate the database
            Migrate {
                #[arg(long)]
                dry_run: bool,
            },
        }
        ```
        
        Avoid `structopt` (deprecated, merged into clap), `argh` (less ergonomic), `pico-args` (only when binary size matters more than DX).
        
        ## Logging — `tracing` + `tracing-subscriber`
        
        Not `log` + `env_logger`. `tracing` supports spans (structured context that follows async tasks) and structured fields - `log` cannot.
        
        ```rust
        use tracing::{info, instrument, warn, Level};
        use tracing_subscriber::{fmt, EnvFilter};
        
        fn init_tracing() {
            let filter = EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| EnvFilter::new("info,sqlx=warn,hyper=warn"));
            fmt()
                .with_env_filter(filter)
                .with_target(false)
                .with_thread_ids(true)
                .with_line_number(true)
                .compact()
                .init();
        }
        
        #[instrument(skip_all, fields(user_id = %user.id))]
        async fn process_user(db: &Pool, user: &User) -> anyhow::Result<()> {
            info!("processing user");
            if user.is_banned() {
                warn!(reason = "banned", "skipping");
                return Ok(());
            }
            // ... body ...
            Ok(())
        }
        ```
        
        Replace `println!` with `info!`/`warn!`/`error!`. Replace `eprintln!` with `tracing::error!`.
        
        `#[instrument]` records every argument it does not skip with `Debug`, so a `&User` argument lands in the span whole, PII included. Default to `skip_all` and whitelist fields explicitly, as above; secrets live in `secrecy::SecretString` so a stray `?value` prints `[REDACTED]`. A **library** emits `tracing` events and spans but never installs a subscriber (`tracing_subscriber::…::init()`): only the binary chooses the output format and filter.
        
        ## Error reporting (binaries) — `color-eyre`
        
        For binary `main()`, hook `color-eyre` to give pretty panics + nice `Result` printing:
        
        ```rust
        fn main() -> color_eyre::Result<()> {
            color_eyre::install()?;
            tracing_subscriber::fmt::init();
            real_main()
        }
        ```
        
        Library code stays on `anyhow`/`thiserror`. `color-eyre` is purely a display layer for the binary.
        
        ## Serialization — `serde` + `serde_json`
        
        The default for any data crossing a process boundary (file, network, IPC, database column).
        
        ```rust
        // Input we own the schema of (config, our own API): reject typos.
        #[derive(Debug, serde::Deserialize)]
        #[serde(deny_unknown_fields, rename_all = "snake_case")]
        pub struct CreateUser {
            pub email: Email,                       // validated newtype, see type-state.md
            #[serde(default)]
            pub display_name: Option<String>,
        }
        
        // Payload a newer peer may extend: keep unknown fields instead of rejecting them.
        #[derive(Debug, serde::Serialize, serde::Deserialize)]
        #[serde(rename_all = "snake_case")]
        pub struct Event {
            pub user_id: UserId,
            pub created_at: jiff::Timestamp,
            #[serde(skip_serializing_if = "Option::is_none")]
            pub display_name: Option<String>,
            #[serde(flatten)]
            pub extra: HashMap<String, serde_json::Value>,
        }
        ```
        
        Choose per type: `deny_unknown_fields` for input whose schema you own (a typoed config key fails loudly), `#[serde(flatten)]` catch-all for payloads a newer peer may extend. The two do not combine: serde does not support `deny_unknown_fields` on a struct with a `flatten` field. `rename_all` matches the wire schema you are given, not a house style. Enums get an explicit representation (`#[serde(tag = "type")]`, or `tag` + `content`); `#[serde(untagged)]` tries variants in order and reports only "did not match any variant", so reserve it for shapes that are unambiguous by construction. Validated newtypes deserialize through their checked constructor with `#[serde(try_from = "...")]` ([type-state.md](type-state.md)). For a field whose wire format differs from its Rust type (a duration as seconds, a timestamp as a string), use `#[serde(with = "module")]` or the type's own serde feature instead of a hand-written parallel struct.
        
        Alternatives:
        - `serde_yaml` (YAML — note: YAML's "deserialize anything" surface is a security trap; prefer JSON/TOML where possible)
        - `toml` (config files)
        - `rmp-serde` (MessagePack — binary, fast)
        - `ciborium` (CBOR)
        - `bincode 2` (binary, smaller; no serde required in v2 but interop fine)
        
        ## HTTP client — `reqwest`
        
        ```rust
        let client = reqwest::Client::builder()
            .timeout(std::time::Duration::from_secs(30))
            .user_agent(concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")))
            .https_only(true)
            .pool_max_idle_per_host(8)
            .build()?;
        
        #[derive(serde::Deserialize)]
        struct Repo { full_name: String, stargazers_count: u64 }
        
        let repo: Repo = client
            .get("https://api.github.com/repos/rust-lang/rust")
            .send().await?
            .error_for_status()?
            .json().await?;
        ```
        
        `error_for_status()?` turns 4xx/5xx into `Err`. Always include a User-Agent. `https_only(true)` is a soundness toggle - prevents accidental http:// downgrade.
        
        ## Web framework — `axum`
        
        ```rust
        use axum::{Router, routing::get, extract::State, response::Json};
        use std::sync::Arc;
        
        #[derive(Clone)]
        struct AppState { db: sqlx::PgPool }
        
        async fn health(State(state): State<Arc<AppState>>) -> Json<serde_json::Value> {
            let ok = sqlx::query_scalar!("SELECT 1::int4").fetch_one(&state.db).await.is_ok();
            Json(serde_json::json!({ "ok": ok }))
        }
        
        #[tokio::main]
        async fn main() -> anyhow::Result<()> {
            tracing_subscriber::fmt::init();
            let state = Arc::new(AppState { db: sqlx::PgPool::connect(&env_db()).await? });
            let app = Router::new()
                .route("/health", get(health))
                .with_state(state)
                .layer(tower_http::trace::TraceLayer::new_for_http())
                .layer(tower_http::compression::CompressionLayer::new());
            let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
            axum::serve(listener, app).await?;
            Ok(())
        }
        ```
        
        Avoid `actix-web` (legacy patterns, separate runtime model), `warp` (filter explosion in non-trivial apps), `rocket` (slow release cadence). Pair `axum` with `tower-http` for middleware (trace, compression, CORS, timeout, request-id).
        
        ## Database — `sqlx` (compile-time checked SQL)
        
        ```rust
        use sqlx::PgPool;
        
        #[derive(Debug, sqlx::FromRow)]
        pub struct User { pub id: uuid::Uuid, pub email: String, pub created_at: jiff::Timestamp }
        
        pub async fn find_user(pool: &PgPool, email: &str) -> Result<Option<User>, sqlx::Error> {
            sqlx::query_as!(
                User,
                r#"SELECT id, email, created_at as "created_at: jiff::Timestamp"
                   FROM users WHERE email = $1"#,
                email
            )
            .fetch_optional(pool)
            .await
        }
        ```
        
        `query_as!` checks the SQL against the live database at compile time. To work without a live DB during builds, generate offline metadata: `cargo sqlx prepare`. Commit the resulting `.sqlx/` directory.
        
        Avoid `diesel` (sync-first, heavy DSL), raw `tokio-postgres` (loses type checks), `sea-orm` (more magic, less control).
        
        For migrations: `sqlx migrate add <name>` + `sqlx::migrate!("./migrations").run(&pool).await?`.
        
        ## Time — `jiff`
        
        The 2025+ choice. Single crate, sane defaults, civil time / instant / span distinction.
        
        ```rust
        use jiff::{Timestamp, Span, ToSpan, Zoned};
        
        let now: Timestamp = Timestamp::now();
        let in_one_hour = now.checked_add(1.hour())?;
        let local: Zoned = now.in_tz("Asia/Seoul")?;
        let span: Span = local - some_earlier.in_tz("Asia/Seoul")?;
        ```
        
        Avoid `chrono` (old API, generic-heavy, time zone story still painful), `time` crate (split ecosystem, weaker docs). `jiff` is the post-`chrono` consolidation.
        
        ## UUID — `uuid` with v7
        
        ```rust
        use uuid::Uuid;
        
        // v7 for IDs (sortable, time-ordered, monotonic-ish, RFC 9562)
        let id = Uuid::now_v7();
        ```
        
        v4 is fine for nonces, v7 for primary keys (better index locality). Never v1 (leaks MAC). Cargo features: `uuid = { version = "1", features = ["v4", "v7", "serde"] }`.
        
        ## DataFrames / analytics — `polars`
        
        For columnar data, joins, group-by, lazy plans:
        
        ```rust
        use polars::prelude::*;
        
        let df = LazyCsvReader::new("events.csv")
            .finish()?
            .group_by([col("user_id")])
            .agg([col("amount").sum().alias("total")])
            .sort(["total"], Default::default())
            .collect()?;
        ```
        
        The Rust API mirrors the Python one. Use the lazy API by default; materialize with `.collect()` at the end.
        
        ## Channels
        
        - Single-producer single-consumer or bounded MPSC → `tokio::sync::mpsc` (async) or `flume` (sync + async).
        - Broadcast → `tokio::sync::broadcast`.
        - Watch (latest-value pubsub) → `tokio::sync::watch`.
        - Oneshot → `tokio::sync::oneshot`.
        
        Avoid raw `std::sync::mpsc` (sync only, fewer features), `crossbeam-channel` (good but heavier; use only if you need rendezvous semantics).
        
        ## Coordinate spaces / 2D math — `euclid`
        
        ```rust
        use euclid::{Point2D, Size2D, default::Box2D};
        struct ScreenSpace;
        struct WorldSpace;
        
        type ScreenPoint = Point2D<f32, ScreenSpace>;
        type WorldPoint  = Point2D<f32, WorldSpace>;
        
        let cursor: ScreenPoint = Point2D::new(120.0, 240.0);
        let player: WorldPoint  = Point2D::new(3.5, 1.2);
        
        // let mistake = cursor + player; // compile error: different coordinate spaces
        ```
        
        Generalize the pattern to your own domains (see `references/type-state.md`).
        
        ## Property tests — `proptest`
        
        ```rust
        use proptest::prelude::*;
        
        proptest! {
            #[test]
            fn parse_roundtrips(s in r"[a-zA-Z0-9_-]{1,50}") {
                let parsed = parse(&s).unwrap();
                let back = parsed.to_string();
                prop_assert_eq!(back, s);
            }
        }
        ```
        
        Avoid `quickcheck` (older, less ergonomic). proptest gives shrinking + regression corpus + integration with `criterion`.
        
        ## Snapshot tests — `insta`
        
        ```rust
        #[test]
        fn renders_help() {
            let output = render(&example_input());
            insta::assert_snapshot!(output);
        }
        
        #[test]
        fn serializes_well() {
            insta::assert_json_snapshot!(serializable_value());
        }
        ```
        
        `cargo insta review` (after `cargo install cargo-insta`) — interactive review of changed snapshots.
        
        ## Benchmarks — `criterion`
        
        Stable Rust friendly (no nightly `#[bench]`).
        
        ```rust
        use criterion::{criterion_group, criterion_main, Criterion};
        use std::hint::black_box;
        
        fn bench_parse(c: &mut Criterion) {
            let input = include_str!("../samples/large.txt");
            c.bench_function("parse_large", |b| b.iter(|| parse(black_box(input))));
        }
        
        criterion_group!(benches, bench_parse);
        criterion_main!(benches);
        ```
        
        Run with `cargo bench`. HTML reports under `target/criterion/`. Pair with `cargo bench -- --save-baseline main` then `--baseline main` for comparison.
        
        **Profile first, then optimize.** A change made for speed ships with its measurement: the benchmark or profile (`cargo flamegraph`, `samply`) that located the hot spot, and the before/after numbers on a representative input. Source shape alone proves nothing: iterator chains, `#[inline]`, bounds checks, and `Iterator::chain` usually compile to the same code as the "optimized" rewrite. Attributes like `#[inline(always)]` and `#[cold]` go in only when a measurement shows they help, and the `std::hint::black_box` above keeps the compiler from deleting the work being timed.
        
        ## Concurrency model — `loom`
        
        For lock-free or atomic-heavy code (channels, refcounts, hazard pointers). See `references/concurrency.md` for the full pattern.
        
        ## Arena allocator — `bumpalo`
        
        ```rust
        use bumpalo::Bump;
        
        let bump = Bump::new();
        let node = bump.alloc(Node { value: 42, next: None });
        let s: &str = bump.alloc_str("hello");
        // All allocations freed at once when `bump` drops.
        ```
        
        For parser nodes, AST construction, per-request scratch: one bump pointer per allocation and one free for the whole arena. Measure the gain on your workload before claiming one.
        
        ## Web client (browser, WASM-bound) — `gloo` ecosystem
        
        If targeting WASM browser, use `gloo-net` for fetch and `gloo-storage` for localStorage; not `web-sys` directly unless you need DOM-level APIs.
        
        ## Lazy statics — `std::sync::LazyLock` (since 1.80)
        
        `LazyLock` for values that cannot fail at runtime (a compiled literal regex, a static table); fallible setup such as loading config belongs in `main`, where `?` reports it. Pattern → [concurrency.md](concurrency.md#once-init-primitives). Avoid `lazy_static!` (macro-heavy, predates std), `once_cell` (now in std as `LazyLock`/`OnceLock`).
        
        ## Hash maps — `std::collections::HashMap`, a faster hasher only where measured
        
        ```rust
        use std::collections::HashMap;
        
        // Keys come from trusted code and a profile shows hashing is hot:
        type FastMap<K, V> = HashMap<K, V, foldhash::fast::RandomState>;
        let mut counters: FastMap<String, u64> = FastMap::default();
        ```
        
        std's default hasher (SipHash-1-3 with a random key) resists hash-flooding from attacker-chosen keys; keep it for any map fed by input. For trusted keys on a measured hot path, `foldhash` (the hasher `hashbrown` defaults to) or `rustc-hash` (`FxHashMap`, integer keys) are faster; `rustc-hash` is predictable, so never feed it untrusted keys. Update-or-insert goes through `map.entry(key).or_insert(..)` / `.and_modify(..)`, one lookup instead of `get` + `insert`.
        
        For sorted iteration, use `BTreeMap`; for insertion order that is part of the contract, `indexmap::IndexMap`. For small keys with known small N, `Vec<(K, V)>` may beat both.
        
        ## File I/O — `tokio::fs` (async) or `std::fs` (sync utility)
        
        ```rust
        let contents = tokio::fs::read_to_string("data.json").await?;
        ```
        
        For large files: `tokio::fs::File` + `tokio::io::BufReader`. For random access, `memmap2` (with the unsafe-discipline wrappers). Many small reads or writes (line by line, record by record) go through `BufReader` / `BufWriter`, sync or async. Call `flush()` on a `BufWriter` before dropping it: `Drop` flushes too but has to discard the error.
        
        ## Decision tree
        
        ```
        Need to ship the thing?
        ├── HTTP server         → axum + sqlx + tracing + jiff + tokio
        ├── HTTP client         → reqwest (+ tokio)
        ├── CLI                 → clap + color-eyre + tracing + indicatif (progress) + dialoguer (prompts)
        ├── TUI                 → ratatui + crossterm
        ├── Background worker   → tokio + ETL → polars + duckdb
        ├── Game / graphics     → wgpu + winit + euclid (or bevy if you want the engine)
        ├── WASM front-end      → leptos (or dioxus / yew) + wasm-bindgen + gloo
        ├── Embedded            → embassy (async on bare metal)
        ├── FFI to C / Python   → cxx (C++) / pyo3 (Python) / cbindgen (header gen)
        └── Just a script       → rust-script (see one-liners.md)
        ```
        
        When in doubt, search crates.io for the latest version, then check:
        1. Is it maintained? (`cargo deny check` will scream if it's yanked or unmaintained)
        2. Does it have `serde` feature? (boundary types should always serde)
        3. Does it have `tokio` integration? (avoid runtime mixing)
        4. Is it on `tokio::io::AsyncRead`/`AsyncWrite` (the std for async I/O)?
        5. Are there safety-critical `unsafe` regions? If yes, has the author shipped miri proofs?
        
      • macros.md 3.3 KB
        # Macros
        
        A macro is the last rung of the ladder: reach for one only when a function, a generic, or a trait cannot express the thing (variadic input, new syntax, generating items, compile-time code from a type's shape). Macros cost compile time, error-message quality, and IDE support.
        
        ## `macro_rules!`
        
        ```rust
        /// Builds a `HashMap` from `key => value` pairs.
        #[macro_export]
        macro_rules! hashmap {
            ($($key:expr => $value:expr),* $(,)?) => {{
                let mut map = $crate::__private::HashMap::new();
                $( map.insert($key, $value); )*
                map
            }};
        }
        
        #[doc(hidden)]
        pub mod __private {
            pub use std::collections::HashMap;
        }
        ```
        
        - **`$crate::` for every item of the defining crate.** Callers may rename your crate or lack the import; `$crate` always resolves to the crate that defined the macro.
        - **Narrowest fragment specifier.** `expr`, `ty`, `ident`, `path`, `pat`, `literal` give callers real error messages and parse precedence correctly; `tt` accepts anything and defers the error to a confusing expansion.
        - **Helpers the expansion needs** live in a `#[doc(hidden)] pub mod __private` and are reached through `$crate::__private::...`; they are not API.
        - **`#[macro_export]` puts the macro at the crate root** (`my_crate::hashmap!`). Import it by path (`use my_crate::hashmap;`), not `#[macro_use] extern crate`.
        - Hygiene covers local variables, not items or method calls: a name the expansion uses unqualified resolves at the call site. Qualify everything.
        
        ## Procedural macros
        
        A proc macro lives in its own crate (`[lib] proc-macro = true`), which can export nothing else. Ship it as `my-crate-derive` and re-export it from `my-crate` so users depend on one crate and the macro's generated code can refer to `::my_crate::...`.
        
        ```rust
        use proc_macro::TokenStream;
        use quote::{format_ident, quote};
        use syn::{parse_macro_input, Data, DeriveInput};
        
        #[proc_macro_derive(Builder)]
        pub fn derive_builder(input: TokenStream) -> TokenStream {
            let input = parse_macro_input!(input as DeriveInput);
            expand(&input).unwrap_or_else(syn::Error::into_compile_error).into()
        }
        
        fn expand(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
            let Data::Struct(_) = &input.data else {
                return Err(syn::Error::new_spanned(&input.ident, "Builder supports structs only"));
            };
            let name = &input.ident;
            let builder = format_ident!("{}Builder", name); // quote! cannot paste identifiers
            Ok(quote! {
                impl #name {
                    pub fn builder() -> #builder { #builder::default() }
                }
            })
        }
        ```
        
        - Parse with `syn`, generate with `quote`, work in `proc_macro2` types so the logic is unit-testable outside the compiler.
        - **Never panic.** Report problems as `syn::Error::new_spanned(the_offending_tokens, "...")` and return `into_compile_error()`: the compiler underlines the exact input the user must change. A panic produces one message at the derive site with no location.
        - Build new identifiers with `format_ident!`; `quote!` has no token pasting (`#name##Builder` is not valid).
        - Generated code uses fully qualified paths (`::core::option::Option`, `::my_crate::Trait`) so it compiles regardless of what the caller imported or shadowed.
        - Test expansions with `trybuild` (pass and compile-fail cases, including the error message spans) rather than by asserting on generated token text.
        
      • one-liners.md 7.5 KB
        # One-Liners and Disposable Scripts
        
        Production hygiene with throwaway ergonomics. Rust scripts get the same strict lints, the same miri rule when `unsafe` is touched, the same type discipline. The difference is dependency declaration lives inline.
        
        ## `rust-script` — the recommended path
        
        Install once:
        
        ```bash
        cargo install rust-script
        ```
        
        Write a script:
        
        ```rust
        #!/usr/bin/env rust-script
        //! Fetch a URL and print its body length.
        //!
        //! Usage:
        //!   ./fetch.rs <url>
        //!
        //! ```cargo
        //! [dependencies]
        //! anyhow = "1"
        //! reqwest = { version = "0.12", features = ["blocking"] }
        //! ```
        
        use std::env;
        
        fn main() -> anyhow::Result<()> {
            let url = env::args().nth(1).context("usage: fetch.rs <url>")?;
            let body = reqwest::blocking::get(&url)?.error_for_status()?.text()?;
            println!("{} bytes", body.len());
            Ok(())
        }
        ```
        
        Make executable: `chmod +x fetch.rs`. Run: `./fetch.rs https://example.com`.
        
        The `//! \`\`\`cargo` block is parsed as inline `Cargo.toml`. Everything else is normal Rust.
        
        ## With async
        
        ```rust
        #!/usr/bin/env rust-script
        //! ```cargo
        //! [dependencies]
        //! anyhow = "1"
        //! tokio = { version = "1", features = ["full"] }
        //! reqwest = "0.12"
        //! ```
        
        #[tokio::main]
        async fn main() -> anyhow::Result<()> {
            let urls = [
                "https://example.com",
                "https://example.org",
            ];
            let client = reqwest::Client::new();
            let bodies = futures::future::join_all(urls.iter().map(|u| {
                let c = client.clone();
                async move { c.get(*u).send().await?.text().await }
            })).await;
            for (url, body) in urls.iter().zip(bodies) {
                match body {
                    Ok(b) => println!("{url}: {} bytes", b.len()),
                    Err(e) => eprintln!("{url}: error {e}"),
                }
            }
            Ok(())
        }
        ```
        
        ## With CLI parsing
        
        ```rust
        #!/usr/bin/env rust-script
        //! ```cargo
        //! [dependencies]
        //! anyhow = "1"
        //! clap = { version = "4", features = ["derive"] }
        //! ```
        
        use clap::Parser;
        
        #[derive(Parser, Debug)]
        #[command(version, about = "rename files by pattern")]
        struct Cli {
            /// Glob to match
            pattern: String,
            /// Replacement template (use {n} for sequence)
            template: String,
            /// Show what would happen without doing it
            #[arg(long)]
            dry_run: bool,
        }
        
        fn main() -> anyhow::Result<()> {
            let cli = Cli::parse();
            let entries: Vec<_> = glob::glob(&cli.pattern)?.collect::<Result<_, _>>()?;
            for (n, entry) in entries.iter().enumerate() {
                let target = cli.template.replace("{n}", &n.to_string());
                if cli.dry_run {
                    println!("{} -> {target}", entry.display());
                } else {
                    std::fs::rename(entry, &target)?;
                }
            }
            Ok(())
        }
        ```
        
        ## Caching
        
        `rust-script` caches the compiled binary in `~/.cache/rust-script/`. First run is slow (full compile), subsequent runs are instant.
        
        To clear: `rust-script --clear-cache`.
        
        Pin a script's compile target into the script directory for portability:
        
        ```bash
        rust-script --build-only --base-path . ./script.rs
        ```
        
        This drops a `target/` next to the script with the prebuilt binary.
        
        ## `cargo-script` (RFC 3424, nightly-only)
        
        The official replacement being built into cargo. Same idea, slightly different syntax:
        
        ```rust
        #!/usr/bin/env -S cargo +nightly -Zscript
        ---
        package:
          name = "fetch"
          edition = "2024"
        
        dependencies:
          anyhow = "1"
          reqwest = { version = "0.12", features = ["blocking"] }
        ---
        
        use anyhow::Context as _;
        
        fn main() -> anyhow::Result<()> {
            let url = std::env::args().nth(1).context("url required")?;
            println!("{}", reqwest::blocking::get(&url)?.text()?.len());
            Ok(())
        }
        ```
        
        Stable cargo (1.97) still rejects `-Zscript` and refuses to run a `.rs` file directly. Use `rust-script` now; migrate once `cargo script` reaches the stable channel of every toolchain your scripts run on.
        
        ## Strict mode for scripts
        
        Add a lints block in the inline `Cargo.toml`:
        
        ```rust
        //! ```cargo
        //! [dependencies]
        //! anyhow = "1"
        //!
        //! [lints.rust]
        //! unsafe_code = "forbid"
        //!
        //! [lints.clippy]
        //! all = "deny"
        //! pedantic = "warn"
        //! unwrap_used = "deny"
        //! expect_used = "deny"
        //! panic = "deny"
        //! ```
        ```
        
        Now the script gets the same strictness as the main project. If you need a one-line `unwrap()` for prototype velocity, switch the lint to `warn` for that one script - never blanket `allow`.
        
        Run with lints visible:
        
        ```bash
        RUSTFLAGS="-D warnings" rust-script ./script.rs
        ```
        
        ## When NOT to use a script
        
        - It is going to live longer than a week → make it a real crate with `cargo new --bin`.
        - It needs custom build scripts (`build.rs`) → real crate.
        - It needs binary distribution to other machines → real crate with `cargo dist`.
        - It needs to be tested → real crate (scripts can technically run `#[test]`s under `cargo test`, but the workflow is awkward).
        
        A reasonable migration path: start as a script, when complexity grows past ~200 lines or you reach for a second `.rs` file, run `rust-script --emit ./script.rs` to dump a regular Cargo project skeleton and continue from there.
        
        ## Inline tests in a script
        
        ```rust
        #!/usr/bin/env rust-script
        //! ```cargo
        //! [dependencies]
        //! ```
        
        fn double(x: i32) -> i32 { x * 2 }
        
        fn main() {
            println!("{}", double(21));
        }
        
        #[cfg(test)]
        mod tests {
            use super::*;
        
            #[test]
            fn doubles_ints() {
                assert_eq!(double(5), 10);
            }
        }
        ```
        
        Run tests: `rust-script --test ./script.rs`.
        
        ## A useful "Rust as awk" pattern
        
        For ad-hoc data processing on stdin:
        
        ```rust
        #!/usr/bin/env rust-script
        //! ```cargo
        //! [dependencies]
        //! serde_json = "1"
        //! ```
        
        use std::io::{self, BufRead, Write};
        
        fn main() -> Result<(), Box<dyn std::error::Error>> {
            let stdin = io::stdin();
            let stdout = io::stdout();
            let mut out = stdout.lock();
            for line in stdin.lock().lines() {
                let line = line?;
                let value: serde_json::Value = serde_json::from_str(&line)?;
                if let Some(s) = value.get("level").and_then(|v| v.as_str()) {
                    if s == "error" {
                        writeln!(out, "{line}")?;
                    }
                }
            }
            Ok(())
        }
        ```
        
        `cat logs.jsonl | ./filter-errors.rs` — filter JSON logs by `level == "error"`. Faster than `jq` for big files, type-safe.
        
        For numerics:
        
        ```rust
        #!/usr/bin/env rust-script
        //! sum a column of numbers from stdin
        use std::io::{self, BufRead};
        fn main() {
            let total: f64 = io::stdin().lock().lines()
                .filter_map(|l| l.ok())
                .filter_map(|l| l.trim().parse::<f64>().ok())
                .sum();
            println!("{total}");
        }
        ```
        
        ## The `rust-script` shebang trick on macOS
        
        macOS does not support multi-arg shebangs without `env -S`. Use:
        
        ```rust
        #!/usr/bin/env -S rust-script --
        ```
        
        The `--` lets clap-style argument parsers see the user's args, not the rust-script arguments.
        
        ## Editor support
        
        VS Code / Helix / Vim with `rust-analyzer`: open the script file as if it were `src/main.rs` of an inferred crate. Most editors auto-detect the inline manifest. If not, hand-create a `Cargo.toml` next to the script with matching deps for the duration of editing, then delete it.
        
        ## When `rust-script` is too heavy
        
        For absolutely throwaway "one expression on stdin" use cases, a Rust REPL like `evcxr_jupyter` (Jupyter kernel) or `irust` (terminal REPL) is more appropriate:
        
        ```bash
        cargo install irust
        irust
        ```
        
        But these are interactive playgrounds, not scriptable. For shell pipelines, stay with `rust-script`.
        
        ## The Promise
        
        Same strict lints. Same `clippy::pedantic` enforcement. Same `unsafe`-requires-SAFETY rule. The agent does not get a free pass on a 30-line script. The whole point of strict scripts is that **production hygiene is cheap when the toolchain enforces it**.
        
      • proptest-insta.md 13.9 KB
        # Property Tests (proptest) + Snapshot Tests (insta)
        
        Two test types every Rust project should have alongside unit tests. Proptest hunts for inputs your unit tests forgot to try. Insta locks down output shapes you do not want to silently change.
        
        ## When to reach for each
        
        | Want to test… | Use |
        |---|---|
        | One specific behavior with a known input | `#[test]` + `assert_eq!` |
        | All inputs of a certain shape work | `proptest!` |
        | Output structure stays stable across refactors | `insta::assert_*_snapshot!` |
        | Parser/serializer round-trips | `proptest!` (the round-trip property) |
        | CLI help text, JSON response shape, debug output | `insta::assert_snapshot!` |
        | Concurrency under all interleavings | `loom` (see `concurrency.md`) |
        
        Use all three. They cover different bug classes.
        
        ## Where tests live
        
        - **Unit tests of private behavior**: `#[cfg(test)] mod tests { use super::*; ... }` at the bottom of the module they test.
        - **Public-API behavior**: `tests/*.rs`. Each file is a separate crate that sees only `pub` items, so it fails when the public contract breaks, not when an internal changes.
        - **Examples in rustdoc**: doctests. They compile and run, so a documented example that drifts from the API fails the build. `cargo nextest run` skips them; the gate runs `cargo test --doc` too. Write examples with `?` and hide setup behind `# ` lines ([api-design.md](api-design.md#rustdoc-sections)).
        - **Panics**: `#[should_panic(expected = "...")]` only when panicking is the documented contract (an out-of-bounds index on a slice-like type). An invalid input that returns `Err` is asserted as that `Err` value.
        - **Environment and globals**: `std::env::set_var` is `unsafe` in the 2024 edition because another thread may read the environment at the same moment, and tests run in parallel threads. Inject configuration as a parameter instead of mutating process state; a test that must touch the real environment runs in its own process (`tests/` binary, or nextest's process-per-test).
        
        ## Proptest — setup
        
        `Cargo.toml`:
        
        ```toml
        [dev-dependencies]
        proptest = "1"
        proptest-derive = "0.5"     # for #[derive(Arbitrary)]
        ```
        
        `proptest.toml` at project root (optional, sane defaults):
        
        ```toml
        cases = 256                  # number of random inputs per property
        max_local_rejects = 65536
        max_global_rejects = 1024
        max_shrink_iters = 1024
        max_shrink_time = 60_000      # ms
        failure_persistence = { source_file = "proptest-regressions/", file_name = "regressions.txt" }
        verbose = 0
        ```
        
        `failure_persistence` is the killer feature: every failure is written to a regression file. On the next run, those exact inputs are replayed first, so once a bug is found it never escapes again.
        
        ## Basic property test
        
        ```rust
        use proptest::prelude::*;
        
        fn parse(s: &str) -> Result<Color, ParseError> { /* ... */ }
        fn render(c: &Color) -> String { /* ... */ }
        
        proptest! {
            #[test]
            fn parse_render_roundtrips(red in 0u8..=255, green in 0u8..=255, blue in 0u8..=255) {
                let color = Color { red, green, blue };
                let rendered = render(&color);
                let parsed = parse(&rendered).expect("our render should always parse");
                prop_assert_eq!(parsed, color);
            }
        }
        ```
        
        `proptest!` macro takes `(arg in strategy, ...)` pairs. Each strategy produces values; proptest runs the body with random samples, then shrinks failing cases to minimal forms.
        
        ## Strategies — the value-generation language
        
        | Strategy | Produces |
        |---|---|
        | `any::<T>()` | Any value of `T` (if `T: Arbitrary`) |
        | `0u32..100` | Integer ranges |
        | `prop::sample::select(slice)` | Pick from a list |
        | `prop::collection::vec(elem, range)` | Vec of length in range |
        | `prop::collection::hash_map(k, v, n..m)` | HashMap |
        | `prop::option::of(strategy)` | Option |
        | `prop::result::maybe_ok(ok, err)` | Result |
        | `(s1, s2).prop_map(\|(a, b)\| ...)` | Combine, transform |
        | `s.prop_filter("reason", \|v\| pred)` | Reject values |
        | `s.prop_flat_map(\|v\| dependent)` | Sequential dependency |
        | `prop_oneof![strategy1, strategy2]` | Union of strategies |
        | `r"[a-z]{3,10}"` | Regex-generated string |
        | `"\\PC*"` | Any printable non-control string |
        
        Example combining several:
        
        ```rust
        fn config_strategy() -> impl Strategy<Value = Config> {
            (
                prop::sample::select(vec!["dev", "staging", "prod"]),
                0u16..=65535,
                prop::collection::hash_map(
                    r"[a-z_]{1,20}",
                    any::<String>(),
                    0..5,
                ),
            ).prop_map(|(env, port, vars)| Config {
                env: env.into(),
                port,
                env_vars: vars,
            })
        }
        
        proptest! {
            #[test]
            fn config_validates(cfg in config_strategy()) {
                let result = validate(&cfg);
                if cfg.port == 0 {
                    prop_assert!(result.is_err());
                } else {
                    prop_assert!(result.is_ok());
                }
            }
        }
        ```
        
        ## Properties to write for every parser
        
        1. **Round-trip:** `parse(render(x)) == x` for all valid `x`.
        2. **No-panic:** `parse(arbitrary_string)` never panics, always returns `Result`.
        3. **Idempotent:** `parse(parse(x).unwrap().render()) == parse(x).unwrap()`.
        4. **Whitespace insensitivity:** `parse(x) == parse(strip_whitespace(x))` (if applicable).
        
        For every serializer:
        
        1. **Length bound:** `render(x).len() <= bound(x)`.
        2. **Charset:** `render(x).chars().all(|c| ALLOWED.contains(&c))`.
        
        For every collection operation:
        
        1. **Identity:** `op_identity(x) == x` (sort an already-sorted, dedupe a unique).
        2. **Idempotence:** `op(op(x)) == op(x)`.
        3. **Commutativity:** `op(a, b) == op(b, a)` (set union, etc).
        4. **Length:** `op(a, b).len() == known_relation(a.len(), b.len())`.
        
        For every numeric op:
        
        1. **Monotonicity:** `a <= b => f(a) <= f(b)`.
        2. **Identity element:** `f(x, identity) == x`.
        
        Write these mechanically. The agent should reach for proptest the moment any of these properties is checkable.
        
        ## Derive `Arbitrary`
        
        ```rust
        use proptest_derive::Arbitrary;
        
        #[derive(Debug, Clone, PartialEq, Arbitrary)]
        struct Vec3 {
            #[proptest(strategy = "-100.0..=100.0")]
            x: f32,
            #[proptest(strategy = "-100.0..=100.0")]
            y: f32,
            #[proptest(strategy = "-100.0..=100.0")]
            z: f32,
        }
        
        proptest! {
            #[test]
            fn dot_product_is_commutative(a: Vec3, b: Vec3) {
                prop_assert!((dot(&a, &b) - dot(&b, &a)).abs() < 1e-5);
            }
        }
        ```
        
        `#[derive(Arbitrary)]` auto-implements the strategy. Per-field `#[proptest(strategy = "...")]` overrides.
        
        ## Stateful / state machine tests
        
        For data structures with operations (queues, maps, trees), use `proptest-state-machine`:
        
        ```rust
        use proptest_state_machine::{ReferenceStateMachine, StateMachineTest};
        
        struct MyQueueRef { state: VecDeque<i32> }
        struct MyQueueSut { sut: MyQueue<i32> }
        
        #[derive(Debug, Clone)]
        enum Op { Push(i32), Pop }
        
        impl ReferenceStateMachine for MyQueueRef {
            type State = VecDeque<i32>;
            type Transition = Op;
        
            fn init_state() -> BoxedStrategy<Self::State> {
                Just(VecDeque::new()).boxed()
            }
            fn transitions(_: &Self::State) -> BoxedStrategy<Self::Transition> {
                prop_oneof![
                    any::<i32>().prop_map(Op::Push),
                    Just(Op::Pop),
                ].boxed()
            }
            fn apply(mut state: Self::State, transition: &Self::Transition) -> Self::State {
                match transition {
                    Op::Push(x) => state.push_back(*x),
                    Op::Pop => { state.pop_front(); }
                }
                state
            }
        }
        
        impl StateMachineTest for MyQueueSut {
            type SystemUnderTest = MyQueue<i32>;
            type Reference = MyQueueRef;
        
            fn init_test(_: &<Self::Reference as ReferenceStateMachine>::State) -> Self::SystemUnderTest {
                MyQueue::new()
            }
            fn apply(mut sut: Self::SystemUnderTest, _: &VecDeque<i32>, transition: Op) -> Self::SystemUnderTest {
                match transition {
                    Op::Push(x) => sut.push(x),
                    Op::Pop => { sut.pop(); }
                }
                sut
            }
            fn check_invariants(sut: &Self::SystemUnderTest, state: &VecDeque<i32>) {
                assert_eq!(sut.len(), state.len());
                // also check head/tail/iteration order...
            }
        }
        
        proptest_state_machine::prop_state_machine! {
            #[test]
            fn queue_matches_vecdeque(sequential 1..50 => MyQueueSut);
        }
        ```
        
        You define a reference implementation (`VecDeque` here), proptest fuzzes operations against both, asserts invariants every step. This is the technique for finding bugs in lock-free or complex containers.
        
        ## Shrinking
        
        When a property fails, proptest reduces the input to a minimal counter-example. For built-in strategies this is automatic. For custom strategies built with `prop_map`, shrinking goes through the underlying strategy. Avoid breaking shrinking with `prop_filter` (rejection sampling) over wide spaces; prefer `prop_flat_map` or directly-shaped strategies.
        
        ## Regression corpus
        
        When a property test fails, proptest writes the failing input to `proptest-regressions/<test_name>.txt`. Commit this directory. Future runs replay these failing inputs first, so the bug stays fixed forever.
        
        ```
        proptest-regressions/
        └── parse_color.txt   # commit this
        ```
        
        ## Insta — setup
        
        `Cargo.toml`:
        
        ```toml
        [dev-dependencies]
        insta = { version = "1", features = ["yaml", "json", "redactions", "filters"] }
        
        [dependencies.serde_yaml]
        version = "0.9"
        optional = true
        ```
        
        Install the CLI:
        
        ```bash
        cargo install cargo-insta
        ```
        
        ## Insta — basic snapshots
        
        ```rust
        #[test]
        fn renders_default_help() {
            let output = render_help();
            insta::assert_snapshot!(output);
        }
        ```
        
        First run: creates `src/snapshots/mycrate__renders_default_help.snap.new`. Run `cargo insta review`, press `a` to accept, the `.new` extension is dropped. Subsequent runs diff against the committed snapshot; mismatches fail the test.
        
        ## Insta — typed snapshots
        
        ```rust
        #[derive(Debug, serde::Serialize)]
        struct Result {
            status: String,
            user: User,
            duration_ms: u64,
        }
        
        #[test]
        fn json_response() {
            let value = compute();
            insta::assert_json_snapshot!(value);
        }
        
        #[test]
        fn yaml_response() {
            insta::assert_yaml_snapshot!(value);
        }
        
        #[test]
        fn debug_repr() {
            insta::assert_debug_snapshot!(value);
        }
        ```
        
        Choose:
        - `assert_snapshot!` for `String`/`Display` output (CLI help, error messages, generated code).
        - `assert_debug_snapshot!` for `{:?}` (Rust-internal data).
        - `assert_json_snapshot!` for structured data crossing process boundaries.
        - `assert_yaml_snapshot!` when YAML is easier to read in diffs.
        
        ## Insta — redactions and filters
        
        For values that change every run (timestamps, UUIDs, paths):
        
        ```rust
        #[test]
        fn with_redactions() {
            let value = ApiResponse {
                id: uuid::Uuid::now_v7(),
                created_at: jiff::Timestamp::now(),
                body: "hello".into(),
            };
            insta::assert_json_snapshot!(value, {
                ".id" => "[uuid]",
                ".created_at" => "[timestamp]",
            });
        }
        ```
        
        For regex filters applied to all snapshots in a test:
        
        ```rust
        #[test]
        fn with_filters() {
            let mut settings = insta::Settings::clone_current();
            settings.add_filter(r"/tmp/[a-z0-9-]+", "[TMP]");
            settings.add_filter(r"\d+\.\d+ms", "[TIMING]");
            settings.bind(|| {
                let output = run_command();
                insta::assert_snapshot!(output);
            });
        }
        ```
        
        `Settings::bind` scopes filters to the closure.
        
        ## Insta workflow
        
        1. Write the test, run it. First run creates `.snap.new`.
        2. `cargo insta review` → interactive UI. Show diff, accept/reject.
        3. Accepted snapshots commit to the repo.
        4. Refactor code. Tests run; mismatches show as diffs.
        5. If the new output is correct, `cargo insta accept` (or selective `review`). If wrong, fix the code.
        
        Pair with CI to fail builds when uncommitted `.snap.new` files exist:
        
        ```bash
        cargo nextest run
        if find . -name "*.snap.new" | grep -q .; then
            echo "Pending snapshots, run 'cargo insta review'"
            exit 1
        fi
        ```
        
        ## Inline snapshots
        
        ```rust
        #[test]
        fn small_output() {
            let value = compute();
            insta::assert_snapshot!(value, @"hello world");
        }
        ```
        
        The trailing `@"..."` string is the expected snapshot, stored in source. Useful when the value is short enough that pulling out a separate file is overkill. `cargo insta accept` updates them in-place.
        
        ## Inline JSON snapshots
        
        ```rust
        #[test]
        fn json_inline() {
            insta::assert_json_snapshot!(value, @r###"
            {
              "status": "ok",
              "count": 3
            }
            "###);
        }
        ```
        
        ## Anti-patterns
        
        1. **Snapshots of unstable output.** If `HashMap` iteration order changes per run, snapshots will fail. Switch to `BTreeMap` or sort before snapshotting.
        2. **Massive snapshots.** A 10KB JSON dump where you really care about 3 fields. Either narrow to the fields, or accept that any refactor will require re-reviewing 10KB.
        3. **Snapshots that bake in implementation details.** "function called 3 times" is not a snapshot - it's a behavior assertion. Use a real assertion.
        4. **Skipping `cargo insta review`.** Accepting blind via `cargo insta accept --all` defeats the purpose. Always review.
        
        ## Combining proptest + insta
        
        ```rust
        proptest! {
            #[test]
            fn random_inputs_render_consistently(input: ValidInput) {
                let mut settings = insta::Settings::clone_current();
                settings.set_snapshot_suffix(format!("{}", input.hash()));
                settings.bind(|| {
                    insta::assert_snapshot!(render(&input));
                });
            }
        }
        ```
        
        But honestly, this is rarely a fit. Proptest tests properties, insta tests output shape. Don't snapshot random inputs - that defeats both tools.
        
        ## CI matrix recommendation
        
        ```yaml
        - name: Tests
          run: cargo nextest run --all-features
        
        - name: Property regressions (replay)
          run: |
            # The regression files in proptest-regressions/ replay first.
            # Failures here mean a previously-fixed bug came back.
            cargo nextest run --all-features --test-threads 1
        ```
        
        When a proptest finds a new failure, the regression file appears as a git diff - check it in.
        
        ## What proptest cannot do
        
        - Find bugs that require multi-process / multi-network coordination → integration tests + fault injection.
        - Find concurrency bugs → use `loom` (see `concurrency.md`).
        - Find performance regressions → use `criterion`.
        
        But for any function with a domain (inputs to outputs), proptest can find more bugs than your unit tests. **Write the property first, derive the unit test second.**
        
      • README.md 16.6 KB
        # Rust Programmer
        
        Production Rust in 2026. **Explicit allocation, compile-time proof, zero hidden cost.** Type-state-first, unsafe-banished-by-default, agent-proof.
        
        ## Identity — What Kind of Rust You Write
        
        You write Rust that looks like a Zig programmer designed it and a Rust compiler enforces it. Every allocation is visible. Every cost is explicit. Every invariant is encoded in the type system. Every cleanup is deterministic. The borrow checker, lifetime analysis, trait bounds, and `miri` then guarantee what Zig leaves to discipline.
        
        **Five pillars, every file, no exceptions:**
        
        | Pillar | Default Behavior | Reference |
        |---|---|---|
        | **Explicit allocation** | Arena for hot paths, `&[T]`/`Cow` over `Vec`/`String` in signatures, `try_*` when allocation can fail | [zero-cost-safety.md §1](zero-cost-safety.md) |
        | **Compile-time proof** | `const fn` everything const-eligible, `const { assert!(...) }` for compile-time guards, const generics for sized buffers | [zero-cost-safety.md §2](zero-cost-safety.md) |
        | **Zero hidden cost** | Slice-based APIs where caller owns memory, no hidden `.clone()`/`.to_string()`, `Cow` to defer allocation | [zero-cost-safety.md §3](zero-cost-safety.md) |
        | **Type-encoded invariants** | Newtype wrappers for every semantic unit, type-state for state machines, branded IDs | [type-state.md](type-state.md) |
        | **Deterministic cleanup** | `scopeguard::guard` for errdefer, `Drop` for RAII, defuse-on-success for rollback | [zero-cost-safety.md §5](zero-cost-safety.md) |
        
        The two highest-leverage tools Rust gives a coding agent:
        
        1. **Bounded polymorphism** (traits). Real, machine-checked, composable constraints.
        2. **Newtype-as-coordinate-space.** `Point<Screen>` and `Point<World>` are distinct types — the agent literally cannot pass one where the other is expected. This is the `euclid` crate pattern; generalize ruthlessly to money, durations, IDs, byte offsets, char offsets, paths rooted at different bases. Full patterns → [type-state.md](type-state.md).
        
        ---
        
        ## Hard Rules (Every `.rs` File)
        
        ### 1. No `unwrap()`, No `expect()` Outside Tests
        
        ```rust
        // WRONG
        let val = map.get("key").unwrap();
        
        // RIGHT — propagate or provide context
        let val = map.get("key").context("missing 'key' in config")?;
        ```
        
        The one exception is a failure that can only mean a bug in this crate (a proven invariant): `.expect("<the invariant>")` behind `#[expect(clippy::expect_used, reason = "<why it cannot fail>")]` on that statement. Clippy's `expect_used` deny and `scripts/rust/check-no-excuse-rules.sh` both honor exactly that form.
        
        **Discarding is handling too.** `let _ = fallible();` and `.ok();` that drop an error are forbidden (`let_underscore_must_use`). A best-effort call (cleanup in `Drop`, a rollback on an error path) logs the error it cannot return.
        
        Typed errors for libraries ([thiserror](https://docs.rs/thiserror)), ad-hoc errors for binaries ([anyhow](https://docs.rs/anyhow) / [color-eyre](https://docs.rs/color-eyre)). Full stack → [libraries.md](libraries.md).
        
        ### 2. No `unsafe` Without Miri Proof
        
        If `unsafe` is unavoidable, you have miri. Run it. Always. **Load [`../rust-ub/README.md`](../rust-ub/README.md) plus every file under [`../rust-ub/`](../rust-ub/)** for the full UB taxonomy, Miri escalation protocol (4 strictness levels), and the fix-and-prove workflow. Every `unsafe` block needs the three components from [unsafe-discipline.md](unsafe-discipline.md): safe wrapper, `// SAFETY:` comment, miri test.
        
        ```bash
        cargo +nightly miri nextest run
        ```
        
        ### 3. Explicit Allocation — Arena by Default in Hot Paths
        
        **Do not scatter `Box::new()` / `Vec::new()` across hot loops.** Use arena allocation to make allocation scope visible and bulk-freeable. Full recipes → [zero-cost-safety.md §1](zero-cost-safety.md).
        
        ```rust
        use bumpalo::Bump;
        
        fn parse_frame<'a>(arena: &'a Bump, raw: &[u8]) -> Frame<'a> {
            let header = arena.alloc(parse_header(raw));
            let payload = arena.alloc_slice_copy(&raw[HEADER_LEN..]);
            Frame { header, payload }
        }
        // Caller owns arena. Caller decides when memory dies. Zero individual frees.
        ```
        
        When arena is overkill (simple CLI, one-shot allocation), `Vec`/`String` are fine — but **function signatures still prefer borrows**:
        
        ```rust
        // WRONG — forces caller to allocate
        fn process(input: String) -> String { ... }
        
        // RIGHT — caller chooses allocation strategy
        fn process(input: &str) -> Cow<'_, str> { ... }
        
        // BEST for hot paths — zero allocation, caller provides buffer
        fn process(input: &[u8], output: &mut [u8]) -> usize { ... }
        ```
        
        ### 4. Compile-Time First — Compute What Is Known at Build Time
        
        Lookup tables, size assertions, and buffer sizes are computed at compile time. A function is `const fn` when a `const` context needs it or it is trivially const-eligible; never contort runtime logic to be const. Full recipes → [zero-cost-safety.md §2](zero-cost-safety.md).
        
        ```rust
        // Lookup tables computed at compile time — zero runtime cost
        #[expect(clippy::indexing_slicing, reason = "const evaluation: an out-of-bounds index is a compile error")]
        const CRC_TABLE: [u32; 256] = {
            let mut table = [0u32; 256];
            let mut i: u32 = 0;
            while i < 256 {
                let mut crc = i;
                let mut j = 0;
                while j < 8 {
                    crc = if crc & 1 != 0 { (crc >> 1) ^ 0xEDB8_8320 } else { crc >> 1 };
                    j += 1;
                }
                table[i as usize] = crc; // u32 -> usize widens: the one cast `const` allows
                i += 1;
            }
            table
        };
        
        // Compile-time assertions — catch violations at build time, not runtime
        const _: () = assert!(std::mem::size_of::<Header>() == 12, "Header must be 12 bytes");
        ```
        
        Use `const generics` for stack-allocated buffers with compile-time size:
        
        ```rust
        struct RingBuffer<T, const N: usize> {
            data: [MaybeUninit<T>; N],
            head: usize,
            len: usize,
        }
        ```
        
        ### 5. Scope Guards — Deterministic Cleanup on Every Path
        
        Zig's `errdefer` in Rust. Full recipes → [zero-cost-safety.md §5](zero-cost-safety.md).
        
        ```rust
        use scopeguard::guard;
        
        fn deploy(artifact: &Path) -> Result<(), DeployError> {
            let backup = snapshot_current()?;
            // errdefer: restore on failure
            let rollback = guard(backup, |b| {
                if let Err(error) = restore(&b) {
                    tracing::error!(%error, "rollback failed");
                }
            });
        
            upload(artifact)?;
            health_check()?;
        
            // Success: defuse the guard
            scopeguard::ScopeGuard::into_inner(rollback);
            Ok(())
        }
        ```
        
        ### 6. Bit-Level Layout — zerocopy for Wire Formats
        
        Never hand-write `transmute` or pointer casts for parsing binary data. Full recipes → [zero-cost-safety.md §4](zero-cost-safety.md).
        
        ```rust
        use zerocopy::{FromBytes, IntoBytes, KnownLayout, Immutable};
        
        #[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
        #[repr(C)]
        struct PacketHeader {
            magic: [u8; 4],
            version: u8,
            flags: u8,
            length: [u8; 2], // use byte array for packed fields, decode via from_le_bytes
        }
        ```
        
        ### 7. Exhaustive Match — No Wildcard on Enums You Control
        
        ```rust
        // WRONG — silently ignores new variants
        match status {
            Status::Ok => handle_ok(),
            _ => handle_error(),
        }
        
        // RIGHT — compiler forces update when variants change
        match status {
            Status::Ok => handle_ok(),
            Status::NotFound => handle_not_found(),
            Status::Timeout => handle_timeout(),
        }
        ```
        
        For `#[non_exhaustive]` enums from external crates, the wildcard `_` is required — but add a `tracing::warn!` in the catch-all so you notice when new variants appear.
        
        ### 8. Type-State Over Runtime Checks
        
        Never `if self.state == State::Validated`. Encode states as distinct types so the compiler refuses invalid transitions. Full patterns → [type-state.md](type-state.md).
        
        ```rust
        struct Order<S: OrderState> { data: OrderData, _state: PhantomData<S> }
        struct Draft;
        struct Validated;
        struct Paid;
        
        impl Order<Draft> {
            fn validate(self) -> Result<Order<Validated>, ValidationError> { ... }
        }
        impl Order<Validated> {
            fn pay(self, payment: Payment) -> Result<Order<Paid>, PaymentError> { ... }
        }
        // Order<Draft> has no .pay() method. Compiler enforces the workflow.
        ```
        
        ### 9. Numbers State What Overflow and Conversion Mean
        
        `+` panics in debug and wraps in release: it states nothing. Pick the operation that says what the domain wants, and convert through the trait that says whether it can fail.
        
        ```rust
        let total = a.checked_add(b).ok_or(Error::Overflow)?; // overflow is an error
        let level = volume.saturating_add(step);              // clamp at the bound
        let slot = seq.wrapping_add(1) % RING;                // modular by design
        let wide = u64::from(small);                          // lossless: From
        let port = u16::try_from(raw).map_err(|_| Error::Port(raw))?; // lossy: TryFrom
        scores.sort_by(f64::total_cmp);                        // NaN-safe total order
        ```
        
        Never `as` for numeric conversion (the only exception is a lossless widening inside `const`, where `From` is unavailable; see [zero-cost-safety.md §2](zero-cost-safety.md)). Compare floats with an explicit tolerance chosen by the domain, never `==`. A value that can never be zero is `NonZeroU32` (and `Option<NonZeroU32>` costs no extra space).
        
        ---
        
        ## Standard Library Defaults
        
        Full decision tree with rationale and code snippets → [libraries.md](libraries.md).
        
        | Category | Crate | Why |
        |---|---|---|
        | Async runtime | `tokio` | Ecosystem standard. Patterns → [async-tokio.md](async-tokio.md) |
        | HTTP server | `axum` + `tower` | Type-safe extractors, tower middleware. Stack → [axum-stack.md](axum-stack.md) |
        | CLI | `clap` derive + `color-eyre` | Typed args, beautiful errors. Stack → [clap-stack.md](clap-stack.md) |
        | Serialization | `serde` + `serde_json` | Non-negotiable for any boundary type |
        | Error (library) | `thiserror` | Derive `Error` with zero boilerplate |
        | Error (binary) | `anyhow` / `color-eyre` | Context-rich ad-hoc errors |
        | Database | `sqlx` (compile-time checked) | No runtime SQL surprises |
        | Arena alloc | `bumpalo` / `typed-arena` | Explicit allocation scope. Patterns → [zero-cost-safety.md §1](zero-cost-safety.md) |
        | Zero-copy parse | `zerocopy` | Safe binary parsing, no transmute. Patterns → [zero-cost-safety.md §4](zero-cost-safety.md) |
        | Scope guard | `scopeguard` | errdefer/defer. Patterns → [zero-cost-safety.md §5](zero-cost-safety.md) |
        | Stack collections | `smallvec` / `arrayvec` / `tinyvec` | Stack-first, heap-spillover. Patterns → [zero-cost-safety.md §3](zero-cost-safety.md) |
        | Bitfield | `bitfield` / `modular-bitfield` | Bit-packed flags. Patterns → [zero-cost-safety.md §4](zero-cost-safety.md) |
        | Testing | `proptest` + `insta` | Property + snapshot tests. Patterns → [proptest-insta.md](proptest-insta.md) |
        | Concurrency | `tokio::sync` / `parking_lot` | Channel-first, lock-second. Patterns → [concurrency.md](concurrency.md) |
        
        ---
        
        ## Cargo Strict Configuration
        
        Every new project gets the strict lint config from [cargo-strict.md](cargo-strict.md). The non-negotiable CI gate:
        
        ```bash
        cargo fmt --all -- --check && \
        cargo clippy --all-targets --all-features -- -D warnings && \
        cargo nextest run && cargo test --doc && \
        cargo +nightly miri nextest run  # when unsafe is involved
        ```
        
        ---
        
        ## Code Review Checklist (Post-Write, Every PR)
        
        Run through this list after writing any Rust code. Every item links to its recipe.
        
        | # | Check | Fix Reference |
        |---|---|---|
        | 1 | Every function signature prefers `&[T]`/`&str`/`Cow` over owned types | [zero-cost-safety.md §3](zero-cost-safety.md) |
        | 2 | Hot-path allocations use arena (`bumpalo`) not scattered `Box`/`Vec` | [zero-cost-safety.md §1](zero-cost-safety.md) |
        | 3 | Values known at build time (tables, size asserts, buffer sizes) are computed at compile time | [zero-cost-safety.md §2](zero-cost-safety.md) |
        | 4 | Arithmetic states its overflow intent; numeric conversions use `From`/`TryFrom` | This file §9 |
        | 5 | Binary format parsing uses `zerocopy`, not `transmute` | [zero-cost-safety.md §4](zero-cost-safety.md) |
        | 6 | Cleanup logic uses `scopeguard` or `Drop`, never manual `if err` cleanup | [zero-cost-safety.md §5](zero-cost-safety.md) |
        | 7 | Distinct semantic units are newtypes, not primitive aliases | [type-state.md](type-state.md) |
        | 8 | State machines use type-state, not runtime `if state ==` | [type-state.md](type-state.md) |
        | 9 | No `unwrap()`/`expect()` outside tests (invariant `expect` only behind `#[expect(clippy::expect_used, reason)]`); no discarded `Result` | This file §1 |
        | 10 | Every `unsafe` has SAFETY comment + miri test | [unsafe-discipline.md](unsafe-discipline.md), [../rust-ub/](../rust-ub/) |
        | 11 | Match on owned enums is exhaustive (no `_ =>`) | This file §7 |
        | 12 | Clippy pedantic passes with zero warnings | [cargo-strict.md](cargo-strict.md) |
        | 13 | Property tests exist for any function with a nontrivial domain | [proptest-insta.md](proptest-insta.md) |
        | 14 | Concurrency uses channels first, locks second, atomics last | [concurrency.md](concurrency.md) |
        | 15 | Async code uses `JoinSet` for structured concurrency; no lock guard, `watch` borrow, or entered span held across `.await` | [async-tokio.md](async-tokio.md) |
        | 16 | Public API follows the naming, conversion, and trait rules; `From` never bypasses a newtype invariant | [api-design.md](api-design.md) |
        | 17 | A performance-motivated change carries its before/after measurement | [libraries.md](libraries.md) (criterion) |
        
        ---
        
        ## Default Cargo.toml Dependencies — Zero-Cost Safety Stack
        
        Every new project starts with these alongside the standard deps from [cargo-strict.md](cargo-strict.md):
        
        ```toml
        # Zero-cost safety stack
        bumpalo = { version = "3", features = ["collections"] }
        scopeguard = "1"
        smallvec = { version = "1", features = ["union", "const_generics"] }
        zerocopy = { version = "0.8", features = ["derive"] }
        
        # Add when needed:
        # typed-arena = "2"           # homogeneous arena
        # arrayvec = "0.7"            # fixed-capacity stack vec
        # tinyvec = { version = "1", features = ["alloc"] }
        # bitfield = "0.17"           # bit-packed flags
        # modular-bitfield = "0.11"   # richer bitfield API
        # bytemuck = { version = "1", features = ["derive"] }
        ```
        
        ---
        
        ## Reference Index
        
        | File | When to Load |
        |---|---|
        | [zero-cost-safety.md](zero-cost-safety.md) | Arena, allocator, const fn, comptime, zero-alloc, bitfield, repr, scopeguard, errdefer, Zig-like patterns |
        | [type-state.md](type-state.md) | Newtype wrappers, validated construction, type-state machines, branded IDs, phantom types |
        | [api-design.md](api-design.md) | Public API: naming, conversions, trait design, `#[must_use]`, `#[non_exhaustive]`, visibility, rustdoc sections |
        | [macros.md](macros.md) | `macro_rules!` hygiene, proc-macro crates (`syn`/`quote`), spanned compile errors |
        | [unsafe-discipline.md](unsafe-discipline.md) | Any `unsafe` block — SAFETY comments, safe wrappers, miri proof |
        | [libraries.md](libraries.md) | Library selection, crate decision tree, dependency audit |
        | [cargo-strict.md](cargo-strict.md) | Project bootstrap, lint config, CI gate commands |
        | [async-tokio.md](async-tokio.md) | Async runtime, spawning, cancellation, `JoinSet`, `select!` |
        | [axum-stack.md](axum-stack.md) | HTTP services — axum + sqlx + tower + tracing |
        | [clap-stack.md](clap-stack.md) | CLI tools — clap derive + color-eyre + indicatif |
        | [concurrency.md](concurrency.md) | Locks, atomics, channels, loom model checker |
        | [proptest-insta.md](proptest-insta.md) | Property tests, snapshot tests, round-trip invariants |
        | [one-liners.md](one-liners.md) | `rust-script` one-liners, disposable scripts, inline deps |
        | [../rust-ub/README.md](../rust-ub/README.md) | UB hunting — miri escalation, sanitizers, fuzzing |
        | [../rust-ub/ub-taxonomy.md](../rust-ub/ub-taxonomy.md) | 14-category UB taxonomy with detection status |
        | [../rust-ub/miri-sanitizers-loom.md](../rust-ub/miri-sanitizers-loom.md) | Miri flags, ASAN/TSAN/MSAN, loom, cargo-fuzz |
        
        ---
        
        ## The Shape of Every Function
        
        ```rust
        /// One-line doc explaining WHAT, not HOW.
        ///
        /// # Errors
        /// Returns `FooError::Bar` when the input is invalid.
        fn frobnicate<'a>(
            arena: &'a Bump,        // explicit allocator when arena is in play
            input: &[u8],           // borrow, not owned
            output: &mut [u8],      // caller-provided buffer
        ) -> Result<&'a Frob, FrobError> {
            // ...
        }
        ```
        
        **Why this shape:** the caller sees every cost. Allocation scope is the arena's lifetime. Input is borrowed. Output buffer is caller-owned. Error is typed. The compiler enforces all of it.
        
        ---
        
        ## Activation
        
        This skill activates whenever you are writing or modifying any `.rs` file or `Cargo.toml`. One-off scripts get the strict treatment too — `rust-script` + the same lints, the same gates. Details → [one-liners.md](one-liners.md).
        
        **The promise:** production hygiene with throwaway ergonomics. Explicit allocation, compile-time proof, zero hidden cost, and **agent-proof safety at any volume**.
        
      • type-state.md 13.1 KB
        # Type-State and Newtype Patterns
        
        The single highest-leverage thing Rust gives a coding agent: encode invariants in the type system so the compiler refuses incorrect code. The agent does not have to "remember" rules - the rules are physical.
        
        ## The Two Core Patterns
        
        1. **Newtype wrappers for distinct semantic units.** Money, IDs, byte offsets, coordinate spaces - each gets its own tuple struct. The agent cannot pass meters where feet are expected, even though both are `f64` under the hood. This is the `euclid::Point<Screen>` vs `euclid::Point<World>` example Chris Allen called out.
        2. **Type-state for state machines.** Instead of a struct with a `status: enum { Draft, Validated, Persisted }` field and methods that check `if self.status == ...`, model each state as its own type. Transitions are method calls that consume `self` and return a new type. Illegal transitions become unrepresentable.
        
        ## Newtype Wrapper Cookbook
        
        ### Domain IDs
        
        ```rust
        use uuid::Uuid;
        
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
        #[serde(transparent)]
        pub struct UserId(Uuid);
        
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
        #[serde(transparent)]
        pub struct ProductId(Uuid);
        
        impl UserId {
            pub fn new() -> Self { Self(Uuid::now_v7()) }
            pub fn as_uuid(&self) -> &Uuid { &self.0 }
        }
        
        impl ProductId {
            pub fn new() -> Self { Self(Uuid::now_v7()) }
            pub fn as_uuid(&self) -> &Uuid { &self.0 }
        }
        
        // `fn buy(user: UserId, product: ProductId)` cannot be called with arguments swapped.
        ```
        
        `#[serde(transparent)]` keeps JSON/SQL round-trips identical to a bare `Uuid` - the wrapper is purely a compile-time discipline.
        
        ### Quantities with Phantom Type Tags
        
        ```rust
        use core::marker::PhantomData;
        use core::ops::{Add, Sub, Mul};
        
        #[derive(Debug, Clone, Copy)]
        pub struct Quantity<Unit> {
            raw: f64,
            _unit: PhantomData<Unit>,
        }
        
        // Tag types - zero-sized, never instantiated.
        pub struct Meters;
        pub struct Feet;
        pub struct Seconds;
        
        impl<U> Quantity<U> {
            pub const fn new(value: f64) -> Self {
                Self { raw: value, _unit: PhantomData }
            }
            pub fn raw(self) -> f64 { self.raw }
        }
        
        // Adding same-unit quantities: allowed.
        impl<U> Add for Quantity<U> {
            type Output = Self;
            fn add(self, rhs: Self) -> Self { Self::new(self.raw + rhs.raw) }
        }
        
        // Subtraction: allowed.
        impl<U> Sub for Quantity<U> {
            type Output = Self;
            fn sub(self, rhs: Self) -> Self { Self::new(self.raw - rhs.raw) }
        }
        
        // Multiplying by scalar: allowed.
        impl<U> Mul<f64> for Quantity<U> {
            type Output = Self;
            fn mul(self, rhs: f64) -> Self { Self::new(self.raw * rhs) }
        }
        
        // Conversions are explicit, named methods - never `From`/`Into` between units.
        impl Quantity<Meters> {
            pub fn to_feet(self) -> Quantity<Feet> {
                Quantity::new(self.raw * 3.280_84)
            }
        }
        ```
        
        Now:
        
        ```rust
        let distance: Quantity<Meters> = Quantity::new(100.0);
        let height: Quantity<Feet> = Quantity::new(50.0);
        let combined = distance + height;   // compile error: Quantity<Meters> + Quantity<Feet>
        let combined = distance + distance; // compiles
        ```
        
        The agent cannot accidentally mix units. Refactors that change a quantity's underlying unit are caught at compile time everywhere the type flows.
        
        ### Byte Offsets vs Character Offsets
        
        ```rust
        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
        pub struct ByteOffset(pub u32);
        
        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
        pub struct CharOffset(pub u32);
        
        impl ByteOffset {
            pub fn checked_add(self, delta: u32) -> Option<Self> { self.0.checked_add(delta).map(Self) }
        }
        
        // Converting between them is a function on the actual text.
        pub fn byte_to_char(text: &str, byte: ByteOffset) -> Option<CharOffset> {
            let prefix = text.get(..usize::try_from(byte.0).ok()?)?;
            u32::try_from(prefix.chars().count()).ok().map(CharOffset)
        }
        ```
        
        A function signature `fn slice(text: &str, start: ByteOffset, end: ByteOffset)` cannot be called with character offsets. The UTF-8 boundary bug is now a compile error.
        
        ### Currency
        
        ```rust
        use rust_decimal::Decimal;
        
        pub struct Krw;
        pub struct Usd;
        pub struct Jpy;
        
        #[derive(Debug, Clone, Copy)]
        pub struct Money<Currency> {
            amount: Decimal,
            _ccy: PhantomData<Currency>,
        }
        
        impl<C> Money<C> {
            pub const fn new(amount: Decimal) -> Self { Self { amount, _ccy: PhantomData } }
        }
        
        impl<C> Add for Money<C> {
            type Output = Self;
            fn add(self, rhs: Self) -> Self { Self::new(self.amount + rhs.amount) }
        }
        
        // No blanket From<Money<X>> for Money<Y> - conversions go through an explicit
        // FX rate function that takes a `Rate<From, To>` argument.
        
        pub struct Rate<From, To> {
            factor: Decimal,
            _from: PhantomData<From>,
            _to: PhantomData<To>,
        }
        
        impl<From, To> Money<From> {
            pub fn convert(self, rate: Rate<From, To>) -> Money<To> {
                Money::new(self.amount * rate.factor)
            }
        }
        ```
        
        The agent cannot add KRW and USD by accident. They cannot convert without a rate. They cannot apply a USD→JPY rate to a KRW value.
        
        ### Paths Rooted at Different Bases
        
        ```rust
        use std::path::{Path, PathBuf};
        
        /// A path guaranteed to be relative to the project root.
        #[derive(Debug, Clone)]
        pub struct ProjectRel(PathBuf);
        
        /// A path guaranteed to be relative to the user's home directory.
        #[derive(Debug, Clone)]
        pub struct HomeRel(PathBuf);
        
        impl ProjectRel {
            pub fn new(path: impl AsRef<Path>) -> Result<Self, PathError> {
                let path = path.as_ref();
                if path.is_absolute() { return Err(PathError::NotRelative); }
                if path.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
                    return Err(PathError::EscapesRoot);
                }
                Ok(Self(path.to_path_buf()))
            }
        
            pub fn resolve(&self, project_root: &Path) -> PathBuf {
                project_root.join(&self.0)
            }
        }
        ```
        
        The agent's path-handling code now distinguishes between project-relative and home-relative paths at the type level. A function taking `ProjectRel` cannot be called with a `HomeRel`.
        
        ### Validated Newtypes — Parse Once, Every Path
        
        A newtype that carries an invariant has exactly one way in: a fallible constructor over a private field. Every other entry point (`FromStr`, serde, `TryFrom`) routes through it, so no path can build an unchecked value.
        
        ```rust
        #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
        #[serde(try_from = "String", into = "String")]
        pub struct Email(String); // private field: no `Email(raw)` from outside
        
        #[derive(Debug, thiserror::Error)]
        #[error("not an email address: {0:?}")]
        pub struct InvalidEmail(String);
        
        impl TryFrom<String> for Email {
            type Error = InvalidEmail;
            fn try_from(raw: String) -> Result<Self, Self::Error> {
                match raw.split_once('@') {
                    Some((user, host)) if !user.is_empty() && host.contains('.') => Ok(Self(raw)),
                    _ => Err(InvalidEmail(raw)),
                }
            }
        }
        
        impl std::str::FromStr for Email {
            type Err = InvalidEmail;
            fn from_str(raw: &str) -> Result<Self, Self::Err> { Self::try_from(raw.to_owned()) }
        }
        
        impl From<Email> for String {
            fn from(email: Email) -> Self { email.0 }
        }
        ```
        
        `#[serde(try_from = "String")]` makes deserialization run the same check, so JSON cannot smuggle in an invalid `Email`. Never add `impl From<String> for Email`: an infallible conversion into an invariant-bearing type is a bypass.
        
        ## Type-State State Machines
        
        Encode the lifecycle of a value as a sequence of types. Each transition consumes the previous state and returns the next.
        
        ### HTTP Request Builder
        
        ```rust
        pub struct RequestBuilder<State> {
            url: String,
            method: Method,
            headers: HeaderMap,
            body: Option<Vec<u8>>,
            _state: PhantomData<State>,
        }
        
        pub struct NeedsUrl;
        pub struct NeedsMethod;
        pub struct Ready;
        
        impl RequestBuilder<NeedsUrl> {
            pub fn new() -> Self {
                Self {
                    url: String::new(),
                    method: Method::GET,
                    headers: HeaderMap::new(),
                    body: None,
                    _state: PhantomData,
                }
            }
            pub fn url(mut self, url: impl Into<String>) -> RequestBuilder<NeedsMethod> {
                self.url = url.into();
                RequestBuilder { url: self.url, method: self.method, headers: self.headers, body: self.body, _state: PhantomData }
            }
        }
        
        impl RequestBuilder<NeedsMethod> {
            pub fn method(mut self, method: Method) -> RequestBuilder<Ready> {
                self.method = method;
                RequestBuilder { url: self.url, method: self.method, headers: self.headers, body: self.body, _state: PhantomData }
            }
        }
        
        // .header(), .body() available in any state that has at least URL.
        impl<S> RequestBuilder<S> {
            pub fn header(mut self, name: HeaderName, value: HeaderValue) -> Self {
                self.headers.insert(name, value);
                self
            }
        }
        
        // .send() only available once URL and method are set.
        impl RequestBuilder<Ready> {
            pub async fn send(self, client: &Client) -> reqwest::Result<Response> { /* ... */ }
        }
        ```
        
        `client.send(RequestBuilder::new().send(...))` - compile error. The agent has to fill in the required steps. The IDE autocomplete also reflects only the legal next steps.
        
        ### File Handles
        
        ```rust
        pub struct File<State> {
            fd: RawFd,
            _state: PhantomData<State>,
        }
        
        pub struct Open;
        pub struct Locked;
        pub struct Closed;
        
        impl File<Open> {
            pub fn open(path: &Path) -> std::io::Result<Self> { /* ... */ }
            pub fn lock_exclusive(self) -> std::io::Result<File<Locked>> { /* flock */ }
            pub fn close(self) -> std::io::Result<File<Closed>> { /* ... */ }
        }
        
        impl File<Locked> {
            pub fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { /* ... */ }
            pub fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { /* ... */ }
            pub fn unlock(self) -> std::io::Result<File<Open>> { /* ... */ }
        }
        
        impl File<Closed> {
            // No methods. The type only exists to be dropped.
        }
        ```
        
        You cannot `read()` an unlocked file. You cannot `close()` while holding a lock without unlocking first. You cannot use a closed file at all - it has no methods.
        
        ## Sealed Traits
        
        Sometimes you want a closed set of types implementing a trait, defined by the crate, not extensible by downstream. The sealed trait pattern:
        
        ```rust
        mod sealed {
            pub trait Sealed {}
        }
        
        pub trait Renderer: sealed::Sealed {
            fn render(&self, frame: &mut Frame);
        }
        
        pub struct OpenGl;
        pub struct Vulkan;
        pub struct Metal;
        
        impl sealed::Sealed for OpenGl {}
        impl sealed::Sealed for Vulkan {}
        impl sealed::Sealed for Metal {}
        
        impl Renderer for OpenGl { fn render(&self, f: &mut Frame) { /* ... */ } }
        impl Renderer for Vulkan { fn render(&self, f: &mut Frame) { /* ... */ } }
        impl Renderer for Metal  { fn render(&self, f: &mut Frame) { /* ... */ } }
        ```
        
        Downstream code cannot add new `impl Renderer for Whatever` because they cannot implement `sealed::Sealed` (its module is private). Useful when you want trait dispatch but maintain the invariant that you control all implementations.
        
        ## NonEmpty Collections
        
        ```rust
        #[derive(Debug)]
        pub struct NonEmptyVec<T> {
            head: T,
            tail: Vec<T>,
        }
        
        #[derive(Debug, thiserror::Error)]
        #[error("vector was empty")]
        pub struct Empty;
        
        #[expect(clippy::len_without_is_empty, reason = "never empty by construction")]
        impl<T> NonEmptyVec<T> {
            pub fn try_from_vec(v: Vec<T>) -> Result<Self, Empty> {
                let mut items = v.into_iter();
                let Some(head) = items.next() else { return Err(Empty) };
                Ok(Self { head, tail: items.collect() })
            }
        
            pub const fn first(&self) -> &T { &self.head }
            pub fn len(&self) -> usize { self.tail.len().saturating_add(1) }
        }
        ```
        
        Functions taking `NonEmptyVec<T>` cannot receive an empty vector. The `first()` method returns `&T`, not `Option<&T>`. The agent never has to write `match v.first() { Some(x) => ..., None => panic!(...) }` again.
        
        ## When NOT to Newtype
        
        - For one-off internal computations where the unit lives in a single function and never crosses a boundary.
        - When the wrapper does not change behavior or invariants vs. the underlying type (e.g., a `struct Count(u32)` that is only ever used in one struct).
        - When `From`/`Into` conversions would be ergonomic but would defeat the purpose (if you find yourself wanting `impl From<UserId> for Uuid`, you do not want a newtype - you want a type alias).
        
        A domain newtype never implements `Deref` to its inner type: that re-exposes every method the wrapper exists to fence off, and method resolution silently picks the inner one. `Deref` is for pointer-like wrappers (`Box`, guards, smart handles); a newtype exposes the operations it means to allow.
        
        The cost of a newtype is one tuple struct + the impls you need. The break-even is around three uses across different functions, or any use that crosses an API boundary.
        
        ## When NOT to Use Type-State
        
        - When the state space is small and transitions are simple (`Option<T>` and `Result<T, E>` are state machines already).
        - When the type-state would force runtime branching upward (e.g., reading "should this run as Open or Locked?" from config means you store `Box<dyn FileLike>` anyway).
        - When the API is consumed by code that does not know the state at compile time (heterogeneous collections, dynamic dispatch boundaries).
        
        In those cases, regular enum-tagged states are correct. The line is: **can the call site know the state statically?** If yes, type-state. If no, enum-with-tag.
        
      • unsafe-discipline.md 12.9 KB
        # Unsafe Discipline
        
        The reason Chris Allen's "implementing a persistent memory arena in Rust was not hard" works: the unsafe surface area is microscopic, it lives behind one newtype with one constructor, and every block has a SAFETY comment that names a specific invariant. Coding agents follow the pattern mechanically once the shape is established.
        
        ## The Three Required Components
        
        Every `unsafe` block needs all three. No exceptions.
        
        1. **Safe wrapper.** No `unsafe fn` or raw pointer types in the crate's public API. If a caller needs to construct an instance, the constructor either does the work safely or is `unsafe` with a documented contract.
        2. **SAFETY comment.** A `// SAFETY:` line within 5 lines above the `unsafe { ... }` block, stating which invariant is upheld and where it comes from. Generic phrases ("this is safe because we checked") fail review.
        3. **miri proof.** A test that exercises the unsafe path under `cargo +nightly miri nextest run`. If the path cannot be exercised under miri (FFI, syscalls), provide an alternate proof and gate behind a feature flag ending in `-skip-miri`.
        
        ## The Wrapper Pattern (`NonNull<T>` style)
        
        Reference the screenshot Chris Allen quoted - `std::ptr::NonNull<T>`. Mirror this shape for every raw pointer, raw slice, raw transmute, or uninit memory operation in your own code.
        
        ```rust
        use core::marker::PhantomData;
        use core::ptr::NonNull;
        
        /// A non-null, properly aligned, initialized pointer that does not alias.
        ///
        /// All invariants are upheld by [`Self::new`] (checked) or [`Self::new_unchecked`]
        /// (delegated to the caller's contract). Once you hold an `InitPtr<T>`, every
        /// public method on it is safe to call.
        #[derive(Debug)]
        #[repr(transparent)]
        pub struct InitPtr<T> {
            inner: NonNull<T>,
            _marker: PhantomData<T>,
        }
        
        // Send/Sync are NOT automatic for raw-pointer-bearing types. Decide deliberately.
        // SAFETY: `InitPtr<T>` owns no concurrency state of its own; whether it is
        // Send/Sync depends on `T`. The bounds below mirror `Box<T>`.
        unsafe impl<T: Send> Send for InitPtr<T> {}
        unsafe impl<T: Sync> Sync for InitPtr<T> {}
        
        impl<T> InitPtr<T> {
            /// Wrap a raw pointer after checking alignment and non-null. The
            /// initialization invariant is not statically checkable here; callers must
            /// only feed pointers to memory that was written before this call.
            pub fn new(ptr: *mut T) -> Option<Self> {
                if !ptr.is_aligned() {
                    return None;
                }
                // SAFETY: alignment checked above; `NonNull::new` filters null. The
                // caller is documented to provide an initialized location.
                NonNull::new(ptr).map(|inner| Self { inner, _marker: PhantomData })
            }
        
            /// Wrap a raw pointer the caller asserts is valid.
            ///
            /// # Safety
            ///
            /// - `ptr` is non-null.
            /// - `ptr` is aligned to `align_of::<T>()`.
            /// - `*ptr` is initialized at the time of this call.
            /// - For the lifetime of the returned value, no other `&T` or `&mut T`
            ///   aliases `*ptr`.
            pub unsafe fn new_unchecked(ptr: *mut T) -> Self {
                // SAFETY: caller upholds non-null per the function contract.
                Self { inner: unsafe { NonNull::new_unchecked(ptr) }, _marker: PhantomData }
            }
        
            pub fn as_ref(&self) -> &T {
                // SAFETY: the constructor's invariants guarantee `inner` points at an
                // initialized, aligned, non-aliased `T`. Reborrowing through `&self`
                // ties the resulting lifetime to `self`, enforcing the rest via
                // standard borrow rules.
                unsafe { self.inner.as_ref() }
            }
        
            pub fn as_mut(&mut self) -> &mut T {
                // SAFETY: `&mut self` proves no other reference can alias `inner` for
                // the lifetime of the returned `&mut T`; remaining invariants come
                // from construction.
                unsafe { self.inner.as_mut() }
            }
        }
        ```
        
        The list of features this single shape gives you:
        
        - The agent cannot construct `InitPtr<T>` without going through a checked path or accepting the `unsafe` obligation explicitly.
        - The agent cannot leak the raw pointer; `as_ref` / `as_mut` return safe references with proper lifetimes.
        - The agent cannot accidentally Send/Sync where it shouldn't - the `unsafe impl` is explicit per-bound.
        - `#[repr(transparent)]` means the type is layout-compatible with `*mut T` for FFI, without exposing the raw pointer.
        
        ## SAFETY Comment Grammar
        
        Every comment maps one-to-one to an invariant. Format:
        
        ```rust
        // SAFETY: <which invariant is upheld>: <how it is established here>.
        ```
        
        Anti-examples (do not pass review):
        
        ```rust
        // SAFETY: this is fine
        // SAFETY: we know what we're doing
        // SAFETY: the caller will not pass null
        // SAFETY: tested
        ```
        
        Good examples:
        
        ```rust
        // SAFETY: `len <= self.capacity` was checked at line 87 and `self.ptr` was
        // allocated by the same allocator we are reading through.
        
        // SAFETY: `read_volatile` requires alignment and non-null; both hold because
        // `self.inner` is an `InitPtr<T>` whose constructor enforced them.
        
        // SAFETY: We hold `&mut self`, so no concurrent reader exists. The slice
        // reference is dropped before the next `&self` borrow because we shrink the
        // returned scope manually.
        ```
        
        ## Persistent Memory Arena Pattern (the Chris Allen example)
        
        A persistent memory arena (PMA) is an `mmap`-backed bump allocator that survives process restarts. It is the classic "lots of unsafe under one safe surface" project.
        
        Shape:
        
        ```rust
        pub struct Arena {
            map: Mmap,          // wraps `mmap(2)` - safe wrapper from `memmap2` crate
            head: AtomicUsize,  // current bump offset, atomic for multi-writer if needed
        }
        
        impl Arena {
            pub fn open(path: &Path, capacity: usize) -> std::io::Result<Self> { /* mmap, init header */ }
        
            pub fn alloc<T>(&self, value: T) -> Result<Handle<T>, ArenaFull> {
                let layout = Layout::new::<T>();
                let aligned = align_up(self.head.load(Acquire), layout.align());
                let next = aligned.checked_add(layout.size()).ok_or(ArenaFull)?;
                if next > self.map.len() { return Err(ArenaFull); }
                // CAS the head forward; retry on contention.
                // ... omitted for brevity ...
        
                // SAFETY: `aligned + layout.size() <= self.map.len()` was just proven.
                // The mmap region is exclusively owned by this arena while we hold the
                // bump. `aligned` is aligned to `layout.align()` by `align_up`. No
                // other writer can have observed this offset because the CAS above
                // returned `Ok`.
                let ptr = unsafe { self.map.as_mut_ptr().add(aligned) }.cast::<T>();
                // SAFETY: `ptr` is non-null (mmap base + offset), aligned (above),
                // exclusively owned (CAS), and we are about to initialize it.
                unsafe { ptr::write(ptr, value) };
        
                // SAFETY: same invariants; we wrap the now-initialized pointer in the
                // safe handle which encapsulates further accesses.
                Ok(unsafe { Handle::new_unchecked(ptr, self) })
            }
        }
        
        pub struct Handle<'a, T> {
            inner: InitPtr<T>,
            _arena: PhantomData<&'a Arena>,
        }
        ```
        
        Three `unsafe` blocks, three SAFETY comments, one safe handle type emerging on the other side. The agent now uses `Handle<T>` everywhere - never `*mut T`.
        
        ## Miri Invocation
        
        ```bash
        # install once
        rustup install nightly
        rustup component add miri rust-src --toolchain nightly
        
        # run on every change that touches unsafe
        MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check" \
          cargo +nightly miri nextest run --all-features
        ```
        
        What miri catches that the borrow checker cannot:
        
        - Use-after-free
        - Double-free
        - Reads of uninitialized memory
        - Pointer-from-integer reconstruction that violates strict provenance
        - Alignment lies (transmuting unaligned data)
        - Stacked borrows / Tree borrows aliasing violations
        - Data races (single-threaded model, but catches concurrent access through `UnsafeCell` misuse)
        - Atomic ordering bugs in some patterns
        - Memory leaks (reported by default at exit; `-Zmiri-ignore-leaks` turns it off)
        
        ## FFI Declarations (Rust 2024)
        
        The 2024 edition makes the unsafety of FFI items visible at the declaration:
        
        ```rust
        use core::ffi::{c_char, c_int};
        
        // SAFETY: these signatures match <string.h>/<stdlib.h> for every target we build.
        unsafe extern "C" {
            /// No preconditions: callable from safe code.
            pub safe fn abs(x: c_int) -> c_int;
            /// Caller must pass a valid NUL-terminated string.
            pub unsafe fn strlen(s: *const c_char) -> usize;
        }
        
        // Exported symbol: the attribute itself is unsafe because a name clash is UB.
        #[unsafe(no_mangle)]
        pub extern "C" fn mylib_version() -> u32 { 3 }
        ```
        
        - A bare `extern "C" { ... }` block is a hard error in the 2024 edition; write `unsafe extern` and mark each item `safe fn` (no caller obligations) or `unsafe fn`. Marking an item `safe` is a promise you make for every caller; when in doubt, it is `unsafe`.
        - `#[no_mangle]`, `#[export_name]`, and `#[link_section]` are written `#[unsafe(...)]`.
        - A panic that unwinds out of an `extern "C"` function aborts the process (1.81+). Catch it with `std::panic::catch_unwind` at the boundary and return an error code; use `extern "C-unwind"` only when the foreign side is built to unwind.
        
        ## Uninitialized Memory
        
        `MaybeUninit<T>` is the only way to hold memory that is not yet a valid `T`. `mem::uninitialized()` is always wrong, and `mem::zeroed()` is UB for any type whose all-zero bit pattern is invalid (references, `NonZero*`, `bool`-bearing enums, most `repr(Rust)` structs). An uninitialized array is `[const { MaybeUninit::<T>::uninit() }; N]`; write every element through `MaybeUninit::write`, then convert once, with a SAFETY comment stating that all `N` elements were written.
        
        ## When Miri Cannot Run
        
        Certain paths are off-limits for miri: most syscalls beyond a curated allowlist, real network I/O, `std::process` calls, OS-specific FFI, hardware-dependent intrinsics on non-x86. Strategy:
        
        1. **Isolate.** Put the un-mirifiable code in its own module behind `#[cfg(feature = "ffi-real")]` or similar.
        2. **Mock at the boundary.** For everything below the FFI boundary, write a safe Rust fake (a `Vec<u8>`-backed "disk", a fake clock, an in-memory socket pair). Expose it as a trait the production code consumes.
        3. **Test the fake under miri.** The fake implementation exercises the same logic minus the syscall. If the logic is unsafe (raw pointer manipulation in the fake "disk" buffer), miri catches the bug.
        4. **Test the real path under regular `cargo test`.** With `cargo nextest run --features ffi-real`. No miri, but the surface area is now just the syscall boundary.
        5. **Document.** A `# Safety` section in the rustdoc names the obligations the FFI puts on us, and a `# Testing` section explains the mock-vs-real split.
        
        ## Loom for Concurrency
        
        When `unsafe` participates in a concurrent algorithm (lock-free queue, hazard pointers, custom Arc), miri's single-thread model is insufficient. Use `loom`:
        
        ```rust
        #[cfg(loom)]
        use loom::sync::atomic::{AtomicUsize, Ordering};
        #[cfg(not(loom))]
        use std::sync::atomic::{AtomicUsize, Ordering};
        
        #[cfg(test)]
        mod tests {
            use super::*;
        
            #[test]
            #[cfg(loom)]
            fn concurrent_push_pop() {
                loom::model(|| {
                    let queue = std::sync::Arc::new(MyQueue::new());
                    let q1 = std::sync::Arc::clone(&queue);
                    let q2 = std::sync::Arc::clone(&queue);
                    let h1 = loom::thread::spawn(move || q1.push(1));
                    let h2 = loom::thread::spawn(move || q2.pop());
                    h1.join().unwrap();
                    h2.join().unwrap();
                });
            }
        }
        ```
        
        Run: `RUSTFLAGS="--cfg loom" cargo test --release`. Loom systematically explores the interleavings its bounded model allows for the test scope; combined with miri on the single-thread paths, that is strong machine-checked evidence, not a proof over every execution.
        
        ## The Forbidden List
        
        Reject in code review, automatic CI fail:
        
        - `unsafe { ... }` with no SAFETY comment within 5 lines above.
        - `unsafe { unsafe_op_a(); unsafe_op_b(); }` (multiple unsafe ops in one block - split them, one SAFETY each). Clippy: `multiple_unsafe_ops_per_block`.
        - `unsafe fn` exposed publicly without a documented `# Safety` section in rustdoc.
        - `std::mem::transmute` for anything but lifetime extension on the same layout (and that should usually be `core::mem::transmute_copy` or `bytemuck::cast` if the relayout is well-defined).
        - `std::ptr::read_unaligned` / `write_unaligned` without a comment explaining why aligned access is impossible.
        - `from_raw_parts` / `from_raw_parts_mut` without proving the source pointer's provenance covers the entire slice.
        - `Arc::get_mut_unchecked`, `Box::leak` to bypass ownership, `MaybeUninit::assume_init` on partially-initialized data, `mem::uninitialized`, `mem::zeroed` for a type whose all-zero pattern is not valid.
        - `unsafe impl Send`, `unsafe impl Sync` on types containing raw pointers, without a comment naming exactly which interior-mutability rule is upheld.
        - Any `unsafe` block whose justification depends on "in practice this never happens".
        
        ## The One-Line Summary
        
        > Wrap once. Prove once. Test under miri. Never let `unsafe` escape.
        
      • zero-cost-safety.md 16.9 KB
        # Zero-Cost Safety — Zig Ergonomics in Rust
        
        Rust already owns memory safety. This reference adds the patterns that give you Zig's *ergonomic* safety — explicit allocation control, compile-time computation, zero-hidden-cost APIs, bit-level layout, and deterministic cleanup — without leaving the Rust toolchain.
        
        **When to load this file:** arena, allocator, bumpalo, const fn, const generics, comptime, zero-alloc, no-alloc, slice-based API, `#[repr]`, packed struct, bitfield, scopeguard, errdefer, RAII cleanup, Zig-like patterns.
        
        ---
        
        ## 1. Explicit Allocators — Arena Pattern
        
        Zig passes `allocator: Allocator` to every function. Rust's stable equivalent: arena crates that make allocation scope visible and bulk-freeable.
        
        ### bumpalo — The Default Arena
        
        ```rust
        use bumpalo::Bump;
        
        fn parse_tokens<'a>(arena: &'a Bump, input: &[u8]) -> Vec<&'a str> {
            // All allocations go into `arena`. Caller controls lifetime.
            // When `arena` drops, everything frees in one shot.
            let token = arena.alloc_str("hello");
            let slice = arena.alloc_slice_copy(&[1u8, 2, 3]);
            vec![token] // Vec itself is on heap; contents point into arena
        }
        
        // Usage: caller owns the arena, decides when memory dies.
        let arena = Bump::new();
        let tokens = parse_tokens(&arena, b"...");
        drop(arena); // all arena memory freed, zero individual deallocations
        ```
        
        **When to use:** parsers, compilers, game frame allocators, request-scoped web handlers, any hot loop where individual `Box`/`Vec` alloc+free overhead matters.
        
        ### typed-arena — Homogeneous Arena
        
        ```rust
        use typed_arena::Arena;
        
        struct AstNode { kind: u8, children: Vec<&'static AstNode> } // simplified
        
        let node_arena: Arena<AstNode> = Arena::new();
        let root = node_arena.alloc(AstNode { kind: 0, children: vec![] });
        // All nodes share arena lifetime. No individual free.
        ```
        
        **When to use:** tree/graph structures where all nodes have the same type and same lifetime.
        
        ### allocator_api (nightly) — Full Zig Parity
        
        ```rust
        #![feature(allocator_api)]
        use std::alloc::Global;
        
        // Vec parameterized by allocator — exactly like Zig.
        let v: Vec<u8, &Bump> = Vec::new_in(&arena);
        
        // Custom allocator for tracking, limiting, or redirecting allocation
        struct CountingAlloc { inner: Global, count: AtomicUsize }
        unsafe impl Allocator for CountingAlloc { /* ... */ }
        ```
        
        **When to use:** when you need allocator-generic data structures on nightly. For stable code, prefer `bumpalo` directly.
        
        ### Decision Tree
        
        ```
        Need arena allocation?
        ├── All items same type, same lifetime → typed-arena
        ├── Mixed types, same lifetime → bumpalo
        ├── Need allocator-generic containers → allocator_api (nightly)
        └── Just need fewer allocations → SmallVec / ArrayVec / tinyvec (stack-first)
        ```
        
        ### Cargo.toml
        
        ```toml
        bumpalo = { version = "3", features = ["collections"] }
        typed-arena = "2"
        smallvec = { version = "1", features = ["union", "const_generics"] }
        tinyvec = { version = "1", features = ["alloc"] }
        ```
        
        ---
        
        ## 2. Compile-Time Computation — const fn, const generics, proc macros
        
        Zig's `comptime` runs arbitrary code at compile time. Rust splits this across three mechanisms.
        
        ### const fn — Compile-Time Pure Functions
        
        ```rust
        const fn fibonacci(n: usize) -> usize {
            match n {
                0 => 0,
                1 => 1,
                _ => fibonacci(n - 1) + fibonacci(n - 2),
            }
        }
        
        const FIB_20: usize = fibonacci(20); // computed at compile time: 6765
        
        // Use in array sizes
        #[expect(clippy::indexing_slicing, reason = "const evaluation: an out-of-bounds index is a compile error")]
        const LOOKUP: [u8; 256] = {
            let mut table = [0u8; 256];
            let mut i: u8 = 0;
            loop {
                table[i as usize] = i.wrapping_mul(7); // u8 -> usize widens: lossless
                if i == u8::MAX { break; }
                i += 1;
            }
            table
        };
        ```
        
        `const fn` bodies may use `if`, `match`, `loop`/`while`, and mutable locals; trait methods (`From`, `TryFrom`, `Iterator`) are not callable in `const` on stable. That makes a **lossless widening `as` inside `const`** the one accepted numeric cast: iterate in the narrow type and widen to index, as above, so no value can truncate. Use `const { }` blocks (Rust 1.79+) for inline compile-time assertions.
        
        ```rust
        fn process<const N: usize>(data: &[u8; N]) {
            const { assert!(N > 0, "N must be positive") }; // compile-time panic if N == 0
            // ...
        }
        ```
        
        ### const generics — Type-Level Values
        
        ```rust
        #[derive(Debug)]
        struct Buffer<const N: usize> {
            data: [u8; N],
            len: usize,
        }
        
        impl<const N: usize> Buffer<N> {
            const fn new() -> Self {
                Self { data: [0; N], len: 0 }
            }
        
            fn push(&mut self, byte: u8) -> Result<(), BufferFullError> {
                let slot = self.data.get_mut(self.len).ok_or(BufferFullError)?;
                *slot = byte;
                self.len = self.len.saturating_add(1); // < N after the get_mut above
                Ok(())
            }
        }
        
        impl<const N: usize> Default for Buffer<N> {
            fn default() -> Self { Self::new() }
        }
        
        // Compiler enforces: Buffer<16> and Buffer<32> are distinct types.
        let small: Buffer<16> = Buffer::new();
        let large: Buffer<1024> = Buffer::new();
        ```
        
        ### proc macros — Code Generation (Zig comptime type creation)
        
        When `const fn` is not enough (generating struct fields, impl blocks, or derive logic), proc macros fill the gap. Crate layout, `syn`/`quote`, identifier construction, and spanned errors → [macros.md](macros.md).
        
        **Decision tree:**
        
        ```
        Need compile-time value computation? → const fn
        Need type parameterized by value?    → const generics
        Need to generate new types/impls?    → proc macro (derive or attribute)
        Need compile-time string processing? → proc macro
        Need typenum-level arithmetic?       → typenum / generic-array (rare)
        ```
        
        ---
        
        ## 3. Zero-Allocation API Design — No Hidden Costs
        
        Zig's philosophy: no operator overloading, no hidden allocation, every cost visible. Rust achieves this with discipline.
        
        ### Slice-Based APIs — Caller Owns Memory
        
        ```rust
        // BAD: hidden allocation in return type
        fn process(input: &str) -> String {
            input.to_uppercase() // allocates
        }
        
        // GOOD: caller provides output buffer, zero allocation
        fn process(input: &[u8], output: &mut [u8]) -> usize {
            for (dst, src) in output.iter_mut().zip(input) {
                *dst = src.to_ascii_uppercase();
            }
            input.len().min(output.len()) // bytes written; zip stopped at the shorter slice
        }
        
        // GOOD: return borrowed data when possible
        fn find_token(input: &str) -> Option<&str> {
            input.split_whitespace().next() // no allocation — borrows from input
        }
        ```
        
        ### try_* APIs — Fallible Allocation
        
        ```rust
        // Allocation can fail explicitly (like Zig's allocator returning error)
        let mut v = Vec::new();
        v.try_reserve(1_000_000)?; // returns Result, not panic
        
        // For Box:
        let b = Box::try_new(42)?; // nightly, or use allocator_api
        ```
        
        ### SmallVec / ArrayVec — Stack-First Collections
        
        ```rust
        use smallvec::SmallVec;
        use arrayvec::ArrayVec;
        
        // SmallVec: stack for small counts, heap spillover for large
        let mut tags: SmallVec<[u8; 8]> = SmallVec::new();
        tags.push(1); // on stack if <= 8 elements
        
        // ArrayVec: purely stack, fixed capacity, no heap ever
        let mut buf: ArrayVec<u8, 64> = ArrayVec::new();
        buf.try_push(42).map_err(|_| BufferFullError)?; // typed error instead of panic
        ```
        
        ### Move Out of `&mut` Without Cloning
        
        ```rust
        // Take the value, leave Default behind: no clone, no Option dance.
        let pending = std::mem::take(&mut self.pending);      // Vec/String/HashMap
        let old = std::mem::replace(&mut self.state, State::Idle);
        let conn = self.conn.take();                          // Option<T> field
        ```
        
        A `.clone()` whose only purpose is to satisfy the borrow checker behind `&mut self` is almost always one of these three.
        
        ### Reuse and Pre-Size
        
        - Reserve from a known size: `Vec::with_capacity(n)` / `String::with_capacity(n)`, or `reserve(n)` before a batch `extend`.
        - In a loop, hoist a scratch buffer and `clear()` it each iteration instead of allocating a fresh one; `clear` keeps the capacity. Drop or `shrink_to` it when one outlier would pin a huge allocation.
        - Format into an existing buffer with `write!(buf, ...)` (`use std::fmt::Write as _`) instead of building intermediate `format!` strings, and pass `&str` literals where no formatting happens.
        
        Apply these on paths a profile or benchmark shows to be hot; elsewhere the clearer allocation wins.
        
        ### Cow — Defer Allocation Until Mutation
        
        ```rust
        use std::borrow::Cow;
        
        fn normalize(input: &str) -> Cow<'_, str> {
            if input.contains('\t') {
                Cow::Owned(input.replace('\t', "    ")) // allocates only when needed
            } else {
                Cow::Borrowed(input) // zero-cost pass-through
            }
        }
        ```
        
        ### The #![no_std] Discipline
        
        For maximum allocation control, go `#![no_std]`:
        
        ```rust
        #![no_std]
        extern crate alloc; // opt-in to heap when needed
        
        use alloc::vec::Vec;      // explicit: I chose to allocate
        use alloc::string::String; // explicit: I chose to allocate
        ```
        
        Even in `std` code, the *mindset* applies: prefer `&[T]` over `Vec<T>` in function signatures, `&str` over `String`, `&Path` over `PathBuf`.
        
        ### Clippy Lints for Hidden Allocations
        
        The groups in [cargo-strict.md](cargo-strict.md) already enable the allocation lints: `unnecessary_to_owned` and `vec_init_then_push` (in `all`, denied), `redundant_clone` (nursery), `large_stack_arrays` (pedantic). Do not re-list them in `[lints.clippy]`: a per-lint `"warn"` entry overrides the group and silently downgrades a deny.
        
        ---
        
        ## 4. Bit-Level Layout — repr, Packed Structs, Bitfields
        
        Zig: `packed struct` with bit-level field control. Rust matches with `#[repr]` attributes and bitfield crates.
        
        ### #[repr(C)] — Guaranteed C-Compatible Layout
        
        ```rust
        #[repr(C)]
        struct Header {
            magic: [u8; 4],
            version: u16,
            flags: u16,
            length: u32,
        }
        // Layout is C ABI: fields in declaration order, C padding rules.
        // Safe to transmute from/to byte arrays via zerocopy.
        ```
        
        ### #[repr(C, packed)] — No Padding
        
        ```rust
        #[repr(C, packed)]
        struct WireHeader {
            tag: u8,
            length: u16, // NOT aligned to 2-byte boundary
            checksum: u32,
        }
        // Total size: exactly 7 bytes. No padding.
        // WARNING: taking &self.length is UB if unaligned. Use read_unaligned or zerocopy.
        ```
        
        **Safe access pattern:**
        
        ```rust
        use std::ptr;
        
        impl WireHeader {
            fn length(&self) -> u16 {
                // SAFETY: packed field may be unaligned; ptr::read_unaligned handles this.
                unsafe { ptr::read_unaligned(ptr::addr_of!(self.length)) }
            }
        }
        
        // Better: use zerocopy to avoid manual unsafe entirely
        use zerocopy::{FromBytes, IntoBytes, KnownLayout, Immutable};
        
        #[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
        #[repr(C, packed)]
        struct WireHeader {
            tag: u8,
            length: [u8; 2], // manual byte array avoids alignment issues
            checksum: [u8; 4],
        }
        
        impl WireHeader {
            fn length(&self) -> u16 { u16::from_le_bytes(self.length) }
            fn checksum(&self) -> u32 { u32::from_le_bytes(self.checksum) }
        }
        ```
        
        ### bitfield — Bit-Level Flag Packing
        
        ```rust
        use bitfield::bitfield;
        
        bitfield! {
            pub struct Permissions(u8);
            impl Debug;
            pub bool, readable,  set_readable:  0;
            pub bool, writable,  set_writable:  1;
            pub bool, executable, set_executable: 2;
            pub u8,   level,     set_level:     5, 3; // bits 3-5
        }
        
        let mut p = Permissions(0);
        p.set_readable(true);
        p.set_level(5);
        assert!(p.readable());
        assert_eq!(p.level(), 5);
        ```
        
        ### modular-bitfield — Richer Bitfield API
        
        ```rust
        use modular_bitfield::prelude::*;
        
        #[bitfield(bits = 16)]
        #[derive(Debug)]
        pub struct StatusWord {
            ready: bool,           // 1 bit
            error_code: B4,        // 4 bits
            #[skip] __: B3,        // 3 bits padding
            priority: B8,          // 8 bits
        }
        ```
        
        ### zerocopy — Safe Zero-Copy Parsing
        
        ```rust
        use zerocopy::{FromBytes, IntoBytes, KnownLayout, Immutable, Ref};
        
        #[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
        #[repr(C)]
        struct Packet {
            header: [u8; 4],
            payload_len: u32,
        }
        
        fn parse(bytes: &[u8]) -> Option<&Packet> {
            Ref::<_, Packet>::from_prefix(bytes).map(|(pkt, _rest)| pkt.into_ref()).ok()
        }
        // Zero-copy, zero-allocation, fully safe. No transmute, no pointer cast.
        ```
        
        ### Cargo.toml
        
        ```toml
        zerocopy = { version = "0.8", features = ["derive"] }
        bitfield = "0.17"
        modular-bitfield = "0.11"
        bytemuck = { version = "1", features = ["derive"] }  # alternative to zerocopy
        ```
        
        ---
        
        ## 5. Scope Guards — errdefer / Deterministic Cleanup
        
        Zig's `errdefer` runs cleanup only on error paths. Rust's `Drop` always runs, but `scopeguard` gives fine-grained control.
        
        ### scopeguard — The errdefer Equivalent
        
        ```rust
        use scopeguard::{guard, ScopeGuard};
        use std::fs;
        
        fn create_and_process(path: &str) -> std::io::Result<()> {
            let file = fs::File::create(path)?;
            // If anything below fails, clean up the file.
            // This is exactly Zig's errdefer.
            let cleanup = guard((), |()| {
                if let Err(error) = fs::remove_file(path) {
                    tracing::warn!(%error, path, "cleanup after failed write");
                }
            });
        
            write_data(&file)?;
            validate_data(path)?;
        
            // Success: defuse the guard so it does NOT run cleanup.
            ScopeGuard::into_inner(cleanup);
            Ok(())
        }
        ```
        
        ### defer! — Always-Run Cleanup (like Zig's defer)
        
        ```rust
        use scopeguard::defer;
        
        fn with_temp_dir() -> anyhow::Result<()> {
            let dir = tempfile::tempdir()?;
            defer! {
                // Runs when scope exits, success or failure.
                tracing::debug!(path = %dir.path().display(), "leaving temp dir scope");
                // dir's Drop also cleans up, but this shows the pattern.
            }
        
            do_work(dir.path())?;
            Ok(())
        }
        ```
        
        ### Drop as RAII Cleanup
        
        ```rust
        #[derive(Debug)]
        struct TempFile { path: std::path::PathBuf }
        
        impl TempFile {
            fn new(path: impl Into<std::path::PathBuf>) -> std::io::Result<Self> {
                let path = path.into();
                std::fs::File::create(&path)?;
                Ok(Self { path })
            }
        }
        
        impl Drop for TempFile {
            fn drop(&mut self) {
                // Drop cannot return an error; report it instead of discarding it.
                if let Err(error) = std::fs::remove_file(&self.path) {
                    tracing::warn!(%error, path = %self.path.display(), "temp file not removed");
                }
            }
        }
        
        // Usage: file is auto-cleaned when `tmp` goes out of scope.
        let tmp = TempFile::new("/tmp/scratch.dat")?;
        ```
        
        ### Drop Order Is Part of the Contract
        
        Locals drop in reverse declaration order; struct fields drop in declaration order (first field first). When one resource must outlive another (a transaction before its connection, a worker handle before the channel it drains), encode it: declare the dependent field first, or call `drop(x)` at the exact point the dependency ends. Never rely on an order the code does not state.
        
        ### The errdefer Pattern — Defuse on Success
        
        The key insight from Zig's `errdefer`: you want cleanup on error but NOT on success. In Rust:
        
        ```rust
        use scopeguard::{guard, ScopeGuard};
        
        fn deploy(artifact: &Path) -> Result<(), DeployError> {
            let backup = backup_current()?;
        
            // errdefer: restore backup if anything fails
            let rollback = guard(backup, |b| {
                if let Err(error) = restore_from_backup(&b) {
                    tracing::error!(%error, "rollback failed; manual restore needed");
                }
            });
        
            upload(artifact)?;
            health_check()?;
        
            // Success path: defuse the rollback guard
            ScopeGuard::into_inner(rollback);
            Ok(())
        }
        ```
        
        ### Cargo.toml
        
        ```toml
        scopeguard = "1"
        tempfile = "3"  # idiomatic RAII temp files/dirs
        ```
        
        ---
        
        ## Summary: Zig Advantage → Rust Pattern
        
        | Zig Feature | Rust Equivalent | Difficulty | Reference |
        |---|---|---|---|
        | Explicit allocator passing | `bumpalo` / `typed-arena` / `allocator_api` | Easy | §1 |
        | `comptime` value computation | `const fn` + `const { }` blocks | Easy | §2 |
        | `comptime` type generation | proc macros (derive / attribute) | Medium | §2 |
        | No hidden allocations | `#![no_std]` / slice-based APIs / `Cow` | Style choice | §3 |
        | `packed struct` / bitfields | `#[repr(C, packed)]` / `bitfield` / `zerocopy` | Easy | §4 |
        | `errdefer` | `scopeguard::guard` + defuse on success | Easy | §5 |
        | `defer` | `scopeguard::defer!` / `Drop` | Easy | §5 |
        
        All achievable within Rust's single toolchain. You get Zig's explicitness **plus** the borrow checker, lifetime analysis, trait bounds, and `miri`. The combination is strictly more powerful than either alone.
        
        ## When NOT to Use These Patterns
        
        - **Arena allocation** overkill for simple CLI tools that allocate once and exit.
        - **Zero-alloc APIs** hurt readability when the function naturally produces owned data. Don't force `&mut [u8]` output buffers on a function that logically returns `String`.
        - **`#[repr(packed)]`** only for wire formats and FFI. Never for regular domain types.
        - **Scope guards** unnecessary when `Drop` on the value itself handles cleanup (e.g., `tempfile::NamedTempFile` already does this).
        - **`const fn`** everything? No — only when a `const` context needs the function or it is trivially const-eligible. Don't contort logic just to be const.
        
        The goal is **visible costs and explicit control**, not asceticism. Use `String` and `Vec` freely when they're the right tool. Reach for these patterns when allocation behavior matters for correctness or performance.
        
    • rust-ub
      • miri-sanitizers-loom.md 11.5 KB
        # Miri, Sanitizers, Loom, and Fuzzing — The UB Detection Arsenal
        
        Miri is the **primary weapon**. Everything else is supplementary for the gaps Miri cannot reach.
        
        ---
        
        ## Miri — The First and Last Line of Defense
        
        ### What Miri Is
        
        Miri is an interpreter for Rust's MIR (Mid-level IR). It executes your test suite inside a virtual machine that tracks every byte of memory for validity, provenance, alignment, initialization, and aliasing. It is **deterministic** — same inputs, same result — and it can find UB that no amount of testing on real hardware will ever trigger.
        
        ### Why Miri Is Non-Negotiable
        
        - Detects 12 of 14 UB categories (see `ub-taxonomy.md`).
        - Catches aliasing violations that compile and run correctly on every platform today but are UB that future compiler optimizations will exploit.
        - Catches data races under a configurable scheduling model.
        - Catches provenance violations that are impossible to observe on real hardware.
        - **Zero false positives** — if Miri says it is UB, it is UB. Period.
        
        ### Installation
        
        ```bash
        rustup install nightly
        rustup component add miri rust-src --toolchain nightly
        ```
        
        Verify:
        ```bash
        cargo +nightly miri --version
        ```
        
        ### Running Miri
        
        **Default run (Stacked Borrows, standard checks):**
        ```bash
        cargo +nightly miri test
        ```
        
        **With nextest (recommended for projects already using nextest):**
        ```bash
        cargo +nightly miri nextest run
        ```
        
        **Specific test:**
        ```bash
        cargo +nightly miri test -- test_name
        ```
        
        **Run a binary:**
        ```bash
        cargo +nightly miri run
        ```
        
        ### MIRIFLAGS — The Dial-Up Knobs
        
        These flags are set via the `MIRIFLAGS` environment variable. The agent should use ALL of the strictness flags during a UB audit.
        
        #### Aliasing Model
        
        ```bash
        # Default: Stacked Borrows (strict)
        cargo +nightly miri test
        
        # Tree Borrows (newer, more permissive — use as a second pass)
        MIRIFLAGS="-Zmiri-tree-borrows" cargo +nightly miri test
        ```
        
        **Protocol:** Run Stacked Borrows first. If it fails, fix it. Then run Tree Borrows as a separate pass. The models are not nested: Stacked Borrows is stricter about most reborrow patterns, but Tree Borrows rejects some code Stacked Borrows accepts. Ship only when both are clean.
        
        #### Strict Provenance
        
        ```bash
        MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test
        ```
        
        Catches `ptr as usize as *const T` roundtrips where provenance is lost. **Should be ON for every audit.**
        
        #### Symbolic Alignment Checks
        
        ```bash
        MIRIFLAGS="-Zmiri-symbolic-alignment-check" cargo +nightly miri test
        ```
        
        Catches alignment UB that happens to be aligned on your machine but is not guaranteed by the type system.
        
        #### Data Race Detection Tuning
        
        ```bash
        # Increase preemption rate to stress-test race conditions
        MIRIFLAGS="-Zmiri-preemption-rate=0.5" cargo +nightly miri test
        
        # Disable preemption (sequential scheduling — fewer races found but deterministic)
        MIRIFLAGS="-Zmiri-preemption-rate=0" cargo +nightly miri test
        ```
        
        #### The Full Paranoia Sweep (Use This for Audits)
        
        ```bash
        MIRIFLAGS="\
          -Zmiri-strict-provenance \
          -Zmiri-symbolic-alignment-check \
          -Zmiri-preemption-rate=0.1 \
          -Zmiri-backtrace=full \
          -Zmiri-disable-isolation" \
        cargo +nightly miri test
        ```
        
        Then a second pass with Tree Borrows:
        ```bash
        MIRIFLAGS="\
          -Zmiri-tree-borrows \
          -Zmiri-strict-provenance \
          -Zmiri-symbolic-alignment-check \
          -Zmiri-preemption-rate=0.1 \
          -Zmiri-backtrace=full \
          -Zmiri-disable-isolation" \
        cargo +nightly miri test
        ```
        
        #### Isolation and I/O
        
        Miri runs in isolation by default — no file I/O, no network, no system calls. If your tests need the filesystem:
        ```bash
        MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri test
        ```
        
        Use sparingly — isolation is a feature, not a limitation. Tests that need I/O should have a separate `#[cfg(not(miri))]` path.
        
        ### Miri Limitations
        
        | Cannot do | Workaround |
        |-----------|-----------|
        | Execute FFI / C code | ASAN, MSAN, Valgrind |
        | Run I/O-heavy tests (default) | `-Zmiri-disable-isolation` or `#[cfg(not(miri))]` |
        | Exhaustive interleaving exploration | loom |
        | Find performance bugs | criterion, flamegraph |
        | Run inline assembly | skip with `#[cfg(not(miri))]` |
        | Test OS-specific behavior | real hardware + sanitizers |
        
        ### Miri in CI
        
        ```yaml
        # GitHub Actions example
        miri:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
            - uses: dtolnay/rust-toolchain@nightly
              with:
                components: miri, rust-src
            - name: Miri test (Stacked Borrows + strict provenance)
              run: |
                MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-backtrace=full" \
                cargo +nightly miri test
            - name: Miri test (Tree Borrows)
              run: |
                MIRIFLAGS="-Zmiri-tree-borrows -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-backtrace=full" \
                cargo +nightly miri test
        ```
        
        ### Miri-Incompatible Test Gating
        
        ```rust
        #[test]
        #[cfg_attr(miri, ignore)]  // Miri cannot run this (FFI, I/O, inline asm)
        fn test_requires_real_hardware() {
            // ...
        }
        
        // Or conditionally compile the test body:
        #[test]
        fn test_with_miri_fallback() {
            #[cfg(miri)]
            {
                // Simplified version that avoids FFI
            }
            #[cfg(not(miri))]
            {
                // Full version with FFI
            }
        }
        ```
        
        ---
        
        ## Sanitizers — Where Miri Cannot Reach
        
        Sanitizers are compiler instrumentation passes. They run your actual binary on real hardware with extra checks injected. Use them for FFI, I/O-heavy code, and integration tests.
        
        ### AddressSanitizer (ASAN)
        
        Detects: use-after-free, buffer overflow, stack-use-after-return, double-free, memory leaks.
        
        ```bash
        RUSTFLAGS="-Zsanitizer=address" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
        ```
        
        On macOS:
        ```bash
        RUSTFLAGS="-Zsanitizer=address" cargo +nightly test -Zbuild-std --target aarch64-apple-darwin
        ```
        
        ### ThreadSanitizer (TSAN)
        
        Detects: data races on non-atomic accesses across threads.
        
        ```bash
        RUSTFLAGS="-Zsanitizer=thread" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
        ```
        
        **When to use over Miri:** Integration tests involving real threads + real I/O + FFI. Miri's data-race detector is superior for pure-Rust code.
        
        ### MemorySanitizer (MSAN)
        
        Detects: reads of uninitialized memory.
        
        ```bash
        RUSTFLAGS="-Zsanitizer=memory -Zsanitizer-memory-track-origins" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
        ```
        
        **When to use over Miri:** FFI code where C/C++ may return uninitialized memory into Rust.
        
        ### UndefinedBehaviorSanitizer (UBSAN)
        
        Detects: integer overflow, misaligned access, null dereference, and other C/C++-style UB at the LLVM level.
        
        ```bash
        RUSTFLAGS="-Zsanitizer=undefined" cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
        ```
        
        ### Sanitizer Limitations
        
        - Require nightly + `-Zbuild-std` (rebuilds the standard library with instrumentation).
        - MSAN requires ALL dependencies (including C libs) to be instrumented — practically hard.
        - Cannot catch aliasing violations (that is Miri's domain).
        - Significant runtime overhead (2-15x slower).
        - Linux has the best support; macOS works for ASAN; Windows support is minimal.
        
        ---
        
        ## Loom — Exhaustive Concurrency Testing
        
        Loom explores all possible thread interleavings of a bounded concurrent program. It is mandatory for lock-free and wait-free primitives.
        
        ### When to Use Loom
        
        - Any `unsafe` code involving atomics with ordering weaker than `SeqCst`.
        - Custom lock implementations.
        - Lock-free queues, stacks, or other concurrent data structures.
        - Any code where you chose `Relaxed`, `Acquire`, or `Release` ordering.
        
        ### When NOT to Use Loom
        
        - Code using only `Mutex`/`RwLock` from std or `parking_lot` — the locks are sound, your usage is the question, and Miri + TSAN cover that.
        - Async code (loom does not model async runtimes — use `tokio::test` + Miri instead).
        
        ### Setup
        
        ```toml
        [dev-dependencies]
        loom = "0.7"
        ```
        
        ### Loom Test Pattern
        
        ```rust
        #[cfg(loom)]
        mod loom_tests {
            use loom::sync::atomic::{AtomicUsize, Ordering};
            use loom::sync::Arc;
            use loom::thread;
        
            #[test]
            fn concurrent_increment_is_sound() {
                loom::model(|| {
                    let counter = Arc::new(AtomicUsize::new(0));
        
                    let threads: Vec<_> = (0..2).map(|_| {
                        let c = counter.clone();
                        thread::spawn(move || {
                            c.fetch_add(1, Ordering::SeqCst);
                        })
                    }).collect();
        
                    for t in threads {
                        t.join().unwrap();
                    }
        
                    assert_eq!(counter.load(Ordering::SeqCst), 2);
                });
            }
        }
        ```
        
        ### Conditional Compilation for Loom
        
        ```rust
        #[cfg(loom)]
        use loom::sync::atomic::{AtomicUsize, Ordering};
        #[cfg(not(loom))]
        use std::sync::atomic::{AtomicUsize, Ordering};
        ```
        
        ### Running Loom Tests
        
        ```bash
        # Loom tests only (use cfg flag)
        RUSTFLAGS="--cfg loom" cargo test --lib -- loom_tests
        
        # With release optimizations (loom is slow)
        RUSTFLAGS="--cfg loom" cargo test --lib --release -- loom_tests
        ```
        
        ### Loom + Miri Interaction
        
        Loom and Miri solve different problems:
        - **Miri** checks a single execution for UB (aliasing, validity, provenance).
        - **Loom** checks all interleavings for correctness (ordering, atomicity).
        
        Run BOTH on lock-free code:
        ```bash
        # Step 1: loom for interleaving correctness
        RUSTFLAGS="--cfg loom" cargo test --lib --release -- loom_tests
        
        # Step 2: Miri for UB in each path
        cargo +nightly miri test -- concurrent_tests
        ```
        
        ---
        
        ## Cargo-Fuzz — Property-Based UB Hunting
        
        Fuzzing generates random inputs to maximize code coverage and find crashes, panics, and UB.
        
        ### Setup
        
        ```bash
        cargo install cargo-fuzz
        cargo fuzz init
        ```
        
        ### Fuzz Target
        
        ```rust
        // fuzz/fuzz_targets/parse_input.rs
        #![no_main]
        use libfuzzer_sys::fuzz_target;
        
        fuzz_target!(|data: &[u8]| {
            // Your parsing/deserialization/processing code here.
            // If it panics or triggers UB, the fuzzer catches it.
            let _ = my_crate::parse(data);
        });
        ```
        
        ### Running
        
        ```bash
        # Run until interrupted
        cargo +nightly fuzz run parse_input
        
        # Run with ASAN (catches memory bugs in unsafe code)
        cargo +nightly fuzz run parse_input -- -rss_limit_mb=4096
        
        # Minimize a crashing input
        cargo +nightly fuzz tmin parse_input artifacts/parse_input/crash-xxxxx
        ```
        
        ### Fuzz + Miri Pipeline
        
        When the fuzzer finds a crashing input:
        1. Minimize it with `cargo fuzz tmin`.
        2. Add it as a regression test.
        3. Run the regression test under Miri to classify whether it is a panic (safe) or UB (must fix).
        
        ```bash
        # After adding the input as a test case:
        cargo +nightly miri test -- test_fuzz_regression_001
        ```
        
        ---
        
        ## Tool Selection Decision Tree
        
        ```
        Start
          │
          ├── Is it pure Rust (no FFI, no I/O)?
          │     YES → Miri (full paranoia flags)
          │     │     └── Also: loom (if atomics/lock-free)
          │     │     └── Also: proptest (if parsing/serialization)
          │     │     └── Also: cargo-fuzz (if untrusted input)
          │     │
          │     NO → Does it involve FFI?
          │           YES → ASAN + MSAN on integration tests
          │           │     └── Miri on the Rust-side handling
          │           │     └── cbindgen in CI for layout verification
          │           │
          │           NO → Is it I/O-heavy?
          │                 YES → TSAN for thread safety
          │                 │     └── Miri with -Zmiri-disable-isolation where possible
          │                 │
          │                 NO → Miri (full paranoia flags)
          │
          └── Always: Miri is the default. Other tools supplement.
        ```
        
        ## The One Rule
        
        > **When in doubt, run Miri.** If Miri cannot run it, write a version it can run, and test that under Miri. Then test the real version under sanitizers. Never ship `unsafe` code that has not passed Miri.
        
      • README.md 9.7 KB
        
        # Rust Undefined Behavior Exorcist
        
        You are a UB hunter. Your job is to find, classify, prove, and eliminate every instance of undefined behavior in Rust code. **Miri is your primary weapon** — everything else supplements where Miri cannot reach.
        
        ## Core Philosophy
        
        1. **Miri first, always.** Before reading a single line of `unsafe`, run Miri. Before proposing a fix, run Miri. After applying a fix, run Miri. Miri is the oracle.
        2. **Classify before fixing.** Every UB finding gets classified against the 14-category taxonomy (see [ub-taxonomy.md](ub-taxonomy.md)). This prevents misdiagnosis and ensures the fix targets the root cause, not a symptom.
        3. **Prove the fix.** A fix is not done until Miri passes with full paranoia flags. If Miri cannot run the test (FFI, I/O), the fix is not done until the appropriate sanitizer passes.
        4. **Bead handoff.** Each resolved UB instance is a "bead" — a discrete, documented, proven fix. Hand it off with: the UB category, the root cause, the fix, and the Miri proof.
        
        ## The UB Taxonomy
        
        14 categories. The full reference is in [ub-taxonomy.md](ub-taxonomy.md). Memorize the categories; classify every finding:
        
        | # | Category | Miri? |
        |---|----------|-------|
        | 1 | Aliasing violations (Stacked/Tree Borrows) | YES |
        | 2 | Data races | YES |
        | 3 | Use-after-free / dangling pointers | YES |
        | 4 | Uninitialized memory | YES |
        | 5 | Invalid values (type invariant violations) | YES |
        | 6 | Misaligned pointer access | YES |
        | 7 | Pin invariant violations | PARTIAL |
        | 8 | FFI boundary UB | LIMITED |
        | 9 | Incorrect Send/Sync implementations | YES (via race) |
        | 10 | Out-of-bounds memory access | YES |
        | 11 | Provenance violations | YES (strict mode) |
        | 12 | Double free / invalid free | YES |
        | 13 | Library / unsafe contract violations | PARTIAL |
        | 14 | Unwinding across extern "C" | PARTIAL |
        
        ## The Hunt Workflow
        
        ### Phase 1: Reconnaissance
        
        1. **Find all `unsafe` blocks and `unsafe impl`s:**
           ```bash
           rg 'unsafe\s*(fn|impl|{|\{)' --type rust -n
           ```
        
        2. **Find all `unsafe` trait implementations:**
           ```bash
           rg 'unsafe\s+impl\s+(Send|Sync)' --type rust -n
           ```
        
        3. **Find transmute / pointer casts / raw pointer derefs:**
           ```bash
           rg '(transmute|transmute_copy|from_raw|into_raw|as_ptr|as_mut_ptr|offset|add|sub|read|write|copy|ptr::null)' --type rust -n
           ```
        
        4. **Find FFI boundaries:**
           ```bash
           rg 'extern\s+"C"' --type rust -n
           ```
        
        5. **Count and catalog.** Create a hit list: file, line, `unsafe` category, initial risk assessment (high/medium/low based on the UB taxonomy).
        
        ### Phase 2: Miri Sweep (THE CRITICAL PHASE)
        
        Run Miri with escalating strictness. **Do not skip any level.**
        
        **Level 1 — Default (Stacked Borrows):**
        ```bash
        cargo +nightly miri test 2>&1
        ```
        
        **Level 2 — Strict Provenance + Symbolic Alignment:**
        ```bash
        MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-backtrace=full" \
          cargo +nightly miri test 2>&1
        ```
        
        **Level 3 — Full Paranoia (the audit standard):**
        ```bash
        MIRIFLAGS="\
          -Zmiri-strict-provenance \
          -Zmiri-symbolic-alignment-check \
          -Zmiri-preemption-rate=0.1 \
          -Zmiri-backtrace=full \
          -Zmiri-disable-isolation" \
          cargo +nightly miri test 2>&1
        ```
        
        **Level 4 — Tree Borrows (second model confirmation):**
        ```bash
        MIRIFLAGS="\
          -Zmiri-tree-borrows \
          -Zmiri-strict-provenance \
          -Zmiri-symbolic-alignment-check \
          -Zmiri-preemption-rate=0.1 \
          -Zmiri-backtrace=full \
          -Zmiri-disable-isolation" \
          cargo +nightly miri test 2>&1
        ```
        
        **Interpret results:**
        - Fails at Level 1 → Definite UB. Fix immediately.
        - Passes Level 1, fails Level 2 → Provenance or alignment UB. Fix.
        - Passes Levels 1-3, fails Level 4 → Tree Borrows found something Stacked Borrows missed (unusual). Investigate — may be a Tree Borrows false positive, but usually indicates fragile aliasing.
        - Passes all 4 → Miri-clean. Proceed to supplementary tools.
        
        ### Phase 3: Supplementary Scans
        
        For code Miri cannot fully cover:
        
        **Concurrent code with custom atomics:**
        ```bash
        RUSTFLAGS="--cfg loom" cargo test --lib --release -- loom_tests 2>&1
        ```
        
        **FFI-heavy code:**
        ```bash
        RUSTFLAGS="-Zsanitizer=address" cargo +nightly test -Zbuild-std --target $(rustc -vV | rg host | awk '{print $2}') 2>&1
        ```
        
        **Untrusted input parsing:**
        ```bash
        cargo +nightly fuzz run <target> -- -max_total_time=300 2>&1
        ```
        
        ### Phase 4: Fix-and-Prove Loop
        
        For each UB finding:
        
        1. **Classify** against the 14-category taxonomy.
        2. **Write the SAFETY comment** explaining what is wrong and what the fix must achieve.
        3. **Apply the minimal fix.** Do not refactor — fix the UB and nothing else.
        4. **Run Miri (Level 3 minimum) on the specific test that triggered the UB.**
        5. **Run Miri (Level 3) on the full test suite** to check for regressions.
        6. **Document the bead:**
           ```
           BEAD: [Category #] [Short description]
           FILE: [path:line]
           ROOT CAUSE: [one sentence]
           FIX: [one sentence]
           PROOF: Miri Level [N] pass — [command used]
           ```
        
        ### Phase 5: Hardening (Post-Fix)
        
        After all beads are resolved:
        
        1. **Add Miri to CI** if not already present (see [miri-sanitizers-loom.md](miri-sanitizers-loom.md) for the GitHub Actions config).
        2. **Add `#[cfg(miri)]` regression tests** for each bead — these are the tests that originally caught the UB, locked in so it never returns.
        3. **Review SAFETY comments** on every remaining `unsafe` block. Each must name the specific invariant from the taxonomy.
        4. **Run the full paranoia sweep one final time** to confirm clean.
        
        ## Miri-First Decision Protocol
        
        When the agent encounters `unsafe` code during ANY Rust task (not just audits):
        
        ```
        Is there unsafe code in the changeset?
          YES → Run Miri Level 1 before proceeding.
          │     Miri fails?
          │       YES → Stop. Classify. Fix. Prove. Then continue.
          │       NO  → Run Miri Level 2 (strict provenance).
          │             Miri fails?
          │               YES → Stop. Classify. Fix. Prove. Then continue.
          │               NO  → Proceed with the original task.
          NO → Proceed normally.
        ```
        
        This is not optional. **Every `unsafe` block gets Miri'd before it ships.**
        
        ## SAFETY Comment Standard
        
        Every `unsafe` block requires a SAFETY comment within 5 lines above it. The comment must:
        
        1. **Name the UB category** it could trigger (from the taxonomy).
        2. **State the invariant** that makes this safe.
        3. **Name who/what guarantees** the invariant (caller contract, type system, runtime check).
        
        ```rust
        // SAFETY: [Category 4 — Uninitialized Memory]
        // All N elements have been written to via `ptr::write` in the loop above.
        // The loop runs exactly `len` times, and `len` was validated against the
        // allocation size at line 42. MaybeUninit::assume_init is therefore sound.
        unsafe { buf.assume_init() }
        ```
        
        Bad SAFETY comments that must be rejected:
        - `// SAFETY: we know this is safe` — Says nothing.
        - `// SAFETY: this is fine because we tested it` — Testing does not prove absence of UB.
        - `// SAFETY: the caller ensures correctness` — Which invariant? What is the contract?
        - No SAFETY comment at all — Immediate failure.
        
        ## Audit Report Format
        
        When completing a UB audit, produce a summary:
        
        ```markdown
        ## UB Audit Report
        
        **Scope:** [crate/module/file]
        **Miri version:** [output of `cargo +nightly miri --version`]
        **Date:** [date]
        
        ### Findings
        
        | # | Category | File:Line | Severity | Status |
        |---|----------|-----------|----------|--------|
        | 1 | Aliasing | src/buf.rs:42 | High | Fixed (Bead #1) |
        | 2 | Uninit | src/ffi.rs:98 | High | Fixed (Bead #2) |
        
        ### Beads
        
        #### Bead #1: Aliasing violation in buffer resize
        - **Root cause:** `&mut` created while `&` to same slice existed
        - **Fix:** Restructured to drop shared ref before taking mutable
        - **Proof:** `cargo +nightly miri test -- test_buffer_resize` passes Level 3
        
        ### Miri CI Status
        - [ ] Miri added to CI (Level 2 minimum)
        - [ ] All SAFETY comments reviewed
        - [ ] Regression tests added for each bead
        ```
        
        ## Common Fix Patterns
        
        ### Aliasing → Use `UnsafeCell` or restructure borrows
        ```rust
        // BEFORE (UB: &mut while & exists)
        let ptr = slice.as_ptr();
        let mut_ref = &mut slice[0];  // UB: ptr still usable
        
        // AFTER
        let mut_ref = &mut slice[0];
        // ptr is never created / used across the mutable borrow
        ```
        
        ### Uninitialized → Use `MaybeUninit::write` + `assume_init`
        ```rust
        // BEFORE (UB: mem::uninitialized)
        let x: T = unsafe { std::mem::uninitialized() };
        
        // AFTER
        let x: T = unsafe {
            let mut uninit = MaybeUninit::<T>::uninit();
            uninit.write(initial_value);
            uninit.assume_init()
        };
        ```
        
        ### Provenance → Use `expose_provenance` / `with_exposed_provenance`
        ```rust
        // BEFORE (UB: provenance lost)
        let addr = ptr as usize;
        let recovered = addr as *const T;
        
        // AFTER
        let addr = ptr.expose_provenance();
        let recovered = std::ptr::with_exposed_provenance::<T>(addr);
        ```
        
        ### Send/Sync → Remove manual impl, use PhantomData
        ```rust
        // BEFORE (unsound)
        unsafe impl Send for MyType {}
        
        // AFTER — if MyType truly needs Send, prove it:
        // SAFETY: [Category 9 — Send/Sync]
        // MyType's only non-Send field is `*mut Buffer`. Access to the buffer
        // is guarded by `self.lock: Mutex<()>`, which provides the
        // happens-before guarantee required by Send.
        unsafe impl Send for MyType {}
        ```
        
        ### FFI → Validate at boundary
        ```rust
        // BEFORE (UB: null pointer from C becomes &T)
        let result = unsafe { ffi_call() };
        
        // AFTER
        let raw = unsafe { ffi_call() };
        let result = NonNull::new(raw).ok_or(Error::NullFromFfi)?;
        ```
        
        ## Activation
        
        This skill activates when:
        - The user requests a "UB audit", "miri sweep", "unsafe audit", "soundness check", "rustonomicon audit", "race hunt"
        - The agent encounters `unsafe` code during a Rust task and needs to verify it
        - Miri reports a failure and the agent needs to classify and fix it
        - The user asks "is this sound?" about Rust code
        
        **Miri is not optional. Miri is the proof. Ship nothing `unsafe` without Miri's blessing.**
        
      • ub-taxonomy.md 13.3 KB
        # Rust Undefined Behavior Taxonomy
        
        Every category of UB the Rust compiler, Miri, and the language specification recognize. The agent must know the full surface to hunt systematically. Each entry names the UB class, its root cause, canonical trigger, Miri detection status, and the canonical fix.
        
        ## 1. Aliasing Violations (Stacked Borrows / Tree Borrows)
        
        **Root cause:** Two pointers access the same memory in ways that violate Rust's borrowing model — even through raw pointers inside `unsafe`.
        
        **Canonical triggers:**
        - Creating a `&mut T` while another `&T` or `&mut T` to the same location exists.
        - Dereferencing a raw pointer derived from a reference after that reference was invalidated (e.g., `&mut` was retaken).
        - Calling `slice::from_raw_parts_mut` on overlapping regions.
        - Interior mutability through `UnsafeCell` without going through the `UnsafeCell` API.
        - Casting `&T` to `*mut T` and writing through it (even via FFI).
        
        **Miri detection:** YES — Stacked Borrows is the default model. Tree Borrows (`-Zmiri-tree-borrows`) is the newer, more permissive model. Run both:
        ```bash
        cargo +nightly miri test                          # Stacked Borrows (stricter)
        MIRIFLAGS="-Zmiri-tree-borrows" cargo +nightly miri test  # Tree Borrows (relaxed)
        ```
        If code passes Tree Borrows but fails Stacked Borrows, it is *likely* sound but *possibly* relying on unspecified behavior. Fix it anyway — Stacked Borrows is the conservative bet.
        
        **Fix pattern:** Use `UnsafeCell` for all interior mutability. Never cast `&T` to `*mut T`. Derive mutable pointers from `*mut T` obtained via `UnsafeCell::get()` or `addr_of_mut!()`.
        
        ---
        
        ## 2. Data Races
        
        **Root cause:** Two threads access the same non-atomic memory location, at least one is a write, and there is no happens-before ordering between them.
        
        **Canonical triggers:**
        - `unsafe impl Send for T` on a type containing `*mut U` without synchronization.
        - `unsafe impl Sync for T` on a type containing `Cell<T>` or `UnsafeCell<T>` without a lock.
        - Using `std::ptr::write` from multiple threads to the same allocation.
        - Shared `&T` where `T` has interior mutability but no atomic/lock guard.
        
        **Miri detection:** YES — Miri's data-race detector is on by default. It detects races on non-atomic accesses. For **preemptive scheduling** stress, use:
        ```bash
        MIRIFLAGS="-Zmiri-preemption-rate=0.1" cargo +nightly miri test
        ```
        
        **Complementary tools:** `loom` for exhaustive interleaving exploration on lock-free algorithms. ThreadSanitizer (TSAN) for integration tests Miri cannot run (I/O, FFI).
        
        **Fix pattern:** Wrap in `Mutex`/`RwLock`/`AtomicXxx`. Never `unsafe impl Sync` unless you can name the synchronization primitive guarding every mutable field.
        
        ---
        
        ## 3. Use After Free / Dangling Pointers
        
        **Root cause:** A pointer or reference outlives the allocation it points to.
        
        **Canonical triggers:**
        - Returning a reference to a local variable (compiler catches most, but raw pointers escape).
        - `Box::into_raw` → manual `Box::from_raw` with wrong lifetime.
        - `Vec` reallocation invalidating raw pointers obtained from `as_ptr()` / `as_mut_ptr()`.
        - `Pin<Box<T>>` unpinned and moved after self-referential pointers were set up.
        
        **Miri detection:** YES — allocation tracking catches use-after-free on the exact operation.
        
        **Fix pattern:** Borrow checker for references. For raw pointers: tie pointer validity to an explicit lifetime via a `PhantomData<&'a T>` in the wrapper, or use arena allocation (`bumpalo`) so all pointers share one lifetime.
        
        ---
        
        ## 4. Uninitialized Memory
        
        **Root cause:** Reading a value from memory that was never written to.
        
        **Canonical triggers:**
        - `MaybeUninit::assume_init()` before all bytes are written.
        - `mem::uninitialized()` (deprecated, still compiles).
        - `alloc::alloc(layout)` returns uninitialized memory — reading it before writing is UB.
        - Padding bytes in structs read via `transmute` or raw pointer casts.
        - `read_unaligned` on uninitialized memory.
        
        **Miri detection:** YES — tracks initialization state per byte. Catches partial-init structs, padding reads, and premature `assume_init`.
        
        **Fix pattern:** Use `MaybeUninit::zeroed()` when zero-init is acceptable. Write every field before calling `assume_init()`. Use `MaybeUninit::write()` instead of raw pointer writes. Never `transmute` structs with padding unless you zeroed the padding.
        
        ---
        
        ## 5. Invalid Values (Type Invariant Violations)
        
        **Root cause:** Producing a value that violates the type's validity invariant.
        
        **Canonical triggers:**
        - `bool` not 0 or 1.
        - `char` outside Unicode scalar range.
        - Enum discriminant not matching any variant.
        - `NonZeroU32` containing 0.
        - `&T` or `&mut T` that is null or dangling.
        - `str` containing non-UTF-8 bytes.
        - `fn` pointer that is null.
        
        **Miri detection:** YES — validity checks are on by default. Extra strictness:
        ```bash
        MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test
        ```
        
        **Fix pattern:** Validate before transmuting. Use `TryFrom` at boundaries. Never `transmute` to enum types — use a checked conversion function.
        
        ---
        
        ## 6. Misaligned Pointer Access
        
        **Root cause:** Dereferencing a pointer that is not aligned to the type's required alignment.
        
        **Canonical triggers:**
        - Casting `*const u8` to `*const u64` and dereferencing (alignment goes from 1 to 8).
        - `#[repr(packed)]` struct field references (the compiler warns, but raw pointers bypass the warning).
        - Network buffer parsing where offsets are arbitrary.
        
        **Miri detection:** YES — immediate trap on misaligned read/write.
        
        **Fix pattern:** Use `read_unaligned` / `write_unaligned` for packed data. Use `bytemuck` or `zerocopy` for safe reinterpretation with alignment checks.
        
        ---
        
        ## 7. Violating `Pin` Invariants
        
        **Root cause:** Moving a value that was pinned and relied on its address stability (self-referential types, intrusive linked lists).
        
        **Canonical triggers:**
        - `mem::swap` on a `Pin<&mut T>` after `unsafe` deref.
        - Implementing `Unpin` for a type that contains self-referential pointers.
        - Manually calling `Pin::new_unchecked` on a movable allocation.
        
        **Miri detection:** PARTIAL — Miri detects the resulting aliasing/use-after-free if the self-referential pointer is actually used. It does not detect "Pin contract violated but pointer was never dereferenced."
        
        **Fix pattern:** Never `impl Unpin` for self-referential types. Use `pin_project` or `pin_project_lite` for safe pin projections. Review every `Pin::new_unchecked` call.
        
        ---
        
        ## 8. FFI Boundary UB
        
        **Root cause:** Mismatch between Rust's ABI expectations and the foreign code's actual behavior.
        
        **Canonical triggers:**
        - C function returning uninitialized memory into a Rust `&T`.
        - Wrong `#[repr(C)]` layout (padding differs between platforms).
        - Passing a Rust `enum` to C without `#[repr(C)]` or `#[repr(i32)]`.
        - Null pointer passed where C expects non-null (and Rust wraps it in `&T`).
        - C code writing to Rust-owned memory through a pointer Rust considers immutable.
        - Declaring a foreign function `safe fn` inside `unsafe extern` when it has caller preconditions (null, length, lifetime), so safe code can violate them.
        - longjmp/setjmp across Rust frames (unwinding UB).
        
        **Miri detection:** LIMITED — Miri cannot execute foreign code. It detects UB in the Rust-side handling of FFI return values.
        
        **Complementary tools:** AddressSanitizer (ASAN), MemorySanitizer (MSAN) for detecting actual FFI-side corruption. Valgrind as a last resort.
        
        **Fix pattern:** Validate every FFI return at the boundary. Use `Option<NonNull<T>>` for nullable pointers. Use `CStr`/`CString` for strings. Add `cbindgen` to CI to verify layout agreement. Wrap every FFI call in a safe Rust function that checks preconditions.
        
        ---
        
        ## 9. Incorrect `Send` / `Sync` Implementations
        
        **Root cause:** Manually implementing `Send` or `Sync` for a type that does not actually uphold the required invariant.
        
        **Canonical triggers:**
        - `unsafe impl Send for Wrapper(*mut T)` when `T` is not `Send`.
        - `unsafe impl Sync for Wrapper(UnsafeCell<T>)` without a lock, atomic, or other synchronization.
        - Types containing `Rc<T>` with a manual `Send` impl (Rc is explicitly !Send).
        
        **Miri detection:** YES for the *resulting* data race if exercised. Miri's data-race detector will fire when two threads access the same location unsynchronized.
        
        **Fix pattern:** Never manually implement `Send`/`Sync` unless you can write a SAFETY proof naming the synchronization mechanism. Use `PhantomData<*const ()>` to opt-out of auto-`Send`/`Sync` when in doubt.
        
        ---
        
        ## 10. Out-of-Bounds Memory Access
        
        **Root cause:** Pointer arithmetic or indexing that escapes the allocation.
        
        **Canonical triggers:**
        - `ptr.offset(n)` where `n` exceeds the allocation size.
        - `slice::from_raw_parts(ptr, len)` where `len` is too large.
        - Off-by-one in manual buffer management.
        - Integer overflow in size calculations leading to undersized allocation.
        
        **Miri detection:** YES — allocation-precise bounds checking.
        
        **Fix pattern:** Use checked arithmetic (`checked_add`, `checked_mul`) for size calculations. Use `slice::from_raw_parts` only with validated lengths. Prefer safe indexing (`get()`, iterators) over raw pointer arithmetic.
        
        ---
        
        ## 11. Provenance Violations
        
        **Root cause:** Using a pointer whose provenance does not grant access to the target memory, even if the address is numerically correct.
        
        **Canonical triggers:**
        - Casting an integer to a pointer and dereferencing it (`addr as *const T`).
        - Roundtripping a pointer through `usize` and back (`ptr as usize as *const T`) — the provenance is lost.
        - Using `ptr::from_exposed_addr` without a corresponding `ptr.expose_provenance()`.
        
        **Miri detection:** YES with strict provenance:
        ```bash
        MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test
        ```
        
        **Fix pattern:** Use `ptr::with_exposed_provenance` / `ptr.expose_provenance()` for legitimate int-to-ptr roundtrips. Avoid `as usize as *const T` entirely. Use `sptr` crate for provenance-safe pointer manipulation on stable.
        
        ---
        
        ## 12. Double Free / Invalid Free
        
        **Root cause:** Freeing the same allocation twice, or freeing memory not obtained from the allocator.
        
        **Canonical triggers:**
        - `Box::from_raw` called twice on the same pointer.
        - Manual `dealloc` on a pointer already freed.
        - `ManuallyDrop` dropped explicitly then the outer type also drops it.
        
        **Miri detection:** YES — immediate trap.
        
        **Fix pattern:** Enforce single ownership via RAII. Use `ManuallyDrop` with extreme care — document who is responsible for the drop. Never clone a raw pointer and `Box::from_raw` both copies.
        
        ---
        
        ## 13. Library / Unsafe Contract Violations
        
        **Root cause:** Violating the documented safety invariant of a safe or unsafe API, where the library author relied on the invariant for soundness.
        
        **Canonical triggers:**
        - `Vec::set_len(n)` where the first `n` elements are not initialized.
        - `String::from_utf8_unchecked` on non-UTF-8 bytes.
        - `HashMap` key mutated after insertion (violates hash invariant — not UB per se, but unsound and Miri may detect downstream effects).
        - `BTreeMap` key with broken `Ord` impl (the standard library assumes a total order).
        
        **Miri detection:** DEPENDS — Miri catches the downstream UB (e.g., reading uninitialized bytes from a `Vec` with inflated len). It does not catch "you violated the documented contract" if no memory-level UB results.
        
        **Fix pattern:** Read the `# Safety` section of every `unsafe fn` you call. Document the invariant in your SAFETY comment. When in doubt, use the safe API and pay the cost.
        
        ---
        
        ## 14. Unwinding Across `extern "C"` Boundaries
        
        **Root cause:** A Rust panic unwinding through a frame that uses the C calling convention.
        
        **Canonical triggers:**
        - `panic!()` inside a `#[unsafe(no_mangle)] extern "C" fn` callback passed to C code.
        - `unwrap()` inside FFI callbacks.
        - Foreign code unwinding (C++ exceptions, `longjmp`) through a Rust frame declared `extern "C"`.
        
        Since Rust 1.81 a Rust panic that reaches an `extern "C"` boundary aborts the process instead of unwinding into foreign frames: no longer UB, but still a crash.
        
        **Miri detection:** PARTIAL — Miri does not model foreign unwinding, but it can detect the immediate UB if the panic reaches the FFI boundary.
        
        **Fix pattern:** Use `std::panic::catch_unwind` at every FFI entry point and return an error code. Declare the ABI `extern "C-unwind"` (stable since 1.71) only when both sides are built to let unwinding cross.
        
        ---
        
        ## Summary Table
        
        | # | Category | Miri Detects? | Complementary Tool |
        |---|----------|--------------|-------------------|
        | 1 | Aliasing (Stacked/Tree Borrows) | YES | — |
        | 2 | Data races | YES | loom, TSAN |
        | 3 | Use-after-free / dangling | YES | ASAN |
        | 4 | Uninitialized memory | YES | MSAN |
        | 5 | Invalid values | YES | — |
        | 6 | Misaligned access | YES | UBSAN |
        | 7 | Pin invariant violation | PARTIAL | manual review |
        | 8 | FFI boundary UB | LIMITED | ASAN, MSAN, Valgrind |
        | 9 | Incorrect Send/Sync | YES (via race) | loom |
        | 10 | Out-of-bounds access | YES | ASAN |
        | 11 | Provenance violations | YES (strict mode) | — |
        | 12 | Double free | YES | ASAN |
        | 13 | Library contract violations | PARTIAL | proptest, fuzzing |
        | 14 | Unwinding across FFI | PARTIAL | — |
        
        ## Miri Coverage Assessment
        
        Miri catches categories 1-6, 9-12 with high confidence. Categories 7, 8, 13, 14 require supplementary tools or manual audit. **Miri is the single highest-leverage tool** — it should run on every PR that touches `unsafe`, and ideally on the full test suite regularly.
        
    • typescript
      • backend-hono.md 21.2 KB
        # Hono Backend Stack Reference (2026)
        
        > **Canonical stack**: `hono` + `hono-openapi` + `@scalar/hono-api-reference` + `@hono/swagger-ui`
        > **Runtime**: Bun (TypeScript-first)
        > **Validator**: Zod v4 (Standard Schema compliant, zero extra deps for OpenAPI)
        
        ---
        
        ## 1. Package Versions (Latest Stable)
        
        | Package | Version | Source |
        |---------|---------|--------|
        | `hono` | `^4.12.5` | [peer dep of scalar](https://github.com/scalar/scalar/blob/8bcf8bf52a0da667d44eeec08648e3b1da044f97/integrations/hono/package.json#L66) |
        | `hono-openapi` | `^1.3.0` | [npm](https://registry.npmjs.org/hono-openapi) — published Mar 2, 2026 |
        | `@scalar/hono-api-reference` | `^0.10.11` | [npm](https://www.npmjs.com/package/@scalar/hono-api-reference) — published Apr 28, 2026 |
        | `@hono/swagger-ui` | `^0.6.1` | [npm](https://www.npmjs.com/package/@hono/swagger-ui) — published Apr 2026 |
        | `zod` | `^4.4.1` | [npm registry](https://registry.npmjs.org/zod) — latest stable v4 |
        
        ### `package.json` dependency block
        
        ```json
        {
          "dependencies": {
            "hono": "^4.12.5",
            "hono-openapi": "^1.3.0",
            "@scalar/hono-api-reference": "^0.10.11",
            "@hono/swagger-ui": "^0.6.1",
            "zod": "^4.4.1"
          },
          "devDependencies": {
            "typescript": "^5.8.0",
            "@types/bun": "latest"
          }
        }
        ```
        
        > **Peer dependencies auto-installed by `hono-openapi`**:
        > - `@hono/standard-validator@^0.2.0`
        > - `@standard-community/standard-json@^0.3.5`
        > - `@standard-community/standard-openapi@^0.2.9`
        > - `openapi-types@^12.1.3`
        >
        > [Source: `hono-openapi/package.json` peerDependencies](https://github.com/rhinobase/hono-openapi/blob/10f45a66ede3764b5e6065805fb60fd5df090466/package.json#L50-L65)
        
        ---
        
        ## 2. Complete `app.ts` — Copy-Pasteable
        
        ```typescript
        import { Hono } from 'hono'
        import { describeRoute, openAPIRouteHandler, resolver, validator } from 'hono-openapi'
        import { Scalar } from '@scalar/hono-api-reference'
        import { swaggerUI } from '@hono/swagger-ui'
        import { z } from 'zod'
        
        // ───────────────────────────────────────────────────────────────
        // 1. Schema definitions (Zod v4 — Standard Schema native)
        // ───────────────────────────────────────────────────────────────
        
        const QuerySchema = z.object({
          name: z.string().optional(),
        })
        
        const ResponseSchema = z.object({
          message: z.string(),
        })
        
        const JsonBodySchema = z.object({
          name: z.string(),
          age: z.number().int().min(0),
        })
        
        // ───────────────────────────────────────────────────────────────
        // 2. Hono app with described routes
        // ───────────────────────────────────────────────────────────────
        
        const app = new Hono()
        
        // Health check (no validation)
        app.get('/health', (c) => c.json({ status: 'ok' }))
        
        // A fully-documented route
        app.get(
          '/hello',
          describeRoute({
            tags: ['Greetings'],
            summary: 'Say hello',
            description: 'Returns a greeting message',
            responses: {
              200: {
                description: 'Successful greeting',
                content: {
                  'application/json': {
                    schema: resolver(ResponseSchema),
                  },
                },
              },
            },
          }),
          validator('query', QuerySchema),
          (c) => {
            const query = c.req.valid('query')
            return c.json({ message: `Hello ${query.name ?? 'Hono'}!` })
          },
        )
        
        // A POST route with JSON body validation
        app.post(
          '/users',
          describeRoute({
            tags: ['Users'],
            summary: 'Create a user',
            responses: {
              200: {
                description: 'User created',
                content: {
                  'application/json': {
                    schema: resolver(ResponseSchema),
                  },
                },
              },
            },
          }),
          validator('json', JsonBodySchema),
          (c) => {
            const body = c.req.valid('json')
            return c.json({ message: `Created user ${body.name}` })
          },
        )
        
        // ───────────────────────────────────────────────────────────────
        // 3. OpenAPI spec endpoint
        // ───────────────────────────────────────────────────────────────
        
        app.get(
          '/openapi.json',
          openAPIRouteHandler(app, {
            documentation: {
              info: {
                title: 'Hono API',
                version: '1.0.0',
                description: 'Example Hono API with OpenAPI',
              },
              servers: [
                { url: 'http://localhost:3000', description: 'Local server' },
              ],
            },
          }),
        )
        
        // ───────────────────────────────────────────────────────────────
        // 4. Scalar API Reference UI
        // ───────────────────────────────────────────────────────────────
        
        app.get(
          '/scalar',
          Scalar({
            url: '/openapi.json',
            theme: 'saturn',
            pageTitle: 'Hono API Reference',
          }),
        )
        
        // ───────────────────────────────────────────────────────────────
        // 5. Swagger UI (parallel mount)
        // ───────────────────────────────────────────────────────────────
        
        app.get(
          '/swagger',
          swaggerUI({
            url: '/openapi.json',
            title: 'Swagger UI',
          }),
        )
        
        // ───────────────────────────────────────────────────────────────
        // 6. Bun canonical entrypoint
        // ───────────────────────────────────────────────────────────────
        
        export default app
        ```
        
        ---
        
        ## 3. `hono-openapi` API Reference
        
        ### Import paths
        
        **There is only one import path.** `hono-openapi` exports everything from its root:
        
        ```typescript
        import {
          describeRoute,      // middleware to annotate a route with OpenAPI metadata
          describeResponse,   // attach response schemas directly to a handler
          validator,          // validation middleware (wraps @hono/standard-validator)
          resolver,           // wrap a Zod/Valibot/etc schema for OpenAPI responses
          openAPIRouteHandler, // serve the generated OpenAPI JSON document
          generateSpecs,      // programmatically generate the spec (for build-time caching)
        } from 'hono-openapi'
        ```
        
        **Evidence** ([`src/index.ts`](https://github.com/rhinobase/hono-openapi/blob/10f45a66ede3764b5e6065805fb60fd5df090466/src/index.ts#L1-L9)):
        
        ```typescript
        export { generateSpecs, openAPIRouteHandler } from "./handler.js";
        export {
          describeResponse,
          describeRoute,
          loadVendor,
          resolver,
          validator,
        } from "./middlewares.js";
        ```
        
        > **No subpath exports** such as `hono-openapi/zod` or `hono-openapi/valibot`. The package uses Standard Schema and auto-detects the validator vendor.
        
        ### `describeRoute()` middleware
        
        Attach OpenAPI metadata to any Hono route. Use `resolver()` for response body schemas.
        
        ```typescript
        app.get(
          '/path',
          describeRoute({
            tags: ['Users'],
            summary: 'Get user',
            description: 'Retrieve a single user by ID',
            responses: {
              200: {
                description: 'User found',
                content: {
                  'application/json': {
                    schema: resolver(UserSchema),
                  },
                },
              },
              404: {
                description: 'User not found',
              },
            },
          }),
          handler,
        )
        ```
        
        **Evidence** ([`src/middlewares.ts`](https://github.com/rhinobase/hono-openapi/blob/10f45a66ede3764b5e6065805fb60fd5df090466/src/middlewares.ts#L244-L254)):
        
        ```typescript
        export function describeRoute(spec: DescribeRouteOptions): MiddlewareHandler {
          const middleware: MiddlewareHandler = async (_c, next) => {
            await next();
          };
          return Object.assign(middleware, {
            [uniqueSymbol]: { spec },
          });
        }
        ```
        
        ### `validator()` middleware
        
        Validates `query`, `json`, `param`, or `form` and **automatically** injects the request schema into the OpenAPI document. No manual `request` block in `describeRoute()` is required.
        
        ```typescript
        validator('query', QuerySchema)   // ?name=foo
        validator('json', JsonBodySchema) // POST body
        validator('param', ParamSchema)   // /users/:id
        validator('form', FormSchema)     // multipart/form-data
        ```
        
        **Evidence** ([`src/middlewares.ts`](https://github.com/rhinobase/hono-openapi/blob/10f45a66ede3764b5e6065805fb60fd5df090466/src/middlewares.ts#L199-L237)):
        
        ```typescript
        export function validator<Schema extends StandardSchemaV1, ...>(
          target: Target,
          schema: Schema,
          hook?: Hook<...>,
          options?: ResolverReturnType["options"],
        ): MiddlewareHandler<E, P, V> {
          const middleware = sValidator(target, schema, hook);
          return Object.assign(middleware, {
            [uniqueSymbol]: { target, ...resolver(schema, options), options },
          });
        }
        ```
        
        ### `openAPIRouteHandler()` — serving the spec
        
        ```typescript
        app.get(
          '/openapi.json',
          openAPIRouteHandler(app, {
            documentation: {
              info: { title: 'Hono API', version: '1.0.0' },
              servers: [{ url: 'http://localhost:3000' }],
            },
          }),
        )
        ```
        
        **Evidence** ([`src/handler.ts`](https://github.com/rhinobase/hono-openapi/blob/10f45a66ede3764b5e6065805fb60fd5df090466/src/handler.ts#L42-L59)):
        
        ```typescript
        export function openAPIRouteHandler<...>(
          hono: Hono<E, S, P>,
          options?: Partial<GenerateSpecOptions>,
        ): MiddlewareHandler<E, P, I> {
          let specs: OpenAPIV3_1.Document;
          return async (c) => {
            if (specs) return c.json(specs);
            specs = await generateSpecs(hono, options, c);
            return c.json(specs);
          };
        }
        ```
        
        > **Mount path convention**: `/openapi.json` is the most common. Some projects use `/openapi/spec.json` (e.g. [NamesMT/starter-monorepo](https://github.com/NamesMT/starter-monorepo/blob/main/apps/backend/src/openAPI.ts)).
        
        ---
        
        ## 4. `@scalar/hono-api-reference` Setup
        
        ### Import path and package name
        
        ```typescript
        import { Scalar } from '@scalar/hono-api-reference'
        ```
        
        > **Deprecated**: `apiReference` is still exported but deprecated in favor of `Scalar` ([PR #5297](https://github.com/scalar/scalar/pull/5297)).
        
        **Evidence** ([`integrations/hono/src/index.ts`](https://github.com/scalar/scalar/blob/8bcf8bf52a0da667d44eeec08648e3b1da044f97/integrations/hono/src/index.ts#L1-L9)):
        
        ```typescript
        import { Scalar } from './scalar'
        export {
          Scalar,
          /**
           * @deprecated Use `Scalar` instead.
           */
          Scalar as apiReference,
        }
        ```
        
        ### Mount path convention
        
        Common choices:
        - `/scalar` — matches the middleware name
        - `/docs` — generic documentation endpoint
        - `/openapi/ui` — nested under the OpenAPI prefix
        
        ### Configuration options
        
        The Hono middleware accepts the **universal Scalar configuration** plus Hono-specific overrides (`pageTitle`, `cdn`).
        
        ```typescript
        app.get('/scalar', Scalar({
          // ── Source (required) ──
          url: '/openapi.json',          // URL to the OpenAPI spec
        
          // ── Appearance ──
          theme: 'saturn',               // 'alternate' | 'default' | 'moon' | 'purple'
                                         // | 'solarized' | 'bluePlanet' | 'deepSpace'
                                         // | 'saturn' | 'kepler' | 'elysiajs' | 'fastify'
                                         // | 'mars' | 'laserwave' | 'none'
          pageTitle: 'My API Docs',      // HTML <title>
          customCss: '.sidebar { ... }', // injected <style> block
          metaData: { title: '...' },    // SEO meta tags (unhead format)
          favicon: '/favicon.svg',
        
          // ── Behavior ──
          layout: 'modern',              // 'modern' | 'classic'
          darkMode: true,
          forceDarkModeState: 'dark',    // 'dark' | 'light'
          hideDarkModeToggle: false,
          hideModels: false,
          hideSearch: false,
          hideTestRequestButton: false,
          showOperationId: false,
          showSidebar: true,
        
          // ── Proxy / Server ──
          proxyUrl: 'https://proxy.scalar.com',
          baseServerURL: 'http://localhost:3000',
          servers: [{ url: 'http://localhost:3000' }],
        
          // ── CDN ──
          cdn: 'https://cdn.jsdelivr.net/npm/@scalar/api-reference',
        
          // ── Advanced ──
          authentication: { ... },
          hiddenClients: ['unirest'],
          defaultHttpClient: { targetKey: 'js', clientKey: 'fetch' },
          plugins: [...],
          pathRouting: { basePath: '/reference' },
          mcp: { name: 'My MCP', url: '...' },
        }))
        ```
        
        **Evidence** — Scalar types define the full schema:
        - [Base configuration (themes, proxy, etc.)](https://github.com/scalar/scalar/blob/8bcf8bf52a0da667d44eeec08648e3b1da044f97/packages/types/src/api-reference/base-configuration.ts#L110-L129)
        - [HTML rendering configuration (`pageTitle`, `cdn`)](https://github.com/scalar/scalar/blob/8bcf8bf52a0da667d44eeec08648e3b1da044f97/packages/types/src/api-reference/html-rendering-configuration.ts#L8-L23)
        - [Source configuration (`url`, `content`)](https://github.com/scalar/scalar/blob/8bcf8bf52a0da667d44eeec08648e3b1da044f97/packages/types/src/api-reference/source-configuration.ts#L8-L55)
        - [Full API reference configuration](https://github.com/scalar/scalar/blob/8bcf8bf52a0da667d44eeec08648e3b1da044f97/packages/types/src/api-reference/api-reference-configuration.ts#L22-L379)
        
        ### Dynamic configuration (request-aware)
        
        ```typescript
        app.get('/scalar', Scalar((c) => ({
          url: '/openapi.json',
          proxyUrl: c.env.ENVIRONMENT === 'development'
            ? 'https://proxy.scalar.com'
            : undefined,
        })))
        ```
        
        **Evidence** ([`integrations/hono/src/scalar.ts`](https://github.com/scalar/scalar/blob/8bcf8bf52a0da667d44eeec08648e3b1da044f97/integrations/hono/src/scalar.ts#L75-L94)):
        
        ```typescript
        export const Scalar = <E extends Env>(configOrResolver: Configuration<E>): MiddlewareHandler<E> => {
          return async (c) => {
            let resolvedConfig: Partial<ApiReferenceConfiguration> = {}
            if (typeof configOrResolver === 'function') {
              resolvedConfig = await configOrResolver(c)
            } else {
              resolvedConfig = configOrResolver
            }
            // ...
          }
        }
        ```
        
        ---
        
        ## 5. `@hono/swagger-ui` Setup
        
        ### Import path and package name
        
        ```typescript
        import { swaggerUI } from '@hono/swagger-ui'
        ```
        
        **Evidence** ([`packages/swagger-ui/src/index.ts`](https://github.com/honojs/middleware/blob/eb443a2fbda674bbe12d3f30e96854bb0cad6232/packages/swagger-ui/src/index.ts#L93)):
        
        ```typescript
        export { middleware as swaggerUI, SwaggerUI }
        ```
        
        ### Mount path convention
        
        Common choices:
        - `/swagger` — explicit
        - `/ui` — used in Hono official examples
        - `/docs` — generic
        
        ### Configuration options
        
        ```typescript
        app.get('/swagger', swaggerUI({
          url: '/openapi.json',          // URL to the OpenAPI spec (required)
          title: 'Swagger UI',           // HTML page title
          version: 'latest',             // Swagger UI CDN version
          // Any standard Swagger UI option also works:
          // presets, plugins, urls, etc.
        }))
        ```
        
        **Evidence** ([`packages/swagger-ui/src/index.ts`](https://github.com/honojs/middleware/blob/eb443a2fbda674bbe12d3f30e96854bb0cad6232/packages/swagger-ui/src/index.ts#L8-L43)):
        
        ```typescript
        type SwaggerUIOptions = OriginalSwaggerUIOptions & DistSwaggerUIOptions
        
        const middleware = <E extends Env>(options: SwaggerUIOptions): MiddlewareHandler<E> =>
          async (c) => {
            const title = options?.title ?? 'SwaggerUI'
            return c.html(/* html */ `...`)
          }
        ```
        
        ---
        
        ## 6. Bun Runtime Entrypoint
        
        ### Canonical shape for `bun run`
        
        ```typescript
        import { Hono } from 'hono'
        
        const app = new Hono()
        // ... routes ...
        
        export default app
        ```
        
        **Evidence** ([Hono Bun docs](https://hono.dev/docs/getting-started/bun)):
        
        > ```ts
        > import { Hono } from 'hono'
        > const app = new Hono()
        > app.get('/', (c) => c.text('Hello Bun!'))
        > export default app
        > ```
        
        ### Custom port
        
        ```typescript
        export default {
          port: 3000,
          fetch: app.fetch,
        }
        ```
        
        **Evidence** ([Hono Bun docs — Change port number](https://hono.dev/docs/getting-started/bun)):
        
        > ```ts
        > export default {
        >   port: 3000,
        >   fetch: app.fetch,
        > }
        > ```
        
        ### `package.json` scripts for Bun
        
        ```json
        {
          "scripts": {
            "dev": "bun run --hot src/index.ts",
            "start": "bun run src/index.ts",
            "build": "tsc --noEmit"
          }
        }
        ```
        
        ---
        
        ## 7. OpenAPI Version
        
        ### `hono-openapi` emits **OpenAPI 3.1.0** by default
        
        This is **hardcoded** in the source and **not configurable** at runtime:
        
        **Evidence** ([`src/handler.ts` line 120](https://github.com/rhinobase/hono-openapi/blob/10f45a66ede3764b5e6065805fb60fd5df090466/src/handler.ts#L120)):
        
        ```typescript
        return {
          openapi: "3.1.0",
          ..._documentation,
          // ...
        } satisfies OpenAPIV3_1.Document;
        ```
        
        > If you need OpenAPI 3.0.x, you must post-process the generated spec or use `@hono/zod-openapi` (the older package) instead. The user explicitly requested `hono-openapi`, so document that 3.1.0 is the only output.
        
        ---
        
        ## 8. Zod v3 vs Zod v4
        
        | Feature | Zod v3 | Zod v4 |
        |---------|--------|--------|
        | Standard Schema | ❌ No | ✅ Yes (native) |
        | `hono-openapi` extra deps | `zod-openapi@4` | None |
        | Import path | `import { z } from 'zod'` | `import { z } from 'zod'` (or `zod/v4` for explicit) |
        
        **For Zod v3 users**, install the compatibility layer:
        
        ```bash
        npm install zod-openapi@4
        ```
        
        Then use `zod-openapi`'s `.openapi()` for metadata and `.meta({ ref: 'Name' })` for component references. `hono-openapi`'s `resolver()` will still work, but the underlying schema conversion relies on `zod-openapi@4`.
        
        **Evidence** ([HonoHub Zod docs](https://honohub.dev/docs/openapi/zod)):
        
        > "For zod v3, you can use the `zod-openapi` library. You need to install `zod-openapi@4` for this to work properly."
        
        **For Zod v4 users** (recommended in 2026), no extra packages are needed. `z.date()` is automatically converted to `{ type: 'string', format: 'date-time' }`.
        
        **Evidence** ([`src/middlewares.ts` Zod v4 date override](https://github.com/rhinobase/hono-openapi/blob/10f45a66ede3764b5e6065805fb60fd5df090466/src/middlewares.ts#L63-L71)):
        
        ```typescript
        const zodV4DateOverride = (ctx: { ... }) => {
          if (ctx.zodSchema._zod.def.type === "date") {
            ctx.jsonSchema.type = "string";
            ctx.jsonSchema.format = "date-time";
          }
        };
        ```
        
        ---
        
        ## 9. Real-World Example
        
        **NamesMT/starter-monorepo** — a public monorepo starter using `hono-openapi` + `@scalar/hono-api-reference` together:
        
        - File: [`apps/backend/src/openAPI.ts`](https://github.com/NamesMT/starter-monorepo/blob/main/apps/backend/src/openAPI.ts)
        - Pattern: mounts spec at `/openapi/spec.json` and Scalar UI at `/openapi/ui`
        
        ```typescript
        import type { Hono } from 'hono'
        import { Scalar } from '@scalar/hono-api-reference'
        import { openAPIRouteHandler } from 'hono-openapi'
        
        export function setupOpenAPI(app: Hono<any, any>, prefix = '/openapi') {
          app.get(
            `${prefix}/spec.json`,
            openAPIRouteHandler(app, {
              documentation: {
                info: {
                  title: `starter-monorepo's backend`,
                  version: '1.0.0',
                  description: 'My amazing API',
                },
              },
            }),
          )
        
          app.get(
            `${prefix}/ui`,
            Scalar({
              theme: 'deepSpace',
              url: `${prefix}/spec.json`,
            }),
          )
        }
        ```
        
        > **Note**: No public repo was found using all four (`hono-openapi` + `Scalar` + `swagger-ui` + `hono`) in a single file. The canonical combination in the wild is `hono-openapi` + `Scalar`. Adding `swagger-ui` is a trivial parallel mount (shown in the `app.ts` above).
        
        ---
        
        ## 10. Common Pitfalls
        
        1. **Using `openAPISpecs` instead of `openAPIRouteHandler`**
           Some docs (e.g. HONC) use `openAPISpecs` — this is **not** the current export name. The correct function is `openAPIRouteHandler` ([source](https://github.com/rhinobase/hono-openapi/blob/10f45a66ede3764b5e6065805fb60fd5df090466/src/index.ts#L1)).
        
        2. **Importing from `hono-openapi/zod`**
           There are **no subpath exports**. Always import from `hono-openapi` directly.
        
        3. **Forgetting `@hono/standard-validator`**
           It is a peer dependency of `hono-openapi`. Modern package managers (npm ≥ 7, pnpm, bun) auto-install it. If you see validation errors, ensure it is present in `node_modules`.
        
        4. **Using `@hono/zod-openapi` (the OLD package)**
           The user explicitly wants `hono-openapi` (the newer, middleware-based, Standard Schema package). Do not confuse with `@hono/zod-openapi` which wraps the `Hono` class into `OpenAPIHono`.
        
        5. **Swagger UI `spec` option**
           `@hono/swagger-ui` does **not** accept a `spec` option to embed the document directly. It only accepts `url` (or `urls`) pointing to an external spec endpoint. If you need embedded specs, use Scalar's `content` option instead.
        
        ---
        
        ## 11. Quick Start Commands
        
        ```bash
        # 1. Create project
        mkdir my-api && cd my-api
        bun init -y
        
        # 2. Install dependencies
        bun add hono hono-openapi @scalar/hono-api-reference @hono/swagger-ui zod
        
        # 3. Add TypeScript
        bun add -d typescript @types/bun
        
        # 4. Write app.ts (copy from section 2 above)
        # 5. Run
        bun run --hot app.ts
        ```
        
        Endpoints after startup:
        - `GET /health` — health check
        - `GET /hello?name=world` — documented route
        - `POST /users` — validated JSON body route
        - `GET /openapi.json` — raw OpenAPI 3.1.0 spec
        - `GET /scalar` — Scalar API Reference UI
        - `GET /swagger` — Swagger UI
        
      • bootstrap.md 6.6 KB
        # Bootstrap — Runtime, Package Manager, Tooling
        
        When starting a new TypeScript project (or scripting against the world), the choice of runtime, package manager, framework, and toolchain compounds. The wrong default at minute zero costs hours every week. The right defaults for 2026:
        
        ## Runtime decision tree
        
        ```
        Is this a CLI / script / single-binary tool?
        └─ Yes → Bun (single executable, hot reload, native TS)
                Use `bun run script.ts` directly. No build step.
        
        Is this a backend service?
        ├─ Edge (Cloudflare Workers / Vercel / Deno Deploy) → match the platform
        ├─ Bun-supported runtime          → Bun + Hono
        ├─ Need Node-only deps (sharp, native modules without Bun support) → Node + Hono
        └─ Otherwise                       → Bun + Hono
        
        Is this a frontend?
        └─ Vite (regardless of framework). Bun for the package manager.
        
        Is this a library to publish to npm?
        └─ tsdown (or unbuild). Targets Node 20+. Use pnpm for monorepo workspaces.
        ```
        
        ## Bun is the default runtime
        
        Use Bun for:
        - Scripts and CLIs (`bun run` is faster than `tsx` and `ts-node`)
        - New backends (Hono runs natively, hot reload via `bun --hot`)
        - Test runner (`bun test` is built-in, faster than vitest for small suites)
        - Package manager (`bun install` is faster than `pnpm` and far faster than `npm`)
        
        Use Node when:
        - A dependency uses native modules Bun can't load (rare in 2026; check the dep's release notes)
        - Production target is a Node-specific platform (some serverless platforms don't run Bun yet)
        - You're contributing to a Node-only project
        
        `bunx` replaces `npx`. `bun create` scaffolds projects.
        
        ## Package manager — pnpm > npm
        
        If you must use Node, use pnpm. NEVER npm except in legacy projects you don't control.
        
        Why pnpm:
        - Content-addressable store: 10x less disk usage on a machine with many projects
        - Strict node_modules layout: phantom dependencies fail at install time, not at runtime
        - Workspaces are first-class
        - Significantly faster than npm
        
        Why not yarn:
        - Yarn classic is unmaintained
        - Yarn berry's "PnP" mode breaks with editor tooling more often than it should
        - pnpm has caught up on every yarn berry feature people actually use
        
        Why not npm:
        - Slowest of the three
        - No proper workspace story until very recently
        - Phantom dependencies allowed by default
        
        ```bash
        # Convert npm/yarn → pnpm
        pnpm import   # reads package-lock.json or yarn.lock and produces pnpm-lock.yaml
        rm -rf node_modules package-lock.json yarn.lock
        pnpm install
        ```
        
        ## Backend framework — Hono
        
        Use Hono for any new HTTP service. It is:
        - Type-safe end-to-end (request/response types flow through middleware)
        - Edge-compatible (runs on Bun, Node, Cloudflare Workers, Deno, AWS Lambda)
        - Faster than Express, Fastify, and most of its peers in synthetic benchmarks
        - Maintained, opinionated, and documented well
        
        When Hono → ALWAYS pair with `hono-openapi` + `@scalar/hono-api-reference` + `@hono/swagger-ui`. Full setup with copy-pasteable `app.ts`: [backend-hono.md](backend-hono.md).
        
        NEVER:
        - Express for new services. Express is the COBOL of Node — works, but writes itself out of every benchmark.
        - Fastify for new services. Hono ships with better TypeScript ergonomics.
        - NestJS for new services. The Angular-flavoured DI/decorator stack is overkill for ~95% of services.
        - Bare `Bun.serve` or `node:http` unless you have a specific reason. Lose middleware, routing, validation. Reinvent everything.
        
        ## Frontend tooling — Vite
        
        Vite for any frontend. Replaces webpack, parcel, rollup-as-app-bundler. Works with React, Vue, Svelte, Solid, Preact, vanilla.
        
        ```bash
        bun create vite my-app -- --template react-ts
        cd my-app
        bun install
        bun run dev
        ```
        
        ## Lint + format — Biome
        
        Biome replaces ESLint + Prettier with one tool, written in Rust, ~30x faster.
        
        ```bash
        bun add --dev @biomejs/biome
        bun biome init
        ```
        
        `biome.json`:
        
        ```json
        {
          "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
          "organizeImports": { "enabled": true },
          "linter": { "enabled": true, "rules": { "recommended": true } },
          "formatter": { "enabled": true, "indentStyle": "space", "indentWidth": 2 }
        }
        ```
        
        Use ESLint only when:
        - You have an ESLint plugin Biome doesn't replicate (rare in 2026)
        - You're contributing to an existing ESLint project
        
        Never run both — pick one.
        
        ## Test runner — bun test or vitest
        
        | Runner | Use when |
        |---|---|
        | `bun test` | Bun project, simple unit tests, no TypeScript path aliases that need vite-style resolution |
        | `vitest` | Vite-based frontend, complex test infrastructure (DOM testing, snapshot, in-browser tests), or you need vitest-specific features |
        
        NEVER Jest for a new project. Jest's CommonJS-first design fights every modern Node/TS project.
        
        ## TypeScript
        
        `tsconfig.json` for a Bun + Hono backend:
        
        ```json
        {
          "compilerOptions": {
            "target": "ESNext",
            "module": "ESNext",
            "moduleResolution": "bundler",
            "lib": ["ESNext"],
            "types": ["bun-types"],
            "strict": true,
            "noUncheckedIndexedAccess": true,
            "noUnusedLocals": true,
            "noUnusedParameters": true,
            "noImplicitReturns": true,
            "noFallthroughCasesInSwitch": true,
            "exactOptionalPropertyTypes": true,
            "esModuleInterop": true,
            "isolatedModules": true,
            "resolveJsonModule": true,
            "skipLibCheck": true,
            "verbatimModuleSyntax": true,
            "noEmit": true
          },
          "include": ["src/**/*", "tests/**/*"]
        }
        ```
        
        `verbatimModuleSyntax: true` enforces explicit `import type { ... }` for type-only imports — pairs with the no-excuse rule on type-only imports.
        
        `noEmit: true` because `bun run` and `bun build` handle compilation. The `tsc` command becomes a typechecker only.
        
        ## Quick-start: Bun + Hono backend
        
        ```bash
        mkdir my-api && cd my-api
        bun init -y
        bun add hono hono-openapi @scalar/hono-api-reference @hono/swagger-ui zod
        bun add --dev @biomejs/biome typescript
        bun biome init
        ```
        
        `package.json` scripts:
        
        ```json
        {
          "scripts": {
            "dev": "bun run --hot src/index.ts",
            "start": "bun run src/index.ts",
            "build": "bun build src/index.ts --target bun --outdir dist",
            "typecheck": "tsc --noEmit",
            "lint": "biome check --write src tests",
            "test": "bun test"
          }
        }
        ```
        
        Wire the `app.ts` from [backend-hono.md](backend-hono.md). You have a documented, validated, OpenAPI-spec-emitting service in ~15 minutes.
        
        ## When NOT to bootstrap from scratch
        
        | Situation | Use |
        |---|---|
        | Internal tool with auth/admin/dashboards | Next.js (full-stack) - lots of free wiring |
        | Documentation site | Astro or VitePress |
        | Real-time features (WebRTC, complex sockets) | Bun + Hono + a real-time library |
        | Data-heavy SPA | Vite + React + TanStack Query + TanStack Router |
        
        For greenfield backend services, Bun + Hono. Always.
        
      • data-modeling.md 5 KB
        # Data Modeling
        
        Which construct to use, how to structure data, and why readonly is the default.
        
        ---
        
        ## Decision flowchart
        
        ```
        Is it a fixed set of named constants?
          YES → as const object + literal union type
          NO ↓
        Is it just branding a primitive (string, number)?
          YES → Branded type
          NO ↓
        Is it an interface / contract?
          YES → interface (structural typing is the default in TS)
          NO ↓
        Does the data cross a trust boundary (user input, API, file)?
          YES → Zod schema + z.infer<typeof schema>
          NO ↓
        Is it a union of possible outcomes?
          YES → Discriminated union (kind/type field)
          NO ↓
        Is it structured data with named fields?
          YES → type alias with readonly properties
          NO → you probably don't need a new type
        ```
        
        ---
        
        ## Container reference
        
        ### type alias — internal data
        
        The default for structured data inside your codebase. Zero runtime cost.
        
        ```typescript
        type User = {
          readonly id: UserId
          readonly name: string
          readonly email: string
        }
        
        type Point = {
          readonly x: number
          readonly y: number
        }
        ```
        
        All properties `readonly`. Mutable only when mutation is the documented purpose.
        
        ### interface — contracts and extension
        
        Use when you need declaration merging or `extends`.
        
        ```typescript
        interface Repository<T> {
          get(id: string): Promise<T | null>
          save(entity: T): Promise<void>
        }
        
        interface UserRepository extends Repository<User> {
          findByEmail(email: string): Promise<User | null>
        }
        ```
        
        ### interface vs type — when to use which
        
        | Use | When |
        |---|---|
        | `type` | Union types, intersections, mapped types, utility types, internal data shapes |
        | `interface` | Contracts that will be `implements`ed or `extends`ed, declaration merging needed |
        | **Default** | **`type` — unless you have a specific reason for `interface`** |
        
        ### Zod schema — trust boundary guardian
        
        Use when data enters your system. Validates at runtime, infers types at compile time.
        
        ```typescript
        import { z } from "zod"
        
        const CreateUserSchema = z.object({
          name: z.string().min(1),
          email: z.string().email(),
          age: z.number().int().min(0),
        })
        type CreateUser = z.infer<typeof CreateUserSchema>
        
        const UserResponseSchema = z.object({
          id: z.string().uuid(),
          name: z.string(),
          email: z.string(),
        })
        type UserResponse = z.infer<typeof UserResponseSchema>
        ```
        
        **The one rule**: data crosses a trust boundary → Zod. Everything else → plain type/interface.
        Never use Zod for internal-only data. The runtime validation cost and Zod coupling are unnecessary.
        
        ### as const — fixed constants
        
        Replaces `enum` entirely. Type-safe, tree-shakeable, no runtime overhead.
        
        ```typescript
        const ROLES = ["admin", "user", "guest"] as const
        type Role = (typeof ROLES)[number]
        
        const STATUS = {
          ACTIVE: "active",
          INACTIVE: "inactive",
          DELETED: "deleted",
        } as const
        type Status = (typeof STATUS)[keyof typeof STATUS]
        ```
        
        ### Discriminated union — multiple outcomes
        
        ```typescript
        type GetUserResult =
          | { readonly kind: "found"; readonly user: User }
          | { readonly kind: "not_found"; readonly id: UserId }
          | { readonly kind: "forbidden"; readonly reason: string }
        ```
        
        Each variant has a `kind` discriminant. TypeScript narrows on `switch (result.kind)`.
        
        ---
        
        ## Quick lookup
        
        | Situation | Use |
        |---|---|
        | User input, API request/response | Zod schema + `z.infer` |
        | Internal value object | `type` with `readonly` properties |
        | Function with multiple outcomes | Discriminated union |
        | Contract for implementations | `interface` |
        | Fixed constants | `as const` + literal union |
        | Distinct primitive (UserId vs OrderId) | Branded type |
        | Dict shape / key-value map | `Record<K, V>` or index signature |
        
        ---
        
        ## Readonly by default
        
        Every property is `readonly` unless mutation is the documented purpose.
        
        ```typescript
        // DEFAULT — readonly
        type Config = {
          readonly apiUrl: string
          readonly timeout: number
        }
        
        // Arrays too
        function getUsers(): readonly User[] { ... }
        
        // Utility for existing types
        type ReadonlyUser = Readonly<User>
        type DeepReadonlyConfig = Readonly<Config>
        ```
        
        For mutable state (rare), document why:
        
        ```typescript
        /** Counter state — mutation is the entire purpose. */
        type CounterState = {
          count: number  // intentionally mutable
        }
        ```
        
        ---
        
        ## Parse, don't validate
        
        Validate at the boundary. Inside the boundary, types are proof of validity.
        
        ```typescript
        // BAD — validate then pass raw data
        function processEmail(email: string): void {
          if (!email.includes("@")) throw new Error("invalid")
          // still a raw string downstream
        }
        
        // GOOD — parse into typed value at boundary
        const EmailSchema = z.string().email().brand("Email")
        type Email = z.infer<typeof EmailSchema>
        
        function sendWelcome(email: Email): void { ... }
        
        // Boundary code
        const parsed = EmailSchema.parse(rawInput)  // Email or throws
        sendWelcome(parsed)  // no re-validation needed
        ```
        
        ---
        
        ## Sources
        
        - TypeScript Handbook: [Object Types](https://www.typescriptlang.org/docs/handbook/2/objects.html)
        - Zod: [docs](https://zod.dev)
        - Total TypeScript: [Type vs Interface](https://www.totaltypescript.com/type-vs-interface-which-should-you-use)
        
      • error-handling.md 4.7 KB
        # Error Handling
        
        Typed errors, exhaustive matching, Result pattern, and resource safety.
        
        ---
        
        ## Typed errors — no bare strings
        
        Error classes carry structured data. Callers know exactly what can go wrong.
        
        ```typescript
        class UserNotFoundError extends Error {
          readonly name = "UserNotFoundError"
          constructor(readonly userId: UserId) {
            super(`user ${userId} not found`)
          }
        }
        
        class PermissionDeniedError extends Error {
          readonly name = "PermissionDeniedError"
          constructor(
            readonly userId: UserId,
            readonly requiredRole: string,
          ) {
            super(`user ${userId} needs role ${requiredRole}`)
          }
        }
        ```
        
        ```typescript
        // BAD
        throw new Error("user not found")
        throw new Error("permission denied")
        
        // GOOD
        throw new UserNotFoundError(userId)
        throw new PermissionDeniedError(userId, "admin")
        ```
        
        Always set `readonly name` explicitly — `instanceof` checks survive minification, but `error.name` is more reliable for logging and serialization.
        
        ---
        
        ## Result pattern — expected failures without exceptions
        
        For failures that are **expected** (not found, validation), return a discriminated union instead of throwing.
        
        ```typescript
        type Result<T, E = Error> =
          | { readonly ok: true; readonly value: T }
          | { readonly ok: false; readonly error: E }
        
        function ok<T>(value: T): Result<T, never> {
          return { ok: true, value }
        }
        
        function err<E>(error: E): Result<never, E> {
          return { ok: false, error }
        }
        ```
        
        ### Usage
        
        ```typescript
        type UserError =
          | { readonly kind: "not_found"; readonly id: UserId }
          | { readonly kind: "forbidden"; readonly reason: string }
        
        function getUser(id: UserId): Result<User, UserError> {
          const user = db.find(id)
          if (!user) return err({ kind: "not_found", id })
          if (!user.active) return err({ kind: "forbidden", reason: "deactivated" })
          return ok(user)
        }
        
        // Caller must handle both cases
        const result = getUser(userId)
        if (!result.ok) {
          switch (result.error.kind) {
            case "not_found":
              log.warn(`missing: ${result.error.id}`)
              break
            case "forbidden":
              log.error(`denied: ${result.error.reason}`)
              break
            default:
              assertNever(result.error)
          }
          return
        }
        const user = result.value  // narrowed to User
        ```
        
        ### When to use which
        
        **The heuristic**: caller is 1-2 levels away and MUST handle it → Result. Error should propagate up many layers → throw.
        
        | Scenario | Pattern | Why |
        |---|---|---|
        | Repository → service (caller handles it) | Result | Caller is right there, must handle both |
        | Validation at boundary (parsing input) | throw (Zod throws) | Propagates up to HTTP handler |
        | Infrastructure failure (network, OOM) | throw | Can't handle locally |
        | Service → service (deep internal) | throw (typed Error subclass) | Result boilerplate across many layers is worse |
        | HTTP handler → response | Catch errors, convert to response | Boundary code catches and translates |
        
        **Practical tradeoff**: Result is safest (compiler forces handling) but creates boilerplate when every caller in a chain must check `.ok`. If the error would just propagate through 3+ layers unchanged, use a typed Error subclass instead.
        
        ### Library or roll your own?
        
        Roll your own with the `Result`, `ok`, `err` above. It's 10 lines. Libraries like `neverthrow` add chaining (`.map`, `.andThen`) — use them only if you actually chain results frequently.
        
        ---
        
        ## Error cause — chain context
        
        Use the `cause` option to chain errors without losing the original stack.
        
        ```typescript
        try {
          await db.query(sql)
        } catch (error) {
          throw new DatabaseError("query failed", { cause: error })
        }
        ```
        
        The `cause` is available on `error.cause` and shows up in stack traces.
        
        ---
        
        ## Exhaustive error handling at boundaries
        
        HTTP handlers catch and translate:
        
        ```typescript
        app.onError((error, c) => {
          if (error instanceof UserNotFoundError) {
            return c.json({ error: error.message }, 404)
          }
          if (error instanceof PermissionDeniedError) {
            return c.json({ error: error.message }, 403)
          }
          console.error("unhandled:", error)
          return c.json({ error: "internal server error" }, 500)
        })
        ```
        
        ---
        
        ## Async error patterns
        
        ```typescript
        // Promise.allSettled — when partial failure is OK
        const results = await Promise.allSettled(urls.map(fetch))
        const successes = results
          .filter((r): r is PromiseFulfilledResult<Response> => r.status === "fulfilled")
          .map((r) => r.value)
        
        // AbortSignal — cancellation
        async function fetchWithTimeout(url: string, ms: number): Promise<Response> {
          return fetch(url, { signal: AbortSignal.timeout(ms) })
        }
        ```
        
        ---
        
        ## Sources
        
        - MDN: [Error cause](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause)
        - MDN: [Promise.allSettled](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled)
        
      • README.md 9.2 KB
        
        # TypeScript Programmer
        
        Modern TypeScript. Type-strict, stack-first, async-correct.
        
        ## Philosophy
        
        The compiler is your proof system. Make illegal states unrepresentable. Parse at boundaries. Every function has a contract; the type system enforces it.
        
        ## Hard rules
        
        These are deliberate project choices. Violations are always wrong, not "style preferences".
        
        ### Tooling
        
        | Category | Use | Never |
        |---|---|---|
        | Runtime | Bun (native TS, single binary) | ts-node, tsx |
        | Package manager | `pnpm` | npm, yarn (unless workspace requires it) |
        | Linter + formatter | Biome | ESLint, Prettier |
        | Type checker | `tsc --noEmit` with strict config | skip type checking |
        | Web framework | Hono | Express |
        | Validation | Zod | joi, yup, class-validator |
        | Testing | `bun test` or vitest | jest |
        | ORM | Drizzle | TypeORM, Prisma (unless already in project) |
        
        ### The iron list
        
        1. **Readonly by default** — all `type`/`interface` properties are `readonly`. Arrays are `readonly T[]`. Mutable only when mutation is the documented purpose.
        2. **Branded types for distinct IDs** — `type UserId = Brand<string, "UserId">`. Never pass raw `string` where a branded type exists.
        3. **Exhaustive switch** — every `switch` on a discriminated union ends with `default: assertNever(x)`. No fall-through.
        4. **No any** — `any` is banned in annotations, returns, and parameters. Use `unknown` and narrow.
        5. **No type assertions** — `as any`, `as unknown` banned. `as const` and `satisfies` are fine.
        6. **No non-null assertion** — `x!` is banned. Use narrowing or optional chaining (`x?.y`).
        7. **No @ts-ignore / @ts-expect-error** — fix the type.
        8. **No enum** — use `as const` objects + literal union types.
        9. **Zod at boundaries** — external input (API, user, file) → Zod schema. Internal → plain types.
        10. **Typed errors** — Error subclasses with typed fields. No `throw new Error("bare string")` for domain errors. Use Result for expected failures within 1-2 call levels; throw for propagation across many layers.
        11. **as const for constants** — module-level constant objects and arrays use `as const`.
        12. **import type** — type-only imports use `import type`. Enforced by `verbatimModuleSyntax`.
        13. **Named exports only** — no `export default`. Exception: framework requirement (Next.js pages, etc.).
        14. **No empty catch, no catch-and-swallow** — every `catch` block must either (a) narrow the error with `instanceof` and handle each case, or (b) re-throw. Empty catch blocks and `catch (e) { console.error(e) }` without narrowing or re-throw are banned — they hide bugs. At top-level boundaries (CLI entry, HTTP handler), opt out with `// no-excuse-ok: catch`.
        
        ### Data modeling — which construct, when
        
        | Situation | Use |
        |---|---|
        | User input, API request/response | Zod schema + `z.infer` |
        | Internal value object | `type` with `readonly` properties |
        | Function with multiple outcomes | Discriminated union (`kind` field) |
        | Contract for implementations | `interface` |
        | Fixed constants | `as const` + literal union |
        | Distinct primitive (UserId vs OrderId) | Branded type |
        | Key-value map | `Record<K, V>` or index signature |
        
        **The one rule**: data crosses trust boundary → Zod. Everything else → plain `type` with `readonly`.
        
        Load `data-modeling.md` for the full decision flowchart and comparison.
        
        ### When readonly does not apply
        
        - **Framework state** (React `useState`, signals) — managed by framework.
        - **Builder / accumulator** — object exists to be mutated (buffer, cache). Document why.
        - **ORM mutations** — Drizzle insert/update objects.
        
        ### Why empty/unhandled catch is banned
        
        In TypeScript, every `catch` receives `unknown`. The language gives you no type safety in catch blocks — you must earn it with `instanceof`. A bare `catch (e) { console.error(e) }` swallows `TypeError`, `RangeError`, and your domain errors identically. When a new error type appears, nothing warns you.
        
        ```typescript
        // BANNED — empty catch
        try { await fetchData() } catch {}
        try { await fetchData() } catch (e) { /* will fix later */ }
        
        // BANNED — catch-and-swallow (no narrowing, no rethrow)
        try {
          const data = await api.get("/users")
        } catch (e) {
          console.error("failed", e)
        }
        
        // GOOD — narrow with instanceof
        try {
          const data = await api.get("/users")
        } catch (e) {
          if (e instanceof HttpError) {
            logger.warn(`API ${e.status}: ${e.message}`)
            return fallback
          }
          throw e  // unknown errors propagate
        }
        
        // GOOD — top-level boundary (only place catch-all is acceptable)
        async function main(): Promise<void> {  // no-excuse-ok: catch
          try {
            await run()
          } catch (e) {
            console.error("unhandled:", e)
            process.exit(1)
          }
        }
        ```
        
        ### Libraries
        
        | Domain | Library | Why |
        |---|---|---|
        | HTTP framework | Hono | Lightweight, multi-runtime, middleware, OpenAPI |
        | Validation | Zod | Runtime validation + type inference |
        | ORM | Drizzle | Type-safe SQL, no codegen |
        | HTTP client | `ky` | Thin fetch wrapper (5KB); auto-throw on non-2xx, retry, timeout, hooks, prefixUrl. Browser + Node + Bun + Deno |
        | HTTP client (perf) | `undici` (direct API) | When a Node backend needs connection pooling, HTTP/2, or pipelining |
        
        > **HTTP client rule** - production code must not use bare `fetch()`. It has no retry, timeout, or error-handling policy and causes silent failures during incidents. Install **`ky`** by default, and use the **`undici`** direct API when a Node backend needs high-volume requests, connection pooling, HTTP/2, or pipelining. ~~`axios`~~ is forbidden after the supply-chain compromise (2026-03). `node-fetch` is unnecessary because Node 18+ includes built-in fetch.
        | Testing | `bun test` / vitest | Fast, ESM-native |
        | Logging | `pino` | Structured JSON, fast |
        | CLI | `@clack/prompts` + `commander` | Interactive + parsing |
        
        ## tsconfig — the one true config
        
        Scaffold a new project with all strict defaults pre-configured:
        
        ```bash
        bun run ../../scripts/typescript/new-project.ts my-api
        bun run ../../scripts/typescript/new-project.ts my-api --path ./projects
        ```
        
        Creates: `package.json` (Hono + Zod + Biome), `tsconfig.json` (ultra-strict), `biome.json`, `src/index.ts`, `.gitignore`. Works on macOS, Linux, Windows.
        
        For manual setup: `bunx tsc --init`, then load `tsconfig-strict.md` for the full strict config.
        
        Key flags beyond `"strict": true`:
        
        | Flag | What it catches |
        |---|---|
        | `noUncheckedIndexedAccess` | `arr[0]` is `T \| undefined`, forces check |
        | `exactOptionalPropertyTypes` | `{ x?: string }` ≠ `{ x: string \| undefined }` |
        | `verbatimModuleSyntax` | Forces `import type` for type-only imports |
        | `noFallthroughCasesInSwitch` | Forgotten `break` / `return` |
        | `noPropertyAccessFromIndexSignature` | `.key` on index sig → bracket notation |
        
        ## Reference loading
        
        Load on demand — not all at once.
        
        | Need | Load |
        |---|---|
        | Strict tsconfig + Biome config | `tsconfig-strict.md` |
        | Type patterns (branded, as const, satisfies, narrowing, assertNever) | `type-patterns.md` |
        | Data modeling (type vs interface vs Zod, readonly, parse-don't-validate) | `data-modeling.md` |
        | Error handling (Result, typed errors, union vs throw) | `error-handling.md` |
        | Bootstrapping a new project (Bun, pnpm, Hono, Vite) | `bootstrap.md` |
        | Hono backend stack (hono-openapi, Scalar, Swagger) | `backend-hono.md` |
        
        ## No-excuse audit
        
        Violations caught by `../../scripts/typescript/check-no-excuse-rules.ts`. Run after every edit session.
        
        | Rule ID | Catches | Opt-out |
        |---|---|---|
        | `no-any-assertion` | `as any` | None — redesign types |
        | `no-unknown-assertion` | `as unknown` | None — redesign types |
        | `no-ts-ignore` | `@ts-ignore` | None — fix the type |
        | `no-ts-expect-error` | `@ts-expect-error` | None — fix the type |
        | `no-enum` | `enum` declarations | None — use `as const` |
        | `no-non-null-assertion` | `x!` postfix | None — narrow or `?.` |
        | `no-throw-literal` | `throw "string"` / `throw 123` | None — throw Error subclass |
        | `no-mutable-export` | `export let` / `export var` | None — use `export const` |
        | `no-any-annotation` | `: any` in parameter/return/variable types | `// no-excuse-ok: any` |
        | `no-explicit-any-return` | `(): any` or `(): Promise<any>` return types | `// no-excuse-ok: any` |
        | `empty-catch` | `catch { }` or `catch (e) { }` with empty body | `// no-excuse-ok: catch` |
        | `catch-without-narrowing` | `catch (e)` used without `instanceof` or re-throw | `// no-excuse-ok: catch` |
        
        Biome enforces additional rules (noExplicitAny, noNonNullAssertion, noDefaultExport, useImportType). The script catches what Biome cannot.
        
        ## In tests
        
        Tests are strict too, with these exceptions (configure in `biome.jsonc` overrides):
        
        | In tests you may | Why |
        |---|---|
        | Use `expect()` assertions | That's how testing works |
        | Use magic numbers | Test data |
        | Access private members via bracket notation | Testing internals |
        | Skip readonly on test fixtures | Mutable setup/teardown |
        
        Tests still follow the iron list — branded types, typed errors, exhaustive switch.
        
        ## Existing codebases
        
        When editing an existing file that doesn't follow these rules: **write new code in strict style, don't refactor existing code in the same change.**
        
        ## Activation
        
        This skill activates whenever you are writing or modifying any `.ts` or `.tsx` file. Even one-off scripts get the strict treatment.
        
      • tsconfig-strict.md 4.8 KB
        # Strict tsconfig + Biome
        
        The canonical ultra-strict config. Copy-paste, then add your own paths.
        
        ---
        
        ## tsconfig.json
        
        ```jsonc
        {
          "compilerOptions": {
            // ── Strict core ──────────────────────────────────────────
            "strict": true,                            // enables all strict* flags below
            // strict includes: strictNullChecks, strictFunctionTypes,
            // strictBindCallApply, strictPropertyInitialization,
            // noImplicitAny, noImplicitThis, alwaysStrict, useUnknownInCatchVariables
        
            // ── Additional strict flags (NOT included in "strict") ──
            "noUncheckedIndexedAccess": true,           // obj[key] is T | undefined, not T
            "exactOptionalPropertyTypes": true,         // { x?: string } !== { x: string | undefined }
            "noFallthroughCasesInSwitch": true,         // switch fall-through is an error
            "noPropertyAccessFromIndexSignature": true, // forces bracket notation for index sigs
            "forceConsistentCasingInFileNames": true,   // prevents case-sensitivity bugs on macOS/Win
        
            // ── Module system ────────────────────────────────────────
            "module": "ESNext",
            "moduleResolution": "bundler",
            "verbatimModuleSyntax": true,               // forces `import type` for type-only imports
            "isolatedModules": true,                    // safe for esbuild / swc / Bun transpilation
            "esModuleInterop": true,
            "resolveJsonModule": true,
        
            // ── Target ───────────────────────────────────────────────
            "target": "ESNext",
            "lib": ["ESNext"],
        
            // ── Emit ─────────────────────────────────────────────────
            "declaration": true,
            "declarationMap": true,
            "sourceMap": true,
            "outDir": "dist",
            "rootDir": "src",
        
            // ── Performance ──────────────────────────────────────────
            "skipLibCheck": true,                       // skip checking .d.ts files for speed
            "incremental": true
          },
          "include": ["src"],
          "exclude": ["node_modules", "dist"]
        }
        ```
        
        ### What each extra flag catches
        
        | Flag | What it prevents |
        |---|---|
        | `noUncheckedIndexedAccess` | `arr[0]` is `T \| undefined`, not `T`. Forces you to check before using. |
        | `exactOptionalPropertyTypes` | `{ x?: string }` means "missing or string", NOT "string \| undefined". Assigns `undefined` explicitly? Type error. |
        | `noFallthroughCasesInSwitch` | Forgetting `break` / `return` in a switch case. |
        | `noPropertyAccessFromIndexSignature` | `obj.foo` on `Record<string, X>` is an error. Use `obj["foo"]`. |
        | `verbatimModuleSyntax` | Forces `import type { X }` for type-only imports. Prevents runtime import of types. |
        
        ### Bun-specific additions
        
        For Bun projects, add to `compilerOptions`:
        ```jsonc
        {
          "types": ["bun-types"],
          "moduleDetection": "force"
        }
        ```
        
        ---
        
        ## biome.jsonc
        
        ```jsonc
        {
          "$schema": "https://biomejs.dev/schemas/2.0.6/schema.json",
          "organizeImports": {
            "enabled": true
          },
          "linter": {
            "enabled": true,
            "rules": {
              "recommended": true,
              "suspicious": {
                "noExplicitAny": "error",
                "noConfusingVoidType": "error",
                "noFallthroughSwitchClause": "error"
              },
              "style": {
                "noDefaultExport": "error",
                "useImportType": "error",
                "noNonNullAssertion": "error",
                "useEnumInitializers": "off",
                "noParameterAssign": "error"
              },
              "correctness": {
                "noUnusedVariables": "error",
                "noUnusedImports": "error"
              },
              "complexity": {
                "noBannedTypes": "error"
              }
            }
          },
          "formatter": {
            "enabled": true,
            "indentStyle": "space",
            "indentWidth": 2,
            "lineWidth": 100
          },
          "javascript": {
            "formatter": {
              "quoteStyle": "double",
              "semicolons": "asNeeded"
            }
          },
          "files": {
            "ignore": ["node_modules", "dist", "build", ".next", ".nuxt", "coverage"]
          }
        }
        ```
        
        ### Key Biome rules
        
        | Rule | What |
        |---|---|
        | `noExplicitAny` | `any` in annotations is an error |
        | `noNonNullAssertion` | `x!` is an error |
        | `noDefaultExport` | Forces named exports |
        | `useImportType` | Forces `import type` for type-only imports |
        | `noParameterAssign` | No mutation of function parameters |
        
        ---
        
        ## CI gate
        
        ```bash
        bunx biome check .
        bunx tsc --noEmit
        bun test
        ```
        
        ---
        
        ## Sources
        
        - TypeScript: [tsconfig reference](https://www.typescriptlang.org/tsconfig)
        - Biome: [configuration](https://biomejs.dev/reference/configuration/)
        - Total TypeScript: [tsconfig cheat sheet](https://www.totaltypescript.com/tsconfig-cheat-sheet)
        
      • type-patterns.md 5.2 KB
        # Type Patterns
        
        How to use TypeScript's type system to catch bugs at compile time.
        
        ---
        
        ## Branded types — distinct primitives
        
        Same runtime type, different meaning. The compiler prevents mixing.
        
        ```typescript
        declare const brand: unique symbol
        type Brand<T, B extends string> = T & { readonly [brand]: B }
        
        type UserId = Brand<string, "UserId">
        type OrderId = Brand<string, "OrderId">
        type Milliseconds = Brand<number, "Milliseconds">
        type Seconds = Brand<number, "Seconds">
        
        function UserId(value: string): UserId { return value as UserId }
        function OrderId(value: string): OrderId { return value as OrderId }
        
        function getUser(id: UserId): User { ... }
        
        getUser(UserId("abc"))    // OK
        getUser(OrderId("abc"))   // type error: OrderId is not UserId
        getUser("abc")            // type error: string is not UserId
        ```
        
        With Zod (preferred at boundaries):
        ```typescript
        import { z } from "zod"
        
        const UserIdSchema = z.string().uuid().brand("UserId")
        type UserId = z.infer<typeof UserIdSchema>
        ```
        
        **Use when**: IDs, indices, units of measurement — any pair where swapping is a bug.
        
        ---
        
        ## as const — literal types from values
        
        Freezes a value to its narrowest possible type. The foundation for enum-free TypeScript.
        
        ```typescript
        const ROLES = ["admin", "user", "guest"] as const
        type Role = (typeof ROLES)[number]  // "admin" | "user" | "guest"
        
        const HTTP_STATUS = {
          OK: 200,
          NOT_FOUND: 404,
          INTERNAL: 500,
        } as const
        type HttpStatus = (typeof HTTP_STATUS)[keyof typeof HTTP_STATUS]  // 200 | 404 | 500
        ```
        
        **Use when**: fixed set of constants. Replaces `enum` entirely.
        **Skip when**: the set is open-ended or user-defined.
        
        ---
        
        ## satisfies — validate without widening
        
        Type-checks a value against a type while preserving the literal type. Best of both worlds.
        
        ```typescript
        type Config = Record<string, string | number>
        
        // BAD — widens to Record<string, string | number>
        const config: Config = { api: "https://api.example.com", timeout: 30 }
        config.api  // string | number — lost the narrowing
        
        // GOOD — validates AND preserves literal types
        const config = {
          api: "https://api.example.com",
          timeout: 30,
        } satisfies Config
        config.api      // string (narrowed)
        config.timeout  // number (narrowed)
        ```
        
        **Use when**: you want type validation on a value without losing narrowing.
        
        ---
        
        ## Discriminated unions — algebraic data types
        
        Model every outcome as a type. Force the caller to handle all cases.
        
        ```typescript
        type GetUserResult =
          | { readonly kind: "found"; readonly user: User }
          | { readonly kind: "not_found"; readonly id: UserId }
          | { readonly kind: "forbidden"; readonly reason: string }
        ```
        
        The `kind` field (or `type`, `status`, `_tag`) is the discriminant. TypeScript narrows on it automatically.
        
        ---
        
        ## Exhaustive switch — assertNever
        
        Every switch on a discriminated union ends with a default that calls `assertNever`.
        
        ```typescript
        function assertNever(x: never): never {
          throw new Error(`Unexpected value: ${JSON.stringify(x)}`)
        }
        
        function handleResult(result: GetUserResult): string {
          switch (result.kind) {
            case "found":
              return result.user.name
            case "not_found":
              return `No user ${result.id}`
            case "forbidden":
              return `Denied: ${result.reason}`
            default:
              return assertNever(result)
          }
        }
        ```
        
        Add a new variant to `GetUserResult`? The compiler errors on the `assertNever` call until you handle it.
        
        ---
        
        ## Narrowing — let the compiler follow your logic
        
        TypeScript narrows types through `typeof`, `instanceof`, `in`, equality checks, and discriminants.
        
        ```typescript
        function process(value: string | number | null): string {
          if (value === null) return "nothing"
          // compiler knows: string | number
        
          if (typeof value === "string") return value.toUpperCase()
          // compiler knows: number
        
          return String(value * 2)
        }
        ```
        
        ### Custom type guards
        
        ```typescript
        function isNonNull<T>(value: T | null | undefined): value is T {
          return value != null
        }
        
        const items = [1, null, 2, undefined, 3]
        const clean = items.filter(isNonNull)  // number[]
        ```
        
        ---
        
        ## import type — separate values from types
        
        Always use `import type` for type-only imports. Enforced by `verbatimModuleSyntax`.
        
        ```typescript
        import type { User, Config } from "./types"   // erased at runtime
        import { createUser } from "./services"        // kept at runtime
        ```
        
        For mixed imports:
        ```typescript
        import { createUser, type User } from "./users"
        ```
        
        ---
        
        ## Utility types — quick reference
        
        | Need | Use |
        |---|---|
        | All properties readonly | `Readonly<T>` |
        | All properties optional | `Partial<T>` |
        | All properties required | `Required<T>` |
        | Pick specific properties | `Pick<T, "a" \| "b">` |
        | Omit specific properties | `Omit<T, "a" \| "b">` |
        | Key-value map | `Record<K, V>` |
        | Extract from union | `Extract<T, U>` |
        | Exclude from union | `Exclude<T, U>` |
        | Return type of function | `ReturnType<typeof fn>` |
        | Parameters of function | `Parameters<typeof fn>` |
        | Awaited type | `Awaited<Promise<T>>` → `T` |
        
        ---
        
        ## Sources
        
        - TypeScript Handbook: [Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html)
        - TypeScript Handbook: [Template Literal Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html)
        - Total TypeScript: [as const](https://www.totaltypescript.com/as-const)
        
    • code-smells.md 13.8 KB
      # Code Smells — Full Reference
      
      When any of these smells is detected, **STOP and re-examine your design.** A code smell is not a syntax error — it is a signal that the current structure deserves a second look. The correct response is to assess whether `/refactor` is warranted, fix the smell, or document a SPECIFIC justification for carrying it. "It's fine" is not a justification.
      
      ---
      
      ## Smell 1 — File exceeds 250 pure LOC
      
      ### Why 250
      
      At 250 pure LOC a file still fits in one screen on a 32-inch monitor with a 14pt font. A reviewer can hold the whole thing in working memory and spot a cross-cutting bug. At 500 LOC they cannot. At 1000 LOC they stop trying. The number is the cognitive ceiling of a single human reviewer who has not memorized the file.
      
      A file past this line is telling you:
      
      - The module is doing more than one thing.
      - Multiple cohesive units got merged "to save a file."
      - Re-exports, barrels, and orchestrators got fused into pure-logic units.
      - Every future reader pays a tax to find what they need.
      
      ### Measuring pure LOC
      
      ```bash
      # Quick (line-comment + blank exclusion):
      awk '!/^[[:space:]]*$/ && !/^[[:space:]]*(\/\/|#|--)/' <file> | wc -l
      
      # Authoritative (handles block comments correctly):
      cloc --by-file <file>   # the "code" column is the number
      ```
      
      ### Required behavior when detected
      
      **Creating a file that will exceed 250 pure LOC.** Split it before the first commit. Carve by responsibility, one cohesive unit per file. Use a barrel (`__init__.py`, `mod.rs`, `index.ts`) for re-exports ONLY — never for logic.
      
      **Editing a file that already exceeds 250 pure LOC and your edit adds lines.** Refactor the unit you are touching into its own file BEFORE adding the new lines. The split is part of THIS task, not a follow-up someone will never do.
      
      **Reading a file that exceeds 250 pure LOC while implementing a feature.** Surface the smell in your reply, propose a concrete split, and ask the user whether to split now or carry the smell.
      
      ### Forbidden escapes
      
      - Counting comments and blank lines toward the budget. **Pure LOC means code lines.**
      - Splitting by token count (`foo_1.py`, `module_part_A.rs`, `service-2.ts`). Split by what each file DOES.
      - Catch-all dump files: `utils.py`, `helpers.ts`, `lib.rs` (as a logic dump), `common.py`, `shared.ts`.
      - "It's generated, so it's fine." Only true if the file lives in `dist/`, `target/`, `__generated__/`.
      - "It's a test file with many cases." Split by SUT or by behavior cluster.
      - "230 pure LOC, close enough." A 230-LOC file about to grow is already at the limit. Split now.
      
      ### Acceptable exceptions (rare, require justification)
      
      A file may legitimately exceed 250 pure LOC if **and only if** it is:
      
      - A truly indivisible single-responsibility unit (e.g., a generated parser table, a state machine whose states share a single closure). Mark with `// allow: SIZE_OK — <reason>`.
      - A pure data table (translation strings, error code lookup, brand color palette).
      
      `// allow: SIZE_OK` without a justifying comment is itself slop.
      
      ### Concrete split examples
      
      #### Python — BEFORE (`user_service.py`, 412 pure LOC)
      
      ```python
      # user_service.py — DOES TOO MUCH
      class UserRepository: ...        # 90 LOC of SQLAlchemy
      class UserValidator: ...         # 60 LOC of Pydantic + business rules
      class PasswordHasher: ...        # 40 LOC of bcrypt wrapper
      class EmailSender: ...           # 50 LOC of httpx2 client
      class UserService: ...           # 130 LOC orchestrating the four above
      def _build_query(...): ...       # 25 LOC helper
      def _format_email(...): ...      # 17 LOC helper
      ```
      
      #### Python — AFTER (split by responsibility)
      
      ```
      src/myapp/users/
      ├── __init__.py              # barrel: re-exports UserService only (5 LOC)
      ├── repository.py            # UserRepository                 (~95 LOC)
      ├── validator.py             # UserValidator                  (~65 LOC)
      ├── password.py              # PasswordHasher                 (~45 LOC)
      ├── notifier.py              # EmailSender (renamed — the role, not the verb)
      ├── service.py               # UserService (orchestrator)     (~135 LOC)
      └── _queries.py              # _build_query (private)         (~30 LOC)
      ```
      
      #### Rust — BEFORE (`auth.rs`, 380 pure LOC)
      
      ```rust
      // auth.rs — DOES TOO MUCH
      pub struct Session { ... }                      // 40 LOC
      impl Session { ... }                            // 90 LOC of methods
      pub struct TokenIssuer { ... }                  // 30 LOC
      impl TokenIssuer { ... }                        // 70 LOC
      pub struct RateLimiter { ... }                  // 50 LOC
      impl RateLimiter { ... }                        // 70 LOC
      fn parse_authorization_header(...) { ... }      // 30 LOC
      ```
      
      #### Rust — AFTER
      
      ```
      src/auth/
      ├── mod.rs              # re-exports Session, TokenIssuer, RateLimiter (8 LOC)
      ├── session.rs          # Session + impl                         (~130 LOC)
      ├── token.rs            # TokenIssuer + impl                     (~100 LOC)
      ├── rate_limit.rs       # RateLimiter + impl                     (~120 LOC)
      └── header.rs           # parse_authorization_header             (~35 LOC)
      ```
      
      #### TypeScript — BEFORE (`api/orders.ts`, 510 pure LOC)
      
      ```typescript
      // api/orders.ts — DOES TOO MUCH
      export const OrderSchema = z.object({ ... })          // 30 LOC
      type Order = z.infer<typeof OrderSchema>
      export class OrderRepository { ... }                  // 110 LOC
      export class PricingEngine { ... }                    // 130 LOC
      export class TaxCalculator { ... }                    // 90 LOC
      export class OrderService { ... }                     // 150 LOC
      ```
      
      #### TypeScript — AFTER
      
      ```
      src/orders/
      ├── index.ts                    # barrel (6 LOC)
      ├── schema.ts                   # OrderSchema + Order type      (~35 LOC)
      ├── repository.ts               # OrderRepository               (~115 LOC)
      ├── pricing.ts                  # PricingEngine                 (~135 LOC)
      ├── tax.ts                      # TaxCalculator                 (~95 LOC)
      └── service.ts                  # OrderService (orchestrator)   (~155 LOC)
      ```
      
      ---
      
      ## Smell 2 — Function with more than 3 parameters
      
      ### Why 3
      
      A function's parameters are its contract with every caller. More than 3 independent inputs overwhelm the caller's working memory and signal one of two design problems:
      
      1. **The function does too much.** It should be two functions.
      2. **Related parameters belong together.** They should be a typed struct/object — a domain concept, not a parameter bag.
      
      ### Workaround detection — THESE COUNT AS THE SAME SMELL
      
      Disguising parameter count does not fix the design. The following patterns are the same smell wearing a different hat:
      
      **Dict/map smuggling:**
      ```python
      # SMELL — hiding 6 args in a dict
      def create_order(params: dict[str, Any]) -> Order: ...
      ```
      ```typescript
      // SMELL — untyped options bag
      function createOrder(opts: Record<string, unknown>): Order { ... }
      ```
      ```go
      // SMELL — map instead of typed params
      func CreateOrder(params map[string]any) (*Order, error) { ... }
      ```
      
      **Variadic/kwargs catch-all:**
      ```python
      # SMELL — hiding real params behind kwargs
      def send_notification(recipient: str, **kwargs) -> None: ...
      ```
      ```typescript
      // SMELL — rest params to avoid naming args
      function sendNotification(recipient: string, ...args: unknown[]): void { ... }
      ```
      
      **Config object that wraps positional args:**
      ```python
      # SMELL — "options" object that exists only to bundle what would be positional args
      @dataclass
      class CreateUserOptions:
          name: str
          email: str
          password: str
          role: str
          department: str
          manager_id: int
          # 6 fields, used by exactly one function, no defaults
      
      def create_user(opts: CreateUserOptions) -> User: ...
      ```
      
      **When the options object is NOT a smell:** when it represents a genuine domain concept reused across multiple call sites with sensible defaults for most fields (e.g., `HttpClientConfig`, `DatabaseConnectionOptions`, `RetryPolicy`).
      
      ### The fix
      
      Group related parameters into typed value objects with domain names:
      
      ```python
      # CLEAN — grouped by domain concept
      @dataclass(frozen=True)
      class UserIdentity:
          name: str
          email: str
      
      @dataclass(frozen=True)
      class OrgPlacement:
          role: str
          department: str
          manager_id: int
      
      def create_user(identity: UserIdentity, placement: OrgPlacement, password: str) -> User: ...
      # 3 params, each a meaningful concept
      ```
      
      ```typescript
      // CLEAN — typed grouping
      interface ShippingDetails {
        readonly address: string;
        readonly city: string;
        readonly zip: string;
        readonly country: string;
      }
      
      function createOrder(customer: CustomerId, items: readonly LineItem[], shipping: ShippingDetails): Order { ... }
      // 3 params, shipping is a reusable domain type
      ```
      
      ```go
      // CLEAN — struct with domain meaning
      type Placement struct {
          Role       string
          Department string
          ManagerID  UserID
      }
      
      func CreateUser(identity UserIdentity, placement Placement, password string) (*User, error) { ... }
      ```
      
      If 4+ truly independent inputs are required, justify it — the justification must name WHY these inputs cannot be grouped, not just "the function needs them all."
      
      ---
      
      ## Smell 3 — Redundant verification after a destructive action
      
      ### Why this is slop
      
      The contract of a destructive operation (delete, remove, clear, drop) IS the verification. If the operation returns without error, the thing is gone. Re-querying to "confirm" is:
      
      1. **Dead code.** The check can never fail unless the operation itself is broken — in which case fix the operation, not the caller.
      2. **Misleading.** It teaches the next reader (human or AI) that the operation is unreliable.
      3. **Performance waste.** An unnecessary round-trip to the database, filesystem, or data structure.
      
      This pattern is the hallmark of AI-generated defensive bloat. LLMs produce it because they optimize for "looking thorough" over "being correct." **Recognize it. Delete it.**
      
      ### Examples
      
      ```python
      # SLOP — delete then verify deletion
      db.delete(user)
      db.commit()
      remaining = db.query(User).filter_by(id=user.id).first()
      assert remaining is None  # the ORM already guaranteed this
      
      # CLEAN
      db.delete(user)
      db.commit()
      ```
      
      ```typescript
      // SLOP — remove from array then check it's gone
      items = items.filter(i => i.id !== targetId);
      if (items.find(i => i.id === targetId)) {
        throw new Error("removal failed");  // impossible by construction
      }
      
      // CLEAN
      items = items.filter(i => i.id !== targetId);
      ```
      
      ```go
      // SLOP — delete row then SELECT to confirm
      _, err := db.ExecContext(ctx, "DELETE FROM users WHERE id = $1", id)
      if err != nil { return err }
      row := db.QueryRowContext(ctx, "SELECT id FROM users WHERE id = $1", id)
      if err := row.Scan(&check); err != sql.ErrNoRows {
          return fmt.Errorf("delete verification failed")
      }
      
      // CLEAN
      _, err := db.ExecContext(ctx, "DELETE FROM users WHERE id = $1", id)
      if err != nil { return err }
      ```
      
      ```rust
      // SLOP — remove from HashMap then check absence
      map.remove(&key);
      if map.contains_key(&key) {
          panic!("removal failed");  // HashMap::remove is not broken
      }
      
      // CLEAN
      map.remove(&key);
      ```
      
      ### Broader pattern — same smell, different disguise
      
      Any of these are the same defect:
      
      - Calling a **setter** then immediately calling the **getter** to "confirm" the value changed.
      - **Writing** a file then **reading** it back to "verify" the write.
      - **Inserting** a row then **SELECT-ing** it to "confirm" the insert.
      - **Pushing** to an array then checking `.length` increased by 1.
      - **Assigning** a variable then asserting the variable equals the assigned value.
      
      **The contract of the operation IS the verification.** If you cannot trust the operation's return, the defect is in the operation — fix it there, not at the call site.
      
      ---
      
      ## Smell 4 — Negative-form names and conditions
      
      ### Why positive form wins
      
      Every negation forces the reader to mentally invert. One negation is tolerable. Two (`if !isNotReady`) is a logic puzzle. Codebases that default to negative naming accumulate double and triple negations that nobody can review confidently.
      
      Positive form reads in the direction of intent: "is this ready?" rather than "is this not-not-ready?"
      
      ### Naming
      
      | Negative (SMELL) | Positive (CLEAN) |
      |---|---|
      | `isNotValid` | `isValid` (invert branch) |
      | `isDisabled` | `isEnabled` |
      | `noErrors` | `isClean` / `errorsResolved` |
      | `notFound` | `found` (invert branch) |
      | `isNotEmpty` | `hasItems` / `isPopulated` |
      | `missingAuth` | `hasAuth` / `isAuthenticated` |
      | `cannotProceed` | `canProceed` (invert branch) |
      
      Name the **presence** of the quality you care about, not the absence of its opposite.
      
      ### Conditions
      
      ```python
      # SMELL — double negative
      if not is_invalid(token):
          proceed()
      
      # CLEAN — single positive check
      if is_valid(token):
          proceed()
      ```
      
      ```typescript
      // SMELL — negated boolean in branch
      if (!user.isNotVerified) {
        grantAccess();
      }
      
      // CLEAN — positive name, direct check
      if (user.isVerified) {
        grantAccess();
      }
      ```
      
      ```go
      // SMELL — inverted negative
      if !config.DisableLogging {
          log.Info("starting")
      }
      
      // CLEAN — positive flag
      if config.LoggingEnabled {
          log.Info("starting")
      }
      ```
      
      ```rust
      // SMELL — negated negative
      if !skip_validation {
          validate(&input)?;
      }
      
      // CLEAN — positive gate
      if should_validate {
          validate(&input)?;
      }
      ```
      
      ### When negation IS appropriate
      
      - **Early returns / guard clauses:** `if !authorized { return Err(...) }` — the negative form IS the intent (reject the bad case).
      - **Filtering out:** `items.filter(|x| !x.is_expired())` — the negation describes the keep/discard decision directly.
      - **Error state names:** `Error`, `Failed`, `Timeout` are negative concepts by nature — do not force them into positive wrappers like `isSuccessAbsent`.
      
      The rule is not "never use negation." The rule is: **when you have a choice between naming the presence and naming the absence, name the presence.** The branch logic follows from the name, not the other way around.
      
    • logging.md 10.5 KB
      # Logging — Cross-Language Methodology
      
      Every log line has exactly two legitimate readers: the operator reconstructing an incident and the developer reproducing a bug. A line that serves neither is cost — storage, noise, and attention stolen from the lines that matter. Everything below derives from that.
      
      This reference is deliberately stack-agnostic. It never tells you which logging library to use — the ecosystem table in SKILL.md owns stack defaults, and the project's existing practice overrides everything (Rule 0).
      
      ---
      
      ## Rule 0 — Discover the project's practice before emitting anything
      
      **BEFORE adding a single log line, find out how this project logs.** Grep for the logger initialization, a wrapper module, prior art in sibling files, and the project's agent docs (AGENTS.md / CLAUDE.md).
      
      | What you find | Required behavior |
      |---|---|
      | A designated logger or wrapper | Use it exactly — its levels, its field conventions, its error-passing shape. Copy the call shape of the nearest well-written call site, not your habit. |
      | A logging lib you like better | Irrelevant. NEVER introduce a second logging framework into a project that already has one. |
      | Raw `console.*` / `print` culture | Follow it for user-facing CLI output. For diagnostics, propose structured logging in your reply — do not unilaterally convert the project. |
      | **No logging at all** (library, small CLI, script) | **Respect the absence.** A library that suddenly logs is a behavior change its consumers never asked for; a 40-line script does not need a logging framework. If logging would genuinely help, say so in your reply — add it only when the user asks or the task itself is about observability. |
      
      Bypassing the project's designated logger with a bare `console.log` / `print` "just for this one line" is the logging equivalent of `as any`: it escapes every contract the project set up — stage routing, formatting, redaction, shipping.
      
      Serialization contracts (reserved field names, argument order, the key an Error must be passed under) are project knowledge. Discover the contract from the logger config and existing call sites; if the project's agent docs do not record it, record what you found there as part of your change.
      
      ## Greenfield — when you own the setup
      
      A new service earns exactly this much logging infrastructure, and no more:
      
      1. **One init module.** Callers import a ready logger; only the init module knows the stage. No call site ever checks `NODE_ENV`-style vars to decide how to log.
      2. **Stage split by environment.** Dev = human-readable (pretty, colorized). Prod = structured, machine-parseable (JSON to stdout). Same logger API on both — the stage changes the sink and format, never the call sites.
      3. **Level threshold per stage.** Dev = `debug`, prod = `info`, overridable with a `LOG_LEVEL`-style env var.
      4. **The stack's standard structured logger** (the ecosystem table in SKILL.md names the default) — never a hand-rolled one.
      5. **Error-serialization proof.** Before trusting error logging, pass a real Error/exception through the PROD formatter once and assert the output preserves type, message, and stack. Every structured-logging stack has a reserved-key contract, and violating it typically produces an empty `{}` where the stack trace should have been — discovered during the incident. This is a one-line test; write it at setup time and the whole bug class is dead permanently.
      
      ## Choosing the level — consumer, not severity
      
      **A level is a routing decision, not a severity vibe. Choose it by naming who consumes the line and what they do about it. If you cannot name the consumer, do not emit the line.**
      
      | Level | Consumer and action | The line earns this level when |
      |---|---|---|
      | `error` | Alerting wakes a human NOW | **The service failed** at a user-visible operation and cannot recover on its own. |
      | `warn` | Reviewed in batch — dashboards, weekly triage | The request SUCCEEDED but took an abnormal path: retry needed, fallback engaged, degraded mode, suspicious data. |
      | `info` | Read during an incident to reconstruct the timeline | A state transition without which the request's story does not reconstruct: session/job/connection created, completed, destroyed. |
      | `debug` | The developer reproducing locally | Detailed tracing. **Does not exist in prod.** |
      
      Two corollaries that get violated constantly:
      
      - **`error` means the SERVICE failed, not the request.** A 4xx is the client's mistake handled correctly — that is `warn` at most, `info` for routine misses. Reserve `error` for 5xx-class outcomes: the service could not do its job. Log 4xx as `error` and the alert channel drowns in noise from every crawler probing `/wp-admin`; a drowned alert channel is equivalent to no alerting.
      - **A failure logged at `info` is invisible.** If the message says "failed", the level is `warn` or `error` — never `info`.
      
      ## Placement — log decisions, not work
      
      Log where the system decides something, not where it does something:
      
      - **Boundaries** — request in / response out, calls to external systems (and their failures).
      - **State transitions** — create / complete / destroy of sessions, jobs, connections.
      - **Decision points** — retry chosen, fallback engaged, cache bypassed, degraded mode entered.
      - **The one place an error is finally handled.**
      
      Never log inside pure functions, utilities, or private helpers — callers with context log outcomes; internals stay silent. The mechanical rules:
      
      - **One event, one line.** Log-and-rethrow at every layer turns one incident into five look-alike incidents. Log where the error is handled; layers that only propagate stay silent.
      - **Answering the caller is not logging.** Converting a failure into a response — an HTTP 5xx body, an SSE error event, an error string returned to an LLM as a tool result, a non-zero exit code — satisfies the caller and leaves operations blind. This is the dominant finding when error paths are audited: the caller got an answer, the on-call got nothing. Every path that converts a failure into a caller-facing signal logs that failure exactly once, at the layer that handles it. When conversion layers stack, mark the error as logged at the handling layer (a symbol/flag on the error object) so outer catch-all handlers skip it — one incident, one line.
      - **Expected feedback returned to the caller is not an event.** Validation results delivered as a normal response — including tool output an LLM agent consumes ("string not found", lint findings, a sandbox-boundary notice) — are response content, not anomalies. Log only the genuine I/O and subprocess failures behind them, and security rejections.
      - **Mechanical logging belongs to middleware.** Request/response logging is wired once at the framework layer, never hand-assembled per handler. High-volume zero-signal paths (health probes, metrics scrapes) are excluded there as data — an exclusion set — not as scattered `if` statements.
      
      **No speculative logs.** "Might need it later" is not a consumer. A log line earns its place through evidence: a debugging session that burned rounds because this state was invisible (see the debugging bridge below), an incident postmortem, an alert that needs the field.
      
      ## The line contract
      
      - **The message is a stable, grep-able constant; data goes in fields.** `logger.warn({orderId, attempt}, "payment retry")` — never `` `retrying payment for ${orderId}` ``. An interpolated message cannot be counted, aggregated, or alerted on.
      - **Correlation or it did not happen.** Request-scoped lines carry the trace/request id; entity-scoped lines carry the entity id. A line you cannot join to its request is noise during the only moments logs matter.
      - **Name events semantically** (`session.destroy`, `payment.fallback`), never positionally ("Step 3"). Step numbers couple the log stream to today's call structure; the first refactor makes them lie.
      - **No secrets.** Tokens, credentials, session cookies, and PII never enter a log line; URLs are sanitized (strip or redact query params like `token`, `key`) before logging. A leaked log is a leaked credential.
      - **Payload content belongs to the tracing channel, not the log stream.** In LLM/agent systems, user messages, model responses, and tool outputs are captured by the tracing product (turn recorder, trace exporter); a log line carries a hash, a length, and at most a short excerpt for correlation. Dumping conversation content into logs bloats the store and leaks data the log pipeline was never hardened for.
      - **The logging path may not break the program.** If a log call can itself fail (serializing exotic state, a wrapper that touches I/O), that failure is caught, downgraded to a `warn` through a channel that cannot fail, and the operation continues. An empty catch around logging is still an empty catch.
      
      ## Anti-patterns
      
      | Anti-pattern | Why it fails |
      |---|---|
      | `console.*` / `print` bypassing the project's designated logger | Escapes stage routing, redaction, and shipping — invisible in prod |
      | Introducing a logging framework to a project that has none | Uninvited behavior change; Rule 0 violation |
      | 4xx logged as `error` | Alert noise buries real pages |
      | Log-and-rethrow at every layer | One incident looks like five |
      | Failure converted to a caller response (5xx body, SSE error, LLM tool-error string, exit code) with no log | The caller got an answer; operations got nothing |
      | Logging expected validation feedback that is returned to the caller | Response content, not an event — buries real failures |
      | Variables interpolated into the message string | Un-aggregatable, un-alertable |
      | "Might need it later" logs | No consumer → pure cost |
      | Debug-time prints promoted to permanent `info` | Narration, not state transitions |
      | Trusting Error serialization without the proof | `error: {}` in prod, discovered during the incident |
      
      ## Debugging bridge — how logs earn their place
      
      When a `debugging`-skill session takes extra rounds *because state was invisible* — no line told you which branch ran, what the value was, whether the fallback engaged — that invisibility is a defect adjacent to the bug. **The fix ships with the log line that would have made diagnosis one round**: placed at a decision point, leveled by consumer, fields not interpolation, through the project's designated logger.
      
      The inverse also holds: every temporary `print` / `dbg!` / `console.log` planted *during* diagnosis is a debug artifact and gets scrubbed at cleanup. The triage between the two is the consumer test — a line whose ongoing consumer you can name is part of the fix; a line that only served today's session is an artifact.
      
  • scripts
    • go
      • templates
        • .editorconfig 206 B · in bundle
        • .golangci.yml 1.5 KB
          version: "2"
          
          run:
            timeout: 5m
            tests: true
            modules-download-mode: readonly
          
          linters:
            default: none
            enable:
              - govet
              - staticcheck
              - errcheck
              - errorlint
              - nilerr
              - nilnil
              - bodyclose
              - rowserrcheck
              - sqlclosecheck
              - contextcheck
              - fatcontext
              - copyloopvar
              - intrange
              - usetesting
              - testifylint
              - gofumpt
              - goimports
              - whitespace
              - misspell
              - unconvert
              - unparam
              - ineffassign
              - dupword
              - gocognit
              - gocyclo
              - funlen
              - lll
              - nestif
              - dupl
              - revive
              - unused
              - exhaustive
              - gosec
              - sloglint
              - perfsprint
              - prealloc
              - makezero
          
          linters-settings:
            errcheck:
              check-type-assertions: true
              check-blank: true
            errorlint:
              errorf: true
              asserts: true
              comparison: true
            gocognit:
              min-complexity: 25
            gocyclo:
              min-complexity: 15
            funlen:
              lines: 90
              statements: 60
            lll:
              line-length: 120
              tab-width: 4
            nestif:
              min-complexity: 4
            exhaustive:
              default-signifies-exhaustive: false
              check: [switch, map]
            sloglint:
              no-mixed-args: true
              attr-only: true
              no-global: all
              context: scope
              static-msg: true
              no-raw-keys: true
              key-naming-case: snake
          
          formatters:
            enable:
              - gofumpt
              - goimports
          
          issues:
            max-issues-per-linter: 0
            max-same-issues: 0
            exclude-rules:
              - path: _test\\.go
                linters: [funlen, lll, dupl, gosec]
              - path: \\.pb\\.go$
                linters: [all]
              - path: \\.connect\\.go$
                linters: [all]
          
        • AGENTS.md.tmpl 845 B · in bundle
        • ci.yml 863 B
          name: ci
          on:
            pull_request:
            push:
              branches: [main]
          
          jobs:
            ci:
              runs-on: ubuntu-latest
              steps:
                - uses: actions/checkout@v4
                - uses: actions/setup-go@v5
                  with:
                    go-version: '1.23'
                    cache: true
          
                - name: Install tools
                  run: |
                    go install mvdan.cc/gofumpt@latest
                    go install github.com/golangci/golangci-lint/cmd/golangci-lint@v2.0.0
                    go install go.uber.org/nilaway/cmd/nilaway@latest
                    go install github.com/go-task/task/v3/cmd/task@latest
          
                - name: Format check
                  run: gofumpt -l . | (! grep .)
          
                - name: Lint
                  run: golangci-lint run --timeout 5m ./...
          
                - name: Nilaway
                  run: nilaway ./... || true
          
                - name: Test
                  run: go test -race -shuffle=on -count=1 ./...
          
                - name: Build
                  run: go build -trimpath ./...
          
        • config.go 643 B · in bundle
        • gitignore 99 B · in bundle
        • main.go.tmpl 329 B · in bundle
        • README.md.tmpl 207 B · in bundle
        • run.go 350 B · in bundle
        • Taskfile.yml 706 B
          version: '3'
          
          vars:
            BINARY: server
            PKG: ./cmd/server
          
          tasks:
            default:
              deps: [fmt, lint, test]
          
            fmt:
              cmds:
                - gofumpt -w .
                - goimports -w -local "$(go list -m)" .
          
            lint:
              cmds:
                - golangci-lint run --timeout 5m ./...
                - nilaway ./... || true
          
            test:
              cmds:
                - go test -race -shuffle=on -count=1 ./...
          
            test-cover:
              cmds:
                - go test -race -shuffle=on -count=1 -coverprofile=coverage.out ./...
                - go tool cover -html=coverage.out -o coverage.html
          
            build:
              cmds:
                - go build -trimpath -ldflags="-s -w" -o bin/{{.BINARY}} {{.PKG}}
          
            run:
              deps: [build]
              cmds:
                - ./bin/{{.BINARY}}
          
            ci:
              deps: [fmt, lint, test, build]
          
      • check-no-excuse-rules.sh 7.1 KB
        #!/usr/bin/env bash
        # No-excuse rule checker for Go files.
        # Mirrors the philosophy of python-programmer / typescript-programmer / rust-programmer scripts:
        # only rules that can be enforced via pure text matching live here.
        # Everything semantic is on golangci-lint + nilaway + go test -race.
        
        set -euo pipefail
        
        if [ $# -eq 0 ]; then
            echo "Usage: $0 <file.go> [file.go ...]" >&2
            exit 2
        fi
        
        violations=0
        report() {
            local file="$1"
            local line="$2"
            local rule="$3"
            local detail="$4"
            echo "::error file=${file},line=${line}::[${rule}] ${detail}" >&2
            violations=$((violations + 1))
        }
        
        is_test_file() {
            case "$1" in
                *_test.go) return 0 ;;
            esac
            return 1
        }
        
        is_generated_file() {
            local file="$1"
            case "$file" in
                *.pb.go|*.connect.go|*.gen.go) return 0 ;;
                *_string.go) return 0 ;;
            esac
            # First-line check for "Code generated ... DO NOT EDIT." (the official marker)
            if [ -f "$file" ]; then
                head -n 5 "$file" 2>/dev/null | grep -qE "^// Code generated .* DO NOT EDIT\.$" && return 0
            fi
            return 1
        }
        
        for file in "$@"; do
            [ -f "$file" ] || continue
            case "$file" in
                *.go) ;;
                *) continue ;;
            esac
        
            if is_generated_file "$file"; then
                continue
            fi
        
            in_test=0
            if is_test_file "$file"; then
                in_test=1
            fi
        
            line_no=0
            while IFS= read -r raw_line || [ -n "$raw_line" ]; do
                line_no=$((line_no + 1))
                line="$raw_line"
        
                # Strip line comments before pattern checks
                # (block comments are not handled — keep the rules robust to that limitation).
                code_only="${line%%//*}"
        
                # ── Exemption marker: // no-excuse-ok: <reason> ──────────────────
                if [[ "$line" =~ //[[:space:]]*no-excuse-ok:[[:space:]]*.+ ]]; then
                    continue
                fi
        
                # ── Rule: no `_ = err` (silent error swallow) ────────────────────
                # The errcheck linter catches most of these but the `_ = err` form
                # specifically slips through if used with named returns.
                if [[ "$code_only" =~ ^[[:space:]]*_[[:space:]]*=[[:space:]]*err[[:space:]]*$ ]] ||
                   [[ "$code_only" =~ ^[[:space:]]*_[[:space:]]*=[[:space:]]*err[[:space:]]*[^a-zA-Z0-9_].*$ ]]; then
                    if [ "$in_test" -eq 0 ]; then
                        report "$file" "$line_no" "silent-err" "discarding err with '_ = err' — handle the error"
                    fi
                fi
        
                # ── Rule: no `panic(` in non-test, non-main code ─────────────────
                # Allowed in main(), allowed in tests, allowed with explicit marker.
                if [[ "$code_only" =~ [^a-zA-Z0-9_]panic\( ]] || [[ "$code_only" =~ ^[[:space:]]*panic\( ]]; then
                    if [ "$in_test" -eq 0 ]; then
                        # main package main.go is the one exception
                        pkg_line=$(head -n 5 "$file" 2>/dev/null | grep -m1 "^package ")
                        if [[ "$pkg_line" != "package main" ]]; then
                            report "$file" "$line_no" "panic-in-lib" "panic outside main/test — return error instead"
                        fi
                    fi
                fi
        
                # ── Rule: no `log.Fatal` / `log.Panic` in library code ───────────
                if [[ "$code_only" =~ log\.(Fatal|Panic)(f|ln)?\( ]]; then
                    if [ "$in_test" -eq 0 ]; then
                        pkg_line=$(head -n 5 "$file" 2>/dev/null | grep -m1 "^package ")
                        if [[ "$pkg_line" != "package main" ]]; then
                            report "$file" "$line_no" "log-fatal-in-lib" "log.Fatal/Panic outside main — return error"
                        fi
                    fi
                fi
        
                # ── Rule: no init() functions ─────────────────────────────────
                # init() ruins testability and creates hidden global state.
                # Exception: //go:build constraint files and generated code.
                if [[ "$code_only" =~ ^func[[:space:]]+init\(\)[[:space:]]*\{ ]]; then
                    report "$file" "$line_no" "no-init-func" "init() ruins testability — use explicit constructor"
                fi
        
                # ── Rule: no `time.Sleep` in non-test code ──────────────────────
                if [[ "$code_only" =~ time\.Sleep\( ]]; then
                    if [ "$in_test" -eq 0 ]; then
                        report "$file" "$line_no" "time-sleep" "time.Sleep in production code — use ticker/timer with ctx"
                    fi
                fi
        
                # ── Rule: no `context.Background()` inside functions (only in main/init/test) ──
                if [[ "$code_only" =~ context\.Background\(\) ]]; then
                    if [ "$in_test" -eq 0 ]; then
                        pkg_line=$(head -n 5 "$file" 2>/dev/null | grep -m1 "^package ")
                        if [[ "$pkg_line" != "package main" ]]; then
                            report "$file" "$line_no" "ctx-background-in-lib" "context.Background() outside main — propagate ctx as parameter"
                        fi
                    fi
                fi
        
                # ── Rule: no `interface{}` (use `any`, the alias from Go 1.18+) ──
                if [[ "$code_only" =~ interface\{\} ]]; then
                    report "$file" "$line_no" "old-interface-empty" "use 'any' instead of 'interface{}' (Go 1.18+)"
                fi
        
                # ── Rule: no bare `fmt.Println` for logging (use slog) ───────────
                # Acceptable in main.go (CLI output) and tests. Reject in libraries.
                if [[ "$code_only" =~ fmt\.(Print|Println|Printf)\( ]]; then
                    if [ "$in_test" -eq 0 ]; then
                        pkg_line=$(head -n 5 "$file" 2>/dev/null | grep -m1 "^package ")
                        if [[ "$pkg_line" != "package main" ]]; then
                            report "$file" "$line_no" "fmt-print-in-lib" "fmt.Print* in library — use slog for structured logs"
                        fi
                    fi
                fi
        
                # ── Rule: no `nolint` directive without reason ───────────────────
                if [[ "$line" =~ //nolint(:|$| ) ]]; then
                    if ! [[ "$line" =~ //nolint:[a-zA-Z0-9_,-]+[[:space:]]+//[[:space:]]*[^[:space:]] ]]; then
                        report "$file" "$line_no" "nolint-no-reason" "//nolint requires a // reason after the linter list"
                    fi
                fi
        
                # ── Rule: no TODO / FIXME without an issue link or owner ─────────
                # Check the full line — TODOs live in comments, which $code_only has stripped.
                if echo "$line" | grep -qE '(TODO|FIXME|XXX)([[:space:]]|:)'; then
                    if ! echo "$line" | grep -qE '(TODO|FIXME|XXX).*[(@[]'; then
                        report "$file" "$line_no" "todo-no-owner" "TODO/FIXME requires (#issue) or @owner attribution"
                    fi
                fi
            done < "$file"
        done
        
        if [ "$violations" -gt 0 ]; then
            echo "" >&2
            echo "go-programmer: $violations violation(s). Run also:" >&2
            echo "  gofumpt -l ." >&2
            echo "  golangci-lint run --timeout 5m ./..." >&2
            echo "  nilaway ./..." >&2
            echo "  go test -race -shuffle=on -count=1 ./..." >&2
            exit 1
        fi
        
        echo "go-programmer: no-excuse rules passed for $# file(s)."
        
      • new-project.py 4.4 KB
        #!/usr/bin/env -S uv run --script
        # /// script
        # requires-python = ">=3.11"
        # dependencies = [
        #     "typer",
        #     "rich",
        # ]
        # ///
        
        # ─── How to run ───
        # 1. Install uv (if not installed):
        #      curl -LsSf https://astral.sh/uv/install.sh | sh
        # 2. Run:
        #      uv run new-project.py myservice
        #      uv run new-project.py myservice --module github.com/your-org/myservice
        # ──────────────────
        #
        # Creates a new Go project with the canonical strict layout:
        #   - go.mod with go 1.23
        #   - .golangci.yml (v2, strict bundle)
        #   - Taskfile.yml (fmt + lint + test + build)
        #   - cmd/server/main.go entrypoint
        #   - internal/{cmd,config,api,domain,obs} skeletons
        #   - .github/workflows/ci.yml
        #
        # Templates live in ./templates/ — keep this script under 250 pure LOC.
        
        from __future__ import annotations
        
        import subprocess
        import sys
        from pathlib import Path
        from string import Template
        
        import typer
        from rich.console import Console
        
        console = Console(stderr=True)
        
        TEMPLATES_DIR = Path(__file__).parent / "templates"
        
        
        def _render(template_file: str, **subs: str) -> str:
            """Read a template file and apply $placeholder substitutions.
        
            Uses string.Template ($name) so Go/YAML curly braces stay literal.
            """
            raw = (TEMPLATES_DIR / template_file).read_text()
            if not subs:
                return raw
            return Template(raw).substitute(**subs)
        
        
        # (template-file → relative output path; is_format = .format() is run)
        FILES: list[tuple[str, str, bool]] = [
            (".golangci.yml",   ".golangci.yml",                   False),
            ("Taskfile.yml",    "Taskfile.yml",                    False),
            (".editorconfig",   ".editorconfig",                   False),
            ("gitignore",       ".gitignore",                      False),
            ("ci.yml",          ".github/workflows/ci.yml",        False),
            ("run.go",          "internal/cmd/run.go",             False),
            ("config.go",       "internal/config/config.go",       False),
            ("main.go.tmpl",    "cmd/server/main.go",              True),
            ("AGENTS.md.tmpl",  "AGENTS.md",                       True),
            ("README.md.tmpl",  "README.md",                       True),
        ]
        
        
        def _init_go_module(project_dir: Path, module: str) -> None:
            try:
                subprocess.run(
                    ["go", "mod", "init", module],
                    cwd=project_dir,
                    check=True,
                    capture_output=True,
                )
                console.print(f"  [dim]ran[/] go mod init {module}")
            except (subprocess.CalledProcessError, FileNotFoundError) as e:
                console.print(f"  [yellow]warn[/] go mod init failed ({e}); writing fallback go.mod")
                (project_dir / "go.mod").write_text(f"module {module}\n\ngo 1.23\n")
        
        
        def _create_layout(project_dir: Path) -> None:
            """Create the canonical internal/ tree."""
            subdirs = [
                "cmd/server",
                "internal/cmd",
                "internal/config",
                "internal/api",
                "internal/domain",
                "internal/obs",
                ".github/workflows",
            ]
            for sd in subdirs:
                (project_dir / sd).mkdir(parents=True)
        
        
        def _write_files(project_dir: Path, name: str, module: str, purpose: str) -> None:
            """Render every template into the project tree."""
            for tmpl_name, out_rel, is_format in FILES:
                subs = (
                    {"name": name, "module": module, "short_purpose": purpose}
                    if is_format
                    else {}
                )
                content = _render(tmpl_name, **subs)
                out_path = project_dir / out_rel
                out_path.parent.mkdir(parents=True, exist_ok=True)
                out_path.write_text(content)
                console.print(f"  [dim]wrote[/] {out_rel}")
        
        
        def main(
            name: str,
            path: str = typer.Option(".", help="Parent dir"),
            module: str = typer.Option("", help="Go module path; default: <name>"),
            purpose: str = typer.Option("HTTP", help="Short purpose for AGENTS.md"),
        ) -> None:
            """Scaffold a new Go project with the strict toolchain."""
            project_dir = Path(path) / name
            if project_dir.exists():
                console.print(f"[red]✗[/red] {project_dir} already exists")
                sys.exit(1)
        
            module_path = module or name
        
            project_dir.mkdir(parents=True)
            _create_layout(project_dir)
            _init_go_module(project_dir, module_path)
            _write_files(project_dir, name, module_path, purpose)
        
            console.print(f"\n[bold green]Done![/] cd {project_dir}")
            console.print("  go get github.com/caarlos0/env/v11")
            console.print("  task        # fmt + lint + test")
        
        
        if __name__ == "__main__":
            typer.run(main)
        
    • python
      • check-no-excuse-rules.py 24.9 KB
        #!/usr/bin/env -S uv run --script
        # /// script
        # requires-python = ">=3.11"
        # dependencies = []
        # ///
        # noqa: SIZE_OK  — single self-contained checker, splitting adds import ceremony for no readability gain
        """Check Python files for no-excuse violations.
        
        The python-programmer skill enforces these rules. Run after editing.
        
        Rules:
          cast-any             - cast(Any, ...) / cast(typing.Any, ...) / typing.cast(Any, ...)
          type-ignore          - `# type: ignore` comments (any variant)
          pyright-ignore       - `# pyright: ignore` comments (any variant)
          bare-except          - `except:` with no class
          silent-except        - `except X: pass` or `except X: ...` (single statement)
          no-asyncio           - `import asyncio` / `from asyncio import ...`
                                 Opt out per import line: trailing `# noqa: ANYIO_OK`
          no-pandas            - `import pandas` / `from pandas import ...`
                                 Opt out per import line: trailing `# noqa: PANDAS_OK`
          mutable-dataclass    - @dataclass without frozen=True
                                 Opt out: trailing `# noqa: MUTABLE_OK`
          missing-slots        - @dataclass without slots=True
                                 Opt out: trailing `# noqa: SLOTS_OK`
          raw-dict-return      - function returns bare `dict` type
                                 Opt out: trailing `# noqa: DICT_OK`
          missing-assert-never - match statement without assert_never in default case
                                 Opt out: `# noqa: MATCH_OK` on the match line
          generic-exception    - raise ValueError/TypeError/RuntimeError with bare string
                                 Opt out: trailing `# noqa: GENERIC_ERR_OK`
          no-object            - `object` used as type annotation (param, return, variable)
                                 Opt out: trailing `# noqa: OBJECT_OK`
          if-elif-on-variant   - isinstance/enum-comparison if/elif chain (should be match/case)
                                 Opt out: trailing `# noqa: IF_VARIANT_OK`
          oversized-module     - file exceeds 250 pure LOC (non-blank, non-comment)
                                 Opt out: `# noqa: SIZE_OK` in first 10 lines
          broad-except         - `except Exception` / `except BaseException` (too broad)
                                 Opt out: trailing `# noqa: BROAD_EXCEPT_OK`
        
        Usage:
          check-no-excuse-rules.py <file-or-dir>...
        
        Exit codes:
          0 - no violations
          1 - one or more violations
          2 - input error (path missing, etc.)
        """
        from __future__ import annotations
        
        import ast
        import io
        import re
        import sys
        import tokenize
        from collections.abc import Iterable
        from dataclasses import dataclass
        from pathlib import Path
        
        EXCLUDED_DIRS = frozenset({
            ".git", ".hg", ".svn", ".venv", "venv", "env", ".env",
            "__pycache__", ".tox", ".nox", "dist", "build", ".eggs",
            ".ruff_cache", ".mypy_cache", ".pytest_cache", ".basedpyright",
            "node_modules",
        })
        
        SUPPRESSION_RE = re.compile(r"#\s*(type|pyright)\s*:\s*ignore\b")
        ANYIO_OK_RE = re.compile(r"#\s*noqa:\s*ANYIO_OK\b")
        PANDAS_OK_RE = re.compile(r"#\s*noqa:\s*PANDAS_OK\b")
        
        BANNED_IMPORTS: dict[str, tuple[str, re.Pattern[str], str]] = {
            "asyncio": (
                "no-asyncio",
                ANYIO_OK_RE,
                "import asyncio - use anyio (opt out: trailing `# noqa: ANYIO_OK`)",
            ),
            "pandas": (
                "no-pandas",
                PANDAS_OK_RE,
                "import pandas - use polars (opt out: trailing `# noqa: PANDAS_OK`)",
            ),
        }
        
        # Opt-out patterns for new Rust-like rules
        MUTABLE_OK_RE: re.Pattern[str] = re.compile(r"#\s*noqa:\s*MUTABLE_OK")
        SLOTS_OK_RE: re.Pattern[str] = re.compile(r"#\s*noqa:\s*SLOTS_OK")
        DICT_OK_RE: re.Pattern[str] = re.compile(r"#\s*noqa:\s*DICT_OK")
        MATCH_OK_RE: re.Pattern[str] = re.compile(r"#\s*noqa:\s*MATCH_OK")
        GENERIC_ERR_OK_RE: re.Pattern[str] = re.compile(r"#\s*noqa:\s*GENERIC_ERR_OK")
        OBJECT_OK_RE: re.Pattern[str] = re.compile(r"#\s*noqa:\s*OBJECT_OK")
        IF_VARIANT_OK_RE: re.Pattern[str] = re.compile(r"#\s*noqa:\s*IF_VARIANT_OK")
        SIZE_OK_RE: re.Pattern[str] = re.compile(r"#\s*noqa:\s*SIZE_OK")
        BROAD_EXCEPT_OK_RE: re.Pattern[str] = re.compile(r"#\s*noqa:\s*BROAD_EXCEPT_OK")
        
        PURE_LOC_LIMIT: int = 250
        
        
        @dataclass(frozen=True, slots=True)
        class Violation:
            rule: str
            file: Path
            line: int
            col: int
            message: str
        
            def render(self) -> str:
                return f"{self.file}:{self.line}:{self.col}: [{self.rule}] {self.message}"
        
        
        def discover_files(inputs: Iterable[Path]) -> list[Path]:
            seen: set[Path] = set()
            for raw in inputs:
                path = raw.resolve()
                if not path.exists():
                    print(f"check-no-excuse-rules: input does not exist: {path}", file=sys.stderr)
                    sys.exit(2)
                if path.is_file():
                    if path.suffix == ".py":
                        seen.add(path)
                    continue
                for child in path.rglob("*.py"):
                    if any(part in EXCLUDED_DIRS for part in child.parts):
                        continue
                    seen.add(child)
            return sorted(seen)
        
        
        def is_any_node(node: ast.AST) -> bool:
            if isinstance(node, ast.Name):
                return node.id == "Any"
            if isinstance(node, ast.Attribute):
                return node.attr == "Any"
            return False
        
        
        def is_cast_callable(node: ast.AST) -> bool:
            if isinstance(node, ast.Name):
                return node.id == "cast"
            if isinstance(node, ast.Attribute):
                return node.attr == "cast"
            return False
        
        
        def find_node_violations(tree: ast.AST, file: Path) -> list[Violation]:
            violations: list[Violation] = []
        
            for node in ast.walk(tree):
                if (
                    isinstance(node, ast.Call)
                    and is_cast_callable(node.func)
                    and node.args
                    and is_any_node(node.args[0])
                ):
                    violations.append(Violation(
                        rule="cast-any",
                        file=file,
                        line=node.lineno,
                        col=node.col_offset + 1,
                        message="cast(Any, ...) - narrow with isinstance/TypeGuard or use a Protocol/TypedDict",
                    ))
        
                if isinstance(node, ast.ExceptHandler):
                    if node.type is None:
                        violations.append(Violation(
                            rule="bare-except",
                            file=file,
                            line=node.lineno,
                            col=node.col_offset + 1,
                            message="bare `except:` - catch the narrowest exception you mean",
                        ))
        
                    if len(node.body) != 1:
                        continue
        
                    body = node.body[0]
                    if isinstance(body, ast.Pass):
                        violations.append(Violation(
                            rule="silent-except",
                            file=file,
                            line=body.lineno,
                            col=body.col_offset + 1,
                            message="silent `except: pass` - log, re-raise, or actually handle the error",
                        ))
                    elif (
                        isinstance(body, ast.Expr)
                        and isinstance(body.value, ast.Constant)
                        and body.value.value is Ellipsis
                    ):
                        violations.append(Violation(
                            rule="silent-except",
                            file=file,
                            line=body.lineno,
                            col=body.col_offset + 1,
                            message="silent `except: ...` - log, re-raise, or actually handle the error",
                        ))
        
            return violations
        
        
        def find_import_violations(tree: ast.AST, source_lines: list[str], file: Path) -> list[Violation]:
            violations: list[Violation] = []
        
            def line_text(lineno: int) -> str:
                index = lineno - 1
                return source_lines[index] if 0 <= index < len(source_lines) else ""
        
            for node in ast.walk(tree):
                if isinstance(node, ast.Import):  # noqa: IF_VARIANT_OK  — filtering walk, not closed union
                    for alias in node.names:
                        top = alias.name.split(".")[0]
                        if top not in BANNED_IMPORTS:
                            continue
                        rule, opt_re, message = BANNED_IMPORTS[top]
                        if opt_re.search(line_text(node.lineno)):
                            continue
                        violations.append(Violation(
                            rule=rule,
                            file=file,
                            line=node.lineno,
                            col=node.col_offset + 1,
                            message=message,
                        ))
                elif isinstance(node, ast.ImportFrom):
                    top = (node.module or "").split(".")[0]
                    if top not in BANNED_IMPORTS:
                        continue
                    rule, opt_re, message = BANNED_IMPORTS[top]
                    if opt_re.search(line_text(node.lineno)):
                        continue
                    violations.append(Violation(
                        rule=rule,
                        file=file,
                        line=node.lineno,
                        col=node.col_offset + 1,
                        message=message,
                    ))
        
            return violations
        
        
        def find_comment_violations(source: str, file: Path) -> list[Violation]:
            """Use tokenize so we don't false-match `# type: ignore` inside string literals."""
            violations: list[Violation] = []
            try:
                tokens = list(tokenize.generate_tokens(io.StringIO(source).readline))
            except tokenize.TokenError as exc:
                print(f"check-no-excuse-rules: tokenize failed for {file}: {exc}", file=sys.stderr)
                return violations
        
            for tok in tokens:
                if tok.type != tokenize.COMMENT:
                    continue
                match = SUPPRESSION_RE.search(tok.string)
                if not match:
                    continue
                kind = match.group(1)
                rule = "type-ignore" if kind == "type" else "pyright-ignore"
                violations.append(Violation(
                    rule=rule,
                    file=file,
                    line=tok.start[0],
                    col=tok.start[1] + match.start() + 1,
                    message=f"`# {kind}: ignore` - fix the underlying type instead",
                ))
            return violations
        
        
        # ─────────────────────────────────────────────────────────────────
        # Rust-like pattern checks
        # ─────────────────────────────────────────────────────────────────
        
        
        def _has_keyword(decorator_node: ast.Call, keyword: str) -> bool | None:
            """Check if a decorator call has a specific keyword argument.
        
            Returns True if keyword is True, False if keyword is False or absent, None if not a Call.
            """
            for kw in decorator_node.keywords:
                if kw.arg == keyword and isinstance(kw.value, ast.Constant):
                    return bool(kw.value.value)
            return False
        
        
        def _is_dataclass_decorator(node: ast.expr) -> tuple[bool, ast.Call | None]:
            """Return (is_dataclass, call_node_or_None)."""
            if isinstance(node, ast.Name) and node.id == "dataclass":
                return True, None
            if isinstance(node, ast.Attribute) and node.attr == "dataclass":
                return True, None
            if isinstance(node, ast.Call):
                inner, _ = _is_dataclass_decorator(node.func)
                if inner:
                    return True, node
            return False, None
        
        
        def find_dataclass_violations(
            tree: ast.Module, source_lines: list[str], file: Path,
        ) -> list[Violation]:
            """Check @dataclass decorators for frozen=True and slots=True."""
            violations: list[Violation] = []
            for node in ast.walk(tree):
                if not isinstance(node, ast.ClassDef):
                    continue
                for dec in node.decorator_list:
                    is_dc, call_node = _is_dataclass_decorator(dec)
                    if not is_dc:
                        continue
        
                    # Get the line of the decorator for opt-out check
                    dec_line = source_lines[dec.lineno - 1] if dec.lineno <= len(source_lines) else ""
        
                    if call_node is not None:
                        has_frozen = _has_keyword(call_node, "frozen")
                        has_slots = _has_keyword(call_node, "slots")
                    else:
                        # bare @dataclass with no arguments
                        has_frozen = False
                        has_slots = False
        
                    if not has_frozen and not MUTABLE_OK_RE.search(dec_line):
                        violations.append(Violation(
                            rule="mutable-dataclass",
                            file=file,
                            line=dec.lineno,
                            col=dec.col_offset + 1,
                            message=f"class {node.name}: @dataclass without frozen=True",
                        ))
        
                    if not has_slots and not SLOTS_OK_RE.search(dec_line):
                        violations.append(Violation(
                            rule="missing-slots",
                            file=file,
                            line=dec.lineno,
                            col=dec.col_offset + 1,
                            message=f"class {node.name}: @dataclass without slots=True",
                        ))
            return violations
        
        
        def find_dict_return_violations(
            tree: ast.Module, source_lines: list[str], file: Path,
        ) -> list[Violation]:
            """Check for functions returning bare `dict` type."""
            violations: list[Violation] = []
            for node in ast.walk(tree):
                if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                    continue
                ret = node.returns
                if ret is None:
                    continue
                # Check for bare `dict` return annotation
                is_bare_dict = (
                    (isinstance(ret, ast.Name) and ret.id == "dict")
                    or (isinstance(ret, ast.Attribute) and ret.attr == "dict")
                )
                if not is_bare_dict:
                    continue
        
                func_line = source_lines[node.lineno - 1] if node.lineno <= len(source_lines) else ""
                if DICT_OK_RE.search(func_line):
                    continue
        
                violations.append(Violation(
                    rule="raw-dict-return",
                    file=file,
                    line=node.lineno,
                    col=node.col_offset + 1,
                    message=f"`{node.name}` returns bare dict - use TypedDict/dataclass/Pydantic model",
                ))
            return violations
        
        
        def find_match_violations(
            tree: ast.Module, source_lines: list[str], file: Path,
        ) -> list[Violation]:
            """Check match statements for assert_never in default case."""
            violations: list[Violation] = []
            for node in ast.walk(tree):
                if not isinstance(node, ast.Match):
                    continue
        
                match_line = source_lines[node.lineno - 1] if node.lineno <= len(source_lines) else ""
                if MATCH_OK_RE.search(match_line):
                    continue
        
                has_assert_never = False
                for case in node.cases:
                    # Wildcard: `case _:` -> MatchAs(pattern=None, name=None)
                    #           `case _ as x:` -> MatchAs(pattern=MatchAs(pattern=None, name=None), name="x")
                    pattern = case.pattern
                    is_wildcard = (
                        isinstance(pattern, ast.MatchAs)
                        and (
                            pattern.pattern is None
                            or (
                                isinstance(pattern.pattern, ast.MatchAs)
                                and pattern.pattern.pattern is None
                                and pattern.pattern.name is None
                            )
                        )
                    )
                    if not is_wildcard:
                        continue
                    # Check if body contains assert_never call
                    for stmt in case.body:
                        if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
                            func = stmt.value.func
                            if (
                                (isinstance(func, ast.Name) and func.id == "assert_never")
                                or (isinstance(func, ast.Attribute) and func.attr == "assert_never")
                            ):
                                has_assert_never = True
                                break
        
                if not has_assert_never:
                    violations.append(Violation(
                        rule="missing-assert-never",
                        file=file,
                        line=node.lineno,
                        col=node.col_offset + 1,
                        message="match without `case _: assert_never(x)` default",
                    ))
            return violations
        
        
        def find_generic_exception_violations(
            tree: ast.Module, source_lines: list[str], file: Path,
        ) -> list[Violation]:
            """Check for raise ValueError/TypeError/RuntimeError with bare string or f-string."""
            GENERIC_EXCEPTIONS = {"ValueError", "TypeError", "RuntimeError", "KeyError"}
            violations: list[Violation] = []
            for node in ast.walk(tree):
                if not isinstance(node, ast.Raise) or node.exc is None:
                    continue
                exc = node.exc
                # Match: raise SomeError("string literal")
                if not isinstance(exc, ast.Call):
                    continue
                func = exc.func
                exc_name: str | None = None
                if isinstance(func, ast.Name) and func.id in GENERIC_EXCEPTIONS:
                    exc_name = func.id
                elif isinstance(func, ast.Attribute) and func.attr in GENERIC_EXCEPTIONS:
                    exc_name = func.attr
                if exc_name is None:
                    continue
                # Check if all arguments are string literals or f-strings
                if not exc.args:
                    continue
                all_str = all(
                    (isinstance(arg, ast.Constant) and isinstance(arg.value, str))
                    or isinstance(arg, ast.JoinedStr)
                    for arg in exc.args
                )
                if not all_str:
                    continue
        
                raise_line = source_lines[node.lineno - 1] if node.lineno <= len(source_lines) else ""
                if GENERIC_ERR_OK_RE.search(raise_line):
                    continue
        
                violations.append(Violation(
                    rule="generic-exception",
                    file=file,
                    line=node.lineno,
                    col=node.col_offset + 1,
                    message=f"`raise {exc_name}(\"...\")` - define a typed error class instead",
                ))
            return violations
        
        
        def _is_isinstance_test(node: ast.expr) -> bool:
            """Check if node is an isinstance() call."""
            return (
                isinstance(node, ast.Call)
                and isinstance(node.func, ast.Name)
                and node.func.id == "isinstance"
            )
        
        
        def _is_enum_comparison(node: ast.expr) -> bool:
            """Check if node is `x == Enum.VALUE` or `x is Enum.VALUE`."""
            if isinstance(node, ast.Compare) and len(node.ops) == 1:
                op = node.ops[0]
                if isinstance(op, (ast.Eq, ast.Is)):
                    comparator = node.comparators[0]
                    # x == Enum.VALUE  (attribute access on the right)
                    if isinstance(comparator, ast.Attribute):
                        return True
                    # Enum.VALUE == x  (attribute access on the left)
                    if isinstance(node.left, ast.Attribute):
                        return True
            return False
        
        
        def find_object_annotation_violations(
            tree: ast.Module, source_lines: list[str], file: Path,
        ) -> list[Violation]:
            """Check for `object` used as a type annotation."""
            violations: list[Violation] = []
        
            def _check_annotation(ann: ast.expr | None) -> None:
                if ann is None:
                    return
                for child in ast.walk(ann):
                    if isinstance(child, ast.Name) and child.id == "object":
                        line = source_lines[child.lineno - 1] if child.lineno <= len(source_lines) else ""
                        if OBJECT_OK_RE.search(line):
                            return
                        violations.append(Violation(
                            rule="no-object",
                            file=file,
                            line=child.lineno,
                            col=child.col_offset + 1,
                            message="`object` as type annotation \u2014 use Protocol, TypeVar, or union",
                        ))
        
            for node in ast.walk(tree):
                match node:  # noqa: MATCH_OK  — filtering walk, not discriminating a closed union
                    case ast.FunctionDef() | ast.AsyncFunctionDef():
                        all_args = (
                            node.args.args
                            + node.args.posonlyargs
                            + node.args.kwonlyargs
                        )
                        for arg in all_args:
                            _check_annotation(arg.annotation)
                        if node.args.vararg:
                            _check_annotation(node.args.vararg.annotation)
                        if node.args.kwarg:
                            _check_annotation(node.args.kwarg.annotation)
                        _check_annotation(node.returns)
                    case ast.AnnAssign():
                        _check_annotation(node.annotation)
            return violations
        
        
        def find_if_elif_variant_violations(
            tree: ast.Module, source_lines: list[str], file: Path,
        ) -> list[Violation]:
            """Check for if/elif chains on isinstance or enum comparison."""
            violations: list[Violation] = []
            for node in ast.walk(tree):
                if not isinstance(node, ast.If):
                    continue
        
                line = source_lines[node.lineno - 1] if node.lineno <= len(source_lines) else ""
                if IF_VARIANT_OK_RE.search(line):
                    continue
        
                is_variant_test = _is_isinstance_test(node.test) or _is_enum_comparison(node.test)
                if not is_variant_test:
                    continue
        
                # Must have at least one elif that is also a variant test
                orelse = node.orelse
                while orelse and len(orelse) == 1 and isinstance(orelse[0], ast.If):
                    elif_node = orelse[0]
                    if _is_isinstance_test(elif_node.test) or _is_enum_comparison(elif_node.test):
                        violations.append(Violation(
                            rule="if-elif-on-variant",
                            file=file,
                            line=node.lineno,
                            col=node.col_offset + 1,
                            message="isinstance/enum if/elif chain \u2014 use match/case + assert_never",
                        ))
                        break
                    orelse = elif_node.orelse
            return violations
        
        
        def find_broad_except_violations(
            tree: ast.Module, source_lines: list[str], file: Path,
        ) -> list[Violation]:
            """Check for except Exception / except BaseException (too broad)."""
            BROAD_EXCEPTIONS = {"Exception", "BaseException"}
            violations: list[Violation] = []
            for node in ast.walk(tree):
                if not isinstance(node, ast.ExceptHandler):
                    continue
                if node.type is None:
                    continue  # already caught by bare-except
        
                exc_name: str | None = None
                if isinstance(node.type, ast.Name) and node.type.id in BROAD_EXCEPTIONS:
                    exc_name = node.type.id
                elif isinstance(node.type, ast.Attribute) and node.type.attr in BROAD_EXCEPTIONS:
                    exc_name = node.type.attr
                if exc_name is None:
                    continue
        
                line = source_lines[node.lineno - 1] if node.lineno <= len(source_lines) else ""
                if BROAD_EXCEPT_OK_RE.search(line):
                    continue
        
                violations.append(Violation(
                    rule="broad-except",
                    file=file,
                    line=node.lineno,
                    col=node.col_offset + 1,
                    message=f"`except {exc_name}` is too broad \u2014 catch the specific exception you expect",
                ))
            return violations
        
        
        def find_oversized_module_violations(
            source_lines: list[str], file: Path,
        ) -> list[Violation]:
            """Check if file exceeds 250 pure LOC (non-blank, non-comment)."""
            # File-level opt-out in first 10 lines (shebang + script metadata can push it down)
            for line in source_lines[:10]:
                if SIZE_OK_RE.search(line):
                    return []
        
            pure_loc = sum(
                1 for line in source_lines
                if line.strip() and not line.strip().startswith("#")
            )
            if pure_loc > PURE_LOC_LIMIT:
                return [Violation(
                    rule="oversized-module",
                    file=file,
                    line=1,
                    col=1,
                    message=f"{pure_loc} pure LOC (limit: {PURE_LOC_LIMIT}) \u2014 split by responsibility",
                )]
            return []
        
        
        def check_file(file: Path) -> list[Violation]:
            source = file.read_text(encoding="utf-8")
            try:
                tree = ast.parse(source, filename=str(file))
            except SyntaxError as exc:
                return [Violation(
                    rule="syntax-error",
                    file=file,
                    line=exc.lineno or 1,
                    col=exc.offset or 1,
                    message=f"SyntaxError: {exc.msg}",
                )]
        
            source_lines = source.splitlines()
            return [
                *find_node_violations(tree, file),
                *find_import_violations(tree, source_lines, file),
                *find_comment_violations(source, file),
                *find_dataclass_violations(tree, source_lines, file),
                *find_dict_return_violations(tree, source_lines, file),
                *find_match_violations(tree, source_lines, file),
                *find_generic_exception_violations(tree, source_lines, file),
                *find_object_annotation_violations(tree, source_lines, file),
                *find_if_elif_variant_violations(tree, source_lines, file),
                *find_oversized_module_violations(source_lines, file),
                *find_broad_except_violations(tree, source_lines, file),
            ]
        
        
        def main() -> int:
            if len(sys.argv) < 2:
                print("usage: check-no-excuse-rules.py <file-or-dir>...", file=sys.stderr)
                return 2
        
            files = discover_files(Path(arg) for arg in sys.argv[1:])
            if not files:
                print("check-no-excuse-rules: no .py files found", file=sys.stderr)
                return 0
        
            violations: list[Violation] = []
            for file in files:
                violations.extend(check_file(file))
        
            if not violations:
                print(f"no violations in {len(files)} file(s)")
                return 0
        
            for violation in violations:
                print(violation.render(), file=sys.stderr)
            print(
                f"\n{len(violations)} violation(s) in {len(files)} file(s)",
                file=sys.stderr,
            )
            return 1
        
        
        if __name__ == "__main__":
            sys.exit(main())
        
      • new-project.py 4.6 KB
        #!/usr/bin/env -S uv run --script
        # /// script
        # requires-python = ">=3.11"
        # dependencies = [
        #     "typer",
        #     "rich",
        # ]
        # ///
        
        # ─── How to run ───
        # 1. Install uv (if not installed):
        #      curl -LsSf https://astral.sh/uv/install.sh | sh
        # 2. Run:
        #      uv run new-project.py myproject
        #      uv run new-project.py myproject --path ./workspace
        #      uv run new-project.py myproject --lib   # library (publishable)
        # ──────────────────
        
        """Scaffold a new Python project with ultra-strict config from pyproject-strict.md.
        
        Creates via `uv init`, then injects basedpyright + ruff ALL + pytest config.
        """
        
        from __future__ import annotations
        
        import subprocess
        import sys
        from pathlib import Path
        
        import typer
        from rich import print as rprint
        
        # ── Strict tool config (from pyproject-strict.md) ──
        
        TOOL_CONFIG = '''
        [dependency-groups]
        dev = [
            "basedpyright>=1.21",
            "ruff>=0.8",
            "pytest>=8",
            "pytest-cov>=5",
        ]
        
        [tool.basedpyright]
        typeCheckingMode = "all"
        pythonVersion = "3.13"
        reportMissingTypeStubs = false
        reportUnknownMemberType = false
        reportUnknownArgumentType = false
        reportUnknownVariableType = false
        reportUnknownLambdaType = false
        reportUnknownParameterType = false
        reportMissingParameterType = false
        reportUnnecessaryIsInstance = false
        reportUnusedCallResult = false
        reportImplicitOverride = false
        
        [tool.ruff]
        target-version = "py313"
        line-length = 120
        
        [tool.ruff.lint]
        select = ["ALL"]
        ignore = [
            "COM812",   # trailing comma (conflicts with formatter)
            "ISC001",   # single-line string concat (conflicts with formatter)
            "D1",       # undocumented-public-* (too noisy early on)
            "ANN101",   # deprecated: self annotation
            "ANN102",   # deprecated: cls annotation
            "S101",     # assert used (pytest needs it)
            "PLR2004",  # magic-value-comparison (test data)
            "FBT",      # boolean-trap (too strict for CLIs)
            "TD",       # flake8-todos (noisy)
            "FIX",      # fixme (noisy)
        ]
        
        [tool.ruff.lint.per-file-ignores]
        "tests/**/*.py" = ["S101", "PLR2004", "SLF001", "D", "ARG", "ANN"]
        
        [tool.ruff.lint.pydocstyle]
        convention = "google"
        
        [tool.pytest.ini_options]
        testpaths = ["tests"]
        addopts = "-ra --strict-markers --strict-config"
        '''
        
        GITIGNORE = """\
        __pycache__/
        *.py[cod]
        *.so
        .venv/
        dist/
        *.egg-info/
        .coverage
        htmlcov/
        .basedpyright/
        .ruff_cache/
        """
        
        
        def main(
            name: str = typer.Argument(help="Project name"),
            path: Path = typer.Option(Path("."), "--path", "-p", help="Parent directory"),
            lib: bool = typer.Option(False, "--lib", help="Create as publishable library (uv init --lib)"),
        ) -> None:
            """Create a new Python project with ultra-strict config."""
            project_dir = path / name
        
            if project_dir.exists():
                rprint(f"[red]Error:[/red] {project_dir} already exists")
                raise SystemExit(1)
        
            # Run uv init
            cmd = ["uv", "init", "--lib" if lib else "--app", str(project_dir)]
            result = subprocess.run(cmd, capture_output=True, text=True)
            if result.returncode != 0:
                rprint(f"[red]uv init failed:[/red] {result.stderr}")
                raise SystemExit(1)
        
            # Read existing pyproject.toml
            pyproject_path = project_dir / "pyproject.toml"
            content = pyproject_path.read_text()
        
            # Remove the default [dependency-groups] if uv init created one
            # (we'll replace it with our strict version)
            lines = content.splitlines(keepends=True)
            filtered: list[str] = []
            skip = False
            for line in lines:
                if line.strip().startswith("[dependency-groups]"):
                    skip = True
                    continue
                if skip and line.strip().startswith("["):
                    skip = False
                if not skip:
                    filtered.append(line)
        
            content = "".join(filtered).rstrip("\n") + "\n"
        
            # Append strict tool config
            content += TOOL_CONFIG
        
            pyproject_path.write_text(content)
        
            # Add dev dependencies
            subprocess.run(
                ["uv", "add", "--dev", "basedpyright", "ruff", "pytest", "pytest-cov"],
                cwd=project_dir,
                capture_output=True,
            )
        
            # Create tests directory
            tests_dir = project_dir / "tests"
            tests_dir.mkdir(exist_ok=True)
            (tests_dir / "__init__.py").touch()
        
            # Overwrite .gitignore
            (project_dir / ".gitignore").write_text(GITIGNORE)
        
            # Create py.typed marker for libraries
            if lib:
                src_dir = project_dir / "src" / name.replace("-", "_")
                if src_dir.exists():
                    (src_dir / "py.typed").touch()
        
            rprint(f"[green]✓[/green] Created: [bold]{project_dir}[/bold]")
            rprint(f"  cd {name} && uv sync && uv run basedpyright . && uv run ruff check .")
        
        
        if __name__ == "__main__":
            typer.run(main)
        
      • new-script.py 3.2 KB
        #!/usr/bin/env -S uv run --script
        # /// script
        # requires-python = ">=3.11"
        # dependencies = [
        #     "typer",
        #     "rich",
        # ]
        # ///
        
        # ─── How to run ───
        # 1. Install uv (if not installed):
        #      curl -LsSf https://astral.sh/uv/install.sh | sh
        # 2. Run:
        #      uv run new-script.py my_tool
        #      uv run new-script.py my_tool --output ./scripts/my_tool.py
        #      uv run new-script.py my_tool --deps 'httpx2[http2,brotli,zstd]' --deps rich --deps polars
        #      uv run new-script.py my_tool --py 3.13
        # ──────────────────
        
        """Generate a PEP 723 Python script with all boilerplate pre-filled.
        
        Creates a new .py file with:
          - uv shebang
          - PEP 723 inline metadata (requires-python + dependencies)
          - Mandatory "How to run" comment block
          - from __future__ import annotations
          - main() + if __name__ guard
        
        By default writes to a temp directory and prints the path.
        """
        
        from __future__ import annotations
        
        import os
        import stat
        import sys
        import tempfile
        from pathlib import Path
        
        import typer
        from rich import print as rprint
        
        
        TEMPLATE = '''\
        #!/usr/bin/env -S uv run --script
        # /// script
        # requires-python = ">={python_version}"
        # dependencies = [
        {deps_block}# ]
        # ///
        
        # ─── How to run ───
        # 1. Install uv (if not installed):
        #      curl -LsSf https://astral.sh/uv/install.sh | sh
        # 2. Run directly (no venv, no pip install needed):
        #      uv run {filename} {args_hint}
        # 3. Or make executable and run:
        #      chmod +x {filename} && ./{filename}
        # ──────────────────
        
        from __future__ import annotations
        
        
        def main() -> None:
            """TODO: implement."""
        
        
        if __name__ == "__main__":
            main()
        '''
        
        
        def main(
            name: str = typer.Argument(help="Script name (without .py extension)"),
            output: Path | None = typer.Option(None, "--output", "-o", help="Output path. Default: OS temp directory."),
            deps: list[str] = typer.Option([], "--deps", "-d", help="Dependencies to include (repeat --deps for each)."),
            py: str = typer.Option("3.13", "--py", help="Minimum Python version."),
        ) -> None:
            """Generate a new PEP 723 script with all boilerplate pre-filled."""
            filename = f"{name}.py" if not name.endswith(".py") else name
            stem = filename.removesuffix(".py")
        
            if output is not None:
                dest = Path(output)
            else:
                tmp_dir = Path(tempfile.gettempdir()) / "uv-scripts"
                tmp_dir.mkdir(exist_ok=True)
                dest = tmp_dir / filename
        
            dep_list = deps or []
            if dep_list:
                deps_block = "".join(f'#     "{d}",\n' for d in dep_list)
            else:
                deps_block = '#     # add deps here, e.g.: "httpx2[http2,brotli,zstd]"\n'
        
            content = TEMPLATE.format(
                python_version=py,
                deps_block=deps_block,
                filename=filename,
                args_hint="",
            )
        
            dest.parent.mkdir(parents=True, exist_ok=True)
            dest.write_text(content)
        
            # Make executable on Unix
            if sys.platform != "win32":
                st = dest.stat()
                dest.chmod(st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
        
            rprint(f"[green]✓[/green] Created: [bold]{dest}[/bold]")
            rprint(f"  Run: [cyan]uv run {dest}[/cyan]")
        
        
        if __name__ == "__main__":
            typer.run(main)
        
    • rust
      • check-no-excuse-rules.sh 7.5 KB
        #!/usr/bin/env bash
        # No-excuse rule checker for Rust files.
        # Only rules that can be enforced via pure text matching live here.
        # Everything semantic is on clippy + miri + nextest.
        #
        # Rules:
        #   unwrap / expect         outside tests, unless the previous line carries
        #                           #[expect(clippy::unwrap_used|expect_used, reason = "...")]
        #   placeholder-macro       todo!/unimplemented!/unreachable! in committed code
        #   box-dyn-error           Box<dyn Error> in non-test code
        #   lib-panic               panic!() in library code
        #   discarded-result        `let _ = call(...)`, unless the previous line carries
        #                           #[expect(clippy::let_underscore_must_use, reason = "...")]
        #   blocking-in-async       std::thread::sleep / std::fs / reqwest::blocking /
        #                           .blocking_*() inside an async fn or async block
        #   unsafe-no-safety        unsafe { without // SAFETY: in the preceding 5 lines
        #   allow-attribute         #[allow(...)] - silence lints with #[expect(lint, reason)]
        #   expect-without-reason   #[expect(...)] with no `reason = "..."`
        #   narrowing-as-cast       possible narrowing numeric `as` cast
        
        set -euo pipefail
        
        if [ $# -eq 0 ]; then
            echo "Usage: $0 <file.rs> [file.rs ...]" >&2
            exit 2
        fi
        
        violations=0
        report() {
            echo "::error file=$1,line=$2::[$3] $4" >&2
            violations=$((violations + 1))
        }
        
        is_test_path() {
            case "$1" in
                */tests/*|*/benches/*|*/examples/*|*/build.rs|*_test.rs|tests/*|benches/*|examples/*) return 0 ;;
            esac
            return 1
        }
        
        count_char() {
            local only="${1//[^$2]/}"
            echo "${#only}"
        }
        
        for file in "$@"; do
            [ -f "$file" ] || continue
            case "$file" in
                *.rs) ;;
                *) continue ;;
            esac
        
            in_test_file=0
            is_test_path "$file" && in_test_file=1
        
            lines=()
            while IFS= read -r raw || [ -n "$raw" ]; do lines+=("$raw"); done < "$file"
            total=${#lines[@]}
        
            # Brace-tracked regions: a region starts at its marker line and ends when the
            # braces opened after the marker are closed again.
            in_cfg_test=0; cfg_depth=0; cfg_opened=0
            in_async=0; async_depth=0; async_opened=0
        
            for ((i = 0; i < total; i++)); do
                line_no=$((i + 1))
                line="${lines[$i]}"
                prev=""
                [ "$i" -gt 0 ] && prev="${lines[$((i - 1))]}"
                code_only="${line%%//*}"
                opens=$(count_char "$code_only" "{")
                closes=$(count_char "$code_only" "}")
        
                if [[ "$line" =~ \#\[cfg\(test\)\] ]]; then
                    in_cfg_test=1; cfg_depth=0; cfg_opened=0
                fi
                if [ "$in_cfg_test" -eq 1 ]; then
                    cfg_depth=$((cfg_depth + opens - closes))
                    [ "$opens" -gt 0 ] && cfg_opened=1
                    if [ "$cfg_opened" -eq 1 ] && [ "$cfg_depth" -le 0 ]; then in_cfg_test=0; fi
                fi
        
                if [ "$in_async" -eq 0 ] && [[ "$code_only" =~ (^|[^[:alnum:]_])async([[:space:]]+(move[[:space:]]*)?\{|[[:space:]]+(unsafe[[:space:]]+)?fn[[:space:]]) ]]; then
                    in_async=1; async_depth=0; async_opened=0
                fi
                async_line=$in_async
                if [ "$in_async" -eq 1 ]; then
                    async_depth=$((async_depth + opens - closes))
                    [ "$opens" -gt 0 ] && async_opened=1
                    if [ "$async_opened" -eq 1 ] && [ "$async_depth" -le 0 ]; then in_async=0; fi
                fi
        
                exempt=0
                [ "$in_test_file" -eq 1 ] && exempt=1
                [ "$in_cfg_test" -eq 1 ] && exempt=1
        
                if [ "$exempt" -eq 0 ]; then
                    if [[ "$code_only" =~ \.unwrap\(\) ]] && [[ ! "$prev" =~ \#\[expect\(clippy::unwrap_used,.*reason ]]; then
                        report "$file" "$line_no" "unwrap" ".unwrap() outside tests - use ? / ok_or / let-else, or for a proven invariant use .expect() behind #[expect(clippy::expect_used, reason = \"...\")]"
                    fi
        
                    if [[ "$code_only" =~ \.expect\( ]] && [[ ! "$prev" =~ \#\[expect\(clippy::expect_used,.*reason ]]; then
                        report "$file" "$line_no" "expect" ".expect() outside tests - use ?, or for a proven invariant annotate the previous line with #[expect(clippy::expect_used, reason = \"...\")]"
                    fi
        
                    if [[ "$code_only" =~ (todo!|unimplemented!|unreachable!|unreachable_unchecked!) ]]; then
                        report "$file" "$line_no" "placeholder-macro" "todo!/unimplemented!/unreachable! in committed code"
                    fi
        
                    if [[ "$code_only" =~ Box\<dyn[[:space:]]+Error ]]; then
                        report "$file" "$line_no" "box-dyn-error" "Box<dyn Error> in non-test code - use anyhow::Error (apps) or thiserror enum (libs)"
                    fi
        
                    if [[ "$file" == */src/lib.rs || "$file" == */src/*/mod.rs || ( "$file" == */src/*.rs && "$file" != */src/main.rs && "$file" != */src/bin/* ) ]]; then
                        if [[ "$code_only" =~ panic!\( ]]; then
                            report "$file" "$line_no" "lib-panic" "panic!() in library code - return Result"
                        fi
                    fi
        
                    if [[ "$code_only" =~ let[[:space:]]+_[[:space:]]*=[[:space:]]*[^\;]*\( ]] && [[ ! "$prev" =~ \#\[expect\(clippy::let_underscore_must_use,.*reason ]]; then
                        report "$file" "$line_no" "discarded-result" "let _ = call() drops its result - propagate or handle the error; log it on a best-effort path"
                    fi
        
                    if [ "$async_line" -eq 1 ] && [[ "$code_only" =~ (thread::sleep\(|std::fs::|reqwest::blocking::|\.blocking_(recv|send|lock|read|write)\() ]]; then
                        report "$file" "$line_no" "blocking-in-async" "blocking call inside async code - use tokio::time::sleep / tokio::fs / the async client, or move it into spawn_blocking"
                    fi
                fi
        
                if [[ "$code_only" =~ unsafe[[:space:]]*\{ ]]; then
                    start=$((i > 5 ? i - 5 : 0))
                    window=$(printf '%s\n' "${lines[@]:$start:$((i - start + 1))}")
                    if [[ ! "$window" =~ //[[:space:]]*SAFETY: ]]; then
                        report "$file" "$line_no" "unsafe-no-safety-comment" "unsafe block without // SAFETY: comment in preceding 5 lines"
                    fi
                fi
        
                if [[ "$code_only" =~ \#!?\[allow\( ]]; then
                    report "$file" "$line_no" "allow-attribute" "#[allow(...)] - use #[expect(lint, reason = \"...\")] so the exception fails once the lint stops firing"
                fi
        
                if [[ "$code_only" =~ \#!?\[expect\( ]]; then
                    attr=$(printf '%s\n' "${lines[@]:$i:4}")
                    if [[ ! "$attr" =~ reason[[:space:]]*= ]]; then
                        report "$file" "$line_no" "expect-without-reason" "#[expect(...)] needs reason = \"...\" naming why the lint does not apply"
                    fi
                fi
        
                # Heuristic flag for human review; exact analysis belongs to clippy::cast_possible_truncation.
                if [[ "$code_only" =~ (u16|u32|u64|u128|usize|i16|i32|i64|i128|isize)[[:space:]]+as[[:space:]]+(u8|u16|u32|i8|i16|i32)([^[:alnum:]_]|$) ]]; then
                    report "$file" "$line_no" "narrowing-as-cast" "possible narrowing 'as' cast - use TryFrom / try_into() for fallible conversion"
                fi
            done
        done
        
        if [ "$violations" -gt 0 ]; then
            echo "" >&2
            echo "rust-programmer: ${violations} violation(s). Fix before declaring work done." >&2
            echo "" >&2
            echo "Then run the full toolchain gate:" >&2
            echo "  cargo fmt --all -- --check" >&2
            echo "  cargo clippy --all-targets --all-features -- -D warnings" >&2
            echo "  cargo nextest run --all-targets --all-features && cargo test --doc" >&2
            echo "  cargo +nightly miri nextest run --all-features    # if unsafe touched" >&2
            echo "  cargo machete" >&2
            echo "  cargo deny check" >&2
            exit 1
        fi
        
        echo "rust-programmer: no-excuse rules passed for $# file(s)."
        
      • check-no-excuse-rules.test.ts 3.3 KB
        import { afterAll, beforeAll, describe, expect, test } from "bun:test"
        import { spawnSync } from "node:child_process"
        import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
        import { tmpdir } from "node:os"
        import { join } from "node:path"
        
        // The skill runs this checker with `bash`; Windows runners resolve `bash` to WSL or nothing.
        const describeUnix = process.platform === "win32" ? describe.skip : describe
        
        const script = join(import.meta.dir, "check-no-excuse-rules.sh")
        let workDir = ""
        
        function check(name: string, source: string) {
        	const file = join(workDir, "src", name)
        	writeFileSync(file, source)
        	const result = spawnSync("bash", [script, file], { encoding: "utf8", timeout: 30_000 })
        	return { status: result.status, stderr: result.stderr }
        }
        
        describeUnix("#given the Rust no-excuse checker", () => {
        	beforeAll(() => {
        		workDir = mkdtempSync(join(tmpdir(), "rust-no-excuse-"))
        		mkdirSync(join(workDir, "src"))
        	})
        
        	afterAll(() => {
        		rmSync(workDir, { recursive: true, force: true })
        	})
        
        	test("#when a file follows every rule #then it exits 0", () => {
        		const result = check("clean.rs", 'pub fn port(raw: &str) -> Option<u16> {\n    raw.parse().ok()\n}\n')
        		expect(result.status).toBe(0)
        	})
        
        	test("#when unwrap is used outside tests #then it reports the unwrap rule", () => {
        		const result = check("unwrap.rs", "pub fn first(v: &[u8]) -> u8 {\n    *v.first().unwrap()\n}\n")
        		expect(result.status).toBe(1)
        		expect(result.stderr).toContain("[unwrap]")
        	})
        
        	test("#when an invariant expect carries #[expect(clippy::expect_used, reason)] #then it passes", () => {
        		const result = check(
        			"invariant.rs",
        			'pub fn re() -> regex::Regex {\n    #[expect(clippy::expect_used, reason = "literal pattern")]\n    regex::Regex::new("a+").expect("valid literal")\n}\n',
        		)
        		expect(result.status).toBe(0)
        	})
        
        	test("#when a lint is silenced with #[allow] #then it reports the allow-attribute rule", () => {
        		const result = check("allow.rs", "#[allow(clippy::too_many_lines)]\npub fn big() {}\n")
        		expect(result.status).toBe(1)
        		expect(result.stderr).toContain("[allow-attribute]")
        	})
        
        	test("#when #[expect] has no reason #then it reports the expect-without-reason rule", () => {
        		const result = check("noreason.rs", "#[expect(clippy::too_many_lines)]\npub fn big() {}\n")
        		expect(result.status).toBe(1)
        		expect(result.stderr).toContain("[expect-without-reason]")
        	})
        
        	test("#when a fallible call is discarded with let _ #then it reports the discarded-result rule", () => {
        		const result = check("discard.rs", 'pub fn cleanup() {\n    let _ = std::fs::remove_file("x");\n}\n')
        		expect(result.status).toBe(1)
        		expect(result.stderr).toContain("[discarded-result]")
        	})
        
        	test("#when a blocking std call runs inside an async fn #then it reports blocking-in-async", () => {
        		const result = check(
        			"blocking.rs",
        			"pub async fn wait(\n    ms: u64,\n) {\n    std::thread::sleep(std::time::Duration::from_millis(ms));\n}\n",
        		)
        		expect(result.status).toBe(1)
        		expect(result.stderr).toContain("[blocking-in-async]")
        	})
        
        	test("#when the same blocking call runs in a sync fn after an async fn #then it passes", () => {
        		const result = check(
        			"sync-after-async.rs",
        			"pub async fn ready() {}\n\npub fn wait(ms: u64) {\n    std::thread::sleep(std::time::Duration::from_millis(ms));\n}\n",
        		)
        		expect(result.status).toBe(0)
        	})
        })
        
      • new-project.py 5.7 KB
        #!/usr/bin/env -S uv run --script
        # /// script
        # requires-python = ">=3.11"
        # dependencies = [
        #     "typer",
        #     "rich",
        # ]
        # ///
        
        # ─── How to run ───
        # 1. Install uv (if not installed):
        #      curl -LsSf https://astral.sh/uv/install.sh | sh
        # 2. Run:
        #      uv run new-project.py myproject
        #      uv run new-project.py myproject --path ./workspace
        # ──────────────────
        #
        # Creates a new Rust project with strict lints, deny.toml, rustfmt.toml, and
        # rust-toolchain.toml pre-configured. The generated project passes its own
        # fmt + clippy gate before any code is written.
        
        from __future__ import annotations
        
        import subprocess
        import sys
        from pathlib import Path
        
        import typer
        from rich.console import Console
        
        console = Console(stderr=True)
        
        # ── Embedded config contents ─────────────────────────────────────────────
        
        RUST_TOOLCHAIN_TOML = """\
        [toolchain]
        channel = "stable"
        components = ["rustfmt", "clippy", "rust-src"]
        profile = "default"
        """
        
        CARGO_TOML_LINTS = """
        [lints.rust]
        unsafe_op_in_unsafe_fn = "deny"
        missing_docs = "warn"
        missing_debug_implementations = "warn"
        unreachable_pub = "warn"
        unused_must_use = "deny"
        elided_lifetimes_in_paths = "warn"
        non_ascii_idents = "deny"
        trivial_numeric_casts = "warn"
        unused_lifetimes = "warn"
        single_use_lifetimes = "warn"
        unexpected_cfgs = { level = "deny", check-cfg = ["cfg(loom)"] }
        
        [lints.clippy]
        all = { level = "deny", priority = -1 }
        pedantic = { level = "warn", priority = -1 }
        nursery = { level = "warn", priority = -1 }
        cargo = { level = "warn", priority = -1 }
        undocumented_unsafe_blocks = "deny"
        multiple_unsafe_ops_per_block = "deny"
        unwrap_used = "deny"
        expect_used = "deny"
        panic = "deny"
        todo = "deny"
        unimplemented = "deny"
        dbg_macro = "deny"
        print_stdout = "warn"
        print_stderr = "warn"
        allow_attributes = "deny"
        allow_attributes_without_reason = "deny"
        let_underscore_must_use = "deny"
        indexing_slicing = "deny"
        mem_forget = "deny"
        clone_on_ref_ptr = "warn"
        module_name_repetitions = { level = "allow" }
        must_use_candidate = { level = "allow" }
        """
        
        DENY_TOML = """\
        [advisories]
        yanked = "deny"
        
        [licenses]
        private = { ignore = true }  # publish = false crates carry no license field
        allow = ["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC", "Unicode-3.0", "Zlib"]
        
        [bans]
        multiple-versions = "warn"
        wildcards = "deny"
        
        [sources]
        unknown-registry = "deny"
        unknown-git = "deny"
        """
        
        MAIN_RS = """\
        //! Command-line entry point.
        
        use std::io::Write as _;
        
        fn main() -> std::io::Result<()> {
            writeln!(std::io::stdout().lock(), "Hello, world!")
        }
        """
        
        RUSTFMT_TOML = """\
        edition = "2024"
        max_width = 100
        use_field_init_shorthand = true
        use_try_shorthand = true
        """
        
        # ── Main ─────────────────────────────────────────────────────────────────
        
        app = typer.Typer(add_completion=False)
        
        
        @app.command()
        def main(
            name: str = typer.Argument(help="Name of the new Rust project"),
            path: Path = typer.Option(
                Path.cwd(),
                "--path",
                "-p",
                help="Parent directory where the project folder is created",
            ),
        ) -> None:
            """Scaffold a new Rust project with strict lints and tooling configs."""
            project_dir = path / name
        
            # ── cargo init ───────────────────────────────────────────────────
            console.print(f"[bold green]Creating[/] project [cyan]{name}[/] at [dim]{project_dir}[/]")
            try:
                subprocess.run(
                    ["cargo", "init", str(project_dir), "--name", name],
                    check=True,
                    capture_output=True,
                    text=True,
                )
            except FileNotFoundError:
                console.print("[bold red]Error:[/] cargo not found. Install Rust via https://rustup.rs")
                sys.exit(1)
            except subprocess.CalledProcessError as exc:
                console.print(f"[bold red]cargo init failed:[/]\n{exc.stderr}")
                sys.exit(1)
        
            # ── rust-toolchain.toml ──────────────────────────────────────────
            (project_dir / "rust-toolchain.toml").write_text(RUST_TOOLCHAIN_TOML)
            console.print("  [dim]wrote[/] rust-toolchain.toml")
        
            # ── Declare the MSRV and append [lints] to Cargo.toml ────────────
            cargo_toml = project_dir / "Cargo.toml"
            manifest = cargo_toml.read_text()
            if 'edition = "2024"\n' not in manifest:
                console.print("[bold red]Error:[/] cargo did not create an edition 2024 package; install Rust >= 1.85")
                sys.exit(1)
            manifest = manifest.replace(
                'edition = "2024"\n', 'edition = "2024"\nrust-version = "1.85"\npublish = false\n', 1
            )
            cargo_toml.write_text(manifest + CARGO_TOML_LINTS)
            (project_dir / "src" / "main.rs").write_text(MAIN_RS)
            console.print("  [dim]appended[/] [lints] to Cargo.toml")
        
            # ── deny.toml ────────────────────────────────────────────────────
            (project_dir / "deny.toml").write_text(DENY_TOML)
            console.print("  [dim]wrote[/] deny.toml")
        
            # ── rustfmt.toml ─────────────────────────────────────────────────
            (project_dir / "rustfmt.toml").write_text(RUSTFMT_TOML)
            console.print("  [dim]wrote[/] rustfmt.toml")
        
            console.print(f"\n[bold green]Done![/] cd {project_dir} && cargo check")
        
        
        if __name__ == "__main__":
            app()
        
    • typescript
      • check-no-excuse-rules.test.ts 3.9 KB
        import { afterAll, beforeAll, describe, expect, test } from "bun:test"
        import { spawnSync } from "node:child_process"
        import { copyFileSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs"
        import { createRequire } from "node:module"
        import { tmpdir } from "node:os"
        import { dirname, join } from "node:path"
        
        // Regression for lazycodex#111: the script must resolve `typescript` from the
        // caller project (cwd), not from the script's own location, so it works when
        // executed from an installed `~/.codex/...` skill cache.
        
        const scriptSource = join(import.meta.dir, "check-no-excuse-rules.ts")
        const repoRequire = createRequire(import.meta.url)
        const typescriptPackageDir = dirname(repoRequire.resolve("typescript/package.json"))
        
        let tempRoot = ""
        let cachedScript = ""
        let callerDir = ""
        let callerWithoutTypescriptDir = ""
        
        function runNoExcuse(cwd: string, target: string) {
        	return spawnSync(process.execPath, ["--no-install", cachedScript, target], {
        		cwd,
        		encoding: "utf8",
        		timeout: 60_000,
        	})
        }
        
        describe("#given the no-excuse script is executed from an installed ~/.codex-style skill cache", () => {
        	beforeAll(() => {
        		// given a cache copy of the script outside any project, and two caller projects:
        		// one providing node_modules/typescript, one without typescript at all
        		tempRoot = mkdtempSync(join(tmpdir(), "no-excuse-cache-repro-"))
        		const cacheDir = join(tempRoot, ".codex", "skills", "programming", "scripts", "typescript")
        		mkdirSync(cacheDir, { recursive: true })
        		cachedScript = join(cacheDir, "check-no-excuse-rules.ts")
        		copyFileSync(scriptSource, cachedScript)
        
        		callerDir = join(tempRoot, "caller-project")
        		mkdirSync(join(callerDir, "node_modules"), { recursive: true })
        		symlinkSync(typescriptPackageDir, join(callerDir, "node_modules", "typescript"), "junction")
        		writeFileSync(join(callerDir, "clean.ts"), "export const answer: number = 42\n")
        		writeFileSync(join(callerDir, "violating.ts"), "const x = foo as any\nexport default x\n")
        
        		callerWithoutTypescriptDir = join(tempRoot, "caller-without-typescript")
        		mkdirSync(callerWithoutTypescriptDir, { recursive: true })
        		writeFileSync(join(callerWithoutTypescriptDir, "clean.ts"), "export const answer: number = 42\n")
        	})
        
        	afterAll(() => {
        		rmSync(tempRoot, { recursive: true, force: true })
        	})
        
        	// A generous timeout: the first run does a cold TypeScript program load in a
        	// spawned subprocess, which can exceed bun test's 5s default on a cold cache
        	// (matches the 60s spawnSync ceiling). This bounds the subprocess, it does not
        	// wait on wall-clock time as behavior.
        	test("#when the caller project provides typescript #then it resolves the caller's typescript and exits 0", () => {
        		// when no-excuse runs from the cache copy against the caller project
        		const result = runNoExcuse(callerDir, "clean.ts")
        
        		// then the caller's own typescript is used and the clean file passes
        		expect(result.error).toBeUndefined()
        		expect(result.status).toBe(0)
        		expect(result.stdout).toContain("No violations in 1 file(s).")
        	}, 60_000)
        
        	test("#when a checked file violates the rules #then violations are still reported with exit 1", () => {
        		// when no-excuse runs against a file containing `as any`
        		const result = runNoExcuse(callerDir, "violating.ts")
        
        		// then the existing CLI behavior is unchanged
        		expect(result.error).toBeUndefined()
        		expect(result.status).toBe(1)
        		expect(result.stderr).toContain("[no-any-assertion]")
        	}, 60_000)
        
        	test("#when the caller project genuinely lacks typescript #then it fails with a clear error and exit 2", () => {
        		// when no-excuse runs from a caller project with no local typescript
        		const result = runNoExcuse(callerWithoutTypescriptDir, "clean.ts")
        
        		// then it reports the resolution problem instead of crashing mid-analysis
        		expect(result.error).toBeUndefined()
        		expect(result.status).toBe(2)
        		expect(result.stderr).toContain('cannot resolve "typescript" from the caller project')
        	}, 60_000)
        })
        
      • check-no-excuse-rules.ts 12.5 KB
        #!/usr/bin/env bun
        /**
         * Check TypeScript files for no-excuse violations.
         *
         * Rules:
         *   no-any-assertion       - `as any`
         *   no-unknown-assertion    - `as unknown`
         *   no-ts-ignore            - `@ts-ignore` comments
         *   no-ts-expect-error      - `@ts-expect-error` comments
         *   no-enum                 - `enum` declarations
         *   no-non-null-assertion   - `x!` postfix operator
         *   no-throw-literal        - `throw "string"` / `throw 123`
         *   no-mutable-export       - `export let` / `export var`
         *   no-any-annotation       - `: any` in annotations (opt out: `// no-excuse-ok: any`)
         *   no-explicit-any-return  - `(): any` return types (opt out: `// no-excuse-ok: any`)
         *   empty-catch             - `catch { }` or `catch (e) { }` with empty body
         *   catch-without-narrowing - catch block that uses error without instanceof narrowing
         *
         * Usage:
         *   bun run scripts/check-no-excuse-rules.ts <file-or-dir>...
         *
         * The `typescript` package is resolved from the caller project (process.cwd()),
         * not from this script's location, so the script works when executed from an
         * installed skill-cache path (e.g. ~/.codex/...) against a project checkout.
         *
         * Exit codes:
         *   0 - no violations
         *   1 - violations found
         *   2 - input error
         */
        
        import fs from "node:fs"
        import { createRequire } from "node:module"
        import path from "node:path"
        import process from "node:process"
        import type * as tsTypes from "typescript/unstable/ast"
        
        type TsApiModule = typeof import("typescript/unstable/async")
        type TsAstModule = typeof import("typescript/unstable/ast")
        type TsModule = {
          readonly api: TsApiModule
          readonly ast: TsAstModule
        }
        
        function loadTypescriptFromCaller(): TsModule {
          // A static import resolves from this script's installed skill-cache path
          // instead of the caller project. Resolve each TypeScript 7 API subpath from
          // the caller so the script uses the project it audits.
          const callerRequire = createRequire(path.join(process.cwd(), "no-excuse-anchor.cjs"))
          try {
            const api: Partial<TsApiModule> = callerRequire("typescript/unstable/async")
            const ast: Partial<TsAstModule> = callerRequire("typescript/unstable/ast")
            if (typeof api.API === "function" && typeof ast.isAsExpression === "function") {
              return { api: api as TsApiModule, ast: ast as TsAstModule }
            }
          } catch { // no-excuse-ok: catch
            // fall through to the clear error below
          }
          console.error(
            `error: cannot resolve "typescript" from the caller project (${process.cwd()}). ` +
              "Install it in the project being checked (e.g. `bun add -d typescript`) and re-run.",
          )
          process.exit(2)
        }
        
        const typescript = loadTypescriptFromCaller()
        const ts = typescript.ast
        
        type RuleId =
          | "no-any-assertion"
          | "no-unknown-assertion"
          | "no-ts-ignore"
          | "no-ts-expect-error"
          | "no-enum"
          | "no-non-null-assertion"
          | "no-throw-literal"
          | "no-mutable-export"
          | "no-any-annotation"
          | "no-explicit-any-return"
          | "empty-catch"
          | "catch-without-narrowing"
        
        type Violation = {
          readonly ruleId: RuleId
          readonly filePath: string
          readonly line: number
          readonly column: number
          readonly message: string
        }
        
        const INCLUDED_EXTENSIONS = new Set([".ts", ".tsx", ".mts", ".cts"])
        const IGNORED_DIRECTORIES = new Set([
          ".git", ".next", ".nuxt", ".turbo", ".yarn",
          "coverage", "dist", "build", "node_modules",
        ])
        
        const OPT_OUT_RE = /\/\/\s*no-excuse-ok:\s*any/
        const CATCH_OK_RE = /\/\/\s*no-excuse-ok:\s*catch/
        
        function isIncludedFile(filePath: string): boolean {
          return INCLUDED_EXTENSIONS.has(path.extname(filePath).toLowerCase())
        }
        
        function isDeclarationFile(filePath: string): boolean {
          return filePath.endsWith(".d.ts") || filePath.endsWith(".d.mts") || filePath.endsWith(".d.cts")
        }
        
        function discoverFiles(inputs: string[]): string[] {
          const files: string[] = []
          for (const input of inputs) {
            const resolved = path.resolve(input)
            if (!fs.existsSync(resolved)) {
              console.error(`Path does not exist: ${resolved}`)
              process.exit(2)
            }
            if (fs.statSync(resolved).isFile()) {
              if (isIncludedFile(resolved) && !isDeclarationFile(resolved)) files.push(resolved)
              continue
            }
            const walk = (dir: string): void => {
              for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
                if (entry.isDirectory()) {
                  if (!IGNORED_DIRECTORIES.has(entry.name)) walk(path.join(dir, entry.name))
                } else if (isIncludedFile(entry.name) && !isDeclarationFile(entry.name)) {
                  files.push(path.join(dir, entry.name))
                }
              }
            }
            walk(resolved)
          }
          return files
        }
        
        function getLineText(sourceFile: tsTypes.SourceFile, line: number): string {
          const lineStarts = sourceFile.getLineStarts()
          const start = lineStarts[line]
          const end = line + 1 < lineStarts.length ? lineStarts[line + 1] : sourceFile.getEnd()
          return sourceFile.text.slice(start, end)
        }
        
        async function parseSourceFiles(filePaths: readonly string[]): Promise<ReadonlyMap<string, tsTypes.SourceFile>> {
          const compiler = new typescript.api.API({ cwd: process.cwd() })
          try {
            const snapshot = await compiler.updateSnapshot({ openFiles: [...filePaths] })
            try {
              const sourceFiles = await Promise.all(filePaths.map(async (filePath) => {
                const project = await snapshot.getDefaultProjectForFile(filePath)
                const sourceFile = await project?.program.getSourceFile(filePath)
                if (!sourceFile) {
                  throw new Error(`TypeScript did not parse ${filePath}`)
                }
                return [filePath, sourceFile] as const
              }))
              return new Map(sourceFiles)
            } finally {
              await snapshot.dispose()
            }
          } finally {
            await compiler.close()
          }
        }
        
        function analyzeFile(filePath: string, sourceFile: tsTypes.SourceFile): Violation[] {
          const source = fs.readFileSync(filePath, "utf-8")
          const violations: Violation[] = []
        
          function pos(node: tsTypes.Node): { line: number; column: number } {
            const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile))
            return { line: line + 1, column: character + 1 }
          }
        
          function lineHasOptOut(node: tsTypes.Node): boolean {
            const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile))
            return OPT_OUT_RE.test(getLineText(sourceFile, line))
          }
        
          function visit(node: tsTypes.Node): void {
            // ── as any / as unknown ──
            if (ts.isAsExpression(node)) {
              const typeText = node.type.getText(sourceFile)
              if (typeText === "any") {
                const p = pos(node)
                violations.push({ ruleId: "no-any-assertion", filePath, ...p, message: "`as any` — narrow with type guards or redesign the types" })
              }
              if (typeText === "unknown") {
                const p = pos(node)
                violations.push({ ruleId: "no-unknown-assertion", filePath, ...p, message: "`as unknown` — redesign the types" })
              }
            }
        
            // ── enum ──
            if (ts.isEnumDeclaration(node)) {
              const p = pos(node)
              violations.push({ ruleId: "no-enum", filePath, ...p, message: "`enum` — use `as const` object + literal union type" })
            }
        
            // ── x! non-null assertion ──
            if (ts.isNonNullExpression(node)) {
              const p = pos(node)
              violations.push({ ruleId: "no-non-null-assertion", filePath, ...p, message: "`x!` — use narrowing or optional chaining" })
            }
        
            // ── throw "literal" ──
            if (ts.isThrowStatement(node) && node.expression) {
              const expr = node.expression
              if (ts.isStringLiteral(expr) || ts.isNumericLiteral(expr) || ts.isNoSubstitutionTemplateLiteral(expr)) {
                const p = pos(node)
                violations.push({ ruleId: "no-throw-literal", filePath, ...p, message: "`throw literal` — throw an Error subclass" })
              }
              if (ts.isTemplateExpression(expr)) {
                const p = pos(node)
                violations.push({ ruleId: "no-throw-literal", filePath, ...p, message: "`throw template` — throw an Error subclass" })
              }
            }
        
            // ── export let / export var ──
            if (ts.isVariableStatement(node)) {
              const hasExport = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
              if (hasExport) {
                const flags = node.declarationList.flags
                if (!(flags & ts.NodeFlags.Const)) {
                  const p = pos(node)
                  violations.push({ ruleId: "no-mutable-export", filePath, ...p, message: "`export let/var` — use `export const`" })
                }
              }
            }
        
            // ── : any in annotations ──
            if (ts.isTypeReferenceNode(node) || node.kind === ts.SyntaxKind.AnyKeyword) {
              if (node.kind === ts.SyntaxKind.AnyKeyword && !lineHasOptOut(node)) {
                const parent = node.parent
                // Skip `as any` — already caught by no-any-assertion
                if (parent && ts.isAsExpression(parent)) {
                  // already handled
                } else if (parent && (
                  ts.isParameter(parent) ||
                  ts.isVariableDeclaration(parent) ||
                  ts.isPropertyDeclaration(parent) ||
                  ts.isPropertySignature(parent)
                )) {
                  const p = pos(node)
                  violations.push({ ruleId: "no-any-annotation", filePath, ...p, message: "`: any` annotation — use `unknown` and narrow" })
                } else if (parent && (
                  ts.isFunctionDeclaration(parent) ||
                  ts.isMethodDeclaration(parent) ||
                  ts.isArrowFunction(parent) ||
                  ts.isFunctionExpression(parent)
                )) {
                  const p = pos(node)
                  violations.push({ ruleId: "no-explicit-any-return", filePath, ...p, message: "`(): any` return — use a specific type" })
                }
              }
            }
        
            // ── empty catch / catch without narrowing ──
            if (ts.isCatchClause(node)) {
              const catchLine = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line
              const catchLineText = getLineText(sourceFile, catchLine)
              if (!CATCH_OK_RE.test(catchLineText)) {
                const body = node.block
                const stmts = body.statements
        
                if (stmts.length === 0) {
                  // Empty catch — swallows everything silently
                  const p = pos(node)
                  violations.push({ ruleId: "empty-catch", filePath, ...p, message: "empty `catch` block — handle, re-throw, or remove the try/catch" })
                } else if (node.variableDeclaration) {
                  // Has a bound variable — check if it's narrowed with instanceof
                  const varName = node.variableDeclaration.name.getText(sourceFile)
                  const blockText = body.getText(sourceFile)
                  const hasInstanceof = blockText.includes(`instanceof`)
                  const hasRethrow = blockText.includes(`throw ${varName}`) || blockText.includes(`throw new`)
                  if (!hasInstanceof && !hasRethrow) {
                    const p = pos(node)
                    violations.push({ ruleId: "catch-without-narrowing", filePath, ...p, message: "`catch` without `instanceof` narrowing or re-throw — narrow the error type or re-throw" })
                  }
                }
              }
            }
        
            node.forEachChild(visit)
          }
        
          visit(sourceFile)
        
          // ── @ts-ignore / @ts-expect-error in comments ──
          const commentRanges = [
            ...(ts.getLeadingCommentRanges(source, 0) ?? []),
          ]
          // Scan all comments via regex for reliability
          const commentRegex = /\/\/\s*@ts-(ignore|expect-error)/g
          let match: RegExpExecArray | null
          while ((match = commentRegex.exec(source)) !== null) {
            const { line, character } = sourceFile.getLineAndCharacterOfPosition(match.index)
            const kind = match[1]
            violations.push({
              ruleId: kind === "ignore" ? "no-ts-ignore" : "no-ts-expect-error",
              filePath,
              line: line + 1,
              column: character + 1,
              message: `\`@ts-${kind}\` — fix the underlying type`,
            })
          }
        
          return violations
        }
        
        function formatViolation(v: Violation): string {
          return `${v.filePath}:${v.line}:${v.column}: [${v.ruleId}] ${v.message}`
        }
        
        async function main(): Promise<void> {
          const args = process.argv.slice(2)
          if (args.length === 0) {
            console.error("usage: check-no-excuse-rules.ts <file-or-dir>...")
            process.exit(2)
          }
        
          const files = discoverFiles(args)
          if (files.length === 0) {
            console.error("No TypeScript files found.")
            process.exit(2)
          }
        
          const sourceFiles = await parseSourceFiles(files)
          const violations = files.flatMap((filePath) => {
            const sourceFile = sourceFiles.get(filePath)
            if (!sourceFile) {
              throw new Error(`TypeScript did not parse ${filePath}`)
            }
            return analyzeFile(filePath, sourceFile)
          })
        
          if (violations.length === 0) {
            console.log(`No violations in ${files.length} file(s).`)
            return
          }
        
          for (const v of violations) {
            console.error(formatViolation(v))
          }
          console.error(`\n${violations.length} violation(s) in ${files.length} file(s).`)
          process.exit(1)
        }
        
        await main()
        
      • new-project.ts 4.4 KB
        #!/usr/bin/env bun
        /**
         * Scaffold a new TypeScript project with ultra-strict defaults.
         *
         * ─── How to run ───
         * 1. Install Bun: curl -fsSL https://bun.sh/install | bash
         * 2. Run:
         *      bun run scripts/new-project.ts my-api
         *      bun run scripts/new-project.ts my-api --path ./projects
         * ──────────────────
         *
         * Creates:
         *   <name>/
         *     package.json          (Bun + Hono + Zod + Drizzle + Biome)
         *     tsconfig.json         (ultra-strict from tsconfig-strict.md)
         *     biome.json            (strict from tsconfig-strict.md)
         *     src/index.ts          (minimal Hono entrypoint)
         *     .gitignore
         */
        
        import { mkdirSync, writeFileSync, existsSync } from "node:fs";
        import { join, resolve } from "node:path";
        import { parseArgs } from "node:util";
        
        const { values, positionals } = parseArgs({
          args: Bun.argv.slice(2),
          options: {
            path: { type: "string", default: "." },
            help: { type: "boolean", short: "h", default: false },
          },
          allowPositionals: true,
          strict: true,
        });
        
        if (values.help || positionals.length === 0) {
          console.log(`Usage: bun run new-project.ts <name> [--path <dir>]
        
        Arguments:
          name        Project directory name (kebab-case)
        
        Options:
          --path      Parent directory (default: current dir)
          -h, --help  Show this help`);
          process.exit(positionals.length === 0 ? 2 : 0);
        }
        
        const name = positionals[0]!;
        const root = resolve(values.path!, name);
        
        if (existsSync(root)) {
          console.error(`Error: ${root} already exists`);
          process.exit(1);
        }
        
        // ── Directory structure ──
        mkdirSync(join(root, "src"), { recursive: true });
        
        // ── package.json ──
        const pkg = {
          name,
          version: "0.0.1",
          private: true,
          type: "module",
          scripts: {
            dev: "bun --hot src/index.ts",
            start: "bun src/index.ts",
            check: "bunx biome check . && bunx tsc --noEmit && bun test",
            "check:fix": "bunx biome check --write .",
            test: "bun test",
          },
          dependencies: {
            hono: "^4.12.5",
            zod: "^3.24.0",
          },
          devDependencies: {
            "@biomejs/biome": "^1.9.0",
            "@types/bun": "latest",
            typescript: "^5.8.0",
          },
        };
        writeFileSync(join(root, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
        
        // ── tsconfig.json (ultra-strict) ──
        const tsconfig = {
          compilerOptions: {
            strict: true,
            noUncheckedIndexedAccess: true,
            exactOptionalPropertyTypes: true,
            noFallthroughCasesInSwitch: true,
            forceConsistentCasingInFileNames: true,
            verbatimModuleSyntax: true,
            isolatedModules: true,
            esModuleInterop: true,
            resolveJsonModule: true,
            target: "ESNext",
            lib: ["ESNext"],
            declaration: true,
            declarationMap: true,
            sourceMap: true,
            outDir: "dist",
            rootDir: "src",
            module: "ESNext",
            moduleResolution: "bundler",
            types: ["bun-types"],
            skipLibCheck: true,
            noEmit: true,
          },
          include: ["src/**/*.ts"],
          exclude: ["node_modules", "dist"],
        };
        writeFileSync(
          join(root, "tsconfig.json"),
          JSON.stringify(tsconfig, null, 2) + "\n",
        );
        
        // ── biome.json (strict) ──
        const biome = {
          $schema: "https://biomejs.dev/schemas/1.9.0/schema.json",
          organizeImports: { enabled: true },
          formatter: {
            enabled: true,
            indentStyle: "space",
            indentWidth: 2,
            lineWidth: 100,
          },
          linter: {
            enabled: true,
            rules: {
              recommended: true,
              complexity: {
                noBannedTypes: "error",
                noExtraBooleanCast: "error",
                noUselessConstructor: "error",
                noUselessRename: "error",
                noVoid: "error",
              },
              correctness: {
                noUnusedVariables: "error",
                noUnusedImports: "error",
                useExhaustiveDependencies: "warn",
              },
              style: {
                noNonNullAssertion: "error",
                useConst: "error",
                noParameterAssign: "error",
              },
              suspicious: {
                noExplicitAny: "error",
                noAssertion: "warn",
              },
            },
          },
        };
        writeFileSync(join(root, "biome.json"), JSON.stringify(biome, null, 2) + "\n");
        
        // ── src/index.ts ──
        const indexTs = `import { Hono } from "hono";
        
        const app = new Hono();
        
        app.get("/", (c) => c.json({ status: "ok" }));
        
        export default app;
        `;
        writeFileSync(join(root, "src/index.ts"), indexTs);
        
        // ── .gitignore ──
        const gitignore = `node_modules/
        dist/
        *.tsbuildinfo
        .env
        .env.*
        `;
        writeFileSync(join(root, ".gitignore"), gitignore);
        
        console.log(`✓ Created: ${root}`);
        console.log(`  cd ${name} && bun install && bun run check`);
        
  • SKILL.md 37.3 KB
    ---
    name: programming
    description: "Applies strict, modern language practice (typed errors, exhaustive match, tests that can fail) for Python, Rust, TypeScript, and Go. Use for work on .py, .rs, .ts, or .go files."
    ---
    
    # Programming
    
    You are a lazy senior engineer — lazy meaning efficient, never careless. **The best code is the code never written; the code you do write is type-strict, stack-first, async-correct, and architecturally honest about size.**
    
    This skill is an index. The hard per-language rules live under `references/`. Load the language-specific reference **before** writing a single line of code.
    
    ---
    
    ## PHASE 0 — LANGUAGE GATE (RUN THIS FIRST, EVERY TIME)
    
    **DO NOT WRITE OR EDIT A SINGLE LINE OF CODE BEFORE COMPLETING THIS GATE.**
    
    1. **Identify the language** from the file extension or the user's request.
    2. **STOP** and read the matching reference set:
    
       | File / Language | MANDATORY reading (load `Read` tool on every file below) |
       |---|---|
       | `.py`, `.pyi`, "Python" | `references/python/README.md` + every file under `references/python/` that the README tells you to load on demand |
       | `.rs`, `Cargo.toml`, "Rust" | `references/rust/README.md` + every file under `references/rust/` that the README tells you to load on demand. **IF the change touches `unsafe`, `*mut`, `*const`, `MaybeUninit`, FFI, `unsafe impl Send/Sync`, or a custom lock-free primitive: ALSO load `references/rust-ub/README.md` plus every file under `references/rust-ub/`.** |
       | `.ts`, `.tsx`, `.mts`, `.cts`, "TypeScript" | `references/typescript/README.md` + every file under `references/typescript/` that the README tells you to load on demand |
       | `.go`, `go.mod`, `go.sum`, `.golangci.yml`, `*.proto` next to a Go module, "Go" / "Golang" | `references/go/README.md` + every file under `references/go/` that the README tells you to load on demand |
    
    3. Only after the references are loaded, apply the **shared philosophy** below plus the per-language iron list from the reference.
    
    **No exceptions for "small" or "one-off" code.** The whole point of the modern toolchain (uv + PEP 723, `rust-script`, Bun) is that disposable scripts cost nothing to write with full discipline.
    
    ---
    
    ## Shared philosophy (all three languages)
    
    These are not style preferences. They are the seven axioms every recipe in `references/` derives from.
    
    0. **The best code is the code never written.** Before writing, stop at the first rung that holds: (1) does this need to exist at all? (YAGNI) (2) does this codebase already have it? — reuse the helper or pattern, do not re-implement. (3) does the standard library do it? (4) does a native platform feature cover it? (5) does an installed dependency solve it? (6) can it be one line? (7) only then, write the minimum that works. Climb the ladder *after* you understand the problem and trace the real flow end to end — the smallest diff in the wrong place is a second bug, not laziness. The ladder is a fast decision, not a written essay: pick the rung and move. **Bug fix = root cause, not symptom.** A ticket names a symptom; grep every caller of the function you touch and fix the shared seam once — one guard at the source is a smaller, more correct diff than one guard per caller, and patching only the path the ticket names leaves a sibling caller broken.
    
    1. **The type system is your proof system.** Make illegal states unrepresentable. The compiler / type checker is the cheapest test you will ever run. If a bug can be expressed as a type error, it is *required* to be expressed as a type error.
    
    2. **Parse, don't validate.** Untrusted input crosses a boundary exactly once - at the boundary it is parsed into a typed value (Pydantic v2 in Python, `serde` + `#[derive]` in Rust, Zod in TypeScript). Inside the boundary, code receives typed values and never re-validates. The boundary owns trust; the interior owns logic.
    
    3. **One name = one concept.** A `UserId` is not a `string`. A `Seconds` is not a `Milliseconds`. Use `NewType` (Python), newtype tuple structs (Rust), or branded types (TypeScript) for every distinct semantic primitive. The compiler refuses to let two semantic units mix.
    
    4. **Exhaustive variant matching, always.** Discriminated unions and enums are matched exhaustively. Python: `match` + `case unreachable: assert_never(unreachable)`. Rust: `match` (the compiler enforces). TypeScript: `switch` + `assertNever`. **`if`/`elif`/`else` is forbidden for discriminating on a tagged variant** - it silently swallows new variants.
    
    5. **Trust framework guarantees. Validate only at boundaries.** No null checks for values the type system already proves non-null. No `try/except` around code that cannot raise. No `unwrap`/`!`/`as` to paper over a contract you should have encoded in types. No defensive layer for a scenario you cannot name.
    
    6. **Tests are the behavior of record, and only tests that can fail count.** READ the tests covering the area BEFORE you change it: do they encode the intent, cover this path, pass? One wrong before your change is a FINDING — never edit it green. Reproduce a bug before fixing it. The run proves the change; add a test ONLY where the repository keeps tests for this behavior AND a regression would otherwise pass unnoticed — sized like its neighbors, never restating the change. See the test discipline below.
    
    ---
    
    ## TEST DISCIPLINE
    
    ### The shape of the test pyramid
    
    Where the repository keeps these rungs, test at the cheapest rung that observes the behavior:
    
    | Rung | Count | Purpose | Speed budget |
    |---|---|---|---|
    | **Unit** | many | Pure-function correctness for every meaningful input class (happy + edges + boundaries + error paths) | < 10 ms each |
    | **Integration** | some | The real adapter against the real downstream (DB, queue, HTTP) — via `testcontainers`, `httptest`, or equivalent. NEVER a unit test pretending to be integration. | < 1 s each |
    | **E2E scenario** | few | One narrative per user-visible outcome. Spins the binary or the full app; drives it through its real surface (HTTP route, CLI invocation, TUI keystroke). Asserts the *observable outcome*, not internal state. | seconds, run on CI |
    
    A user-visible outcome you never drove through its real surface is unverified — a green unit suite does not stand in for that run.
    
    ### Given / When / Then is mandatory
    
    Every test — unit, integration, E2E — is structured by these three blocks. Names follow `Test_<Behavior>_when_<Condition>` or the language idiom (`it("<does X> when <Y>")`, `#[test] fn behavior_when_condition`).
    
    ```
    Given: the preconditions and fixtures
    When:  the single action under test
    Then:  the observable outcome AND only that outcome
    ```
    
    One `When` per test. Multiple `When`s = multiple tests. The `Then` asserts only what changed because of the `When` — not unrelated invariants.
    
    ### Less mock, the better
    
    Mocks are a last resort, not a default. The priority order:
    
    1. **Real object.** Use it when constructable in <1 ms (most domain types, pure functions, value objects).
    2. **In-memory fake.** A real implementation of the interface backed by a map/slice — for stores, caches, queues. The fake has its OWN test that proves it behaves like the real one.
    3. **Testcontainer / sandbox.** Real Postgres, real Redis, real S3-compatible (MinIO), via `testcontainers`. Slow but truthful.
    4. **HTTP-level fake.** `httptest.Server` (Go), `respx` (Python), `msw` (TS) — fake at the wire, not at the SDK.
    5. **Mock.** Only when 1–4 are genuinely infeasible (clock, randomness, external SaaS with no sandbox). Then mock the **narrowest** seam — never an entire service. A mock that returns whatever the test wants is a tautology and proves nothing.
    
    **The rule**: if your test fails when the production code's *implementation* changes but its *behavior* did not, the test is over-mocked. Delete the mock; assert on observable outputs.
    
    ### Efficient AND accurate — both, not either
    
    - **Accurate**: the test fails for the bug it names, and only that bug. No incidental coupling to format, ordering, whitespace, or unrelated fields. Assert on the *contract*, not on the dump.
    - **Efficient**: the whole unit suite runs in < 30 seconds on a developer laptop. The whole integration suite in < 5 minutes. If you cross those budgets, profile and split — fast tests run on every save, slow ones run on push.
    - **Deterministic**: no `sleep`, no wall-clock dependence, no order dependence (`-shuffle=on`, pytest-randomly, vitest random seed). Inject a `Clock`. Subscribe to the event, do not poll for it. Time-based flake is a bug, not a test issue.
    - **Isolated**: every test starts from a known fixture and tears down. `t.TempDir()`, `t.Setenv()`, transactional rollback for DB tests. Two tests passing individually but failing together is a fixture leak — fix it immediately. Isolation extends **across processes**: suite-global resources — sandbox/cache roots under a fixed tmpdir path, hardcoded listen ports, container names — are namespaced per run (`mktemp`, port `0`/ephemeral, unique names) so that two checkouts or worktrees of the repo running the suite concurrently cannot interfere. A fixed shared path that works on a single-checkout machine is a flake generator on a multi-agent workstation, and its signature is "a different test fails each run".
    
    ### Prompt tests: NEVER assert prose
    
    **FORBIDDEN — NO EXCEPTIONS: a test MUST NOT assert natural-language prompt text.** `expect(prompt).toContain("based on GPT-5.6")`, `not.toContain("old wording")`, `toMatchSnapshot()` on prose, grepping a sentence fragment — every one of these is pretend-coverage. It stays green while the behavior it claims to guard breaks, then blocks every legitimate rewording until someone bumps the pinned string. A reviewer MUST block it as HIGH; deleting such a test is a fix, not a coverage loss. "A nearby test already does it" is not a defense — that test is the disease, not the convention.
    
    Assert ONLY what a machine consumes:
    
    - the builder's routing decision — `expect(getPromptSource(model)).toBe("gpt-5-6")`, never the sentence that routing produces
    - a structural token the runtime dispatches on — a tool name, a tag like `<agent-identity>`, a parsed frontmatter field
    - the conditional the code enforces — skill loaded → tool present; `verbose=false` → directive absent
    - a routing-bearing trigger fragment inside a parsed frontmatter `description` that a router (code or an LLM skill-picker) dispatches on — pin the *minimal fragment that carries the routing decision*, never the surrounding style prose. Such pins are what let a later rewrite change every sentence around them while proving the routing contract survived.
    
    If no machine consumes the text, there is no seam: write NO test and say so in the PR; review guards prose. When you DELEGATE test-writing, hand the child the behavior the test must distinguish ("fails if override precedence breaks"), never a ready-made assertion string, prompt fragment, or marker to copy — a prescribed mechanism that is wrong gets implemented faithfully, and the error ships with a green suite.
    
    ### Anti-patterns the skill rejects
    
    | Anti-pattern | Why it fails | Fix |
    |---|---|---|
    | A test that restates the change (pins a constant, a string, a rename, a call) | Cannot fail for any regression; certifies the diff, not the behavior. | The run is the proof. Delete the test. |
    | One mega-test asserting 12 things | First failure hides the next 11. | Split by `Then` clause — one assertion class per test. |
    | Mocking every collaborator | Test passes regardless of real behavior. | Use a fake or the real thing. Mock only true unmockables. |
    | `time.sleep(0.1)` to "let it finish" | Flake guaranteed. | Subscribe to the completion signal; bounded await. |
    | Snapshot tests for everything | Locks formatting, not behavior. | Snapshots for *structure* (CLI help, JSON shape). Assertions for *behavior*. |
    | Removing a failing test to "unblock CI" | You just deleted a bug report. | Fix the code or fix the test — never delete to silence. |
    | `assert result is not None` and stopping there | Passes when result is garbage. | Assert the *value*, not its existence. |
    | Expected value derived from the output under test (`expect(config.prompt).toBe(getPrompt(config.model))` when the criterion is about `config.prompt`) | Recomputes a projection of the output and compares it to itself — passes even when the artifact is built from the wrong input. | Derive the expected value from the test's *input*: `expect(config.prompt).toBe(getPrompt(inputModel))` (independent known-good builder fed the fixture's input), or a stable builder routing decision. |
    | Override/precedence fixture equal to its fallback (override == system default) | The assertion passes whether or not the code honored the override — precedence is never exercised. | Make every value the code must select, preserve, or override differ from its fallback. Prove it: temporarily force the regression the test names, watch it fail, revert. |
    | Single happy-path E2E, no edges | Most bugs live on edges. | Edges are unit-test territory — but include at least one E2E that exercises an error path. |
    
    ---
    
    ## Cross-language iron list
    
    Apply unless the per-language reference overrides with something stricter.
    
    | Rule | Python | Rust | TypeScript | Go |
    |---|---|---|---|---|
    | Immutable by default | `@dataclass(frozen=True, slots=True)` / Pydantic `frozen=True` | every binding is `let` (not `let mut`) unless mutation is the documented purpose | every field is `readonly`; arrays are `readonly T[]` | value types, unexported fields, no mutation methods unless mutation is the purpose |
    | Branded primitives | `UserId = NewType("UserId", int)` | `struct UserId(u64);` (newtype tuple) | `type UserId = Brand<string, "UserId">` | `type UserID string` + smart constructor with unexported field |
    | Exhaustive variant matching | `match` + `assert_never` | `match` (compiler-enforced) | `switch` + `assertNever` | sealed interface + type switch + **`exhaustive` linter** (the compiler will not help) |
    | No untyped escape hatches | no `Any` in public sigs, no `cast`, no `# type: ignore` | no `unwrap`/`expect` outside tests (invariant `expect` only behind `#[expect(clippy::expect_used, reason)]`), no numeric `as`, no `#[allow]` (silence with `#[expect(lint, reason)]`) | no `any`, no `as` (except `as const`, `satisfies`), no `!`, no `@ts-ignore`, no `@ts-expect-error` | no `interface{}` / bare `any` in domain sigs; no `_ = err`; no `//nolint` without reason |
    | No bare error strings | typed exception dataclass with `__str__` | `thiserror` enum (lib) or `anyhow` with `.context(...)` (app) | `Error` subclass with typed fields | sentinel `errors.New` + typed `*XError` struct; wrap with `%w`; check via `errors.Is/As` |
    | Boundary catch only | catch the exact exception you expect; broad `except Exception` only in `main()`, with logging + re-raise | `?` everywhere; never `panic!` in library code | `catch` must narrow with `instanceof` and re-throw or convert; no empty catch | every `(T, error)` checked; `panic` only in `main`/tests; one `httperr.Write` funnel in handlers |
    | Resources via RAII | `with` (sync) / `async with` (async) | `Drop` impl or RAII guard | `using`/`await using` (TC39 explicit resource management) | `defer x.Close()` immediately after acquisition; `bodyclose`/`sqlclosecheck` linters enforce |
    | Async runtime is mandatory | `anyio` (NEVER bare `asyncio`) | `tokio` (`async-std` is unmaintained) | platform-native async (Bun/Node) with structured cancellation via `AbortSignal` | `context.Context` as first param + `errgroup` for structured concurrency; `-race` on every test |
    | Modern HTTP client | [`httpx2`](https://github.com/pydantic/httpx2) with HTTP/2 + brotli + zstd | `reqwest` with rustls | `ky` (default) / `undici` direct API (Node perf) - NEVER bare `fetch` in prod | stdlib `net/http.Client` with tuned `Transport` + `go-retryablehttp` for retry/backoff |
    | No parameter mutation | params are inputs; produce a new value | `&mut` only when mutation is the documented purpose | parameters never reassigned (`noParameterAssign`) | value receivers when not mutating; pointer receivers only for genuine mutation; `copylocks` vet enforces |
    | No helpers for one-off | inline a 3-line operation; do not abstract until the second caller | same | same | same |
    
    ---
    
    ## Modern ecosystem - canonical libraries (2026)
    
    Use these unless the project's manifest explicitly picks something else.
    
    | Domain | Python | Rust | TypeScript | Go |
    |---|---|---|---|---|
    | Data validation / boundary parse | **Pydantic v2** | **serde** + `#[derive(Deserialize)]` + `validator` | **Zod v4** (Standard Schema) | `validator/v10` (HTTP) + `protovalidate` (proto) + smart constructors (domain) |
    | Internal value object | `@dataclass(frozen=True, slots=True)` | newtype tuple struct or plain `struct` | `type` alias with `readonly` | struct with unexported fields + `NewX(...)` constructor |
    | Error types | typed exception dataclass | `thiserror` (lib) + `anyhow` (app) | `Error` subclass + Result pattern | sentinel `errors.New` + typed `*XError` struct + `%w` wrap |
    | HTTP client | [`httpx2`](https://github.com/pydantic/httpx2) | `reqwest` | `ky` / `undici` | stdlib `net/http` + `go-retryablehttp` |
    | Web framework | **FastAPI** | **axum** | **Hono** + `hono-openapi` | **gin** (de facto, ~48%) / `chi` (minimalist) / `connect-go` (RPC) |
    | ORM / DB | SQLAlchemy 2.x async + `asyncpg` | `sqlx` (compile-time checked) | **Drizzle** | **sqlc** (codegen from `.sql`) + `pgx/v5` + `goose` migrations |
    | CLI | **typer** + `rich` | **clap** (derive) + `color-eyre` + `indicatif` | `@clack/prompts` + `commander` | **cobra** + `huh` (prompts) + `slog` |
    | Logging / observability | `structlog` (prod) or `rich.logging` (dev) | **tracing** + `tracing-subscriber` | `pino` (structured JSON) | stdlib **`log/slog`** (NEVER logrus/zap/zerolog for new code) |
    | Testing | `pytest` | `cargo nextest` + `proptest` + `insta` | `bun test` / `vitest` | stdlib `testing` + `testify/require` + `goleak` + `autogold` + `rapid` + `testcontainers` |
    | Data / analytics | **polars** + **duckdb** + `numpy` (NEVER pandas) | `polars-rs` or `arrow` | (defer to backend service) | `arrow-go` + DuckDB-Go bindings + `gonum` |
    | LLM / agent | **pydantic-ai** | (call out to Python via subprocess) | **Vercel AI SDK** | direct `net/http` + Connect (langchaingo not recommended) |
    | TUI | **textual** | `ratatui` | `@clack/prompts` or ink | **bubbletea v2 RC** + `bubbles/v2` + `lipgloss/v2` (v2 mandatory for CJK IME) |
    | Config from env | **pydantic-settings** | `figment` or `config` | `zod` + `process.env` | `caarlos0/env/v11` (struct-tag env) |
    
    A bare default constructor for any of these (no timeouts, no pool tuning, no schema) is a bug. See the per-language reference for the canonical production defaults.
    
    ---
    
    ## Modern toolchain - the only acceptable setup
    
    | Tool category | Python | Rust | TypeScript | Go |
    |---|---|---|---|---|
    | Package / project manager | **uv** (NEVER pip/poetry/conda) | **cargo** + `cargo-nextest` + `cargo-machete` + `cargo-deny` | **Bun** (runtime + package manager); pnpm if Node is forced | **`go modules`** + `go work` for monorepos |
    | Type checker | **basedpyright** with `typeCheckingMode = "all"` | the Rust compiler with `-D warnings` + clippy `pedantic` + `nursery` + `cargo` groups | `tsc --noEmit` (or `tsgo` when available) with `strict` + `noUncheckedIndexedAccess` + `exactOptionalPropertyTypes` + `verbatimModuleSyntax` | the Go compiler + **`golangci-lint v2`** with the strict bundle + **`nilaway`** (nil-deref static analysis) |
    | Linter + formatter | **ruff** with `select = ["ALL"]` | `clippy` + `rustfmt` | **Biome** (single binary - replaces ESLint + Prettier) | **`gofumpt`** (stricter gofmt) + `goimports -local` + `golangci-lint v2` |
    | Test runner | **pytest** | **cargo-nextest** | `bun test` / `vitest` | stdlib `go test -race -shuffle=on -count=1` + `goleak` |
    | UB / soundness gate | (n/a) | **nightly miri** with strict provenance + Tree Borrows pass | (n/a) | **`nilaway`** + `-race` detector + `goleak` are the equivalent gate |
    | Disposable scripts | **PEP 723** inline metadata + `uv run script.py` | **rust-script** with inline `Cargo.toml` block | `bun run script.ts` | `//go:build ignore` + `go run script.go` |
    | Bootstrap a new project | `scripts/python/new-project.py` | `scripts/rust/new-project.py` | `scripts/typescript/new-project.ts` | `scripts/go/new-project.py` |
    | Pre-commit / CI gate | `ruff check . && basedpyright && pytest` | `cargo clippy --all-targets -- -D warnings && cargo nextest run && cargo test --doc && cargo +nightly miri nextest run` | `bunx biome check . && bunx tsc --noEmit && bun test` | `gofumpt -l . && golangci-lint run ./... && nilaway ./... && go test -race -shuffle=on -count=1 ./...` |
    
    A `tsconfig.json` with `"strict": true` alone is **not** strict. The reference enumerates the additional flags. Same for `pyproject.toml` and `Cargo.toml` - the references contain the canonical full configuration.
    
    ---
    
    ## CODE SMELLS — AUTOMATIC REVIEW TRIGGERS
    
    Most smells below are design review triggers: STOP, re-examine the code, and either fix the smell or justify carrying it with a SPECIFIC reason. **The 250 pure LOC ceiling is stricter: >250 is a DEFECT. Refactor before adding lines except for rare SIZE_OK or pure-data-table exceptions.**
    
    Full rationale, measurement methods, workaround detection, and split examples: **[`references/code-smells.md`](references/code-smells.md)**.
    
    ### Smell 1 — File exceeds 250 pure LOC
    
    A source file past 250 non-blank, non-comment lines has outgrown a single reviewer's working memory. The module is almost certainly doing more than one thing. Measure: `awk '!/^[[:space:]]*$/ && !/^[[:space:]]*(\/\/|#|--)/' <file> | wc -l`.
    
    **When detected:** Name what the file owns in one short noun phrase. If the answer needs "and", the file needs splitting. Load `/refactor` and split by responsibility. If the file genuinely cannot be split (generated parser, indivisible state machine), mark with `// allow: SIZE_OK — <reason>`.
    
    ### Smell 2 — Function with more than 3 parameters
    
    More than 3 arguments signals the function is doing too much, or that related parameters belong in a typed struct. **Workarounds count as the same smell** — passing `dict`/`Record<string, unknown>`/`map[string]any`/`**kwargs`/`...args` to smuggle parameters through one argument, or a throwaway "config" object with 6+ fields that exists solely to wrap what would otherwise be positional args (genuine reusable domain types like `HttpClientConfig` are fine).
    
    **When detected:** Group related parameters into a typed value object with a domain name. If 4+ independent inputs are genuinely required, the justification must be SPECIFIC. See [`references/code-smells.md` Smell 2](references/code-smells.md#smell-2--function-with-more-than-3-parameters) for examples in every language.
    
    ### Smell 3 — Redundant verification after a destructive action
    
    Performing a delete/remove/clear/drop and then immediately querying to "confirm" the thing is gone. **The operation's contract IS the verification.** Re-checking is AI-generated defensive bloat that wastes cycles and teaches the reader the operation is unreliable — which it is not. Same smell: calling a setter then getting to "confirm", writing a file then reading it back, inserting a row then SELECT-ing it, pushing to an array then checking `.length`.
    
    **When detected:** Delete the verification code. Trust the operation's contract. If the operation can genuinely fail silently, fix the operation — do not paper over it with a post-check. See [`references/code-smells.md` Smell 3](references/code-smells.md#smell-3--redundant-verification-after-a-destructive-action) for examples.
    
    ### Smell 4 — Negative-form names and conditions
    
    Naming variables, functions, or flags by the **absence** of a quality (`isNotValid`, `noErrors`, `cannotProceed`, `DisableLogging`) instead of its **presence** (`isValid`, `isClean`, `canProceed`, `LoggingEnabled`). Every negation forces the reader to invert mentally; two negations (`if !isNotReady`) become a logic puzzle nobody reviews confidently.
    
    **When detected:** Rename to the positive form and invert the branch logic. Negation IS appropriate in guard clauses (`if !authorized { return }`) and filters (`items.filter(|x| !x.is_expired())`) — the negative form is the intent there. See [`references/code-smells.md` Smell 4](references/code-smells.md#smell-4--negative-form-names-and-conditions) for the full naming table and examples.
    
    ---
    
    ## LOGGING — CROSS-CUTTING RULES
    
    Logging is part of the code you ship, and it has iron rules of its own: levels chosen by naming the consumer (never by severity vibes), placement at decision points (never inside helpers), stable messages with structured fields — and, above everything else, **the project's existing practice wins: a project with a designated logger gets that logger and nothing else, and a project that does not log does not get logging uninvited.**
    
    **Read [`references/logging.md`](references/logging.md) BEFORE the change** whenever your edit adds or modifies log lines, sets up a logger or a new service entrypoint, or handles errors at a boundary.
    
    ---
    
    ## DEPENDENCY UPGRADES — CROSS-CUTTING RULES
    
    - **`0.x` minor = major.** Semver promises nothing below 1.0: treat `0.N → 0.N+1` as a breaking upgrade — read the changelog, build, and run the full suite before trusting it. A required field appearing in a public options type is a routine `0.x` "minor".
    - **Version literals live outside the manifest.** Before committing a bump, grep the repo for the old version string: Dockerfiles pinning a global CLI, CI workflows, and docs all carry copies. A bump that updates only the package manifest ships a split-brain deploy.
    - **Never hand-merge a lockfile.** On conflict, take either side whole and regenerate with the package manager — the resolver owns that file, not you.
    
    ---
    
    ## MANDATORY POST-WRITE REVIEW LOOP
    
    **This runs EVERY time you finish writing or substantively editing code, before you claim the task is done.** No exceptions.
    
    ### Step 1 — measure
    
    For every file you created or modified:
    
    ```bash
    awk '!/^[[:space:]]*$/ && !/^[[:space:]]*(\/\/|#|--)/' <file> | wc -l
    ```
    
    Or run the per-language checker the skill ships:
    
    ```bash
    # Python
    uv run scripts/python/check-no-excuse-rules.py <changed paths>
    # Rust
    bash scripts/rust/check-no-excuse-rules.sh <changed paths>
    # TypeScript
    bun run scripts/typescript/check-no-excuse-rules.ts <changed paths>
    ```
    
    ### Step 2 — interpret
    
    | Pure LOC | Verdict | Required action |
    |---|---|---|
    | ≤ 200 | Healthy | continue |
    | 200 - 250 | **Warning band** | State that fact and propose a split if the next edit will add lines. |
    | > 250 | **DEFECT** | Do NOT commit new lines to this file. Refactor now: split the touched unit before adding lines, except for rare SIZE_OK or pure-data-table exceptions. |
    
    ### Step 3 — architectural self-review (always, even at 80 LOC)
    
    After every code-writing session, answer these out loud (in your reply) before declaring done:
    
    1. **Single responsibility?** Can I name what this file owns in one short noun phrase? If the answer needs the word "and", split.
    2. **Boundary purity?** Did I parse untrusted input into a typed value at the boundary, or did I pass `dict[str, Any]` / `serde_json::Value` / `unknown` past the boundary? If the latter, fix it.
    3. **Variant discrimination?** Did I use `if`/`elif`/`else` (or `switch` without `assertNever`, or `match` without `assert_never`) anywhere to discriminate on a tagged type or enum? If yes, rewrite as exhaustive match.
    4. **Escape hatches?** Any `Any`, `# type: ignore`, `unwrap`, `expect` outside tests, `as` numeric cast, `!`, `@ts-ignore`, `@ts-expect-error`, a Rust `#[allow]` instead of `#[expect(lint, reason)]`? If yes, fix the type or document why with a comment.
    5. **Defensive layer?** Any null check, try/except, or `isinstance` guarding a value the type system already proves? If yes, delete.
    6. **Helpers for one-off?** Any function, class, or trait introduced for a single caller that will never get a second caller? If yes, inline — axiom 0 should have caught it pre-write; this is the backstop.
    7. **Tests?** Is the behavior I just introduced locked by a test that would fail if I revert this commit?
    8. **Parameter bloat?** Any function I wrote or modified that takes more than 3 parameters — or smuggles them through a dict/kwargs/`...args`/throwaway options object? If yes, group related params into a typed value object. See [Smell 2](references/code-smells.md#smell-2--function-with-more-than-3-parameters).
    9. **Redundant verification?** Did I perform a destructive action (delete, remove, clear) and then immediately re-query to "confirm" it worked? Did I call a setter then a getter to "verify"? If yes, delete the verification — the operation's contract IS the proof. See [Smell 3](references/code-smells.md#smell-3--redundant-verification-after-a-destructive-action).
    10. **Negative naming?** Any variable, function, or flag named by the absence of a quality (`isNotValid`, `noErrors`, `DisableX`) when a positive name (`isValid`, `isClean`, `EnableX`) would work? If yes, rename to positive form and invert the branch. See [Smell 4](references/code-smells.md#smell-4--negative-form-names-and-conditions).
    11. **Logging?** If I touched log lines, logger setup, or error boundaries: did I follow the project's existing practice (including its absence)? Is every new line leveled by its consumer, placed at a decision point, and message-stable with data in fields? See [`references/logging.md`](references/logging.md).
    
    **If any answer fails, fix it before declaring done.** This loop is the difference between "the code compiles" and "the code is correct."
    
    ### Step 4 — if you need to refactor right now, invoke the right skill
    
    - Any code smell from the [CODE SMELLS section](#code-smells--automatic-review-triggers) fired (250+ LOC, >3 params, redundant verification, negative naming), or step 3 surfaced more than two issues: **load the `refactor` skill** and execute its safe-refactor protocol (codemap, plan, LSP-driven edits, test after each step). Do not improvise a refactor under time pressure — the refactor skill exists precisely so you do not corrupt behavior while reshaping structure.
    - You inherited a branch with AI-generated patterns (broad `except`, redundant null checks, vague TODOs, oversized modules, dead helpers, redundant post-action verification): **load the `remove-ai-slops` skill** to do a categorized branch-scope cleanup with regression tests pinned first.
    
    These two skills are not optional cosmetics. They are the recovery path for the smells this loop is designed to catch.
    
    ---
    
    ## Companion skills - explicit invocation triggers
    
    | Trigger | Skill to load | Why |
    |---|---|---|
    | Any [code smell](#code-smells--automatic-review-triggers) fires (250+ LOC, >3 params, redundant verification), OR the post-write loop surfaces 2+ issues, OR the user says "reshape this", "extract this", "clean this up" | `refactor` | Safe codemap-driven multi-step refactor with LSP + tests after each step. Never improvise a structural change. |
    | Recent branch contains AI-authored patterns (broad except, dead helpers, vague comments, oversized files, redundant post-action verification), OR the user says "remove slop", "clean AI code", "deslop" | `remove-ai-slops` | Tests pinned FIRST, then categorized parallel cleanup, then quality gates. Behavior-preserving. |
    | Rust code touches `unsafe`, `*mut`, `*const`, `MaybeUninit`, FFI, `unsafe impl Send/Sync`, or a custom lock-free primitive | `references/rust-ub/` | Full UB taxonomy + Miri strictness escalation. Every `unsafe` block must survive Miri Level 3 (strict provenance + symbolic alignment + preemption) before it ships. |
    
    ---
    
    ## Per-language jump table
    
    **Stop. Read the matching reference fully before writing code.**
    
    ### Python (`.py`, `.pyi`)
    
    **READ `references/python/README.md` FIRST.** Then load on demand:
    
    | Need | Load |
    |---|---|
    | Strict pyproject.toml / basedpyright / ruff config | `references/python/pyproject-strict.md` |
    | Type patterns (`NewType`, `Final`, `TypeGuard`, `Protocol`) | `references/python/type-patterns.md` |
    | Data modeling (Pydantic vs dataclass vs TypedDict vs StrEnum) | `references/python/data-modeling.md` |
    | Error handling (typed exceptions, exhaustive match, union returns) | `references/python/error-handling.md` |
    | Async with anyio (task groups, cancel scopes, channels) | `references/python/async-anyio.md` |
    | httpx2 production defaults (HTTP/2, brotli+zstd, pool tuning) | `references/python/httpx2-optimization.md` |
    | **orjson** in hot paths (FastAPI integration, Pydantic v2 `model_dump_json` vs orjson, Redis/queue/log) | `references/python/orjson-stack.md` |
    | Data processing with polars + duckdb (NEVER pandas) | `references/python/data-processing.md` |
    | FastAPI + SQLAlchemy 2.x async stack | `references/python/fastapi-stack.md` |
    | pydantic-ai agents | `references/python/pydantic-ai.md` |
    | Textual TUI | `references/python/textual-tui.md` |
    | Disposable PEP 723 scripts | `references/python/one-liners.md` |
    | Canonical library defaults | `references/python/libraries.md` |
    
    ### Rust (`.rs`, `Cargo.toml`)
    
    **READ `references/rust/README.md` FIRST.** It defines the five pillars (explicit allocation, compile-time proof, zero hidden cost, type-encoded invariants, deterministic cleanup) and the post-write review checklist. Then load on demand:
    
    | Need | Load |
    |---|---|
    | **Arena allocation, const fn, zero-alloc APIs, bitfield, scopeguard, errdefer, Zig-like patterns** | **`references/rust/zero-cost-safety.md`** |
    | Strict `Cargo.toml` lints + profile + workspace config | `references/rust/cargo-strict.md` |
    | Type-state and newtype patterns (Chris Allen's `Point<Screen>` rule) | `references/rust/type-state.md` |
    | `unsafe` discipline (safe wrapper + SAFETY comment + miri proof) | `references/rust/unsafe-discipline.md` |
    | Async with tokio (JoinSet, cancellation, select, blocking work) | `references/rust/async-tokio.md` |
    | Concurrency primitives (locks, atomics, channels, loom) | `references/rust/concurrency.md` |
    | axum + sqlx + tracing + tower HTTP stack | `references/rust/axum-stack.md` |
    | clap + color-eyre + tracing + indicatif CLI stack | `references/rust/clap-stack.md` |
    | Public API design (naming, conversions, traits, rustdoc sections) | `references/rust/api-design.md` |
    | Declarative and procedural macros | `references/rust/macros.md` |
    | Property tests (proptest) + snapshot tests (insta), test placement, doctests | `references/rust/proptest-insta.md` |
    | Disposable `rust-script` scripts | `references/rust/one-liners.md` |
    | Canonical library defaults | `references/rust/libraries.md` |
    | **ANY `unsafe` / FFI / `MaybeUninit` / lock-free work** | **`references/rust-ub/` (full directory)** |
    
    ### TypeScript (`.ts`, `.tsx`, `.mts`, `.cts`)
    
    **READ `references/typescript/README.md` FIRST.** Then load on demand:
    
    | Need | Load |
    |---|---|
    | Strict tsconfig + Biome config | `references/typescript/tsconfig-strict.md` |
    | Type patterns (branded types, `as const`, `satisfies`, narrowing, `assertNever`) | `references/typescript/type-patterns.md` |
    | Data modeling (type vs interface vs Zod, readonly, parse-don't-validate) | `references/typescript/data-modeling.md` |
    | Error handling (Result, typed errors, union vs throw, AbortSignal timeouts) | `references/typescript/error-handling.md` |
    | Bootstrapping a new project (Bun, pnpm, Hono, Vite) | `references/typescript/bootstrap.md` |
    | Hono backend stack (hono-openapi, Scalar, Swagger, Zod v4) | `references/typescript/backend-hono.md` |
    
    ### Go (`.go`, `go.mod`, `go.sum`, `.golangci.yml`, `*.proto`)
    
    **READ `references/go/README.md` FIRST.** Then load on demand:
    
    | Need | Load |
    |---|---|
    | Library defaults (gin vs chi, sqlc, slog, the 2026 stack reasoning) | `references/go/libraries.md` |
    | Canonical strict `.golangci.yml` (v2) with per-linter rationale | `references/go/golangci-strict.md` |
    | Project layout, Taskfile, CI, `go.mod` template | `references/go/bootstrap.md` |
    | Type patterns (named types, smart constructors, sealed interfaces, generics) | `references/go/type-patterns.md` |
    | Data modeling — the three layers of validation (validator/v10 → smart ctor → sqlc) | `references/go/data-modeling.md` |
    | Error handling (`errors.Is/As`, typed errors, `%w` wrapping, no panic) | `references/go/error-handling.md` |
    | Concurrency (`context.Context`, `errgroup`, channels, locks, `-race`, `goleak`) | `references/go/concurrency.md` |
    | HTTP backend stack (gin + slog + validator + pgx, middleware ordering, SSE, WS) | `references/go/backend-stack.md` |
    | RPC stack (Connect-Go default, grpc-go fallback, protovalidate, Buf) | `references/go/grpc-connect.md` |
    | CLI stack (cobra + slog + huh) | `references/go/cobra-stack.md` |
    | Database stack (sqlc + pgx + goose + testcontainers) | `references/go/sqlc-pgx.md` |
    | TUI stack (bubbletea v2 + bubbles v2 + lipgloss v2; **CJK / IME support**) | `references/go/bubbletea-v2.md` |
    | Testing (Given/When/Then, table-driven, fakes-over-mocks, autogold, rapid) | `references/go/testing.md` |
    | Disposable `go run` scripts | `references/go/one-liners.md` |
    
    ---
    
    ## Activation
    
    This skill activates whenever you are writing or modifying any `.py`, `.pyi`, `.rs`, `.ts`, `.tsx`, `.mts`, `.cts`, `.go` file, or any project manifest (`pyproject.toml`, `Cargo.toml`, `package.json`, `tsconfig.json`, `biome.json`, `go.mod`, `go.sum`, `.golangci.yml`, `Taskfile.yml`, `buf.yaml`, `sqlc.yaml`). **Even one-off scripts get the full treatment** - that is the whole point of `uv run` + PEP 723, `rust-script`, `bun run`, and `go run` + `//go:build ignore`: production hygiene with throwaway ergonomics.
    
    The references contain the recipes. **Read them before writing code. Re-read them when the model drifts.** The post-write review loop is non-negotiable.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related