Claude Skill

code-that-fits-in-your-head

Software-engineering heuristics based on Mark Seemann's Code That Fits in Your Head (2021), updated for agent-driven development. Use when writing or reviewing code, refactoring accidental complexity or a Big Ball of Mud, controlling technical or architectural debt in generated c

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

Full trust report

Download codealive-ai-ai-driven-development-skills_code-that-fits-in-your-head-68a302a.zip · 138 KB
Part of codealive-ai/ai-driven-development — 21 skills

Install

skills CLI npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/main/skills/code-that-fits-in-your-head
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install codealive-ai-ai-driven-development@llmmart
Git git clone https://github.com/CodeAlive-AI/ai-driven-development.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole codealive-ai/ai-driven-development collection as a plugin from our marketplace. Git is the plain clone.

README

code-that-fits-in-your-head

Cross-agent skill for keeping software understandable and inexpensive to change. It helps prevent accidental complexity, architectural erosion into a Big Ball of Mud, and silent technical-debt accumulation—especially when coding agents can generate code faster than people can review and own it.

The skill is based on Mark Seemann's Code That Fits in Your Head: Heuristics for Software Engineering (2021), with clearly labelled editorial amendments for agent-driven development.

Use it when a task is about:

  • designing complex code, APIs, domain types, validation boundaries, and invariants
  • decomposing tangled functions, classes, workflows, or subsystems
  • reviewing code for readability, cohesion, coupling, encapsulation, and testability
  • adding features outside-in with tests and a walking skeleton
  • debugging defects with reproducible tests, bisection, and tighter verification loops
  • evolving legacy systems with feature flags, Strangler-style migration, reversible stages, and explicit verification
  • threat-modelling endpoints or services with STRIDE
  • reviewing agent-generated changes for architectural fit, verification integrity, dependency provenance, and new debt

Large tasks are not rejected because they are large. Agents can successfully implement changes spanning tens of thousands of lines when the work has coherent architecture, a clear plan, trustworthy acceptance criteria, and verifiable checkpoints. The skill targets unstructured complexity and unverifiable change—not scope by itself.

How It Works

SKILL.md contains the trigger description and top-level index. guidelines.md is the routing layer: it maps tasks, symptoms, and named practices to the smallest useful reference files.

Code examples are C#-first; every rule and the tooling tables are language-neutral.

The skill uses progressive disclosure. Load one primary file and at most one or two secondary files for the current task instead of reading the whole skill. When a rule proves repeatedly useful in a repository, operationalize it (workflows/operationalize-finding.md) so it persists as a gate and in project agent instructions.

Structure

SKILL.md                 # Trigger, philosophy, chapter index
guidelines.md            # Task/symptom/practice routing
workflows/               # Step-by-step workflows for common engineering tasks
references/              # Focused reference packs by theme
references/tooling/      # Executable measurement commands (complexity, cycles, dead code, …)

Core themes include decomposition, encapsulation, API design, outside-in TDD, separation of concerns, teamwork and Git discipline, software evolution, troubleshooting, security, and measurement tooling. Workflows cover review, outside-in features, debugging, threat modelling, and operationalizing findings into gates.

Source Boundaries

Most reference folders summarize or operationalize ideas from Seemann's book. The references/agent-native/ folder is different: it contains local editorial additions for coding agents—verification integrity, hallucination and dependency grounding, executable guardrails, and accountable review. Editorial amendments placed in book-derived themes are explicitly marked. Do not attribute them to Seemann.

Governing Principles

  • Prefer simple, cohesive designs over clever or speculative abstractions.
  • Keep dependencies, side effects, and ownership boundaries explicit.
  • Judge complexity across the system, not only inside individual methods.
  • Require verification proportional to risk and independent of the implementation where it matters.
  • Never weaken tests, types, CI, or security controls merely to make generated code pass.
  • Treat generated code volume, merge rate, and approval speed as poor proxies for maintainability.

Skill manifest

Code That Fits in Your Head

Engineering heuristics for sustainable software, based on Mark Seemann's 2021 book and clearly labelled agent-era amendments.

Code examples are C#-first; every rule and the tooling tables are language-neutral.

Philosophy (Why This Skill Exists)

Software development is principally a design activity, not construction. An agent may produce most of the text, but people still review, operate, extend, and own the resulting system. These heuristics make software sustainable: understandable, resistant to architectural erosion, and cheap to change after thousands of decisions.

Core mental model from Chapter 1:

Metaphor What it gets right What it misses
Building a house Plans, structure Software endures; there's no construction phase (compiling is free); dependencies can start anywhere
Growing a garden Pruning, refactoring, tending Code does not improve by itself; generated code still needs stewardship
Art / craft Skill, mastery, situational knowledge Doesn't scale; leaves newcomers without guidance
Engineering (the target) Heuristics, review, sign-off, checklists We're not there yet — physical-construction calculations don't apply

"The act of describing a program in unambiguous detail and the act of programming are one and the same." — Kevlin Henney

Practical implications for a code agent:

  1. Successful software endures. Prefer changes that preserve clear boundaries and keep future change affordable.
  2. Complexity is the enemy, not task size. Agents can complete changes spanning tens of thousands of lines when architecture, plan, and acceptance criteria are sound. Reject needless coupling, duplication, hidden effects, and unverifiable bulk—not large scope by itself.
  3. Heuristics, not laws. Understand the purpose of a rule before applying or relaxing it. Project policy overrides generic formatting and workflow conventions.
  4. Verification is part of design. Types, tests, schemas, architecture checks, observability, and explicit acceptance criteria constrain both human- and agent-written code.
  5. Code is a liability. Generated volume is not progress. Prefer the smallest coherent design that solves the problem without accumulating debt.

See references/foundations/ for more on sustainability, readability, and brain-limited design.

How to Use This Skill

  1. Identify the user's task (writing, reviewing, debugging, security review, setting up, etc.)
  2. Read guidelines.md — it maps tasks and symptoms to specific reference files
  3. Load only the reference files relevant to the current task (progressive disclosure)
  4. Apply the rules; when in doubt, consult references/practices-glossary/ for cross-references
  5. When a rule proves repeatedly useful in a repository, operationalize it (workflows/operationalize-finding.md) so it persists as a gate and in project agent instructions.

Chapter Index

Topic Use when...
references/foundations/ Sustainability, readability, complexity control, and code as liability
references/codebase-setup/ Starting or inheriting a code base — git, build automation, warnings-as-errors
references/outside-in-tdd/ Writing new features test-first; walking skeleton, AAA, triangulation, devil's advocate, editing tests
references/encapsulation/ Designing types with invariants; DTO vs Domain Model, always-valid, Postel's law, parse-don't-validate
references/decomposition/ Controlling method and system complexity; cyclomatic complexity, cohesion, coupling, feature envy, fractal architecture
references/api-design/ Designing a public API; affordance, poka-yoke, CQS, hierarchy of communication, naming over comments
references/separation-of-concerns/ Adding cross-cutting concerns; Decorator pattern, logging, what to log, performance vs legibility
references/teamwork-git/ Writing commits, reviewing changes, continuous integration, collective ownership
references/evolution/ Changing running systems; feature flags, Strangler pattern, versioning, regular dependency updates, Conway's law
references/troubleshooting/ Debugging a defect; scientific method, rubber ducking, reproduce-as-test, bisection, non-deterministic defects
references/security/ Threat modelling; STRIDE (spoofing, tampering, repudiation, info disclosure, DoS, elevation)
references/code-navigation/ Onboarding to a code base; big picture, file organisation, cycles, property-based testing, behavioural code analysis
references/tooling/ Executable measurement commands: complexity, cycles, duplication, dead code, hotspots, mutation testing
references/practices-glossary/ Looking up a named book practice and its current status

⚠️ Editorial amendments (NOT from the book)

The folder below is NOT content from Seemann's book. It contains our own additions covering agent-specific concerns the 2021 book does not address. Do not attribute these files to Seemann. See references/agent-native/knowledge.md.

Topic Use when...
references/agent-native/ Agent-specific verification integrity, hallucination and dependency grounding, executable guardrails, and accountable review

Workflows

Composite step-by-step processes live in workflows/:

Task Workflow
Review a pull request / piece of code workflows/review-code.md
Add a new feature from scratch workflows/add-feature-outside-in.md
Investigate and fix a defect workflows/debug-defect.md
Threat-model a new endpoint workflows/threat-model.md
Turn a recurring finding into an executable gate and persist it in project memory workflows/operationalize-finding.md

See guidelines.md for the full routing layer (task → file, symptom → file).

Files (ai-driven-development)
  • references
    • agent-native
      • hallucination-debugging.md 5.7 KB
        # Hallucination Debugging (Agent-Native)
        
        > ⚠️ **Not from the book.** This editorial amendment covers an agent-specific concern Seemann does not address. See `knowledge.md`.
        
        A defect class Seemann's Ch 12 doesn't cover: confident-wrong output from an LLM agent.
        
        ## What Makes It Different
        
        Seemann distinguishes non-deterministic defects (clock, random, threading, external state). Those are *stochastic* — same input may produce different output. Agent hallucinations are *statistical* — the agent produces output that is plausible given a training prior but wrong for this codebase or version.
        
        | Class | Example | Root cause |
        |-------|---------|-----------|
        | Non-deterministic (book) | `DateTime.Now` in a test | Dependency on an external oracle |
        | Hallucination (agent) | Calling `array.contains()` in Python | Wrong prior — language/library mixup |
        
        They need different debugging techniques.
        
        ## Common Hallucination Classes
        
        | Class | Symptom | Example |
        |-------|---------|---------|
        | **Invented API method** | `AttributeError` / "property does not exist" | `requests.fetch()` (it's `requests.get`) |
        | **Outdated syntax** | Compile error on "correct-looking" code | Python 2 `print x` in Python 3 |
        | **Misremembered import** | `ModuleNotFoundError` | `import numpy.array` (real: `from numpy import array`) |
        | **Wrong argument order** | Runtime value error or wrong result | `str.replace(new, old)` instead of `(old, new)` |
        | **Version-mismatched feature** | "Unknown option" / "unsupported" | React `use()` hook on React 17 |
        | **Close-but-not** library name | Install fails or wrong package | `pip install lxml-html` (real: `lxml`) |
        | **Package hallucination** | A plausible package name is absent or attacker-controlled | Installing an invented dependency without provenance checks |
        | **Phantom config key** | Silently ignored, no effect | `tsconfig` option that looks real but isn't |
        | **Type lie** | Tests pass; prod breaks | Returning `User` but typed as `User | null` — caller doesn't null-check |
        
        ## Detection — Where Each Class Surfaces
        
        | Tier | Catches |
        |------|---------|
        | Syntax parser | Outdated syntax |
        | Typechecker | Invented methods, wrong argument types, phantom config keys (with strict schemas) |
        | Import resolution | Misremembered imports, wrong package names |
        | Runtime | Wrong argument order (if types allow), version mismatches |
        | Integration test | Close-but-not API shapes |
        | Production | Type lies (where runtime doesn't validate) |
        
        **Rule**: the higher the tier where the hallucination surfaces, the more expensive the fix. Catch them as early as possible.
        
        ## Debugging Workflow
        
        When a suspect failure occurs:
        
        1. **Isolate the suspicious symbol.** Which exact method/import/option triggered the failure?
        2. **Grep the codebase.** Does this symbol exist here, or did the agent invent it? `rg 'SymbolName'` in the repo and dependencies.
        3. **Check the actual installed version.** `cat package.json`, `pip show X`, `cargo tree`. Not what the agent *thinks* the version is.
        4. **Verify against docs for THAT version.** Generic Stack Overflow answers often don't apply. Pin the version, then check the version's docs.
        5. **Replace with a verified-real equivalent.** Read actual library source / types if needed.
        6. **Add a regression guard.** Typically a typecheck tightening, a schema validation, or a test that fails against the hallucinated symbol.
        
        ## Prevention Rules
        
        1. **Grep-before-use.** Before using a symbol from the codebase, confirm it exists (`rg`, LSP, `cat`). Do not write from memory.
        2. **Read-before-edit.** Before editing a function, read its current source. Don't edit from recall.
        3. **Typecheck-before-commit.** Run strict typechecker on the touched files. Most hallucinations surface here if types are strict.
        4. **Docs-for-this-version, not docs-in-general.** Pin library version, then look at its docs / types.
        5. **When uncertain, run it.** Don't reason about what the code will do — execute it.
        6. **Schema-validate all external responses.** Hallucinated JSON shapes pass through without schema checks.
        7. **Verify before adding a dependency.** Confirm that the package is intended, maintained, available from the trusted registry, compatible with the lockfile, and justified by the design. Never install a plausible name merely because an import failed.
        
        ## Antipatterns
        
        | Pattern | Why it's bad |
        |---------|--------------|
        | "I remember this function exists" | Memory is the hallucination source; verify |
        | "The failure is weird — probably flaky, let's retry" | Likely a hallucination, not flake |
        | Suppressing a typecheck error with `any` | Hides the hallucination; moves the failure to runtime |
        | Adding `try/except` around unknown failure | Swallows the symptom; agent keeps hallucinating |
        | Installing a typo'd package because install succeeded | Typo-squatting supply chain risk |
        
        ## Agent-Specific Tooling
        
        - **LSP / IDE integration**: auto-complete from real symbols only
        - **Strict typecheckers** as first-line defense (`tsc strict`, `pyright strict`, `mypy --strict`)
        - **Lockfiles**: `package-lock.json`, `poetry.lock`, `Cargo.lock` — without them, the "version the agent recalls" drifts from "version installed"
        - **Runtime schema validation** at boundaries (Zod, Pydantic, JSON Schema)
        - **Typo-safe package managers**: prefer managers that resolve to well-known registries; verify downloads
        - **Dependency provenance**: inspect publisher/owner, repository, release history, checksums/signatures where available, and the exact locked version
        
        ## Relation to the Book
        
        Ch 12 on reproducing defects as tests still applies — once you isolate a hallucination, capture it as a failing test (or a failing type) so it can never slip back. Ch 5 on parse-don't-validate is the structural defense: hallucinated data shapes die at the parse boundary.
        
      • knowledge.md 1.7 KB
        # ⚠️ Agent-Native Amendments — NOT from the Book
        
        > **This folder is not Seemann's.** These files address agent-specific concerns absent from the 2021 book. Treat them as editorial guidance and do not attribute them to Mark Seemann.
        
        ## Canonical Topics
        
        | File | Canonical concern |
        |---|---|
        | `verification-loops.md` | Verification integrity, checkpoints, independent oracles, and prohibition on weakening gates |
        | `hallucination-debugging.md` | Invented APIs, version drift, package hallucination, and dependency provenance |
        | `types-as-guardrails.md` | Types, schemas, tests, and executable contracts as complementary constraints |
        | `reviewability.md` | Evidence-based review, risk classification, and accountable human ownership |
        
        Large tasks are explicitly allowed. Agent work spanning tens of thousands of lines can be sound when it follows a coherent architecture and has clear acceptance criteria, reliable verification, and recoverable checkpoints. These files constrain unverifiable or architecturally incoherent work—not size by itself.
        
        ## Status
        
        - **Editorial, not book content.** Connections to book chapters are stated explicitly.
        - **One canonical home per rule.** Book-derived themes link here instead of duplicating cross-cutting agent guidance.
        - **Evolving defaults.** Prefer observable outcomes and repository policy over model- or language-specific rankings.
        
        ## How to Use This Folder
        
        - Start with the relevant book-derived theme for general design guidance.
        - Load one agent-native file when agent authorship creates an additional failure mode.
        - For agent-runtime threats, use the explicitly marked editorial section in `security/rules.md`; security owns that specialized policy.
        - Do not load the whole folder by default.
        
      • reviewability.md 3.7 KB
        # Accountable Review (Agent-Native)
        
        > ⚠️ **Not from the book.** This editorial amendment covers review when an agent authors much of the change. See `knowledge.md`.
        
        Reviewability is the ability to judge intent, architecture, behavior, risk, and evidence without trusting the agent's narrative. It is not measured by approval time, line count, or number of files.
        
        ## Large Changes
        
        Do not reject a change merely because it spans thousands of lines. Large systematic migrations can be easier to validate than small tangled patches when they provide:
        
        - a coherent target architecture and dependency direction;
        - a precise transformation or implementation plan;
        - explicit acceptance and non-regression criteria;
        - trustworthy automated verification;
        - clear exceptions, uncertainties, and recovery strategy;
        - a diff structure that separates generated/mechanical changes from semantic decisions.
        
        Split or stage work when doing so improves architecture, recovery, or verification—not to satisfy an arbitrary size target.
        
        ## Review Contract
        
        An agent-authored change should state:
        
        - **Goal**: intended product or engineering outcome.
        - **Architecture**: boundaries, ownership, and dependency changes.
        - **Approach**: why this design was chosen and what alternatives were rejected.
        - **Verification**: exact commands and evidence, including what each check establishes.
        - **Independent oracle**: source of truth not created from the same assumption as the implementation.
        - **Risk and recovery**: data, security, compatibility, deployment, rollback, and residual uncertainty.
        - **Debt delta**: duplication, coupling, temporary paths, TODOs, dependencies, or cleanup introduced or removed.
        
        The agent must not invent business rationale or hide uncertainty behind confident prose.
        
        ## Risk Lanes
        
        Review effort should reflect risk, but the authoring agent must never assign its own lane.
        
        A lane is selected by repository policy, a machine-checkable predicate, or a human. If none applies, default to human review.
        
        | Lane | Typical examples | Required ownership |
        |---|---|---|
        | Mechanical | deterministic generated output, formatting-only change | automated verification under repository policy |
        | Routine | localized behavior with stable contracts and strong tests | normal human review or explicit repository policy |
        | Material | architecture, security boundary, data migration, public contract, irreversible effect | accountable human approval and independent evidence |
        
        Automated agent review is useful screening. It does not create a second human maintainer, transfer product ownership, or accept residual risk.
        
        ## What to Review First
        
        1. Does the change preserve or improve architectural boundaries?
        2. Does it introduce unnecessary abstraction, duplication, coupling, or dependencies?
        3. Are invariants and side effects explicit?
        4. Are tests and gates trustworthy, or were they weakened to match the implementation?
        5. Are migration and temporary paths removed or assigned an exit condition?
        6. Does the evidence support the claimed behavior and risk?
        
        ## Red Flags
        
        - Mixed unrelated concerns with no architectural reason.
        - New dependency without provenance or design justification.
        - Tests changed only to make the implementation pass.
        - Broad suppressions, disabled checks, or reduced assertions.
        - Parallel implementations, flags, adapters, or TODOs without cleanup ownership.
        - Large prose explanation compensating for unclear names, types, or boundaries.
        - A low-risk label supplied only by the authoring agent.
        - Claims of correctness based solely on merge rate, generated volume, or self-review.
        
        ## Relation to the Book
        
        This extends the book's Git and code-review discipline. The durable principle is accountable, independent judgment—not a fixed PR size, review duration, or approval SLA.
        
      • types-as-guardrails.md 3.2 KB
        # Executable Guardrails (Agent-Native)
        
        > ⚠️ **Not from the book.** This editorial amendment reframes types, schemas, tests, and specifications for agent-authored code. See `knowledge.md`.
        
        Agents benefit from constraints that reject invalid output mechanically. No language ranking is universal, and types alone do not prove behavior or intent.
        
        ## Complementary Guardrails
        
        | Guardrail | Best at catching | Does not prove |
        |---|---|---|
        | Types and static checks | Invalid symbols, signatures, nullability, forbidden dependencies | Business correctness or runtime data shape |
        | Runtime schemas/parsers | Invalid external data and configuration | Correct downstream decisions |
        | Tests and properties | Behavior, invariants, regressions | Completeness when derived from the same mistaken assumption |
        | Explicit contracts | Compatibility and caller/callee expectations | That implementation satisfies the contract without verification |
        | Architecture checks | Cycles, layer violations, restricted dependencies | Good domain boundaries or product fit |
        
        ## Rules
        
        1. **Use the strongest practical constraints for the repository.** New code should enable strict modes where they provide signal. Brownfield systems should ratchet toward stricter checks without flooding the change with unrelated noise.
        2. **Type public and cross-module contracts.** Make optionality, failures, and variants explicit.
        3. **Parse external data into stronger internal types.** Validate HTTP, files, environment, queues, and API responses at trust boundaries.
        4. **Prefer domain types over primitive conventions.** Make invalid states difficult to express.
        5. **Keep examples executable when they define behavior.** Use tests, doctests, schemas, or runnable samples instead of comments that silently drift.
        6. **Fail closed at trust and security boundaries.** Accept compatibility only when it is deliberate and tested.
        7. **Do not suppress a guardrail merely to unblock generation.** Narrow exceptions require a reason and an owner.
        8. **Pair self-authored tests with independent evidence for material changes.** See `verification-loops.md`.
        
        ## Choosing a Stack
        
        Evaluate properties rather than ranking languages:
        
        - quality and completeness of types or stubs;
        - runtime schema support;
        - deterministic build and test tooling;
        - dependency metadata, lockfiles, and provenance;
        - static analysis and architecture-test support;
        - ecosystem maturity and team competence.
        
        A well-governed dynamic-language project can be safer than a poorly structured statically typed one.
        
        ## Antipatterns
        
        | Pattern | Why it is dangerous |
        |---|---|
        | Broad `any`, ignore, or suppression to get green | Removes the constraint exactly where uncertainty exists |
        | Types without runtime boundary validation | External data can still violate compile-time assumptions |
        | Tests that duplicate implementation logic | Both can agree on the same error |
        | Enabling every analyser in brownfield at once | Creates noise and encourages blanket suppression |
        | Selecting a language solely for agent convenience | Ignores domain, ecosystem, operations, and team ownership |
        
        ## Relation to the Book
        
        This strengthens always-valid objects and parse-don't-validate while preserving the book's gradual-ratchet advice for existing systems.
        
      • verification-loops.md 3.9 KB
        # Verification Integrity (Agent-Native)
        
        > ⚠️ **Not from the book.** This editorial amendment covers agent-specific verification risks. See `knowledge.md`.
        
        Agents can implement large changes successfully. The controlling factors are architecture, acceptance criteria, and trustworthy evidence—not a universal limit on lines, files, or task duration.
        
        ## Verification Strategy
        
        Define before implementation:
        
        1. the target architecture and invariants that must remain true;
        2. explicit acceptance and non-regression criteria;
        3. the canonical repository verification command;
        4. focused checks for each meaningful checkpoint;
        5. an independent source of truth for material behavior.
        
        Checkpoints should align with coherent architectural or behavioral stages. Do not force per-line commits or artificial micro-steps when a larger transformation is systematic and mechanically verifiable.
        
        ## Feedback Layers
        
        Use the cheapest relevant layer during implementation and the complete required set before acceptance.
        
        | Layer | Examples | Protects against |
        |---|---|---|
        | Parse/build | compiler, formatter | Invalid syntax and build graph |
        | Types/static checks | typechecker, lint, architecture rules | Contract drift, invented symbols, forbidden dependencies |
        | Focused tests | unit, property, contract | Local logic and invariants |
        | Integration/system tests | real boundaries, E2E | Cross-component behavior |
        | Operational evidence | migration rehearsal, telemetry, rollback test | Deployment and lifecycle risk |
        | Independent acceptance | existing contract, product criteria, hidden cases, human decision | Shared blind spots in agent-written code and tests |
        
        Latency is repository-specific. A slower high-value verifier is not "catastrophic"; run it at an appropriate checkpoint or gate rather than deleting it for throughput.
        
        ## Rules
        
        1. **Verify coherent checkpoints, not only the final result.** Detect divergence before it contaminates later work.
        2. **Use the repository's canonical commands.** Keep local and CI behavior aligned.
        3. **Do not weaken verification to obtain green output.** Never delete assertions, skip tests, broaden suppressions, or relax types without an explicit requirement change and reviewable rationale.
        4. **Require an independent oracle for material risk.** Tests written from the same mistaken interpretation as the implementation are not independent evidence.
        5. **Record evidence.** State what ran, what it establishes, what was not run, and remaining uncertainty.
        6. **Preserve recovery points.** Large migrations need recoverable commits, worktrees, backups, reversible stages, or another appropriate rollback mechanism.
        7. **Stop on contradictory evidence.** Revise the plan or architecture instead of patching around repeated failures.
        
        ## Independent Oracles
        
        Choose according to the risk:
        
        - existing tests or recorded production behavior;
        - product acceptance criteria or examples supplied independently;
        - schema, protocol, or compatibility contracts;
        - property and metamorphic tests;
        - a reference implementation or differential comparison;
        - security/static analysis maintained separately from the change;
        - human acceptance for architecture, intent, and residual risk.
        
        No single oracle proves correctness. Independence means it did not originate from the same unverified assumption as the implementation.
        
        ## Red Flags
        
        - The agent edits tests until they match the implementation without explaining a requirement change.
        - CI differs materially from the local verification path.
        - A large change has no architectural map or acceptance criteria.
        - Every check is authored by the same agent from the same prompt.
        - A failing gate is suppressed rather than understood.
        - The report says "all tests pass" without naming what was run or what remains unverified.
        
        ## Relation to the Book
        
        This extends outside-in TDD, troubleshooting, and automated gates. The book's red-green discipline remains useful; agent authorship adds the need to protect the oracle itself.
        
    • api-design
      • examples.md 6.3 KB
        # API Design Examples
        
        Code examples demonstrating API design principles: affordance, poka-yoke, CQS, naming, and the X-Out exercise.
        
        ## Bad Examples
        
        ### Comment Explaining What the Code Does
        
        ```csharp
        // Reject reservation if it's outside of opening hours
        if (candidate.At.TimeOfDay < OpensAt ||
            LastSeating < candidate.At.TimeOfDay)
            return false;
        ```
        
        **Problems**:
        - The comment and the code can drift out of sync as the code evolves
        - The reader must still parse the conditional to verify the comment
        - The intent is not extractable — callers cannot reuse the check
        
        ### Stringly Typed / X-Out Failure
        
        ```csharp
        public interface IThings
        {
            string DoSomething(string a, string b);
            string GetSomething(string c);
            string MakeSomething(string d, string e);
        }
        ```
        
        **Problems**:
        - If you X-out names, every signature looks identical
        - `string` carries no domain meaning — any value can be passed
        - Nothing prevents callers from swapping arguments by mistake
        
        ### CQS Violation
        
        ```csharp
        // Both mutates state AND returns data
        public int AddReservationAndGetCount(Reservation r)
        {
            _store.Add(r);
            return _store.Count;
        }
        ```
        
        **Problems**:
        - Cannot tell from the signature whether it is "safe" to call
        - Mixes a persistence effect with a count query
        - Forces callers who want only the count to also persist
        
        ### Swiss Army Knife Constructor
        
        ```csharp
        public ReservationsController(
            IReservationsRepository repository,
            TimeOfDay opensAt,
            TimeOfDay lastSeating,
            TimeSpan seatingDuration,
            IEnumerable<Table> tables)
        {
            Repository = repository;
            MaitreD = new MaitreD(opensAt, lastSeating, seatingDuration, tables);
        }
        ```
        
        **Problems**:
        - Controller now knows MaitreD's constructor shape; any change ripples
        - Configuration concerns leak into a class that should handle HTTP
        - No single type represents "restaurant policy"
        
        ## Good Examples
        
        ### MaitreD as Affordance
        
        ```csharp
        public MaitreD(
            TimeOfDay opensAt,
            TimeOfDay lastSeating,
            TimeSpan seatingDuration,
            IEnumerable<Table> tables)
        
        public bool WillAccept(
            DateTime now,
            IEnumerable<Reservation> existingReservations,
            Reservation candidate)
        ```
        
        **Why it works**:
        - Custom `TimeOfDay`, `Reservation`, and `Table` types carry domain meaning (ubiquitous language — "maitre d'" is what a domain expert would say)
        - Constructor demands everything needed up front; none can be `null`
        - `WillAccept` returns `bool`, so by CQS it has no side effects — safe to call
        - X-Out Names: even with names replaced, the types clearly describe a policy constructor and a decision query
        
        ### Well-Named Method Replacing a Comment
        
        ```csharp
        // Before (comment doing the communication)
        // Reject reservation if it's outside of opening hours
        if (candidate.At.TimeOfDay < OpensAt ||
            LastSeating < candidate.At.TimeOfDay)
            return false;
        
        // After (method name doing the communication)
        if (IsOutsideOfOpeningHours(candidate))
            return false;
        ```
        
        **Why it works**:
        - The name is checked by the compiler whenever the method is renamed
        - Intent is reusable — other branches can call `IsOutsideOfOpeningHours`
        - Reader does not need to parse the conditional to confirm the intent
        
        ### Query With No Observable Side Effects (CQS)
        
        ```csharp
        private IEnumerable<Table> Allocate(
            IEnumerable<Reservation> reservations)
        {
            List<Table> availableTables = Tables.ToList();
            foreach (var r in reservations)
            {
                var table = availableTables.Find(t => t.Fits(r.Quantity));
                if (table is { })
                {
                    availableTables.Remove(table);
                    if (table.IsCommunal)
                        availableTables.Add(table.Reserve(r.Quantity));
                }
            }
            return availableTables;
        }
        ```
        
        **Why it works**:
        - Mutates only a local `List<Table>` — not observable to the caller
        - Returns `IEnumerable<Table>`, signalling "this is a Query"
        - Caller can rely on the signature alone; no need to read the body
        
        ### Nullability Expressed in the Type (Poka-Yoke + Hierarchy)
        
        ```csharp
        public interface IReservationsRepository
        {
            Task Create(Reservation reservation);
            Task<IReadOnlyCollection<Reservation>> ReadReservations(DateTime dateTime);
            Task<Reservation?> ReadReservation(Guid id);
        }
        ```
        
        **Why it works**:
        - The `?` on `Reservation?` tells callers they must handle `null`
        - No need for a `GetReservationOrNull` suffix that could rot
        - Applying X-Out Names still leaves each method distinguishable — `Task` vs `Task<IReadOnlyCollection<Reservation>>` vs `Task<Reservation?>` signal Command, list-Query, and lookup-Query respectively
        
        ### Dependency as a Whole Object (Poka-Yoke at the Seam)
        
        ```csharp
        public ReservationsController(
            IReservationsRepository repository,
            MaitreD maitreD)
        {
            Repository = repository;
            MaitreD = maitreD;
        }
        
        public IReservationsRepository Repository { get; }
        public MaitreD MaitreD { get; }
        ```
        
        **Why it works**:
        - Controller depends on `MaitreD`, not on its parts
        - Changes to `MaitreD`'s constructor do not ripple into the controller
        - `MaitreD` is immutable, so it is safe to register as a Singleton
        - The composition root (Startup) is the only place that knows the raw configuration values
        
        ## Refactoring Walkthrough
        
        ### Before
        
        Business logic hard-coded in the controller:
        
        ```csharp
        int reservedSeats = reservations.Sum(r => r.Quantity);
        if (10 < reservedSeats + r.Quantity)
            // reject...
        ```
        
        ### After
        
        The decision is delegated to a domain object with a well-designed API:
        
        ```csharp
        if (!MaitreD.WillAccept(DateTime.Now, reservations, r))
            // reject...
        ```
        
        ### Changes Made
        
        1. **Extracted a domain class** (`MaitreD`) named in the ubiquitous language so that domain experts and code agree on vocabulary.
        2. **Replaced a magic number (10) with a typed collection** (`IEnumerable<Table>`) passed to the constructor — this makes per-restaurant configuration expressible and invalid capacities unrepresentable.
        3. **Named the decision method `WillAccept`** so that the call site reads like business intent. Returning `bool` makes it a Query; by CQS the caller knows no state is mutated.
        4. **Moved side-effect-free allocation to a private Query** (`Allocate`) that returns an `IEnumerable<Table>`, keeping the public API small and the X-Out exercise viable.
        5. **Injected `MaitreD` via the constructor** rather than reconstructing it from exploded config on every request — reducing coupling between the controller and MaitreD's shape.
        
      • knowledge.md 2.6 KB
        # API Design Knowledge
        
        Core concepts for designing public APIs that fit in readers' heads.
        
        ## Overview
        
        An API is an affordance: the set of methods, values, functions, and objects a client has at its disposal. Good design advertises what is possible, makes illegal states hard to express, and communicates intent primarily through types and names rather than comments or documentation.
        
        ## Key Concepts
        
        ### Affordance
        
        The API advertises capabilities through its types. What you cannot express is as important as what you can; well-encapsulated APIs expose only operations that preserve invariants. Discoverability comes from types, not documentation.
        
        ### Poka-Yoke (Mistake-Proofing)
        
        Design so misuse is difficult or impossible. Active poka-yoke inspects as artifacts are created (e.g. TDD); passive poka-yoke builds the constraint into the shape of the thing (compile-time prevention). Prefer specialised APIs over Swiss Army knives (God Classes). A compiler error is faster feedback than a runtime exception.
        
        ### Command Query Separation (CQS)
        
        Every method is either a Command (side effects, returns `void`) or a Query (returns data, no observable side effects) — never both. Local unobservable state changes do not count as side effects. Prefer Queries where possible. **CQS ≠ CQRS**: CQRS is an architectural style that borrows the terminology at a different scale.
        
        ### X-Out Names Exercise
        
        Mentally replace every name with `Xxx` and ask whether the types alone still communicate what each method does. If every method returns `string` or `int`, types disambiguate nothing. Favour specialised types over "stringly typed" APIs.
        
        ### Hierarchy of Communication
        
        Most durable to least:
        
        1. Distinct **types**
        2. Helpful **names**
        3. Good **comments** (for *why*, not *what*)
        4. Automated **tests** as illustrative examples
        5. Helpful **commit messages**
        6. External **documentation**
        
        Only types are compiler-checked. Code is the only artifact guaranteed to be current.
        
        ## Common Misconceptions
        
        - **Myth**: A good API exposes every capability users might want.
          **Reality**: A Swiss Army knife becomes a God Class. Specialised APIs with few, well-typed methods are easier to reason about.
        
        - **Myth**: Comments explain what names cannot.
          **Reality**: Most comments can be replaced by a well-named method. Comments are for *why*, not *what*.
        
        - **Myth**: Returning a value from a method that also mutates state is a convenience.
          **Reality**: It violates CQS and makes the method harder to reason about from the signature alone.
        
        - **Myth**: CQS and CQRS are the same thing.
          **Reality**: CQRS is an architectural style that borrows terminology from CQS but applies it at a different scale.
        
      • rules.md 5 KB
        # API Design Rules
        
        Actionable rules for designing public APIs that advertise their contracts through types and names.
        
        ## Core Rules
        
        ### 1. Every Method Is a Command OR a Query — Never Both
        
        Commands have side effects and return `void` (or `Task`). Queries return data and have no observable side effects. A method that does both violates CQS and is harder to reason about.
        
        - A `void` return advertises "this method exists for its side effect"
        - A non-`void` return promises "calling this does not change observable state"
        - Local state mutation inside the method body is fine if it is not observable to callers
        
        **Example**:
        ```csharp
        // Bad — mutates AND returns (neither Command nor Query)
        public int AddItemAndReturnTotal(Item item) { ... }
        
        // Good — split into a Command and a Query
        public void AddItem(Item item) { ... }
        public int Total { get; }
        ```
        
        ### 2. Prefer Queries Over Commands
        
        Queries are easier to reason about because both their input and output types hint at their intent. Commands communicate only through their input types and name.
        
        ### 3. Make Illegal States Unrepresentable (Poka-Yoke)
        
        Design APIs so that invalid inputs or states cannot be expressed in code. Use custom types, non-nullable references, and required constructor parameters instead of runtime validation.
        
        - Use specialized types (`TimeOfDay`, `Reservation`) instead of primitives
        - Make all constructor parameters required; reject `null`
        - Prefer compile-time errors to runtime exceptions
        
        **Example**:
        ```csharp
        // Bad — caller can pass any int, including invalid hours
        public Reservation(int hour, int minute, string email) { ... }
        
        // Good — TimeOfDay and Email types constrain valid values
        public Reservation(TimeOfDay at, Email email) { ... }
        ```
        
        ### 4. Advertise Contracts With Types, Not Just Names
        
        If the return type is `Task<Reservation?>`, the `?` tells the reader nullability is possible without needing a `GetReservationOrNull` name. Use the type system to carry semantic information so renaming the method later does not lie.
        
        ### 5. Favor Well-Named Methods Over Comments
        
        If a comment explains *what* code does, extract the code into a method whose name says it. Reserve comments for *why* — reasons a future reader could not infer from the code.
        
        **Example**:
        ```csharp
        // Bad
        // Reject reservation if it's outside of opening hours
        if (candidate.At.TimeOfDay < OpensAt ||
            LastSeating < candidate.At.TimeOfDay)
            return false;
        
        // Good
        if (IsOutsideOfOpeningHours(candidate))
            return false;
        ```
        
        ### 6. Apply the X-Out Names Exercise
        
        Mentally replace every method name with `Xxx`. If the types alone still make it obvious what each method does, your types are doing their job. If not, either introduce distinct types or accept that you lean heavily on names.
        
        - Works best when the class exposes only a few methods, each with distinct types
        - Fails for "stringly typed" APIs where every signature looks the same
        - A reason to keep classes focused rather than growing into God Classes
        
        ### 7. Use Complementary Communication Channels
        
        Put enforceable contracts in types, schemas, and tests; put readable intent in names; put non-obvious rationale in comments and commit history; put system-level mission and usage in maintained documentation. None is sufficient alone, and any of them can drift when it is not checked or reviewed.
        
        ### 8. Design for the Reader, Not the Writer
        
        Code is read more than it is written. The reader may be you in six months with none of the context. Optimize every name, type, and signature for understanding at reading time, not typing speed.
        
        ### 9. Avoid Swiss Army Knives
        
        An API with dozens of methods in a single class (a God Class) makes reasoning impossible. Split capabilities into focused, specialized types so that each afforded operation has a distinct signature.
        
        ## Guidelines
        
        - Prefer non-nullable reference types; use `?` only when `null` is genuinely a valid value
        - Evaluate a constructor from its signature alone: required inputs and invalid combinations should be clear
        - When in doubt, write a test that tries to misuse the API — if it is too easy, tighten the types
        - If a class owns configuration, pass the whole class as a dependency rather than exploding its fields
        
        ## Exceptions
        
        - **Legacy databases or transports**: You may have to return generated IDs from an insert. This is solvable within CQS but sometimes framework constraints force pragmatic violations.
        - **Cross-boundary DTOs**: Configuration objects populated from JSON or environment files often have anemic encapsulation by necessity.
        - **Performance-critical paths**: Occasionally a combined Command+Query is justified; document the why and isolate it.
        
        ## Quick Reference
        
        | Rule | Summary |
        |------|---------|
        | CQS | Command (void) or Query (returns data), never both |
        | Poka-yoke | Make invalid states fail to compile |
        | Complementary contracts | Types, tests, schemas, names, rationale, and docs serve different roles |
        | X-Out | Blank every name — if still readable, names underused |
        | No Swiss Army | Favor specialized APIs over God Classes |
        | Write for readers | Optimize for reading, not writing |
        
    • code-navigation
      • knowledge.md 4.4 KB
        # Code Navigation Knowledge
        
        Core concepts for onboarding to an unfamiliar code base when the reader is an agent: progressive maps, tests as documentation, cycles, and history-based hotspot detection.
        
        ## Overview
        
        When you land on a new code base, build a mental model without drowning in detail. Start at the entry point / composition root, zoom out to see the shape of the system, then zoom in only where the task demands it. The file tree is a poor map; the tests and the composition root are good ones. Navigate with search, imports, tests, and git history — not by reading whole directories.
        
        ## Key Concepts
        
        ### Start at the Entry Point
        
        The framework entry point (`Main`, `Startup`, `program.ts`, composition root) is the table of contents. Top-level service registrations and route wiring list major subsystems by name. At each level the code should fit in your head: low complexity, few activated objects, a handful of lines. Learn what exists first; open implementations only when the task needs them.
        
        ### Agent Navigation Strategy
        
        An agent navigates without IDE tabs:
        
        - **Symbol lookup**: `rg 'SymbolName'` or LSP go-to-definition when available.
        - **Find usages**: `rg -w 'SymbolName'` across the repo.
        - **Follow imports**: read the import graph from the file the task touches outward.
        - **Read the test named after the behaviour before the implementation** — tests encode intended usage.
        - **History for "why"**: `git log --follow -- <file>` and blame when the design choice is opaque.
        
        Prefer progressive reading over speculative directory walks. See `rules.md` (Context budget).
        
        ### File Organisation
        
        Follow the repository's existing convention. Flat vs deep is a project choice, not a design rule. File systems force one parent per file, so any hierarchy excludes other valid groupings — that is a trade-off the project has already made. Do not reorganise layout to match a preferred IDE navigation style.
        
        ### Monolith (as default)
        
        A single deployable package containing domain, data access, HTTP, auth, and logging is the simplest shape. Internally structured (functional core / imperative shell, ports and adapters) but shipped as one unit is fine. The anti-pattern is internal spaghetti, not the monolith itself.
        
        ### Cycles
        
        Dependency loops (A uses B uses C uses A) collapse zoom levels so nothing fits in your head. Mainstream languages often permit cycles between classes but forbid them between packages — splitting into packages/projects turns cycle prevention into a compile error. Detect cycles with the commands in `../tooling/commands.md`.
        
        ### Tests as Living Documentation
        
        Read the test suite to learn intended usage. A good test has low complexity and a high abstraction level that communicates intent. Test helpers (`PostReservation`, `GetRestaurant`) are reusable entry points — sometimes generic enough to promote to a production client SDK. "Listen to your tests": if a test is hard to write or set up, the System Under Test is badly designed, not the test.
        
        ### Property-Based Testing
        
        A framework generates arbitrary inputs (skewed toward boundaries) and you assert a property that must hold for all of them. Complements example-based tests; does not replace them. Useful when you can state an invariant ("quantity must be positive") more easily than enumerate cases.
        
        ### Behavioural Code Analysis
        
        Mine Git history for patterns invisible in static code: which files change most often, which change together. Hotspot = high complexity × high change frequency → prime refactoring target. Change coupling catches copy-paste coupling that dependency analysis misses. Detection commands (churn, hotspots, coupling) live in `../tooling/commands.md`. Watch trends, not absolute numbers.
        
        ## Common Misconceptions
        
        - **Myth**: A large flat directory means the code is disorganised.
          **Reality**: Hierarchy forces one axis of grouping. Follow the project's convention; navigation tools find files either way.
        
        - **Myth**: A monolith is an anti-pattern.
          **Reality**: It's the simplest shape. The anti-pattern is internal spaghetti, which you can have in microservices too.
        
        - **Myth**: Property-based testing replaces example-based tests.
          **Reality**: They complement each other. Examples pin concrete behaviour; properties probe invariants.
        
        - **Myth**: Behavioural code analysis is for managers' dashboards.
          **Reality**: It's actionable engineering data — hotspots point at the files most worth refactoring.
        
      • rules.md 7.4 KB
        # Code Navigation Rules
        
        Actionable rules for onboarding to an unfamiliar code base using search, tests, architecture cues, and behavioural data.
        
        ## Core Rules
        
        ### 1. Start at the entry point, then zoom in
        
        When you open a new code base, do not start by reading the whole file tree. Start at the framework's entry point (`Main`, `Startup`, `program.ts`, composition root) and read the top-level configuration as a table of contents.
        
        - Service-registration and route-wiring methods list every major subsystem by name
        - Each call is a pointer you can follow with search or LSP when needed
        - Do not read implementations until you know which one the task requires
        
        **Example**:
        ```csharp
        // Big picture first — this is the "table of contents"
        public void ConfigureServices(IServiceCollection services)
        {
            // ...
            ConfigureAuthorization(services);
            ConfigureRepository(services);
            ConfigureRestaurants(services);
            ConfigureClock(services);
            ConfigurePostOffice(services);
        }
        ```
        
        ### 2. Navigate with search, imports, tests, and history
        
        Use the most direct signal for the question at hand:
        
        | Question | Prefer |
        |----------|--------|
        | Where is this symbol defined? | `rg 'SymbolName'` or LSP go-to-definition |
        | Who calls this? | `rg -w 'SymbolName'` |
        | How is this wired? | Follow imports from the composition root / entry point |
        | What should this do? | Read the test named after the behaviour **before** the implementation |
        | Why is this here? | `git log --follow -- <file>` and blame |
        
        File layout is complementary, not a design rule. Do not reorganise directories for navigation convenience.
        
        ### 3. Read tests first — they encode intent
        
        When a code base has a test suite, the tests are the shortest path to understanding usage. A good test has a high abstraction level: it shows *what* the system does without drowning you in *how*.
        
        - Look for tests named after the behaviour you want to understand
        - Follow test helpers to discover the public shape of the API
        - If you find utilities that are not test-specific, consider promoting them to production
        
        **Example**:
        ```csharp
        [Fact]
        public async Task ReserveTableAtNono()
        {
            using var api = new SelfHostedApi();
            var client = api.CreateClient();
            var dto = Some.Reservation.ToDto();
            dto.Quantity = 6;
            var response = await client.PostReservation("Nono", dto);
            // The test itself documents the happy path
        }
        ```
        
        ### 4. Treat hard-to-write tests as a design smell
        
        If writing a test requires elaborate setup, deep mocking, or global state manipulation, the problem is in the production code, not the test. "Listen to your tests."
        
        - Test pain = design pain
        - Refactor the System Under Test, not the test, when tests get ugly
        - Remember: test code is code; maintain it carefully (tests have no safety net)
        
        ### 5. Check for dependency cycles early
        
        Cycles between classes or packages block understanding because they collapse abstraction levels. If A depends on B which depends on A, no zoom level is self-contained.
        
        - A typical cycle: a Domain Model repository interface that uses ORM row types in its signatures
        - Splitting the code into packages/projects makes cycles a compile error — free poka-yoke
        - Detection commands: `../tooling/commands.md` (cycle and layer tools)
        
        **Example** — cycle to avoid:
        ```csharp
        // Domain Model package
        public interface IRepository
        {
            void Create(Row row); // Row is defined in the data-access package
        }
        
        // Data access package
        public class OrmRepository : IRepository { /* must reference Domain Model */ }
        // => Domain Model depends on Data Access, and vice versa. Cycle.
        ```
        
        ### 6. A monolith is not automatically wrong
        
        Single-package deployment is the simplest shape. The anti-pattern is *internal* spaghetti, not the monolith itself. Judge a monolith by whether its insides follow ports-and-adapters or functional-core-imperative-shell, not by package count.
        
        - Don't recommend a split until you see concrete coupling problems
        - Splitting into packages is a tool for enforcing acyclic dependencies, not a goal
        
        ### 7. Use property-based testing for invariants
        
        When you can describe a property more easily than enumerate cases, reach for a property-based testing library (FsCheck, QuickCheck, Hypothesis, fast-check). The framework generates many inputs per run, biased toward boundary values.
        
        - Good for: "must be positive", "must round-trip", "must be idempotent", "must be sorted"
        - Complement, don't replace, example-based tests — use both
        - Start with built-in wrappers (`NonNegativeInt`, `PositiveInt`) before writing custom generators
        
        **Example**:
        ```csharp
        [Property]
        public void QuantityMustBePositive(
            Guid id, DateTime at, Email email, Name name, NonNegativeInt i)
        {
            var invalidQuantity = -i?.Item ?? 0;
            Assert.Throws<ArgumentOutOfRangeException>(
                () => new Reservation(id, at, email, name, invalidQuantity));
        }
        ```
        
        ### 8. Use behavioural code analysis on legacy bases
        
        For any code base with real history, mine Git to find hotspots (high complexity × high change frequency) and change coupling (files that commit together).
        
        - Commands for churn, hotspots, and coupling: `../tooling/commands.md`
        - Change coupling catches copy-paste dependencies that static analysis misses
        - Watch trends, not absolute numbers — a bad trend is actionable even on a legacy code base
        
        ## Context Budget
        
        In a large repository, read progressively — do not ingest the tree:
        
        1. Entry point / composition root (table of contents).
        2. The one module the task touches.
        3. Its tests (behaviour named after the task).
        4. **Stop.** Do not read whole directories speculatively.
        
        Further discipline:
        
        - Prefer summarising a file's public surface (exports, public methods, type signatures) over ingesting method bodies.
        - When the map is bigger than the remaining context budget, write findings to a scratch note and continue from the note — do not re-read the same files.
        - Follow one import chain at a time; breadth-first directory listing is almost always waste.
        
        ## Guidelines
        
        - Follow the repository's existing file-organisation convention; flat vs deep is a project choice
        - When a test helper has no test-specific logic, consider moving it to a production client SDK
        - Use numerical thresholds from behavioural analysis to direct attention, not as law
        - On a larger team, use knowledge maps (main author per file) to find bus-factor risk
        
        ## Exceptions
        
        When these rules may be relaxed:
        
        - **Framework conventions**: If a framework expects a specific folder layout (e.g. Next.js `app/`, Rails MVC), follow it — fighting conventions costs more than it saves
        - **Regulatory splits**: Some compliance regimes require package-level isolation regardless of coupling
        - **Tiny repositories**: Progressive disclosure still helps, but a full read may fit the budget
        
        ## Quick Reference
        
        | Rule | Summary |
        |------|---------|
        | Start at entry point | Read `Main`/`Startup`/composition root as table of contents |
        | Navigate with search | `rg` / LSP / imports / tests / `git log --follow` |
        | Read tests first | They document intent in runnable form |
        | Listen to tests | Painful tests reveal bad design |
        | Check for cycles | Red flag; packages make them a compile error |
        | Monolith is fine | Only the *internal* structure matters |
        | Property-based for invariants | Framework-generated inputs over hand-picked ones |
        | Behavioural analysis | Git history reveals hotspots and coupling (`../tooling/commands.md`) |
        | Context budget | Entry → one module → its tests → stop; scratch notes beat re-reads |
        
    • codebase-setup
      • checklist.md 5.4 KB
        # Codebase Setup Checklist
        
        Use when setting up a new code base, or auditing an existing one for baseline discipline. Designed as a read-do checklist: read an item, do it, move on. For an existing code base, switch to do-confirm and ideally run it with a second person.
        
        ## Before You Start
        
        - [ ] You have a directory where the code base will live.
        - [ ] You have (or can install) the language toolchain (compiler, package manager).
        - [ ] You have (or can get) access to a CI service — self-hosted or cloud.
        - [ ] You know the target deployment environment (or at least a pre-production stand-in).
        
        ## New Code Base — Top-Level Checklist
        
        The canonical three-item list. Each item expands below.
        
        - [ ] Use Git.
        - [ ] Automate the build.
        - [ ] Turn on all error messages.
        
        ## 1. Initialise Git
        
        - [ ] Run `git init` in the project directory.
        - [ ] (Optional) Add an empty initial commit: `git commit --allow-empty -m "Initial commit"`.
        - [ ] Add a `.gitignore` appropriate to the language/toolchain.
        - [ ] Confirm you can use Git from the command line (not just a GUI).
        - [ ] (Later, not blocking) Connect to a remote host (GitHub, GitLab, etc.).
        
        ## 2. Scaffold the Minimum Application
        
        - [ ] Use a wizard, scaffolding tool, or CLI (e.g. `dotnet new`, `npx create-*`) to generate a minimal runnable app (the "shell").
        - [ ] Confirm the app runs locally and produces observable output (e.g. "Hello World").
        - [ ] Commit the generated code to Git.
        
        ## 3. Automate the Build
        
        - [ ] Create a build script at the repo root (e.g. `build.sh`, `build.ps1`).
        - [ ] Script invokes the language's build tool (e.g. `dotnet build --configuration Release`).
        - [ ] Script targets the **Release** configuration (matches production).
        - [ ] Script is executable and runs cleanly on a fresh checkout.
        - [ ] Commit the build script to Git.
        
        ## 4. Turn On All Error Messages
        
        - [ ] Turn on **warnings as errors** in the build configuration.
        - [ ] Apply the setting to **every build configuration** — Release AND Debug (or equivalent).
        - [ ] Enable language-level static analyser / linter with the default or recommended rule set.
        - [ ] Enable strict language features (e.g. C# nullable reference types, TypeScript `strict: true`).
        - [ ] Run the build and confirm it still passes.
        - [ ] If warnings appear: fix them immediately (there is almost no code yet).
        - [ ] Commit the configuration change.
        
        ## 5. Wire Up a Deployment Pipeline
        
        - [ ] Choose / provision a CI service.
        - [ ] Configure the CI pipeline to run the build script on every push to mainline.
        - [ ] Configure deployment to a pre-production environment on green builds.
        - [ ] (If feasible) Configure deployment to production, gated on a manual sign-off.
        - [ ] Confirm a full push → build → deploy cycle works end to end.
        
        ## Existing Code Base — Ratchet Checklist
        
        Use when retrofitting checks onto a legacy project. Never flip everything at once.
        
        ### Survey
        
        - [ ] List the libraries / packages / projects in the code base.
        - [ ] Run the compiler; capture every warning.
        - [ ] Run any available linter / analyser; capture every warning.
        - [ ] Group warnings by type and by library.
        
        ### Pick a Slice
        
        - [ ] Pick one library.
        - [ ] Pick one warning type with a manageable count (around a dozen).
        - [ ] Read the online documentation for that warning to understand the motivation.
        
        ### Fix and Flip
        
        - [ ] Fix every instance of that warning in the chosen library.
        - [ ] Commit incrementally as fixes are grouped logically.
        - [ ] Merge into mainline.
        - [ ] Flip that warning to **error** so it cannot regress.
        - [ ] Commit the configuration change.
        
        ### Repeat
        
        - [ ] Apply the same warning type to the next library, OR pick a new warning type in the same library.
        - [ ] For opt-in strict features (e.g. nullable reference types), enable file-by-file or project-by-project.
        - [ ] Apply the Boy Scout Rule on every unrelated change.
        
        ## Audit-Mode Quick Check
        
        Use on an unknown repo for a quick baseline-discipline check.
        
        - [ ] Is there a `.git` directory at the root?
        - [ ] Is there a build script committed at the root (or documented entry point)?
        - [ ] Is the build script configured for Release?
        - [ ] Is warnings-as-errors on in the build config?
        - [ ] Is there a linter / analyser configuration committed?
        - [ ] Does `git log` show an initial commit near the project's inception (not retroactive)?
        - [ ] Does the CI config exist (`.github/workflows`, `.gitlab-ci.yml`, `azure-pipelines.yml`, etc.)?
        
        ## Red Flags
        
        Stop and address if you find:
        
        - No version control.
        - Build only runs inside a specific IDE, with no script alternative.
        - Hundreds of compiler warnings in the build log (checklist was skipped).
        - Warnings-as-errors on in Release but off in Debug (or vice versa).
        - No CI — every deployment is a human copying files.
        - A project that started months or years ago with no history of incremental ratcheting.
        
        ## Quick Reference
        
        | Aspect | Ideal | Acceptable | Red Flag |
        |--------|-------|------------|----------|
        | Version control | Git, initial commit on day 1 | Git added retroactively | No VCS |
        | Build | Scripted, Release, runs locally and in CI | Scripted but only runs in CI | IDE-only build |
        | Warnings | Zero, as errors in every config | Zero, as errors in Release only | Hundreds accumulated |
        | Analyser/linter | Enabled with rule set committed | Available but not wired to build | Not installed |
        | CI/CD | Push → build → deploy automated | Push → build only | No CI |
        | Legacy migration | Ratchet in progress, visible in history | Not started but planned | "We'll get to it" |
        
      • knowledge.md 2.2 KB
        # Codebase Setup Knowledge
        
        Why the setup rules exist. The actionable material is in `rules.md` and `checklist.md`; read this only for rationale.
        
        ## Why Checklists
        
        A checklist is a short aid to memory covered in minutes at pause points — not a compliance flowchart. The problem it solves is not lack of skill but *forgetting*: on a complex task, skipping one trivial-but-important step is almost inevitable. Externalising those steps frees working memory (human or context window) for the hard parts.
        
        Two run modes: **read-do** (read an item, do it, move on — fits imperative lists like `checklist.md`) and **do-confirm** (do the work, then verify against the list — for auditing an existing code base, verify with evidence rather than recall).
        
        ## Why Day 1
        
        Retrofitting discipline onto a large code base is a formidable task; on an empty one it costs almost nothing. Zero code means zero warnings to triage and no pipeline to untangle, and each new warning is fixed the moment it appears. The cost of postponing rises non-linearly — which is why the only viable path for legacy code is the gradual ratchet (`rules.md`, rule 5).
        
        ## Automated Checks as Automated Checklists
        
        Compilers, linters, analysers, and warnings-as-errors are machine-enforced checklists that run on every build, controlling for thousands of issues no human would check line by line. Suppressing an occasional false positive is cheap; walking away from the tools is expensive. A machine-enforced rule also survives delivery pressure better than a human-held convention — see `workflows/operationalize-finding.md` for converting conventions into gates.
        
        ## Common Misconceptions
        
        - **Myth**: Checklists are for the unskilled.
          **Reality**: Surgeons and pilots use them *because* they are experts. Skill and memory are different things.
        
        - **Myth**: Turning on all warnings slows the team down.
          **Reality**: Seven warnings today are easier than hundreds in six months. What gets upset is the illusion that the code was maintainable without discipline.
        
        - **Myth**: Strict checks cannot be added to a legacy code base.
          **Reality**: They can — one library, one rule, one warning category at a time (the ratchet).
        
      • rules.md 6.3 KB
        # Codebase Setup Rules
        
        Concrete rules for initialising a new code base or tightening an existing one. These are hard rules: apply them unless you have a specific reason not to.
        
        ## Core Rules
        
        ### 1. Use Git From Line 1
        
        Initialise a Git repository *before* writing any code.
        
        - Run `git init` in the directory where the code will live.
        - Do *not* wait to connect to a remote (GitHub, GitLab, etc.) — that can always come later.
        - Apply this rule to any code base expected to live more than a week. The threshold for creating a repo should be low; you can always delete `.git` to undo.
        
        ### 2. Automate the Build From Line 1
        
        Commit a build script that compiles (and later tests and deploys) the code, runnable by any developer on their machine.
        
        - Create the minimal deployable application first (a "shell" from a wizard/scaffolding tool). Commit and deploy it *before* writing real functionality.
        - Provide one documented, non-interactive verification entry point and commit it. It may be a script, task-runner target, or build-tool command.
        - Configure the build script to produce a **Release** build — the automated build must reflect what goes to production.
        - As build steps are added (tests, packaging, analyzers), add them to the script as well.
        - Keep local and CI behavior aligned. Passing locally is useful evidence only when CI runs the same checks in a comparable environment.
        - Start simple. Don't reach for a full-blown build tool (Cake, Nuke, FAKE, Gradle) unless the simple script demonstrably does not fit.
        
        **Example — minimal `build.sh`**:
        ```bash
        #!/usr/bin/env bash
        dotnet build --configuration Release
        ```
        
        ### 3. Turn On All Error Messages (Warnings as Errors)
        
        Treat every compiler warning, linter warning, and static-analysis warning as a build-breaking error.
        
        - Turn on **warnings as errors** as one of the first things you do. On a new code base there is nothing to break.
        - Apply the setting to **every build configuration** — Release *and* Debug. If your toolchain stores these per-config (e.g. Visual Studio), set both. Put "set both" on your checklist.
        - Enable the language's static analysers / linters (in .NET: Roslyn analysers / FxCop successors).
        - Turn on language features that improve static checking (e.g. C# 8 **Nullable reference types**) immediately, while there is no code to break.
        - Treat the cost of addressing a small warning set today as far lower than hundreds later.
        - False positives exist in linters; suppress them narrowly via the tool's options rather than disabling the rule.
        
        ### 4. Build the Deployment Pipeline Early
        
        Once the build script works locally, wire it to a CI service and a deployment pipeline.
        
        - Pushing to `master` (or the mainline) should trigger an automated pipeline that either deploys to production or leaves it one manual sign-off away.
        - If you don't have a CI server, get one. Cloud-based services exist; the dollar cost is a small fraction of a programmer's salary.
        - If you don't have a production environment yet, target a pre-production environment — preferably one that mirrors production's network topology. Use VMs or containers if hardware parity is not possible.
        
        ### 5. Apply the Gradual Ratchet to Legacy Code Bases
        
        When retrofitting checks onto existing code, turn guards on one slice at a time — never all at once.
        
        - Work library-by-library (package-by-package, project-by-project).
        - Work **one warning type at a time**. Extract the existing warning list, group by type, pick a type with a manageable count (say a dozen), fix them all.
        - Keep the warnings as *warnings* while fixing so the code continues to build. Commit incrementally.
        - Once that type is at zero in that library, **flip it to an error** so it cannot regress.
        - Repeat: pick another warning type, or apply the same type to another library.
        - For nullable reference types (or equivalent opt-in features): enable file-by-file or project-by-project.
        - Improve the touched change surface without mixing unrelated cleanup into the same review.
        
        ### 6. Use Automated Gates as Cultural Armour
        
        A machine-enforced rule is harder to override under delivery pressure than a human-held convention.
        
        - When stakeholders push to "just ship it," a build that fails on warnings is a stronger answer than an opinion.
        - Turn former human decisions into machine-enforced rules wherever possible.
        - Use your judgment: in a healthy organisation, be open about the gates; in an unhealthy one, the gates can quietly protect engineering discipline. Do this for the good of the organisation, not personal agenda.
        
        ## Guidelines
        
        Less strict recommendations:
        
        - Prefer reproducible, non-interactive commands for operations that CI and agents must run. Human interface choice is secondary.
        - Read the online documentation for each analyser rule before suppressing it — most rules encode decades of accumulated knowledge.
        - Don't show screenshots or step-by-step GUI instructions in documentation; they go stale fast.
        
        ## Exceptions
        
        When these rules may be relaxed:
        
        - **Truly ephemeral code**: A throwaway script you will delete within a week can skip `git init`. The threshold should still be low.
        - **Large legacy migrations**: Don't block all work to reach zero warnings — follow the gradual ratchet instead.
        - **Known-bad false positive in analyser**: Suppress the specific rule at the specific site, with a comment explaining why. Do not disable the analyser globally.
        
        ## Quick Reference
        
        | Rule | Summary |
        |------|---------|
        | Use Git | `git init` before first line of code. |
        | Automate build | Commit a build script; Release config; runnable locally. |
        | Warnings as errors | Turn on, in every build config, on day 1. |
        | Enable analysers/linters | Ship a baseline rule set; suppress false positives narrowly. |
        | Enable strict language features | E.g. C# nullable reference types — turn on while code is empty. |
        | Deployment pipeline early | CI wired to mainline; release or near-release on green. |
        | Ratchet for legacy | One library, one warning type at a time; flip to error at zero. |
        | Machine-enforced gates | Convert human conventions to build-breaking rules. |
        
        ## Editorial Amendment (2026) — Not from the Book
        
        Commit toolchain versions and lockfiles; prefer reproducible environments. Dependency provenance and gate integrity are canonical in `../agent-native/hallucination-debugging.md` and `../agent-native/verification-loops.md`.
        
    • decomposition
      • examples.md 7.8 KB
        # Decomposition Examples
        
        Before/after refactorings drawn from Chapter 7 and §13.1 of the restaurant reservation code base.
        
        ## Example 1: Extracting a Low-Cohesion Guard-Clause Block
        
        ### Before
        
        The original `Post` method (listing 6.9) mixes several validation decisions with repository I/O. The first section is the best extraction candidate because it is cohesive, uses no instance members, and can be verified independently.
        
        ### After
        
        ```csharp
        // Step 1: extracted helper. No class fields used → marked static.
        private static bool IsValid(ReservationDto dto)
        {
            return DateTime.TryParse(dto.At, out _)
                && !(dto.Email is null)
                && 0 < dto.Quantity;
        }
        
        // Step 2: caller shrinks to 22 lines, cyclomatic complexity 5.
        public async Task<ActionResult> Post(ReservationDto dto)
        {
            if (dto is null)
                throw new ArgumentNullException(nameof(dto));
            if (!IsValid(dto))
                return new BadRequestResult();
            var d = DateTime.Parse(dto.At!, CultureInfo.InvariantCulture);
            var reservations =
                await Repository.ReadReservations(d).ConfigureAwait(false);
            int reservedSeats = reservations.Sum(r => r.Quantity);
            if (10 < reservedSeats + dto.Quantity)
                return new StatusCodeResult(
                    StatusCodes.Status500InternalServerError);
            var r =
                new Reservation(d, dto.Email!, dto.Name ?? "", dto.Quantity);
            await Repository.Create(r).ConfigureAwait(false);
            return new NoContentResult();
        }
        ```
        
        ### Changes Made
        
        1. Extracted the first guard-clause block (no class fields) into `IsValid` — chosen because its low cohesion with the controller's fields made it the most conspicuous seam.
        2. `IsValid` is marked `static` because a code analyser detected no instance-member usage; this is already a hint of Feature Envy (see Example 2).
        3. The caller exposes fewer simultaneous decisions: three validation branches collapse into the named concept `!IsValid`.
        
        ---
        
        ## Example 2: Fixing Feature Envy by Moving the Method
        
        ### Problem with Example 1
        
        `IsValid` takes a `ReservationDto` parameter and reads only that parameter's state. It envies `ReservationDto`'s features. Also, the `static` marker is itself a code smell in object-oriented design.
        
        ### After
        
        ```csharp
        // IsValid moved onto the class it envied, turned into a property
        // because it takes no input, has no preconditions, and can't throw.
        internal bool IsValid
        {
            get
            {
                return DateTime.TryParse(At, out _)
                    && !(Email is null)
                    && 0 < Quantity;
            }
        }
        
        // Controller call site becomes:
        if (!dto.IsValid)
            return new BadRequestResult();
        ```
        
        ### Changes Made
        
        1. Moved the method onto `ReservationDto` — the class whose features it used.
        2. Converted method to a property per .NET Framework Design Guidelines (no input, no preconditions, no exceptions).
        3. Kept visibility `internal` for now; widen later if other modules need it.
        4. Removed the `static` smell.
        
        ### Still Not Good Enough
        
        Even after this fix, the surrounding `Post` method must use the null-forgiving operator `!` on `dto.At` and `dto.Email`, and must re-parse `dto.At`. That is the *Lost in Translation* smell (D5) — the Boolean `IsValid` eliminated too much. The real fix belongs in `encapsulation/` under Parse-Don't-Validate: replace the Boolean with a method that returns the validated `Reservation?`.
        
        ---
        
        ## Example 3: Sequential Composition in a Domain Query
        
        ### The `WillAccept` Pipeline
        
        `WillAccept` decides whether a restaurant can accept a reservation. It composes four Queries sequentially — constructor, `Where` + `Overlaps`, `Allocate`, `Any` + `Fits` — none of which has side effects.
        
        ```csharp
        // Overlaps: a Query that tests whether two seatings overlap.
        internal bool Overlaps(Reservation other)
        {
            var otherSeating = new Seating(SeatingDuration, other);
            return Start < otherSeating.End && otherSeating.Start < End;
        }
        
        // Allocate: another Query, returning the remaining available tables.
        private IEnumerable<Table> Allocate(
            IEnumerable<Reservation> reservations)
        {
            List<Table> availableTables = Tables.ToList();
            foreach (var r in reservations)
            {
                var table = availableTables.Find(t => t.Fits(r.Quantity));
                if (table is { })
                {
                    availableTables.Remove(table);
                    if (table.IsCommunal)
                        availableTables.Add(table.Reserve(r.Quantity));
                }
            }
            return availableTables;
        }
        ```
        
        ### Why It Works
        
        - Every step is a Query (Command Query Separation holds).
        - The output of one Query is the input of the next (`Where` → `Allocate` → `Any`).
        - Once `WillAccept` returns, nothing else has changed in the world — the reader can forget how it reached the result.
        - No mocks or fakes are required to test any individual step.
        
        ---
        
        ## Example 4: Replacing Nested Composition with Sequential Composition
        
        ### Before (Bad — do not write code like this)
        
        ```csharp
        // Cyclomatic complexity 4; the top level exposes four domain concepts.
        // But one of those objects is an injected IRestaurantManager that
        // hides Manager.TrySave — a method that BOTH saves to the database
        // AND returns a bool. The Query-looking Check actually performs I/O.
        public IRestaurantManager Manager { get; }
        public async Task<ActionResult> Post(ReservationDto dto)
        {
            if (dto is null)
                throw new ArgumentNullException(nameof(dto));
            Reservation? r = dto.Validate();
            if (r is null)
                return new BadRequestResult();
            var isAccepted =
                await Manager.Check(r).ConfigureAwait(false);
            if (!isAccepted)
                return new StatusCodeResult(
                    StatusCodes.Status500InternalServerError);
            return new NoContentResult();
        }
        ```
        
        The hidden side effect inside `Manager.Check` violates CQS, eliminates something essential, and adds a chunk the reader does not see in the metric.
        
        ### After
        
        ```csharp
        // Sequentially composed: each step is explicit in the caller.
        // Nondeterminism (DateTime.Now, Guid.NewGuid) and side effects
        // (Repository.Read/Create) live on the imperative shell; the pure
        // WillAccept decision lives in the functional core.
        [HttpPost]
        public async Task<ActionResult> Post(ReservationDto dto)
        {
            if (dto is null)
                throw new ArgumentNullException(nameof(dto));
            var id = dto.ParseId() ?? Guid.NewGuid();
            Reservation? r = dto.Validate(id);
            if (r is null)
                return new BadRequestResult();
            var reservations = await Repository
                .ReadReservations(r.At)
                .ConfigureAwait(false);
            if (!MaitreD.WillAccept(DateTime.Now, reservations, r))
                return NoTables500InternalServerError();
            await Repository.Create(r).ConfigureAwait(false);
            return Reservation201Created(r);
        }
        ```
        
        ### Changes Made
        
        1. Removed the nested `IRestaurantManager` indirection that hid the write behind a Boolean.
        2. Made nondeterminism explicit (`DateTime.Now`, `Guid.NewGuid`) so the reader sees every input to the decision.
        3. Pushed the side effect (`Repository.Create`) to the edge, after a pure-function decision from `MaitreD.WillAccept`.
        4. The reader can now see the entire pipeline without following dependencies into other classes.
        
        ---
        
        ## Example 5: Progressive Disclosure Before and After
        
        ### Before (Original `Post`, listing 6.9)
        
        The caller exposes all decisions at once: `dto NULL`, `At INVALID`, `Email NULL`, `Name NULL`, `Quantity INVALID`, `TOO LITTLE CAPACITY`, and `HAPPY PATH`.
        
        ### After `Validate` Extraction
        
        The caller now exposes four higher-level decisions: `dto NULL`, `Validate NULL`, `TOO LITTLE CAPACITY`, and `HAPPY PATH`.
        
        ### Zooming Into `Validate`
        
        Inside `Validate`, the related input decisions remain together: `At INVALID`, `Email NULL`, `Quantity INVALID`, `Name NULL` (via `??`), and `HAPPY PATH`.
        
        ### What This Shows
        
        The refactor reduced top-level cyclomatic complexity without removing logic. It distributed related decisions across two coherent zoom levels—the signature of fractal architecture—so each level communicates one understandable responsibility.
        
      • knowledge.md 4.4 KB
        # Decomposition Knowledge
        
        > **Source note:** This book-derived theme includes 2026 editorial reframing for agent-scale changes and system-level complexity. Those additions are not from Seemann.
        
        How to divide software so changes remain local without fragmenting cohesive logic into needless indirection.
        
        ## Code Rot
        
        Code rot is the gradual increase in change cost as branches, state, dependencies, duplication, and temporary paths accumulate. It happens locally inside methods and globally when module boundaries stop constraining change.
        
        Metrics help expose drift, but no single metric proves maintainability. A low-complexity method can participate in a cyclic, duplicated, tightly coupled architecture.
        
        ## Cyclomatic Complexity
        
        Cyclomatic complexity counts independent paths through a piece of code. Straight-line code starts at 1; branches and loops add paths.
        
        Use complexity to focus review and testing, not to drive blind extraction. This skill uses **above 15** as a generic review trigger—roughly twice the book's original threshold of 7. A project may choose a stricter value. Cohesive parsers, decision tables, and generated code can justify higher values when their structure remains explicit and well verified.
        
        ## Responsibility and Cohesion
        
        Cohesion asks whether the members of a unit belong together and change for the same reasons. Decomposition improves design only when the new boundary represents a meaningful concept.
        
        Extracting every few lines creates navigation overhead and hides the real algorithm. Leaving validation, persistence, policy, and formatting intertwined creates a different failure. Prefer boundaries that separate reasons to change, effects, trust levels, or domain concepts.
        
        ## Interacting State
        
        Complexity rises with the number of values and effects that interact simultaneously. Count variables, parameters, fields, mutable state, and external dependencies as diagnostic clues, but group them by concept rather than enforcing a universal item limit.
        
        A domain type can turn several related primitives into one meaningful concept. A generic parameter bag that merely hides unrelated values does not reduce complexity.
        
        ## Pure Core, Explicit Effects
        
        Pure functions are deterministic and have no observable side effects. They compose and test well because the same input produces the same output. Functional-core/imperative-shell design keeps decisions pure where useful and performs I/O, time, randomness, and mutation at explicit boundaries.
        
        Purity is a means, not a mandate. The important property is that effects and their ordering are visible, constrained, and testable.
        
        ## Fractal Coherence
        
        At every zoom level—system, service, module, type, method—the visible parts should form a comprehensible model. A reader should be able to name the main concepts and predict where a change belongs.
        
        This requires progressive disclosure: higher levels expose stable responsibilities and lower levels contain the detail without leaking it everywhere.
        
        ## System-Level Complexity
        
        Big Ball of Mud architecture typically appears through:
        
        - dependency cycles and bidirectional knowledge;
        - shared mutable state and implicit runtime coupling;
        - duplicated domain rules;
        - broad changes spanning unrelated modules;
        - unstable hotspots with repeated corrective edits;
        - stale feature flags, adapters, and parallel implementations;
        - abstractions that have multiple incompatible meanings.
        
        Architecture tests, dependency analysis, duplication detection, and change-history analysis can make these signals visible. They inform judgment; they do not replace it.
        
        ## Large Changes
        
        Large changes are not necessarily complex. A systematic migration can touch thousands of files while preserving a simple transformation and clear target architecture. Conversely, a ten-line patch can add a damaging dependency cycle.
        
        Judge large work by architecture, invariants, verification, reversibility, and whether each affected area has a clear reason to change.
        
        ## Common Misconceptions
        
        - **"Smaller is always better."** Smaller units help only when boundaries carry meaning.
        - **"A metric below threshold means good design."** Metrics miss coupling, duplication, hidden effects, and domain confusion.
        - **"A large diff is automatically unreviewable."** Systematic, well-specified changes can be reviewed through their transformation, architecture, and verification evidence.
        - **"More abstraction reduces complexity."** Abstraction helps only when it eliminates irrelevant detail without hiding essential behavior.
        
      • patterns.md 6.3 KB
        # Decomposition Patterns
        
        Recomposition strategies: once you split a block, how do you put the pieces back together so the whole still fits in your head?
        
        ## Pattern: Sequential Composition
        
        ### Intent
        
        Chain Queries so the output of one becomes the input of the next. Use instead of nesting objects inside objects, which hides side effects and inflates the chunk count.
        
        ### When to Use
        
        - The method performs a pipeline of transformations over data.
        - You can express each step as a Query (no side effect, returns data).
        - You want the reader to see the full pipeline without drilling into dependencies.
        
        ### Structure
        
        ```csharp
        var a = StepOne(input);
        var b = StepTwo(a);
        var c = StepThree(b);
        return Finalise(c);
        ```
        
        ### Example
        
        ```csharp
        // From the restaurant code base: WillAccept composed sequentially from
        // constructor, Where + Overlaps, Allocate, Any + Fits — all Queries.
        var seating = new Seating(SeatingDuration, candidate);
        var relevantReservations = existingReservations.Where(seating.Overlaps);
        var availableTables = Allocate(relevantReservations);
        return availableTables.Any(t => t.Fits(candidate.Quantity));
        ```
        
        ### Benefits
        
        - The reader can trace data flow top-to-bottom.
        - Each step is testable in isolation.
        - No hidden side effects — because no step has any.
        
        ### Considerations
        
        - Requires that each step be a Query. If a step has side effects, push it outward.
        - Works best when types chain cleanly; introduce small value objects when signatures don't line up.
        
        ---
        
        ## Pattern: Nested Composition (Use Sparingly)
        
        ### Intent
        
        Compose objects by embedding one inside another (Composite, most Gang-of-Four patterns). Shown here so you recognise it and know when *not* to reach for it.
        
        ### Why It's Problematic
        
        Every nested object adds side effects to what the reader must remember. By the outer shell you are tracking eight or nine chunks — past the memory limit. Query-looking methods at the top may hide Commands deep in the tree (smell D6).
        
        ### When It Is Acceptable
        
        - Domain *is* a tree (UI widgets, filter chains, AST nodes).
        - You deliberately want polymorphic substitution at a node.
        - Otherwise prefer sequential composition and keep any tree shallow.
        
        ---
        
        ## Pattern: Functional Core, Imperative Shell
        
        ### Intent
        
        Implement complex logic as pure functions; concentrate nondeterminism (time, GUIDs, random, I/O) and side effects at the system's edge.
        
        ### When to Use
        
        - Any non-trivial domain logic.
        - Controllers, message handlers, and `Main` — these are the shell.
        - Whenever you want a function to be easy to test and free of mocks.
        
        ### Structure
        
        ```csharp
        // Shell: gather inputs (nondeterministic), call pure core, apply side effect.
        public async Task<ActionResult> Handle(SomeDto dto)
        {
            var now = DateTime.Now;                       // nondeterministic
            var id = Guid.NewGuid();                      // nondeterministic
            var state = await repo.Read(...);              // I/O
        
            var decision = PureCore(now, id, state, dto); // pure
        
            if (decision.IsAccepted)
                await repo.Write(decision.Result);         // side effect
            return decision.ToResponse();
        }
        ```
        
        ### Example
        
        ```csharp
        // The WillAccept pure function decides; the Post shell performs the I/O.
        [HttpPost]
        public async Task<ActionResult> Post(ReservationDto dto)
        {
            if (dto is null) throw new ArgumentNullException(nameof(dto));
            var id = dto.ParseId() ?? Guid.NewGuid();
            Reservation? r = dto.Validate(id);
            if (r is null) return new BadRequestResult();
        
            var reservations = await Repository
                .ReadReservations(r.At)
                .ConfigureAwait(false);
        
            if (!MaitreD.WillAccept(DateTime.Now, reservations, r))
                return NoTables500InternalServerError();
        
            await Repository.Create(r).ConfigureAwait(false);
            return Reservation201Created(r);
        }
        ```
        
        ### Benefits
        
        - Pure core is trivial to test (no mocks, no fakes).
        - Shell stays thin; side effects are visible at a glance.
        - Aligns with fractal architecture — the shell is the trunk, the core is where the branches live.
        
        ### Considerations
        
        - Requires discipline to keep the core pure; resist injecting clocks or random generators into it — pass those values in.
        - Works in C# just as well as in functional languages.
        
        ---
        
        ## Pattern: Referential Transparency Replacement
        
        ### Intent
        
        Reason about a pure function call by mentally replacing it with its return value, collapsing arbitrary internal complexity to a single chunk.
        
        ### When to Use
        
        - Reading or reviewing code that calls a pure function.
        - Deciding whether a helper can be treated as one chunk — it can, if it is pure.
        
        ### Structure
        
        ```csharp
        // Once you know Foo(x, y) returns 42, treat the expression as 42.
        var result = Bar(Foo(x, y), z);   // in your head: Bar(42, z)
        ```
        
        ### Benefits & Considerations
        
        - Works only if the function is deterministic *and* side-effect-free.
        - Breaks the moment a "pure" function starts reading wall-clock time or mutating shared state.
        - Pure functions always compose when output and input types line up.
        
        ---
        
        ## Pattern: Concept Inventory (Decomposition Heuristic)
        
        ### Intent
        
        A thinking tool: list the independent decisions, state transitions, dependencies, and hidden effects a reader must track at one zoom level. When unrelated concepts compete for attention, decompose by responsibility.
        
        ### When to Use
        
        - Before adding a branch to an already dense method.
        - When a method feels heavy but the metric is still within limits.
        - When picking the first block to extract.
        
        ### Example
        
        For the `Post` method after extracting `Validate()`, the caller exposes four concepts: `dto NULL`, `Validate NULL`, `TOO LITTLE CAPACITY`, and `HAPPY PATH`. The input-validation details remain at the lower zoom level.
        
        ### Considerations
        
        - Heuristic, not a metric or a fixed item limit. Use alongside cyclomatic complexity, state interaction, and coupling.
        - Catches hidden chunks (side effects buried in Queries) that metrics miss.
        
        ---
        
        ## Pattern Selection Guide
        
        | Situation | Recommended Pattern |
        |-----------|--------------------|
        | Pipeline of data transformations | Sequential Composition |
        | Controller or message handler | Functional Core, Imperative Shell |
        | Complex domain logic | Pure functions + Referential Transparency Replacement |
        | Method feels crowded | Concept Inventory diagnosis |
        | You are tempted to nest objects deeply | Reconsider — prefer sequential |
        
      • rules.md 5.2 KB
        # Decomposition Rules
        
        > **Source note:** This book-derived theme includes 2026 editorial reframing for agent-scale changes and system-level complexity. Those additions are not from Seemann.
        
        Heuristics for keeping logic cohesive and preventing local complexity from growing into system-wide mud.
        
        ## Core Rules
        
        ### 1. Review Cyclomatic Complexity Above 15
        
        Count independent paths through a method. When cyclomatic complexity exceeds 15, refactor unless the branching is cohesive, explicit, and better represented in its current form.
        
        - Treat 15 as a review trigger, not a scientific law or automatic rejection.
        - Prefer reducing nested decisions, duplicated conditions, and mixed responsibilities.
        - Do not replace one understandable decision table or parser with a maze of tiny indirections merely to lower the number.
        - Projects may choose a stricter threshold based on language, domain risk, and tooling.
        
        ### 2. Split by Responsibility, Not Display Geometry
        
        Line width, screen height, and editor layout are project concerns, not universal design rules.
        
        - Split when a method mixes responsibilities, effects, abstraction levels, or reasons to change.
        - Keep cohesive transformations together even when they are long.
        - Extract only when the new name and boundary reduce what a reader must understand.
        
        ### 3. Watch Interacting State
        
        Count variables, parameters, fields, mutable state, and external dependencies when logic feels hard to follow. The smell is not a fixed count; it is interaction that cannot be explained as a few coherent concepts.
        
        - Group values that form one domain concept into a type.
        - Reduce live mutable state and push intermediate results forward.
        - Do not hide unrelated values inside a parameter object solely to game a count.
        
        ### 4. Prefer Sequential Composition to Hidden Nesting
        
        Chain transformations so the output of one becomes the input of the next. Avoid object graphs and callbacks that conceal ordering or side effects.
        
        ### 5. Favour a Pure Core and Explicit Effects
        
        - Keep complex decisions deterministic where practical.
        - Concentrate time, randomness, I/O, and mutation at explicit boundaries.
        - Make effect ordering observable and testable.
        
        ### 6. Move Behaviour to the Data It Understands
        
        If a method mainly reads another type's state and ignores its own class, consider moving it to that type or a cohesive domain service. Avoid static helper dumping grounds.
        
        ### 7. Extract Low-Coupling Sections First
        
        Blocks that use no instance state or external effects are low-risk extraction candidates. Preserve a clear abstraction level in the caller.
        
        ### 8. Parse Into Stronger Types
        
        Do not return a Boolean when validation also discovers structured information. Return the parsed domain value or a typed failure so callers do not repeat work.
        
        ### 9. Preserve Coherence at Every Zoom Level
        
        Entry points, modules, types, and methods should each expose a small number of meaningful concepts. The exact number is contextual; the requirement is that a reader can name the parts and predict where a change belongs.
        
        ### 10. Check System Complexity, Not Only Methods
        
        Tiny functions can still form a Big Ball of Mud. Review:
        
        - dependency cycles and violated layer direction;
        - fan-in/fan-out and cross-module coupling;
        - duplicated business rules and competing abstractions;
        - changes that spread across unrelated areas;
        - churn concentrated in unstable hotspots;
        - temporary flags, adapters, and migration paths that never disappear.
        
        Use available architecture tests or static analysis where they provide reliable signals. Do not mandate a particular product or reduce architectural judgment to one score.
        
        ## Guidelines
        
        - Prefer deletion and reuse over adding another helper or abstraction.
        - Constructors should not perform hidden I/O or irreversible effects.
        - Treat `static` helpers and utility modules as smells when they collect unrelated behavior.
        - Use blank-line groupings as clues to responsibility boundaries, not as proof that extraction is needed.
        - Refactor when a metric and the code's semantics both indicate rising change cost.
        
        ## Exceptions
        
        - **Cohesive algorithms and generated code** may be long or branch-heavy while still being the clearest representation.
        - **System edges** coordinate effects; keep the sequence explicit rather than forcing artificial purity.
        - **Production emergencies** may accept temporary complexity with a recorded owner and removal condition.
        - **Project policy** may set different metric thresholds; preserve the underlying goal of comprehensibility and local change.
        
        ## Quick Reference
        
        | Rule | Summary |
        |---|---|
        | CC > 15 triggers review | Investigate complexity; do not refactor mechanically |
        | Split by responsibility | Ignore universal screen and line limits |
        | Watch interacting state | Reduce unrelated live concepts and mutation |
        | Sequential composition | Keep ordering and effects explicit |
        | Pure core, explicit effects | Make decisions deterministic and boundaries visible |
        | Move feature-envious behavior | Put logic with the data it understands |
        | Stronger parse results | Return information, not a lossy Boolean |
        | Every zoom level coherent | Make parts nameable and change locations predictable |
        | Check system structure | Prevent cycles, duplication, sprawl, and permanent migration debt |
        
      • smells.md 9.9 KB
        # Decomposition Smells
        
        > **Source note:** D1–D7 derive from the book theme; D8 (system-wide sprawl), D9 (orphaned generated code), and current thresholds are 2026 editorial adaptations.
        
        Code smells that signal a block needs to be decomposed, with how to detect and fix each. Use during code review and when responding to "this code feels off — what's wrong?"
        
        ---
        
        ## D1: High Cyclomatic Complexity
        
        **What it is**: A single method has more than 15 independent pathways, or fewer paths whose interaction is still difficult to explain and verify.
        
        **How to detect**:
        - Count: start at 1, add 1 for every `if`, `for`, `foreach`, `while`, `do`, `case`, and `??`.
        - Run a complexity tool (`lizard -C 15`, `radon`, ESLint `complexity`, Roslyn CA1502) — see `../tooling/commands.md`.
        - Use the project's configured metric when it has one; otherwise use 15 as a review trigger.
        
        **Why it's bad**:
        - Makes behavior and test coverage harder to reason about when paths interact.
        - Each new branch requires another unit test.
        - Often indicates mixed responsibilities, nested decisions, or duplicated conditions.
        
        **How to fix**:
        - Extract cohesive sections into helpers.
        - Replace chained Boolean checks with a parser that returns the validated value.
        - Introduce Parameter Objects for clusters of related arguments.
        
        **Example**:
        ```csharp
        // Smell: validation, parsing, policy, and persistence branches are mixed
        public async Task<ActionResult> Post(ReservationDto dto)
        {
            if (dto is null) throw new ArgumentNullException(nameof(dto));
            if (!DateTime.TryParse(dto.At, out var d)) return new BadRequestResult();
            if (dto.Email is null) return new BadRequestResult();
            if (dto.Quantity < 1) return new BadRequestResult();
            // ...more branches, plus a ?? that also counts...
        }
        
        // Fixed: extract to a Validate method that returns the domain object
        public async Task<ActionResult> Post(ReservationDto dto)
        {
            if (dto is null) throw new ArgumentNullException(nameof(dto));
            Reservation? r = dto.Validate();
            if (r is null) return new BadRequestResult();
            // remaining logic operates on a guaranteed-valid Reservation
        }
        ```
        
        ---
        
        ## D2: Unfocused Method
        
        **What it is**: A method that mixes responsibilities, abstraction levels, or effects so its purpose cannot be summarized precisely.
        
        **How to detect**:
        - Name the distinct reasons the method might change.
        - Look for interleaved validation, policy, persistence, formatting, or transport logic.
        - Check whether the reader must jump between unrelated concepts to explain the flow.
        
        **Why it's bad**:
        - Mixed responsibilities make changes spread and cause unrelated behavior to regress.
        - Length alone is not the problem: a long cohesive transformation may be clearer than many tiny helpers.
        
        **How to fix**:
        - Extract a boundary only when it represents a meaningful responsibility or effect.
        - Preserve cohesive algorithms and avoid helper chains that merely move lines elsewhere.
        
        ---
        
        ## D3: Too Many Variables
        
        **What it is**: A method coordinates too many unrelated values, mutable states, or dependencies at once.
        
        **How to detect**:
        - Tally every local variable, every parameter, every class field or property touched by the method body.
        - Group the names by domain concept; the smell is many interacting groups, not a universal count.
        
        **Why it's bad**:
        - Unrelated live values make invariants and update ordering difficult to track.
        - Often predicts bugs, because the programmer has already lost track.
        
        **How to fix**:
        - Group related parameters into a Parameter Object.
        - Split the method so each smaller method handles fewer names.
        - Push computed data further along a sequential composition instead of holding it in local state.
        
        ---
        
        ## D4: Feature Envy
        
        **What it is**: A method — often `static` — that reads one parameter's state but ignores its own class's members.
        
        **How to detect**:
        - Method takes a type as a parameter and uses only that parameter's properties.
        - Compiler or analyser suggests `static` (e.g. C# rule CA1822: *Mark members as static*).
        - Asking "what does this operate on?" yields a different class than the one it lives in.
        
        **Why it's bad**:
        - Couples two classes through a third location.
        - Often signals that an abstraction has been split in the wrong place.
        
        **How to fix**:
        - Move the method onto the class whose features it envies.
        - If the new member takes no input, has no preconditions, and cannot throw, make it a property (per .NET design guidelines).
        - Keep it `internal` first; widen visibility only when justified.
        
        **Example**:
        ```csharp
        // Smell: static helper envies ReservationDto
        private static bool IsValid(ReservationDto dto)
        {
            return DateTime.TryParse(dto.At, out _)
                && !(dto.Email is null)
                && 0 < dto.Quantity;
        }
        
        // Fixed: moved onto ReservationDto as a property
        internal bool IsValid
        {
            get
            {
                return DateTime.TryParse(At, out _)
                    && !(Email is null)
                    && 0 < Quantity;
            }
        }
        ```
        
        ---
        
        ## D5: Lost in Translation
        
        **What it is**: A helper that abstracts too aggressively, forcing callers to redo work the helper already did.
        
        **How to detect**:
        - The helper returns `bool` but the caller later needs the parsed value anyway.
        - The caller uses the null-forgiving operator `!` (or equivalent) to bypass compiler checks that the helper invalidated.
        - Data is parsed, discarded, and re-parsed downstream.
        
        **Why it's bad**:
        - Duplicates work.
        - Breaks compiler flow analysis (forces null-forgiving operators).
        - Signals a weak abstraction: too much eliminated, too little amplified.
        
        **How to fix**:
        - Change the signature to return the stronger type (e.g. `Reservation?` instead of `bool`).
        - Adopt Parse-Don't-Validate (covered in `encapsulation/`): projects DTO input into a domain object if preconditions hold.
        
        ---
        
        ## D6: Nested Composition Hiding Side Effects
        
        **What it is**: A Query-looking method that performs side effects inside nested object graphs.
        
        **How to detect**:
        - Signature reads like a predicate (`Task<bool> Check(Reservation r)`) but the implementation also writes to a database or sends an email.
        - X-ing out the method name leaves a signature that suggests asking, not acting.
        - Calling code uses the return value only to choose an HTTP status — yet data is saved.
        
        **Why it's bad**:
        - Violates Command Query Separation.
        - Cyclomatic complexity underestimates the real load because hidden effects add chunks.
        - Hidden effects make the signature an unreliable guide to behaviour and increase the state a reader must reconstruct.
        
        **How to fix**:
        - Split Commands from Queries; let the Query return data and have the caller perform the side effect.
        - Re-compose sequentially. See `patterns.md`.
        
        ---
        
        ## D7: Low Cohesion Section Inside a Class
        
        **What it is**: A contiguous block inside a class that does not touch any of that class's fields.
        
        **How to detect**:
        - Block uses only local variables and method parameters.
        - Static analyser suggests the enclosing method could be `static`.
        - Surrounding sections *do* use class fields — the low-cohesion block sticks out.
        
        **Why it's bad**:
        - Signals the block belongs somewhere else.
        - These blocks are the safest and most rewarding extraction targets.
        
        **How to fix**:
        - Extract a helper; evaluate whether it belongs on a different class (see D4, Feature Envy).
        - Kent Beck: "Things that change at the same rate belong together."
        
        ---
        
        ## D8: System-Wide Sprawl
        
        **What it is**: A locally simple change requires edits across unrelated modules, adds a dependency cycle, duplicates an existing rule, or extends a temporary migration path.
        
        **How to detect**:
        - Inspect dependency direction and cycles.
        - Search for equivalent rules or abstractions before adding another.
        - Review change history for files repeatedly modified together.
        - Identify flags, adapters, and parallel implementations without a removal condition.
        - Cycle detection, hotspot, and change-coupling commands: `../tooling/commands.md`.
        
        **Why it's bad**:
        - Local method metrics can stay green while the architecture becomes a Big Ball of Mud.
        - Future changes lose a predictable home and accumulate more cross-module coordination.
        
        **How to fix**:
        - Restore a clear ownership boundary and dependency direction.
        - Consolidate duplicated rules.
        - Complete or remove stale migration paths.
        - Add an architecture check when the constraint is stable and mechanically expressible.
        
        ---
        
        ## D9: Orphaned Generated Code
        
        **What it is**: Unused helpers, exports, parallel implementations, and stale scaffolding left behind by abandoned implementation attempts. Agents produce these at high rate.
        
        **How to detect**:
        - Dead-code tools per ecosystem — see `../tooling/commands.md` (`knip`, `vulture`, Roslyn unused-member rules, `cargo-udeps`, etc.).
        - For a single symbol: `rg 'SymbolName'` returning only the definition means it is dead.
        - Parallel implementations: two modules that claim the same responsibility, only one of which is wired in.
        
        **Why it's bad**:
        - Inflates the codebase readers and tools must process.
        - Duplicates rules and invites divergent edits to the wrong copy.
        - Hides the real ownership boundary under abandoned scaffolding.
        
        **How to fix**:
        - Delete — deletion is a feature.
        - If kept deliberately, it needs an owner and exit condition (see `foundations/rules.md`, Recording Debt).
        
        ---
        
        ## Quick Detection Table
        
        | ID | Smell | Key Indicator |
        |----|-------|---------------|
        | D1 | High cyclomatic complexity | Branch count + 1 > 15, or paths interact opaquely |
        | D2 | Unfocused method | Mixed responsibilities, levels, or effects |
        | D3 | Too much interacting state | Too many unrelated live concepts |
        | D4 | Feature envy | Uses only one parameter's state; wants to be `static` |
        | D5 | Lost in translation | Returns `bool` but caller re-parses |
        | D6 | Nested side-effect Query | Predicate-shaped signature that secretly writes |
        | D7 | Low-cohesion block | Section uses no class fields |
        | D8 | System-wide sprawl | Cycles, duplicated rules, broad change surface, stale migration paths |
        | D9 | Orphaned generated code | Unused helpers/exports; `rg` hits only the definition |
        
    • encapsulation
      • examples.md 7.2 KB
        # Encapsulation Examples
        
        Curated C# examples demonstrating always-valid objects, parse-don't-validate, and DTO-to-domain conversion.
        
        ## Bad Examples
        
        ### Anemic Domain Model — No Guards in the Constructor
        
        ```csharp
        public Reservation(DateTime at, string email, string name, int quantity)
        {
            At = at;
            Email = email;
            Name = name;
            Quantity = quantity;
        }
        ```
        
        **Problems**:
        - A caller can construct `new Reservation(..., quantity: -3)`.
        - Downstream code must defensively re-check `Quantity > 0`, `Email != null`, etc.
        - The maintenance programmer has no guarantees just by looking at the type.
        
        ### Exclamation-Mark Suppression — Compile-Time Error Traded for Runtime Crash
        
        ```csharp
        var r = new Reservation(
            DateTime.Parse(dto.At!, CultureInfo.InvariantCulture),
            dto.Email!,
            dto.Name!,
            dto.Quantity);
        ```
        
        **Problems**:
        - The `!` operator tells the compiler to stop warning, but it does not make the value non-null.
        - If `dto.At` is null at runtime, `DateTime.Parse` throws — a `NullReferenceException` or `ArgumentNullException` becomes a 500 Internal Server Error.
        - A compile-time error traded for a runtime exception is a poor trade-off.
        
        ### Boolean Validate — Throws Away Parsing Work
        
        ```csharp
        internal bool IsValid()
        {
            if (!DateTime.TryParse(At, out _)) return false;
            if (Email is null) return false;
            if (Quantity < 1) return false;
            return true;
        }
        
        // caller
        if (!dto.IsValid()) return new BadRequestResult();
        var r = new Reservation(
            DateTime.Parse(dto.At!, CultureInfo.InvariantCulture),  // parse AGAIN
            dto.Email!,                                             // suppress AGAIN
            dto.Name ?? "",
            dto.Quantity);
        ```
        
        **Problems**:
        - The parse happens twice — once in `IsValid`, once at construction.
        - The compiler still forces `!` suppressions, because the Boolean return type does not carry the null-safety information forward.
        - Nothing prevents another caller from skipping `IsValid` entirely.
        
        ## Good Examples
        
        ### Always-Valid Constructor with Guard Clause
        
        ```csharp
        public Reservation(
            DateTime at,
            string email,
            string name,
            int quantity)
        {
            if (quantity < 1)
                throw new ArgumentOutOfRangeException(
                    nameof(quantity),
                    "The value must be a positive (non-zero) number.");
            At = at;
            Email = email;
            Name = name;
            Quantity = quantity;
        }
        ```
        
        **Why it works**:
        - Impossible to construct a `Reservation` with a non-positive quantity.
        - Combined with non-nullable reference types, `At`, `Email`, and `Name` are guaranteed populated too.
        - Every downstream method that receives a `Reservation` can dispense with defensive coding.
        
        ### Parse, Don't Validate — DTO Returns a Typed Domain Object
        
        ```csharp
        // On ReservationDto
        internal Reservation? Validate()
        {
            if (!DateTime.TryParse(At, out var d))
                return null;
            if (Email is null)
                return null;
            if (Quantity < 1)
                return null;
            return new Reservation(d, Email, Name ?? "", Quantity);
        }
        ```
        
        **Why it works**:
        - The method signature `Reservation? Validate()` is the abstraction: "does dto represent a valid reservation?".
        - Parsing happens exactly once; the typed result carries the validity forward.
        - Postel's Law: a null `Name` is liberally converted to `""`; a null `Email` or unparseable `At` is rejected.
        - The compiler's nullable-reference-types analyser forces the caller to handle the null case.
        
        ### Controller Using the Parsed Domain Object
        
        ```csharp
        public async Task<ActionResult> Post(ReservationDto dto)
        {
            if (dto is null)
                throw new ArgumentNullException(nameof(dto));
        
            Reservation? r = dto.Validate();
            if (r is null)
                return new BadRequestResult();
        
            var reservations = await Repository
                .ReadReservations(r.At)
                .ConfigureAwait(false);
            int reservedSeats = reservations.Sum(x => x.Quantity);
            if (10 < reservedSeats + r.Quantity)
                return new StatusCodeResult(
                    StatusCodes.Status500InternalServerError);
        
            await Repository.Create(r).ConfigureAwait(false);
            return new NoContentResult();
        }
        ```
        
        **Why it works**:
        - No `!` suppressions anywhere — `r.At`, `r.Email`, `r.Quantity` are all typed.
        - Cyclomatic complexity is low (4); the method fits in your brain.
        - A `null` from `Validate` becomes a 400 Bad Request; a valid `Reservation` flows to the repository.
        
        ### Parametrised Test Triangulating the `quantity` Invariant
        
        ```csharp
        [Theory]
        [InlineData( 0)]
        [InlineData(-1)]
        public void QuantityMustBePositive(int invalidQantity)
        {
            Assert.Throws<ArgumentOutOfRangeException>(
                () => new Reservation(
                    new DateTime(2024, 8, 19, 11, 30, 0),
                    "mail@example.com",
                    "Marie Ilsøe",
                    invalidQantity));
        }
        ```
        
        **Why it works**:
        - Two `[InlineData]` cases document that zero and negative are both invalid — consensus on "is zero a natural number?" varies, so the test pins the decision.
        - Asserts only that *an* `ArgumentOutOfRangeException` is thrown; no brittle coupling to the exception message.
        - Drives the constructor's guard clause via Red → Green → Refactor.
        
        ## Refactoring Walkthrough
        
        ### Before — Controller Carrying All Validation Inline
        
        ```csharp
        public async Task<ActionResult> Post(ReservationDto dto)
        {
            if (dto is null)
                throw new ArgumentNullException(nameof(dto));
            if (!DateTime.TryParse(dto.At, out var d))
                return new BadRequestResult();
            if (dto.Email is null)
                return new BadRequestResult();
            if (dto.Quantity < 1)
                return new BadRequestResult();
            var r =
                new Reservation(d, dto.Email, dto.Name ?? "", dto.Quantity);
            await Repository.Create(r).ConfigureAwait(false);
            return new NoContentResult();
        }
        ```
        
        ### After — Parse Extracted into DTO, Invariant Moved into Domain Constructor
        
        ```csharp
        // ReservationDto.cs
        internal Reservation? Validate()
        {
            if (!DateTime.TryParse(At, out var d)) return null;
            if (Email is null) return null;
            if (Quantity < 1) return null;
            return new Reservation(d, Email, Name ?? "", Quantity);
        }
        
        // Reservation.cs
        public Reservation(DateTime at, string email, string name, int quantity)
        {
            if (quantity < 1)
                throw new ArgumentOutOfRangeException(
                    nameof(quantity),
                    "The value must be a positive (non-zero) number.");
            At = at; Email = email; Name = name; Quantity = quantity;
        }
        
        // ReservationsController.cs
        public async Task<ActionResult> Post(ReservationDto dto)
        {
            if (dto is null)
                throw new ArgumentNullException(nameof(dto));
            Reservation? r = dto.Validate();
            if (r is null)
                return new BadRequestResult();
            await Repository.Create(r).ConfigureAwait(false);
            return new NoContentResult();
        }
        ```
        
        ### Changes Made
        
        1. Pulled parsing out of the controller into `ReservationDto.Validate()` — the controller no longer knows about date formats or null-email details.
        2. Moved the `quantity < 1` invariant from the controller into the `Reservation` constructor — any future caller (not just this controller) is protected.
        3. Changed `Validate` to return `Reservation?` instead of `bool` — parse-don't-validate. Parsing happens once; no `!` suppressions remain.
        4. Controller cyclomatic complexity drops from 6 to 4; `Reservation` is now always-valid.
        
      • knowledge.md 3.5 KB
        # Encapsulation Knowledge
        
        Core concepts for protecting objects from ever existing in an invalid state.
        
        ## Overview
        
        Encapsulation is not merely private fields behind getters and setters. The real idea is that an object guarantees it will never be in an invalid state, and the interaction between object and caller obeys a contract of pre- and postconditions.
        
        ## Key Concepts
        
        ### Encapsulation as Contract
        
        Interact with an object without intimate knowledge of its implementation, via preconditions (caller's responsibilities) and postconditions (object's guarantees). Together they form invariants. This lets you refactor without breaking callers and replace many implementation details with a simpler contract that fits in short-term memory.
        
        ### Always-Valid and Protection of Invariants
        
        Reduced to its essence: an object can never be in an invalid state. If initialisation succeeds, the object is valid — downstream code dispenses with defensive re-checks. Immutable objects are attractive because validity is established once, in the constructor. Every mutating operation must preserve validity.
        
        ### DTO vs Domain Model
        
        A Data Transfer Object carries input across a boundary with nullable, unvalidated fields. A Domain Model object is always-valid and encodes business rules in its types. The DTO's purpose ends the moment it has been parsed into a Domain Model.
        
        ### Parse, Don't Validate
        
        Instead of an `IsValid` Boolean, a parser consumes less-structured input and produces more-structured output (or failure). Validation alone discards information: the caller is told "yes" and must re-parse. A parser projects into a stronger representation that carries validity forward.
        
        ### Explicit Boundary Compatibility (Postel's Law reframed)
        
        Compatibility is a deliberate, tested contract decision — not an unconditional virtue of "liberal acceptance". Parse untrusted input into an explicit supported form; accept only the shapes the public contract defines; reject the rest. Do not silently coerce malformed or invented shapes merely to be "liberal".
        
        ### Natural Numbers as Type-Level Constraints
        
        Use the type system or constructor guards to express natural numbers, non-null strings, valid dates — not just any `int` or `string`. Signed ints allow zero and negatives; for a reservation quantity, neither is correct.
        
        ## Terminology
        
        | Term | Definition |
        |------|------------|
        | Invariant | Condition that is always true for a valid object |
        | Precondition / Postcondition | Caller obligation / object guarantee |
        | Guard Clause | Early check that rejects invalid input |
        | DTO | Nullable, unvalidated wire format |
        | Domain Model | Object that encodes business rules in its type |
        | Postel's Law (reframed) | Accept only deliberate, tested compatibility; reject malformed input |
        
        ## Common Misconceptions
        
        - **Myth**: Encapsulation means private fields with getters and setters.
          **Reality**: That is access control. Encapsulation is protection of invariants.
        
        - **Myth**: The caller is responsible for making sure the data is valid before handing it to a domain object.
          **Reality**: The object knows best what "valid" means; it should reject invalid input itself.
        
        - **Myth**: A `ReservationDto` and a `Reservation` can be the same class.
          **Reality**: The DTO is a wire format with nullable fields; the domain object is always-valid.
        
        - **Myth**: Throwing `NullReferenceException` is fine — it's still an exception.
          **Reality**: `ArgumentNullException` names the argument; `NullReferenceException` carries nothing useful.
        
      • rules.md 6.8 KB
        # Encapsulation Rules
        
        Actionable rules for protecting invariants, validating input, and converting DTOs into domain objects.
        
        ## Core Rules
        
        ### 1. A Domain Object Must Never Exist in an Invalid State
        
        Reduced to its essence, encapsulation guarantees the object can never be in an invalid state. The object — not the caller — is responsible for enforcing this.
        
        - Every constructor must reject inputs that violate invariants.
        - Every mutating operation (if any) must preserve invariants.
        - Prefer immutability: validity only needs to be established once.
        
        **Example**:
        ```csharp
        // Bad — caller is trusted to pass valid data
        public Reservation(DateTime at, string email, string name, int quantity)
        {
            At = at; Email = email; Name = name; Quantity = quantity;
        }
        
        // Good — constructor enforces the invariant
        public Reservation(DateTime at, string email, string name, int quantity)
        {
            if (quantity < 1)
                throw new ArgumentOutOfRangeException(
                    nameof(quantity),
                    "The value must be a positive (non-zero) number.");
            At = at; Email = email; Name = name; Quantity = quantity;
        }
        ```
        
        ### 2. Validate at Construction, Not at Each Call Site
        
        If an object has already been validated at construction, downstream code can dispense with defensive coding. Re-checking invariants at every call site is wasted work and an invitation for drift.
        
        - Do not write `if (reservation.Quantity > 0)` in callers — the type already guarantees it.
        - The maintenance programmer should not have to do detective work to answer "is `Quantity` a natural number?".
        
        ### 3. Parse, Don't Validate
        
        Do not return a Boolean `IsValid`. Return the parsed, typed value — or a null/Maybe indicating failure. A parser consumes less-structured input and produces more-structured output.
        
        - A `Validate()` method on a DTO should return `Reservation?`, not `bool`.
        - Only construct the Domain Model once all preconditions are met.
        - Callers branch on the return value; they never re-parse.
        
        **Example**:
        ```csharp
        // Bad — Boolean throws away information
        internal bool IsValid() { /* ... */ }
        // caller must re-parse At, re-check Email, etc.
        
        // Good — parse once, return the typed result
        internal Reservation? Validate()
        {
            if (!DateTime.TryParse(At, out var d)) return null;
            if (Email is null) return null;
            if (Quantity < 1) return null;
            return new Reservation(d, Email, Name ?? "", Quantity);
        }
        ```
        
        ### 4. Be Explicit at Boundaries: Accept Supported Compatibility, Reject the Rest
        
        Parse untrusted input into an explicit supported form. Do not silently coerce malformed or invented shapes merely to be "liberal". Compatibility is a deliberate product/API decision with tests, not a universal instruction to accept more.
        
        - Convert a missing `Name` to `""` only when the public contract explicitly defines that compatibility.
        - Reject a missing `Email` — you cannot contact the guest without it.
        - Reject a non-parseable `At` — there is no meaningful reservation without a date.
        
        ### 5. Throw `ArgumentNullException`, Not `NullReferenceException`
        
        A `NullReferenceException` carries no useful information. An `ArgumentNullException` carries the name of the argument that was null. Write explicit null guards.
        
        **Example**:
        ```csharp
        public async Task<ActionResult> Post(ReservationDto dto)
        {
            if (dto is null)
                throw new ArgumentNullException(nameof(dto));
            // ...
        }
        ```
        
        ### 6. Return 400 Bad Request for Invalid HTTP Input
        
        At an HTTP boundary, invalid input is a client error, not a server error. Don't let an unhandled exception leak out as 500.
        
        - Guard Clauses at the controller return `new BadRequestResult()`.
        - Constructor exceptions (e.g. `ArgumentOutOfRangeException`) from the domain object are programming errors — by that point the controller should already have rejected the input.
        
        ### 7. Separate DTO from Domain Model
        
        A DTO is the wire format: nullable fields, string dates, untrusted values. A Domain Model is always-valid. Do not reuse the same class for both.
        
        - `ReservationDto` has `string? At`, `string? Email`, `string? Name`, `int Quantity`.
        - `Reservation` has `DateTime At`, non-null `Email`, non-null `Name`, positive `Quantity`.
        - The only bridge between them is a parse/validate method.
        
        ### 8. Drive Invariants with Parametrised Tests
        
        When you need to triangulate a type — "is zero valid? is -1 valid?" — use a parametrised test with several `[InlineData]` cases rather than one test per input. This both documents the invariant and drives the implementation.
        
        **Example**:
        ```csharp
        [Theory]
        [InlineData( 0)]
        [InlineData(-1)]
        public void QuantityMustBePositive(int invalidQantity)
        {
            Assert.Throws<ArgumentOutOfRangeException>(
                () => new Reservation(
                    new DateTime(2024, 8, 19, 11, 30, 0),
                    "mail@example.com",
                    "Marie Ilsøe",
                    invalidQantity));
        }
        ```
        
        ### 9. Move in Verified Transformations (TPP)
        
        Use the Transformation Priority Premise when incremental transformations make the design safer to reason about. A larger systematic transformation is acceptable when architecture and executable constraints make it clear and verifiable.
        
        - Verify each meaningful checkpoint with the relevant focused checks, then run the required complete gates before acceptance.
        - If evidence contradicts the transformation, back out or revise the plan instead of patching around it.
        
        ## Guidelines
        
        - Prefer a dedicated type (e.g. `NaturalNumber`) over a raw `int` when an invariant applies across many methods.
        - If your language lacks nullable reference types, use a `Maybe<T>` / `Option<T>` container instead of `null`.
        - Don't assert on exception messages — the message is not part of behaviour; coupling tests to it causes needless churn.
        - Follow Red → Green → Refactor: after every green, ask "can I simplify this?".
        
        ## Exceptions
        
        When these rules may be relaxed:
        
        - **Legacy boundaries**: When wrapping a legacy API that already returns half-validated data, a transitional adapter with looser invariants may be justified.
        - **Internal-only types**: If a type is only ever constructed from already-validated data within a single module, its guards can be lighter — but document this.
        - **Performance-critical hot paths**: Where measured profiling shows guard clauses dominate, consider moving validation to the boundary only. This is rare.
        
        ## Quick Reference
        
        | Rule | Summary |
        |------|---------|
        | Always Valid | Invalid construction must throw |
        | Validate at construction | Callers need not defend |
        | Parse, don't validate | Return the typed value, not a Boolean |
        | Explicit boundaries | Accept deliberate compatibility; reject malformed input |
        | ArgumentNullException | Name the null argument |
        | 400 on invalid input | Don't leak exceptions as 500 |
        | DTO ≠ Domain | Separate wire format from domain model |
        | Parametrised tests | Triangulate invariants with `[InlineData]` |
        | TPP | Move in small transformations |
        
    • evolution
      • examples.md 7.2 KB
        # Evolution Examples
        
        Concrete C# examples from the restaurant reservation code base showing feature flags and the Strangler pattern in action.
        
        ## Example 1: Calendar Feature Flag
        
        A calendar feature that took roughly two months of elapsed work. Merged to master continuously throughout, without exposing incomplete behaviour in production.
        
        ### Before (no calendar)
        
        ```csharp
        public IActionResult Get()
        {
            return Ok(new HomeDto { Links = new[]
            {
                CreateReservationsLink()
            } });
        }
        ```
        
        The `home` resource returns just the reservations link.
        
        ### With Feature Flag
        
        ```csharp
        public IActionResult Get()
        {
            var links = new List<LinkDto>();
            links.Add(CreateReservationsLink());
            if (enableCalendar)
            {
                links.Add(CreateYearLink());
                links.Add(CreateMonthLink());
                links.Add(CreateDayLink());
            }
            return Ok(new HomeDto { Links = links.ToArray() });
        }
        ```
        
        ### Wiring the Flag
        
        The flag is wrapped in a class because the built-in ASP.NET DI container won't inject primitive values. Configuration reads it with a default of `false`:
        
        ```csharp
        public HomeController(CalendarFlag calendarFlag)
        {
            if (calendarFlag is null)
                throw new ArgumentNullException(nameof(calendarFlag));
            enableCalendar = calendarFlag.Enabled;
        }
        
        // Startup:
        var calendarEnabled = new CalendarFlag(
            Configuration.GetValue<bool>("EnableCalendar"));
        services.AddSingleton(calendarEnabled);
        ```
        
        ### Flipping the Flag in Tests
        
        ```csharp
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            if (builder is null)
                throw new ArgumentNullException(nameof(builder));
        
            builder.ConfigureServices(services =>
            {
                services.RemoveAll<IReservationsRepository>();
                services.AddSingleton<IReservationsRepository>(
                    new FakeDatabase());
                services.RemoveAll<CalendarFlag>();
                services.AddSingleton(new CalendarFlag(true));
            });
        }
        ```
        
        ### After the Release
        
        Once the feature was live, the author deleted the `CalendarFlag` class. Every reference failed to compile. He then "leaned on the compiler" to simplify every `if (enableCalendar)` into just the true branch. Final result: no flag, no conditional, all cleaned up.
        
        **Why it works**:
        - Production stayed unaffected because the config value was never set in production — defaults to `false`.
        - Integration tests still drove the new behaviour.
        - Every commit during the weeks of work was deployable.
        - The cleanup at the end was mechanical (compiler-guided).
        
        ---
        
        ## Example 2: Method-Level Strangler — `ReadReservations`
        
        The existing repository interface only supported reading a single date. A new calendar feature needed a date range.
        
        ### Before
        
        ```csharp
        public interface IReservationsRepository
        {
            Task Create(Reservation reservation);
            Task<IReadOnlyCollection<Reservation>> ReadReservations(
                DateTime dateTime);
            Task<Reservation?> ReadReservation(Guid id);
            Task Update(Reservation reservation);
            Task Delete(Guid id);
        }
        ```
        
        ### Step 1: Add the New Overload
        
        ```csharp
        public interface IReservationsRepository
        {
            Task Create(Reservation reservation);
            Task<IReadOnlyCollection<Reservation>> ReadReservations(
                DateTime dateTime);
            Task<IReadOnlyCollection<Reservation>> ReadReservations(
                DateTime min, DateTime max);        // new
            Task<Reservation?> ReadReservation(Guid id);
            Task Update(Reservation reservation);
            Task Delete(Guid id);
        }
        ```
        
        Both implementers (`SqlReservationsRepository` and `FakeDatabase`) got the new method in the same commit so the build stayed green. About 5-10 minutes of work.
        
        ### Step 2: Migrate Callers One at a Time
        
        ```csharp
        var min = res.At.Date;
        var max = min.AddDays(1).AddTicks(-1);
        var reservations = await Repository
            .ReadReservations(min, max)
            .ConfigureAwait(false);
        ```
        
        Each call site was edited in its own commit. At every point, the tree was mergeable.
        
        ### Step 3: Delete the Old Method
        
        ```csharp
        public interface IReservationsRepository
        {
            Task Create(Reservation reservation);
            Task<IReadOnlyCollection<Reservation>> ReadReservations(
                DateTime min, DateTime max);
            Task<Reservation?> ReadReservation(Guid id);
            Task Update(Reservation reservation);
            Task Delete(Guid id);
        }
        ```
        
        The single-date `ReadReservations` was removed from the interface and from every implementer.
        
        **Why it works**:
        - The new signature *weakened* preconditions (a single date is just a range of length 1), so the new method could subsume the old.
        - The compiler caught every unmigrated call site.
        - Each commit was deployable; the work could be paused and resumed.
        
        ---
        
        ## Example 3: Class-Level Strangler — `Occurrence<T>` to `TimeSlot`
        
        A generic `Occurrence<T>` class had been over-engineered. All real uses associated a `DateTime` with a collection of tables, so a concrete `TimeSlot` class would be clearer.
        
        ### Before: the over-abstract generic
        
        ```csharp
        public class Occurrence<T>
        {
            public Occurrence(DateTime at, T value)
            {
                At = at;
                Value = value;
            }
            public DateTime At { get; }
            public T Value { get; }
        }
        
        // A method signature full of nested generics:
        public IEnumerable<Occurrence<IEnumerable<Table>>> Schedule(
            IEnumerable<Reservation> reservations)
        ```
        
        ### Step 1: Introduce `TimeSlot` Beside `Occurrence<T>`
        
        ```csharp
        public class TimeSlot
        {
            public TimeSlot(DateTime at, IReadOnlyCollection<Table> tables)
            {
                At = at;
                Tables = tables;
            }
            public DateTime At { get; }
            public IReadOnlyCollection<Table> Tables { get; }
        }
        ```
        
        Commit and merge — no behaviour change.
        
        ### Step 2: Add a Temporary Bridge
        
        ```csharp
        internal static TimeSlot ToTimeSlot(
            this Occurrence<IEnumerable<Table>> source)
        {
            return new TimeSlot(source.At, source.Value.ToList());
        }
        ```
        
        ### Step 3: Work Around Return-Type Overloading
        
        C# doesn't allow two methods that differ only by return type. The author renamed the old `Schedule` to `ScheduleOcc`, then created a new `Schedule` with the better return type:
        
        ```csharp
        // Temporary rename of the old method:
        public IEnumerable<Occurrence<IEnumerable<Table>>> ScheduleOcc(
            IEnumerable<Reservation> reservations) { /* ... */ }
        
        // New method with the original name and a concrete return type:
        public IEnumerable<TimeSlot> Schedule(
            IEnumerable<Reservation> reservations) { /* ... */ }
        ```
        
        Helper-method signatures were migrated too:
        
        ```csharp
        // Before
        private TimeDto MakeEntry(Occurrence<IEnumerable<Table>> occurrence)
        
        // After
        private static TimeDto MakeEntry(TimeSlot timeSlot)
        ```
        
        ### Step 4: Migrate Callers, Then Delete the Old Class
        
        Callers were migrated through coherent, verified checkpoints. When all callers used `Schedule` / `TimeSlot`:
        
        1. `ScheduleOcc` was deleted.
        2. The `ToTimeSlot` conversion helper was deleted.
        3. The `Occurrence<T>` class was deleted.
        
        Throughout the process, every meaningful checkpoint left the system in a consistent state that could be verified, integrated, and deployed.
        
        **Why it works**:
        - The new class ships first, standalone, with no behaviour change.
        - The temporary conversion helper contains the bridge to a single place that is trivial to delete later.
        - The rename (`ScheduleOcc`) is explicit scaffolding, clearly short-lived.
        - Every intermediate state is mergeable.
        
      • knowledge.md 2.6 KB
        # Evolution Knowledge
        
        Core concepts for changing running software safely — augmenting code without breaking it.
        
        ## Overview
        
        Existing code bases need new behaviour, modified behaviour, and bug fixes. Evolution focuses on the first two: take small steps that always leave the system in a consistent, deployable state — even when the work spans weeks.
        
        ## Key Concepts
        
        ### Augmenting vs. Modifying In-Place
        
        Augmenting appends new code beside existing code; modifying in-place edits a live method while callers depend on it. Prefer side-by-side replacement when it reduces blast radius, preserves rollback, or makes verification clearer. Rule of thumb: *for any significant change, don't make it in-place; make it side-by-side.*
        
        ### Feature Flags
        
        A configuration value that hides incomplete behaviour from production users while the code ships. Decouples **deploy** from **release**. Default off in production; override on in integration tests; delete the flag once live. Details in `rules.md` and `patterns.md`.
        
        ### Strangler Pattern
        
        Add the new implementation next to the old, migrate callers one at a time, delete the old when nothing calls it. Applies at method, class, and architectural scale. See `patterns.md`.
        
        ### Semantic Versioning and Deprecation
        
        `major.minor.patch`: major = breaking, minor = feature, patch = fix. Before removing a public API, mark it deprecated so callers get a compiler warning; delete only at the next major version. Details in `rules.md`.
        
        ### Dependency-Update Rhythm
        
        Update packages and platform versions on a regular schedule so each step stays small. Same reasoning applies to TLS certificates, domain names, and backup-restore drills.
        
        ### Conway's Law (as design advice)
        
        Expect an interface to form at every team boundary; design deliberately there. The communication structure of the organisation will leak into the architecture whether or not you plan for it — so make those boundaries explicit rather than accidental. This is design advice an agent can act on (name and own the boundary), not org-restructuring advice.
        
        ## Common Misconceptions
        
        - **Myth**: Long-lived feature branches are fine if you rebase frequently.
          **Reality**: They lead to merge hell. Hide the feature behind a flag and merge to mainline instead.
        
        - **Myth**: Strangler is only for replacing whole legacy systems.
          **Reality**: It works at method and class level too — whenever side-by-side migration improves verification, rollback, or blast-radius control.
        
        - **Myth**: If the compiler doesn't complain, you can skip deprecation.
          **Reality**: External callers get no compile errors until they upgrade. Deprecate first, delete later.
        
      • patterns.md 9.7 KB
        # Evolution Patterns
        
        Reusable patterns for changing live code without breaking callers or stalling integration.
        
        ## Pattern: Feature Flag (Calendar Flag Variant)
        
        ### Intent
        
        Decouple **deploying** code from **releasing** behaviour. Ship an incomplete feature to production behind a configuration switch that is off by default, and flip it on once the feature is ready.
        
        ### When to Use
        
        - The work needs staged exposure, crosses risky integration boundaries, or must remain mergeable before release.
        - You practice Continuous Integration or Continuous Deployment and can't justify a long-lived branch.
        - You need to exercise the new behaviour with integration tests before it's user-visible.
        
        ### Structure
        
        ```csharp
        // 1. Wrapper class (needed for DI containers that reject raw primitives).
        public sealed class CalendarFlag
        {
            public CalendarFlag(bool enabled) { Enabled = enabled; }
            public bool Enabled { get; }
        }
        
        // 2. Config reads the flag; defaults to false if absent.
        var calendarEnabled = new CalendarFlag(
            Configuration.GetValue<bool>("EnableCalendar"));
        services.AddSingleton(calendarEnabled);
        
        // 3. Code paths gate the new behaviour on the flag.
        public IActionResult Get()
        {
            var links = new List<LinkDto>();
            links.Add(CreateReservationsLink());
            if (enableCalendar)
            {
                links.Add(CreateYearLink());
                links.Add(CreateMonthLink());
                links.Add(CreateDayLink());
            }
            return Ok(new HomeDto { Links = links.ToArray() });
        }
        
        // 4. Tests override the config to turn the feature on.
        services.RemoveAll<CalendarFlag>();
        services.AddSingleton(new CalendarFlag(true));
        ```
        
        ### Walkthrough
        
        1. Introduce the flag class or config key with a default of "feature off" in production.
        2. Wrap new code paths in `if (flag) { ... }`; the rest of the app stays oblivious.
        3. Override the flag to `true` in integration tests and in your local/dev config.
        4. When the feature is live in production, delete the flag class. The compiler will catch every `if` to simplify; keep only the new branch.
        
        ### Benefits
        
        - Keeps CI healthy on multi-week work.
        - Decouples deploy from release — code can ship before the feature is announced.
        - Integration tests stay meaningful throughout.
        
        ### Considerations
        
        - A flag left in place past its useful life becomes a smell. Delete promptly.
        - Don't use flags for rollback — that's a deployment concern.
        - Do not add a flag when the change has low blast radius, can be verified before merge, and does not need deploy/release separation.
        
        ---
        
        ## Pattern: Method-Level Strangler
        
        ### Intent
        
        Replace a method (often an interface method) when in-place editing would break too many call sites at once. Let the old and new methods coexist, migrate callers gradually, then delete the old method.
        
        ### When to Use
        
        - A method signature needs to change (e.g. wider preconditions, different return type).
        - The method is called from many places and you can't fix them all in one commit.
        - You want every intermediate commit to be deployable.
        
        ### Structure
        
        ```csharp
        // Step 1. Start state.
        public interface IReservationsRepository {
            Task<IReadOnlyCollection<Reservation>> ReadReservations(DateTime dateTime);
            // ...
        }
        
        // Step 2. Add the new method beside the old. Implement on every concrete
        //         class in the same commit so the build stays green.
        public interface IReservationsRepository {
            Task<IReadOnlyCollection<Reservation>> ReadReservations(DateTime dateTime);
            Task<IReadOnlyCollection<Reservation>> ReadReservations(
                DateTime min, DateTime max);      // new
            // ...
        }
        
        // Step 3. Migrate callers one at a time; commit after each.
        var reservations = await Repository
            .ReadReservations(min, max)           // now using the new overload
            .ConfigureAwait(false);
        
        // Step 4. When no callers remain, delete the old method from the
        //         interface and every implementer.
        ```
        
        ### Walkthrough
        
        1. Add the new method to the interface and implement it in every concrete class. Options: full implementation everywhere in one commit; `NotImplementedException` stubs then TDD; or concrete classes first, interface last.
        2. With the new method unused, commit and merge.
        3. Edit call sites one at a time; commit per call site.
        4. When the old method has no callers, delete it from the interface and every implementer (the compiler won't catch this — you must remember).
        
        ### Benefits
        
        - Every commit is deployable.
        - Work can be interleaved with unrelated tasks.
        
        ### Considerations
        
        - Prefer weakening preconditions when designing the new signature (range subsumes single-date).
        - Don't forget to delete the old method from every implementer after the interface update.
        
        ---
        
        ## Pattern: Class-Level Strangler
        
        ### Intent
        
        Replace a class when in-place refactoring would take too long or break too much at once. Add a new class, optionally with a temporary conversion helper, migrate callers, then delete the old class.
        
        ### When to Use
        
        - A class was over-engineered (e.g. unnecessary generics) and a simpler type would clarify the code.
        - A class's responsibility has drifted and you want to split or rename it without a big-bang edit.
        - Multiple callers depend on the class and must be migrated individually.
        
        ### Structure
        
        ```csharp
        // Starting point: the over-engineered generic.
        public class Occurrence<T> { /* DateTime At, T Value */ }
        
        // Step 1. Introduce the concrete replacement beside the old class.
        public class TimeSlot { /* DateTime At, IReadOnlyCollection<Table> Tables */ }
        
        // Step 2. Temporary bridge between old and new.
        internal static TimeSlot ToTimeSlot(
            this Occurrence<IEnumerable<Table>> source) =>
            new TimeSlot(source.At, source.Value.ToList());
        
        // Step 3. If signatures collide (C# has no return-type overloading),
        //         rename the old method temporarily.
        public IEnumerable<Occurrence<IEnumerable<Table>>> ScheduleOcc( /* old */ );
        public IEnumerable<TimeSlot>                       Schedule   ( /* new */ );
        
        // Step 4. Migrate callers one at a time, then delete ScheduleOcc,
        //         ToTimeSlot, and Occurrence<T>.
        ```
        
        ### Walkthrough
        
        1. Add the new class. Commit and merge — no behaviour change.
        2. If helpful, add a conversion method between old and new, marked `internal` to bound its scope.
        3. If language constraints prevent a one-to-one replacement (C# has no return-type overloading), rename the old method to a temporary name (e.g. `ScheduleOcc`) and give the original name to the new method.
        4. Migrate callers in coherent, verifiable batches. A systematic transformation may cover many callers at once when the mapping and oracle are trustworthy.
        5. When the old class has no callers, delete it along with the conversion helper.
        
        ### Benefits
        
        - Each meaningful checkpoint is verified and leaves a clear recovery path.
        - Old and new can run side by side indefinitely.
        - Teammates can keep working during the migration.
        
        ### Considerations
        
        - Conversion helpers and temporary renames are scaffolding — remove them at the end.
        - If migration remains incomplete across integrations, document it and prevent new uses of the old class.
        
        ---
        
        ## Pattern: Expand-Contract (Database and Schema Migration)
        
        ### Intent
        
        Change a live database schema (or any durable store contract) without a single-step destructive migration. Expand the schema, migrate readers and writers, then contract away the old shape — each stage separately deployable and reversible.
        
        ### When to Use
        
        - Adding, renaming, splitting, or removing a column, table, field, or index on a production schema.
        - Any schema change where a mixed fleet of old and new application versions may run concurrently.
        - Backfills that must complete before the old representation can be removed.
        
        ### Structure
        
        Three stages, each its own deployable unit:
        
        1. **Expand** — add the new representation (nullable column, new table, new field) without removing the old. Dual-write or backfill so both shapes hold the data.
        2. **Migrate readers** — point all readers (and eventually writers) at the new representation; verify with a migration rehearsal against a production-like copy.
        3. **Contract** — remove the old column, path, or dual-write once nothing depends on it.
        
        Never combine expand and contract into one destructive step on a live schema.
        
        ### Walkthrough
        
        1. Ship the expand migration: new nullable column / table / field. Application still reads the old path; optionally dual-writes.
        2. Backfill historical rows; monitor dual-write consistency.
        3. Deploy readers that prefer the new field (with fallback if needed), then writers that only write the new field.
        4. Confirm no remaining readers of the old path (search, metrics, dual-write lag at zero).
        5. Ship the contract migration: drop the old column/path. Keep each step reversible until the contract is proven.
        
        ### Benefits
        
        - Old and new application versions can coexist during rollout.
        - Each stage is a recovery point; a bad reader deploy rolls back without schema loss.
        - Backfill pressure is visible and measurable before the old shape disappears.
        
        ### Considerations
        
        - Dual-write windows are temporary debt — record an owner and exit condition.
        - Rehearse against a production-like copy before the expand or contract that cannot be trivially undone.
        - Coordinate with Expand-Contract at the API layer when external clients also see the shape change.
        
        ---
        
        ## Pattern Selection Guide
        
        | Situation | Recommended Pattern |
        |-----------|--------------------|
        | Adding behaviour that needs staged exposure or deploy/release separation | Feature Flag |
        | Splitting deploy from release | Feature Flag |
        | Changing a single method signature | Method-Level Strangler |
        | Replacing one class with another | Class-Level Strangler |
        | Replacing a subsystem or legacy module | Strangler at architectural scale |
        | Changing a live database schema | Expand-Contract |
        | Removing a public API | Deprecate first, then Strangler-style removal at next major version |
        
      • rules.md 5.6 KB
        # Evolution Rules
        
        Actionable rules for changing live software without breaking callers or stalling integration.
        
        ## Core Rules
        
        ### 1. Use Feature Flags to Split Deploy from Release
        
        When incomplete user-visible behavior must be deployed or integrated safely, hide it behind a feature flag. Use the flag because of release and blast-radius needs, not because the work exceeded a wall-clock threshold.
        
        - Wire the flag through configuration (appsettings, environment variables, config service).
        - Default to off — missing config must mean "feature disabled".
        - Drive the new behaviour with integration tests that flip the flag on.
        
        **Example**:
        ```csharp
        // In production config, EnableCalendar is absent, so this resolves to false
        var calendarEnabled = new CalendarFlag(
            Configuration.GetValue<bool>("EnableCalendar"));
        services.AddSingleton(calendarEnabled);
        ```
        
        ### 2. Remove Feature Flags After the Release Lands
        
        A flag that outlives its purpose becomes a code smell. Delete the flag class/variable, then lean on the compiler to clean up every branch that referenced it. Deleting code is a feature.
        
        - Schedule the cleanup as part of the release work, not "later".
        - Don't keep flags around "just in case" — rollback is a separate concern (deployment-level, not code-level).
        
        ### 3. Replace Subsystems with the Strangler Pattern
        
        When replacing a widely used method, class, module, or subsystem, prefer the Strangler pattern when side-by-side migration reduces blast radius or makes verification and rollback safer. Do not choose it solely from estimated implementation time.
        
        1. Add the new method/class beside the old.
        2. Migrate callers one at a time, committing after each.
        3. Delete the old code once no caller remains.
        
        - Every commit during this process must leave the system consistent and deployable.
        - Preserve recoverable, verified migration checkpoints appropriate to the system.
        - If two implementations can't legally coexist (e.g. C# return-type overloading), rename the old one temporarily (`ScheduleOcc` vs `Schedule`).
        
        ### 4. Use Semantic Versioning for Anything Others Depend On
        
        `major.minor.patch`:
        
        - **Major** bump for breaking changes (removed API, changed signature, altered behaviour contract).
        - **Minor** bump for new features that don't break callers.
        - **Patch** bump for bug fixes.
        
        Even if you don't publish the version number, think in SemVer terms when reviewing diffs — it clarifies what clients will feel.
        
        ### 5. Give Users Advance Warning of Deprecations
        
        If you must break a public API, deprecate first. For libraries with external consumers, 6+ months of advance warning is reasonable.
        
        - Use the language's deprecation mechanism: `[Obsolete("...")]` in C#, `@Deprecated` in Java.
        - Include a hint in the message pointing to the replacement.
        - Only delete the deprecated API at the next major version.
        
        **Example**:
        ```csharp
        [Obsolete("Use Get method with restaurant ID.")]
        [HttpGet("calendar/{year}/{month}")]
        public Task<ActionResult> LegacyGet(int year, int month)
        ```
        
        ### 6. Update Dependencies on a Schedule
        
        Don't wait for a CVE or a forced upgrade. Pick a rhythm — weekly, every sprint, every other month — and run updates then, regardless of whether anything "needs" updating.
        
        - Attach the update to an existing cadence (e.g. first day of a new sprint).
        - Never make it the *last* thing in a sprint — it'll be dropped for something urgent.
        - Same discipline applies to language/runtime versions, TLS certificates, domain renewals, and backup-restore drills.
        
        For agent-authored dependency changes, verify package identity, provenance, locked version, compatibility, and removal of obsolete transitive workarounds. See `../agent-native/hallucination-debugging.md`.
        
        ### 7. Be Aware of Conway's Law When Designing
        
        If your system crosses a team boundary, expect an interface to form there, whether you plan one or not. If you want a particular architecture, organise teams to match it.
        
        - A system that crosses three teams will have three modules whether the design document says so or not.
        - Ad-hoc, entirely oral communication tends to produce spaghetti.
        - Written, asynchronous collaboration (pull requests, reviews) tends to produce cleaner boundaries.
        
        ## Guidelines
        
        - Prefer *weakening preconditions* over adding overloads when strangling a method. A range-of-dates reader can subsume a single-date reader; the reverse is not true.
        - When you add a method to an interface, add it to every implementer in the same commit so the build stays green.
        - Bundle multiple small breaking changes into one major release if that reduces caller pain — but not if each change forces massive rework on its own.
        - Prefer avoiding breaking changes altogether. Stable libraries can live on the same major version for years.
        
        ## Exceptions
        
        - **Monolith with no external API**: Breakage is cheap, so you can be less strict about SemVer. Still think in those terms for your own clarity.
        - **Narrow, easily verified change**: In-place editing is fine when blast radius and rollback are clear; Strangler would add needless machinery.
        - **Emergency CVE**: Update the vulnerable dependency immediately, out of rhythm.
        
        ## Quick Reference
        
        | Rule | Summary |
        |------|---------|
        | Feature-flag incomplete release | Decouple deploy from release when risk requires it |
        | Delete flags promptly | Never let flags rot in the codebase |
        | Strangler for replacements | Add new, migrate, delete old |
        | SemVer breaks = major | Breaking changes require a major bump |
        | Deprecate before delete | Give callers advance warning |
        | Update dependencies regularly | Schedule it; don't wait for pain |
        | Mind Conway's Law | Team structure will show up in the code |
        
    • foundations
      • knowledge.md 4.6 KB
        # Foundations Knowledge
        
        > **Source note:** This book-derived theme includes 2026 editorial reframing for agent-driven development. Agent-specific additions are not from Seemann.
        
        Why understandable code, explicit architecture, and technical-debt control remain essential when agents write much of the implementation.
        
        ## Sustainability
        
        Sustainability is the ability to keep supporting an organisation through code changes as effectively in six months or six years as today. Software becomes expensive when each change requires reconstructing hidden context, crossing unstable boundaries, or working around accumulated compromises.
        
        The central constraint is no longer typing capacity. Agentic development makes implementation abundant while human ownership, architectural judgment, verification, and operational responsibility remain scarce.
        
        ## Code as Liability
        
        Code is useful only through the behavior it provides. Every additional abstraction, branch, dependency, and configuration path creates something that must be understood, tested, secured, upgraded, and eventually removed.
        
        Generated volume therefore is not an asset by itself. The desirable output is a coherent capability with the least accidental machinery and a clear path for future change.
        
        ## Human Comprehension Still Matters
        
        Humans have limited working memory and rely on incomplete mental models. The book uses seven as a memorable symbol for this constraint, not as a universal scientific limit for every dependency, variable, or branch.
        
        Agents have different limits: context can be externalised through files and tools, but long context does not guarantee correct architecture or lifecycle stewardship. Both humans and agents benefit from cohesion, explicit boundaries, good names, executable contracts, and progressive disclosure.
        
        ## Complexity and Big Ball of Mud
        
        Accidental complexity is structure that the problem does not require. It accumulates through:
        
        - responsibilities that change for different reasons living together;
        - implicit dependencies and side effects;
        - duplicated logic and competing abstractions;
        - cycles between modules or layers;
        - temporary migrations, flags, and compatibility paths that never disappear;
        - local fixes that ignore system-wide architecture.
        
        A Big Ball of Mud is the system-level outcome: boundaries stop constraining change, so every feature can touch everything. Small methods do not prevent it. The defence combines local decomposition with module boundaries, dependency direction, verification, and active deletion of obsolete paths.
        
        ## Technical Debt
        
        Technical debt is a deliberate or accidental choice that increases future change cost. Not all debt is forbidden, but invisible debt compounds.
        
        A responsible compromise records:
        
        - what was traded away;
        - why the compromise is acceptable now;
        - what risk it creates;
        - who owns it;
        - the condition or date for repayment.
        
        Agents must not create speculative abstractions, TODOs, disabled checks, or permanent compatibility branches merely because they are cheap to generate.
        
        ## Large Tasks
        
        Task size is not a maintainability metric. Agents can complete migrations and implementations spanning tens of thousands of lines when the work has:
        
        - a coherent target architecture;
        - explicit boundaries and dependency direction;
        - clear acceptance and non-regression criteria;
        - trustworthy automated verification;
        - observable checkpoints and rollback or recovery paths.
        
        The smell is unstructured scope: unclear ownership, mixed concerns, unverifiable behavior, or changes whose architectural effect cannot be explained.
        
        ## Key Principles
        
        | Principle | Meaning |
        |---|---|
        | Code is a liability | Maintain only machinery that earns its lifecycle cost |
        | Optimize for change | Prefer designs that keep future modifications local |
        | Cohesion over arbitrary size | Keep related behavior together and conflicting responsibilities apart |
        | Explicit boundaries | Make dependencies, effects, and ownership visible |
        | Verification is design | Types, tests, schemas, and architecture checks constrain change |
        | Debt must be visible | Temporary compromises need an owner and exit condition |
        | Large is not automatically complex | Architecture and verification matter more than line count |
        
        ## How It Relates To
        
        - **Decomposition** controls complexity inside methods, types, modules, and dependency graphs.
        - **Encapsulation** protects invariants so callers need less defensive knowledge.
        - **API design** makes intended use and side effects explicit.
        - **Evolution** prevents migrations, flags, and compatibility layers from becoming permanent mud.
        - **Agent-native guidance** protects verification integrity and human ownership when generation is cheap.
        
      • rules.md 5.5 KB
        # Foundations Rules
        
        > **Source note:** This book-derived theme includes 2026 editorial reframing for agent-driven development. Agent-specific additions are not from Seemann.
        
        Rules for keeping software understandable, maintainable, and resistant to accidental complexity.
        
        ## Core Rules
        
        ### 1. Write for the Next Reader and Changer
        
        Code is read, reviewed, debugged, and extended far more often than it is authored.
        
        - Prefer plain code over clever compression.
        - Make intent, dependencies, invariants, and side effects discoverable.
        - Optimise for future modification, not generation speed.
        - Treat code that is easy to generate but difficult to own as a liability.
        
        ### 2. Keep Each Unit Conceptually Cohesive
        
        Human working memory is limited, but no universal item count defines good design.
        
        - Give each function, type, module, and service a coherent responsibility.
        - Keep dependencies and interacting concepts few enough to name and explain.
        - Split code when branches, state, effects, or responsibilities interfere with one another.
        - Do not split cohesive logic merely to satisfy a superficial size metric.
        
        ### 3. Make Context Discoverable and Effects Explicit
        
        A reader or agent should be able to find the information required to change a unit safely.
        
        - Avoid ambient mutable state and hidden side effects.
        - Prefer explicit inputs, outputs, dependencies, and ownership boundaries.
        - Keep repository-local architecture, contracts, and verification commands current.
        - Use progressive disclosure: provide a short map to authoritative details instead of duplicating every fact locally.
        
        ### 4. Treat Code as a Liability, Not an Asset
        
        More code means more behavior to understand, verify, secure, and maintain.
        
        - Delete dead, duplicated, speculative, and obsolete code.
        - Reuse an existing coherent abstraction before adding another.
        - Reject boilerplate and generated volume that do not improve the design.
        - Prefer the smallest complete solution, not the smallest diff at the cost of architecture.
        
        ### 5. Optimize for Sustainability
        
        Sustainable software remains affordable to change after years of maintenance.
        
        - Preserve cohesion and explicit boundaries before local delivery pressure erodes them.
        - Treat refactoring, security, architecture, and observability as lifecycle work, not optional polish.
        - Use mechanical checks for stable objective constraints and human judgment for intent and architecture.
        - Record debt deliberately with an owner and removal condition; do not let temporary compromises become invisible defaults.
        
        ### 6. Deliberate Before Accepting, Not Before Generating
        
        Agents can produce large changes quickly. Artificially slowing generation is not the goal.
        
        - Plan large work around stable architectural boundaries and explicit acceptance criteria.
        - Verify meaningful checkpoints and preserve evidence of correctness.
        - Pause or revise the plan when evidence contradicts assumptions.
        - Do not confuse task size with complexity: a large systematic migration may be safer than a small tangled patch.
        
        ## Guidelines
        
        - Prefer established libraries over reinventing solved infrastructure.
        - Re-check conclusions that look obvious but depend on hidden assumptions.
        - Prefer written, versioned rationale over transient conversation for important decisions.
        - Keep terminology consistent across code, tests, documentation, and product language.
        
        ## Default Thresholds (unless project policy overrides)
        
        These are defaults to act on. A repository may override them; when it does, record the override in project agent instructions (`CLAUDE.md` / `AGENTS.md`) so every session uses the same numbers. Measurement commands live in `../tooling/commands.md`.
        
        | Signal | Default |
        |--------|---------|
        | Cyclomatic complexity review trigger | 15 (review above this; project may set stricter) |
        | Inner-loop tests for the touched behaviour | Seconds, not minutes (project-configurable; no universal stopwatch) |
        | Integration with mainline | Integrate/rebase against mainline at least daily when others change the same area |
        | Before writing a new helper | Search for an existing implementation (`rg` by domain term) |
        
        ## Recording Debt
        
        Every deliberate compromise gets a greppable marker at the site:
        
        ```text
        TODO(owner: X, exit: <condition or date>, ref: <issue-link>)
        ```
        
        Or the repository's tracker equivalent with the same three fields (owner, exit condition, reference).
        
        - A TODO without owner **and** exit condition is a review blocker.
        - Sweep markers when touching a file (Boy Scout Rule): close those whose exit condition is met; re-home or escalate the rest.
        - Temporary debt without a marker becomes invisible default — see Core Rule 5.
        
        ## Exceptions
        
        - **Exploratory spike**: readability may be relaxed only when the spike is isolated and will be discarded or deliberately rewritten.
        - **Fixed external contract**: awkward shapes may be unavoidable at a boundary; translate them into cleaner internal types.
        - **Production emergency**: accept temporary debt only with explicit follow-up, owner, and verification of the narrow fix.
        
        ## Quick Reference
        
        | Rule | Summary |
        |---|---|
        | Next reader and changer | Optimize for ownership, not authorship speed |
        | Conceptual cohesion | Split conflicting responsibilities, not cohesive work |
        | Discoverable context | Make dependencies, effects, and sources of truth findable |
        | Code is liability | Delete duplication and speculative volume |
        | Sustainability | Resist erosion and make debt explicit |
        | Deliberate acceptance | Large work is fine when architecture and verification are sound |
        
    • outside-in-tdd
      • examples.md 8.5 KB
        # Outside-In TDD Examples
        
        Curated C# examples demonstrating outside-in TDD practices from the book. Each shows a concrete step along the vertical slice: starting a skeleton, driving behaviour from the boundary inward, using parametrised tests, and defeating the Devil's Advocate.
        
        ## Example 1: Walking Skeleton + Characterisation Test
        
        This is the first test in the codebase. It targets the outermost boundary (HTTP) and asserts only the weakest stable property — a 2xx status. It was written after the project scaffolder ran, so it's a characterisation test, not red-green TDD.
        
        ```csharp
        [Fact]
        [SuppressMessage(
            "Usage", "CA2234:Pass system uri objects instead of strings",
            Justification = "URL isn't passed as variable, but as literal.")]
        public async Task HomeIsOk()
        {
            using var factory = new WebApplicationFactory<Startup>();
            var client = factory.CreateClient();
        
            var response = await client.GetAsync("");
        
            Assert.True(
                response.IsSuccessStatusCode,
                $"Actual status code: {response.StatusCode}.");
        }
        ```
        
        **Why it works**:
        - One assertion, loosest possible — future changes to the body or status code won't break it unless the endpoint goes down entirely.
        - AAA structure: factory + client (arrange), `GetAsync` (act), `Assert.True` (assert), separated by blank lines.
        - Assertion message tells future readers what failed, not just "expected True but got False".
        - `[SuppressMessage]` has a `Justification` explaining *why* the rule is suppressed here.
        
        ## Example 2: Boundary Test Driving a New Feature
        
        The first test that drives behaviour, not just characterises. It posts a valid reservation as JSON and asserts success. Details of `PostReservation` are hidden in a helper method to amplify what matters: post a reservation, get a success.
        
        ```csharp
        [Fact]
        public async Task PostValidReservation()
        {
            var response = await PostReservation(new {
                date = "2023-03-10 19:00",
                email = "katinka@example.com",
                name = "Katinka Ingabogovinanana",
                quantity = 2 });
        
            Assert.True(
                response.IsSuccessStatusCode,
                $"Actual status code: {response.StatusCode}.");
        }
        ```
        
        The hidden helper (a SUT Encapsulation Method / Test Utility Method):
        
        ```csharp
        [SuppressMessage(
            "Usage", "CA2234:Pass system uri objects instead of strings",
            Justification = "URL isn't passed as variable, but as literal.")]
        private async Task<HttpResponseMessage> PostReservation(object reservation)
        {
            using var factory = new RestaurantApiFactory();
            var client = factory.CreateClient();
        
            string json = JsonSerializer.Serialize(reservation);
            using var content = new StringContent(json);
            content.Headers.ContentType.MediaType = "application/json";
            return await client.PostAsync("reservations", content);
        }
        ```
        
        **Why it works**:
        - The test body reads like a specification of behaviour, not an HTTP tutorial.
        - The helper encapsulates URL conventions; the API can later move to hypermedia without breaking tests.
        - Only asserts `IsSuccessStatusCode` — happy path, light touch.
        
        ## Example 3: Unit Test with AAA and a Fake Object
        
        Once the boundary test passes minimally, drive behaviour deeper with a unit test against `ReservationsController` directly. This is the inward step of outside-in.
        
        ```csharp
        [Fact]
        public async Task PostValidReservationWhenDatabaseIsEmpty()
        {
            var db = new FakeDatabase();
            var sut = new ReservationsController(db);
        
            var dto = new ReservationDto
            {
                At = "2023-11-24 19:00",
                Email = "juliad@example.net",
                Name = "Julia Domna",
                Quantity = 5
            };
            await sut.Post(dto);
        
            var expected = new Reservation(
                new DateTime(2023, 11, 24, 19, 0, 0),
                dto.Email,
                dto.Name,
                dto.Quantity);
            Assert.Contains(expected, db);
        }
        ```
        
        **Why it works**:
        - 2-2-2 balance across arrange / act / assert.
        - `FakeDatabase` is a test-only `IReservationsRepository` that inherits from `Collection<Reservation>`, so `Assert.Contains` works for free.
        - The `Reservation` domain type has structural equality, so `expected` equals the stored instance by value.
        - DTO (`ReservationDto`) receives the wire format with nullable strings; `Reservation` (domain type) enforces invariants.
        
        ## Example 4: Parametrised Test with Devil's Advocate Counter-Case
        
        The Devil implemented `Post` with a hard-coded branch on `if (dto.Email == "shli@example.org") return 500`. Adding one `[InlineData]` row that posts from that email into an *empty* database rejects the stupid implementation.
        
        ```csharp
        [Theory]
        [InlineData(
            "2023-11-24 19:00", "juliad@example.net", "Julia Domna", 5)]
        [InlineData("2024-02-13 18:15", "x@example.com", "Xenia Ng", 9)]
        [InlineData("2023-08-23 16:55", "kite@example.edu", null, 2)]
        [InlineData("2022-03-18 17:30", "shli@example.org", "Shanghai Li", 5)]
        public async Task PostValidReservationWhenDatabaseIsEmpty(
            string at, string email, string name, int quantity)
        {
            var db = new FakeDatabase();
            var sut = new ReservationsController(db);
        
            var dto = new ReservationDto { At = at, Email = email, Name = name, Quantity = quantity };
            await sut.Post(dto);
        
            var expected = new Reservation(
                DateTime.Parse(dto.At, CultureInfo.InvariantCulture),
                dto.Email, dto.Name ?? "", dto.Quantity);
            Assert.Contains(expected, db);
        }
        ```
        
        **Why it works**:
        - Adding the fourth `[InlineData]` line is the safest kind of test-code edit (pure append).
        - It forces the production code off a constant branch and toward considering the wider state (existing reservations).
        - Same test method, same assertions, just more data — no risk of weakening postconditions elsewhere.
        
        ## Example 5: Strengthening Postconditions by Adding Assertions
        
        A test initially asserts only the status code. Later, to prevent false negatives (any unrelated 500 would pass), append more assertions.
        
        Before:
        
        ```csharp
        Assert.Equal(
            HttpStatusCode.InternalServerError,
            response.StatusCode);
        ```
        
        After:
        
        ```csharp
        Assert.Equal(
            HttpStatusCode.InternalServerError,
            response.StatusCode);
        Assert.NotNull(response.Content);
        var content = await response.Content.ReadAsStringAsync();
        Assert.Contains(
            "tables",
            content,
            StringComparison.OrdinalIgnoreCase);
        ```
        
        **Why it works**:
        - Purely additive — no existing assertion touched, no assertion deleted.
        - Strengthens postconditions: random 500s (e.g. a missing connection string) no longer pass.
        - Analogous to Liskov's "subtypes may strengthen postconditions" — moving forward in time is like moving to a subtype.
        
        ## Example 6: Test Spy Refactored in Isolation
        
        Adding `EmailReservationDeleted` to `IPostOffice` forces the spy to distinguish which method was called. Refactor the spy alone — stash production changes, change the spy, make sure tests compile and pass, then `git stash pop` and continue.
        
        Before (captures only reservations, not which method was called):
        
        ```csharp
        public class SpyPostOffice : Collection<Reservation>, IPostOffice
        {
            public Task EmailReservationCreated(Reservation reservation)
            {
                Add(reservation);
                return Task.CompletedTask;
            }
        }
        ```
        
        After (captures both method kind and reservation):
        
        ```csharp
        internal class SpyPostOffice :
            Collection<SpyPostOffice.Observation>, IPostOffice
        {
            public Task EmailReservationCreated(Reservation reservation)
            {
                Add(new Observation(Event.Created, reservation));
                return Task.CompletedTask;
            }
        
            public Task EmailReservationDeleted(Reservation reservation)
            {
                Add(new Observation(Event.Deleted, reservation));
                return Task.CompletedTask;
            }
        
            internal enum Event { Created = 0, Deleted = 1 }
        }
        ```
        
        **Why it works**:
        - The refactor touches only test code — production code stayed stashed.
        - The spy's assertions had to be rewritten (collection element type changed) but in a separate, reviewable commit.
        - Eliminates the category of errors where a test-code refactor inadvertently strengthens preconditions and papers over bugs.
        
        ## Changes Made (across the vertical slice)
        
        1. Scaffolder produced a working "Hello World" endpoint.
        2. Added a characterisation test and CI build script.
        3. Added a boundary test posting JSON — failed, then added an MVC controller to pass it.
        4. Dropped inward to a unit test of the controller — triggered creation of DTO, domain model, repository interface, and Fake Object.
        5. Added a real SQL implementation as a Humble Object (untested at unit level).
        6. Added further parametrised test cases to defeat Devil's Advocate implementations, then refactored to a clean `Sum` once red-green-refactor opened up.
        
      • knowledge.md 3.1 KB
        # Outside-In TDD Knowledge
        
        Core concepts for test-first development: walking skeleton, vertical slice, characterisation tests, and the safety net of a test suite.
        
        ## Overview
        
        Outside-in TDD starts tests at the system boundary (HTTP, CLI, message queue) and works inward. The first goal is a Walking Skeleton: a thin vertical slice from data ingress to data persistence that ships working software early. Tests act as a driver of change — they force the production code to justify itself.
        
        ## Key Concepts
        
        ### Walking Skeleton
        
        A minimal, automatically deployable slice that exercises every part of the architecture without doing anything useful yet. It gives you a test suite, a build script, a deployment pipeline, and running software — all at once, and all minimal.
        
        ### Vertical Slice
        
        A feature implemented end-to-end — from the outer boundary all the way to data persistence — using the simplest possible code at each layer. Pick the simplest feature, prefer data input so later tests have data to read, aim for the happy path first, and avoid Speculative Generality.
        
        ### Outside-In Test-Driven Development
        
        First tests exercise the high-level boundary of the System Under Test; later tests work inward to finer-grained units as needed. Boundary tests catch combinatorial explosion at the outer shell; unit tests handle edge cases.
        
        ### Characterisation Test
        
        A test written after the fact that describes the behaviour of existing software, usually to protect against regressions. Not true TDD (no red phase for new behaviour), but the right starting point for wizard-generated or legacy code. Assert only superficial, stable properties when behaviour is expected to change soon.
        
        ### Arrange Act Assert (AAA)
        
        Three-phase structure: arrange preconditions, act on the SUT, assert the outcome. The blank line between phases is a heuristic. The act phase is usually the smallest.
        
        ### Triangulation
        
        Adding more specific test cases to force the production code to become more generic. "As the tests get more specific, the code gets more generic." (Robert C. Martin).
        
        ### Safety-Net Asymmetry
        
        The test suite lets you refactor production code with confidence. Production code has a safety net (the tests); test code does *not*. Test code can only be checked by seeing it fail against broken production code. Edit tests carefully and commit independently.
        
        ## Common Misconceptions
        
        - **Myth**: The first vertical slice is pointless because it just saves a hard-coded value.
          **Reality**: It establishes running software, a deployment pipeline, and a test suite. Everything else is additive from there.
        
        - **Myth**: A unit test should have exactly one assertion ("Assertion Roulette").
          **Reality**: Assertion Roulette is interleaving assert/act sections or unlabelled assertions. Multiple assertions that strengthen postconditions are fine.
        
        - **Myth**: You must strictly TDD every class.
          **Reality**: Humble Objects (DB access, UI glue) and tool-generated code may skip TDD.
        
        - **Myth**: You can refactor unit tests the same way as production code.
          **Reality**: Refactoring's precondition is "solid tests" — test code doesn't have that safety net.
        
      • patterns.md 5.9 KB
        # Outside-In TDD Patterns
        
        Named patterns for test-driven development as practised in the book.
        
        ## Pattern: Walking Skeleton
        
        ### Intent
        
        A deployable, testable, end-to-end slice of an application, established as early as possible — before any feature code exists.
        
        ### When to Use
        
        - Starting a new codebase.
        - Any time you need to prove a deployment pipeline works before weeks of feature work.
        
        ### Structure
        
        1. Scaffold a new project (wizard / template).
        2. Check it in; build script runs `dotnet test` (not just `dotnet build`).
        3. Add a test project with one characterisation test at the outermost boundary.
        4. Configure CI to run the script; deploy the result.
        
        ### Example
        
        ```csharp
        [Fact]
        public async Task HomeIsOk()
        {
            using var factory = new WebApplicationFactory<Startup>();
            var client = factory.CreateClient();
        
            var response = await client.GetAsync("");
        
            Assert.True(
                response.IsSuccessStatusCode,
                $"Actual status code: {response.StatusCode}.");
        }
        ```
        
        ### Benefits / Considerations
        
        - Feedback on the full lifecycle from day one; later features are additive.
        - Feels pointless ("just Hello World") but the value is in the infrastructure proof.
        
        ---
        
        ## Pattern: Characterisation Test
        
        ### Intent
        
        Capture current behaviour of existing code to enable refactoring or feature additions without regression fear.
        
        ### When to Use
        
        - Inherited codebase with no tests, scaffolded code to lock in, or before refactoring legacy code.
        
        ### Structure
        
        Arrange minimal SUT, act as a real client would, assert ONLY the weakest stable property.
        
        ### Example
        
        The Walking Skeleton test above is also a characterisation test — written after `dotnet new` scaffolded the project, asserts only `IsSuccessStatusCode`, and lets the team change the response body later without breaking the test.
        
        ### Considerations
        
        - Not strict TDD (no red phase). Resist over-asserting — lock in only what must not regress.
        
        ---
        
        ## Pattern: Devil's Advocate
        
        ### Intent
        
        Stress-test a test suite by trying to pass it with obviously wrong production code. The resulting stupid implementation tells you what test to write next.
        
        ### When to Use
        
        - A test just went green and you aren't sure more tests are needed.
        - Reviewing someone else's TDD work.
        - Teaching TDD — exposes weak test sets.
        
        ### Structure
        
        1. Write (or imagine) the simplest wrong code that passes all current tests — usually a hard-coded constant or a branch on a literal.
        2. Ask: which new test case rejects it?
        3. Add that case. Prefer another `[InlineData]` over a new method.
        4. Re-run; continue until the stupid version no longer passes.
        
        ### Example
        
        Given `OverbookAttempt` passes, the Devil writes:
        
        ```csharp
        if (dto.Email == "shli@example.org")
            return new StatusCodeResult(
                StatusCodes.Status500InternalServerError);
        ```
        
        Counter-case added to an *existing* test:
        
        ```csharp
        [InlineData("2022-03-18 17:30", "shli@example.org", "Shanghai Li", 5)]
        ```
        
        Same email, empty database, success expected — the stupid branch dies.
        
        ### Benefits
        
        - Concrete heuristic for "do I need another test?"
        - Drives the transformation from constant → scalar → variable logic.
        
        ### Considerations
        
        - Pair with Red-Green-Refactor. Once the structure is right, refactoring is often better than yet another test.
        
        ---
        
        ## Pattern: Transformation Priority Premise (TPP)
        
        ### Intent
        
        When passing a failing test, prefer the simpler code transformation on an ordered list (constant < scalar variable < conditional < loop < recursion). Keeps code generality in lockstep with test specificity.
        
        ### Example
        
        `Reservation` starts hard-coded in `Post` (constant). Next transformation, constant → scalar: read values from the DTO. Later, `reservedSeats` uses `SingleOrDefault` (scalar on a one-element collection); next transformation, scalar → aggregate, is `Sum`.
        
        ### Considerations
        
        - Full coverage lives in the encapsulation/ theme; referenced here as a decision aid for outside-in TDD.
        
        ---
        
        ## Pattern: Fake Object for I/O Isolation
        
        ### Intent
        
        Replace an out-of-process dependency (DB, HTTP, queue) with an in-memory implementation of the same interface that has real observable behaviour.
        
        ### When to Use
        
        - Unit tests needing persistence semantics without a real store.
        - Boundary tests where DI must still work end-to-end.
        
        ### Structure
        
        1. Define an interface for the dependency in production code.
        2. In tests, inherit from an in-memory collection and implement the interface by delegating to self.
        3. Swap real for fake via DI.
        
        ### Example
        
        ```csharp
        public class FakeDatabase :
            Collection<Reservation>, IReservationsRepository
        {
            public Task Create(Reservation reservation)
            {
                Add(reservation);
                return Task.CompletedTask;
            }
        }
        ```
        
        Boundary wiring:
        
        ```csharp
        public class RestaurantApiFactory : WebApplicationFactory<Startup>
        {
            protected override void ConfigureWebHost(IWebHostBuilder builder)
            {
                builder.ConfigureServices(services =>
                {
                    services.RemoveAll<IReservationsRepository>();
                    services.AddSingleton<IReservationsRepository>(
                        new FakeDatabase());
                });
            }
        }
        ```
        
        ### Benefits / Considerations
        
        - State-based assertions work trivially (`Assert.Contains(expected, db)`); fast and deterministic.
        - Keep Fake behaviour realistic (date filtering, ordering) or tests pass while production breaks.
        - Complement with a small number of real-infrastructure integration tests (Humble Object).
        
        ---
        
        ## Pattern Selection Guide
        
        | Situation | Recommended Pattern |
        |-----------|--------------------|
        | Starting a brand-new codebase | Walking Skeleton + Characterisation Test |
        | Adding first useful feature | Outside-In + Fake Object |
        | Just made a test green, unsure what next | Devil's Advocate |
        | Green test with obvious generalisation | Red-Green-Refactor (skip new test) |
        | Two ways to pass a test | Transformation Priority Premise |
        | Replacing DB / queue / HTTP in tests | Fake Object |
        
      • rules.md 6.9 KB
        # Outside-In TDD Rules
        
        Rules for driving code with tests: AAA structure, seeing tests fail, balancing static analysis, choosing between red-green-refactor and Devil's Advocate, and deciding when you have enough tests.
        
        ## Core Rules
        
        ### 1. Make Arrange / Act / Assert Visually Clear
        
        Separate setup, execution, and observation so a reader can identify each phase immediately. Blank lines, helper methods, or framework conventions may express the structure; exact whitespace is a project style choice.
        
        - Arrange: prepare everything the test needs (SUT, dependencies, inputs).
        - Act: invoke the operation under test.
        - Assert: verify the observed outcome against the expected outcome.
        
        If comments are required to find the act or assertion, simplify the test or extract setup that obscures behavior.
        
        **Example**:
        ```csharp
        // Bad: Extra blank lines; phases are ambiguous
        [Fact]
        public async Task PostValidReservationWhenDatabaseIsEmpty()
        {
            var db = new FakeDatabase();
        
            var sut = new ReservationsController(db);
            var dto = new ReservationDto { ... };
        
            await sut.Post(dto);
            var expected = new Reservation(...);
        
            Assert.Contains(expected, db);
        }
        
        // Good: Exactly two blank lines delineate three phases
        [Fact]
        public async Task PostValidReservationWhenDatabaseIsEmpty()
        {
            var db = new FakeDatabase();
            var sut = new ReservationsController(db);
        
            var dto = new ReservationDto { ... };
            await sut.Post(dto);
        
            var expected = new Reservation(...);
            Assert.Contains(expected, db);
        }
        ```
        
        ### 2. Start at the Boundary, Work Inward
        
        First tests go against the outermost API (HTTP, CLI, queue). As combinatorial complexity appears, add unit tests for smaller units in isolation. Don't try to cover every edge case at the boundary.
        
        ### 3. See Every Test Fail Before You Trust It
        
        A test you haven't seen fail may be tautological — it could pass even with broken production code.
        
        - With TDD's red-green flow you see this for free on new tests.
        - When you edit an existing test or write one after the production code, deliberately sabotage the SUT (return a hard-coded value, comment out logic) and confirm the test fails. Use `git stash` or staged changes to discard the sabotage cleanly.
        
        ### 4. Start With Stable Boundary Assertions; Strengthen Before Acceptance
        
        During the first walking-skeleton slice, assert the smallest stable observable contract. Before the feature is accepted, cover the user-visible outcome and material side effects. A superficial success status is not sufficient final evidence when behavior matters.
        
        ### 5. Preserve Test Intent When Modifying Tests
        
        Additive edits are easier to review:
        - Add a new test method.
        - Add a test case to a parametrised `[Theory]`.
        - Add another assertion to an existing act phase.
        
        Changing or removing assertions may be correct when requirements change or tests are refactored, but the reason and changed oracle must be explicit. Never weaken tests merely to make production code pass.
        
        ### 6. Make Oracle Changes Auditable
        
        Reviewers must be able to distinguish changed behavior, changed test oracle, and mechanical test refactoring. Separate commits are one good technique, but do not force broken intermediate states or contort an atomic change.
        
        ### 7. Apply Useful Static Analysis to Test Code
        
        Use the strictest practical checks that produce useful signal. Disable rules that genuinely do not apply to test code, and suppress narrowly with a reason rather than weakening the entire test project.
        
        **Always document *why* you suppress**:
        ```csharp
        [SuppressMessage(
            "Usage", "CA2234:Pass system uri objects instead of strings",
            Justification = "URL isn't passed as variable, but as literal.")]
        ```
        
        ### 8. Use Devil's Advocate to Decide When You Need Another Test
        
        After a test passes, ask: can I write a deliberately stupid implementation that still passes? If yes, that's a signal to add a test case — often just another `[InlineData]` line — that would reject the stupid version. If no, your test set is strong enough for now. To automate this heuristic on a touched module, run mutation testing (Stryker, mutmut, PIT, cargo-mutants — see `../tooling/commands.md`); surviving mutants are the missing test cases.
        
        ### 9. Switch from Devil's Advocate to Red-Green-Refactor Once Structure Exists
        
        Devil's Advocate forces you to add tests. Red-Green-Refactor says: once a test is green, look for a safe generalisation (replace a `SingleOrDefault` hack with `Sum`). Don't add a test when refactoring gets you the same correctness.
        
        ### 10. Separate DTO from Domain Model
        
        The type that receives the wire format (JSON) has no invariants — all fields nullable, string-typed. The domain type enforces invariants and is what tests and production logic assert on. Don't let one type serve both roles.
        
        ## Guidelines
        
        - Aim for balance between arrange / act / assert sections. A 2-2-2 or 1-1-1 shape reads better than 5-1-1.
        - When an act section is a single line hidden behind a lot of setup, extract a SUT Encapsulation Method or Test Utility Method in the test project.
        - Write down edge cases you think of while writing a test — don't derail to implement them mid-test.
        - Commit after each passing test run; consider pushing through the deployment pipeline.
        - Prefer Value Objects in the Domain Model — structural equality makes elegant `Assert.Contains(expected, actual)` possible.
        
        ## Exceptions
        
        - **Characterisation tests**: No red phase by definition. Assert only the weakest stable property of the existing code.
        - **Humble Objects** (SQL repositories, framework glue): May skip unit tests; push logic out and cover them with integration tests later.
        - **Auto-generated code** (IDE-generated `Equals`, `GetHashCode`, constructors): Trust the generator; no need to triangulate.
        
        ## Deciding You Have Enough Tests
        
        No quantitative rule exists. Ask:
        
        1. How likely is a regression? (Assume benign intent from teammates.)
        2. What's the impact of that regression?
        
        If either is high, add the test. Any defect that reaches production has tautologically demonstrated it can happen — always add a regression test when you fix one.
        
        ## Quick Reference
        
        | Rule | Summary |
        |------|---------|
        | AAA | Make setup, execution, and observation obvious |
        | Outside-In | Boundary first, unit tests inward |
        | See Tests Fail | Never trust a test you haven't seen red |
        | Light Assertions Early | Boundary tests assert weakest stable property |
        | Preserve test intent | Oracle changes require explicit rationale |
        | Auditable refactoring | Distinguish behavior, oracle, and mechanical edits |
        | Moderate Static Analysis | Suppress with justification; disable where it doesn't fit |
        | Devil's Advocate | Weak tests let stupid code pass; add test or refactor |
        | When Enough Tests | Weigh probability × impact; no hard number |
        
        ## Editorial Amendment (2026) — Not from the Book
        
        Pairing self-authored tests with an independent oracle and never weakening verification are covered canonically in `../agent-native/verification-loops.md`.
        
    • practices-glossary
      • knowledge.md 11.8 KB
        # Practices Glossary
        
        Fast-lookup reference for currently retained named practices from the book. Project-specific formatting and screen-size conventions are intentionally omitted.
        
        ## Quick Lookup Table
        
        | Practice | One-line summary | Deep dive |
        |----------|------------------|-----------|
        | Arrange Act Assert | Structure tests in three clearly separated sections | `outside-in-tdd/rules.md` |
        | Bisection | Halve the code repeatedly to isolate a defect | `troubleshooting/rules.md` |
        | Checklist for a New Code Base | Short startup checklist (Git, build automation, all warnings on) | `codebase-setup/rules.md` |
        | Command Query Separation | A method is either a Command (side effects) or a Query (returns data), never both | `api-design/rules.md` |
        | Count the Variables | Count locals, parameters, and fields in a method; keep the total low | `decomposition/rules.md` |
        | Cyclomatic Complexity | Path-count metric; values above 15 trigger review by default | `decomposition/rules.md` |
        | Explicit Seams for Cross-Cutting Concerns | Keep logging, caching, and resilience outside domain logic using a scope-appropriate seam | `separation-of-concerns/rules.md` |
        | Devil's Advocate | Deliberately mis-implement the SUT to expose missing tests | `outside-in-tdd/rules.md` |
        | Feature Flag | Hide incomplete features so you can keep integrating | `evolution/rules.md` |
        | Functional Core, Imperative Shell | Push pure functions to the core, keep side effects at the edge | `decomposition/rules.md` |
        | Complementary Communication | Put enforceable contracts in executable artifacts and rationale in prose/history | `api-design/rules.md` |
        | Justify Exceptions from the Rule | Deviating from a rule is OK — if documented and justified | `teamwork-git/rules.md` |
        | Parse, Don't Validate | Convert unstructured data to structured types as early as possible | `encapsulation/rules.md` |
        | Explicit Boundary Parsing | Accept deliberate compatibility and reject malformed input | `encapsulation/rules.md` |
        | Red Green Refactor | TDD loop: failing test → simplest pass → refactor → repeat | `outside-in-tdd/rules.md` |
        | Regularly Update Dependencies | Schedule dependency updates; never fall far behind | `evolution/rules.md` |
        | Reproduce Defects as Tests | Turn every reproducible bug into an automated test | `troubleshooting/rules.md` |
        | Review Code | Have another person review every change; rejection must be a real option | `teamwork-git/rules.md` |
        | Semantic Versioning | Version releases by compatibility (MAJOR.MINOR.PATCH) | `evolution/rules.md` |
        | Separate Refactoring of Test and Production Code | Never refactor test and production code simultaneously | `outside-in-tdd/rules.md` |
        | Slice | Ship small vertical slices that each improve a working system | `outside-in-tdd/rules.md` |
        | Strangler | Add the new implementation alongside the old, migrate gradually | `evolution/rules.md` |
        | Threat-Model | Make deliberate security decisions using STRIDE | `security/rules.md` |
        | Transformation Priority Premise | Prefer small transformations that keep code in valid states | `outside-in-tdd/rules.md` |
        | X-driven Development | Always drive your code with something (a test, analyzer, refactor tool) | `outside-in-tdd/rules.md` |
        | X Out Names | Mentally replace method names with Xs to test signature clarity | `api-design/rules.md` |
        
        ## Practices
        
        ### Arrange Act Assert
        **Definition**: Structure automated tests according to the Arrange Act Assert pattern. Make it clear to readers where one section ends and the next begins.
        **Use when**: Writing or reviewing any unit test.
        **Deep dive**: `outside-in-tdd/rules.md`
        
        ### Bisection
        **Definition**: When struggling to understand a bug, remove half of your code and check if the problem persists. Keep halving until you have a minimal working example — at that point the cause is usually obvious.
        **Use when**: A defect's cause is not clear from reading the code.
        **Deep dive**: `troubleshooting/rules.md`
        
        ### Checklist for a New Code Base
        **Definition**: When creating a new code base, follow a short checklist. A suggested starter: use Git, automate the build, turn on all error messages. Modify to fit your context, but keep it short.
        **Use when**: Starting a new repository or project within a solution.
        **Deep dive**: `codebase-setup/rules.md`
        
        ### Command Query Separation
        **Definition**: Separate Commands from Queries. Commands are procedures that have side effects. Queries are functions that return data. Every method should be either a Command or a Query, but not both.
        **Use when**: Designing any method or API surface.
        **Deep dive**: `api-design/rules.md`
        
        ### Count the Variables
        **Definition**: Count all the variables involved in a method implementation — local variables, method parameters, and class fields. Keep the total number low.
        **Use when**: A method feels hard to reason about.
        **Deep dive**: `decomposition/rules.md`
        
        ### Cyclomatic Complexity
        **Definition**: Cyclomatic complexity measures the number of pathways through a piece of code. This skill uses above 15 as a review trigger, not an automatic rejection or proof of bad design. It also helps identify path-coverage needs.
        **Use when**: Deciding whether a method needs to be broken up.
        **Deep dive**: `decomposition/rules.md`
        
        ### Explicit Seams for Cross-Cutting Concerns
        **Definition**: Keep logging, caching, telemetry, and resilience outside domain logic. Choose a Decorator, middleware, filter, interceptor, or pipeline according to scope and keep wiring visible.
        **Use when**: You need logging, caching, retries, or auditing around core logic.
        **Deep dive**: `separation-of-concerns/rules.md`
        
        ### Devil's Advocate
        **Definition**: Deliberately implement the System Under Test incorrectly. The more incorrect you can make it while still passing the tests, the more test cases you should consider adding. A heuristic for evaluating whether more tests would improve confidence.
        **Use when**: Reviewing an existing test suite for gaps.
        **Deep dive**: `outside-in-tdd/rules.md`
        
        ### Feature Flag
        **Definition**: Hide incomplete behavior behind a feature flag when deploy and release must be separated or blast radius controlled.
        **Use when**: Incomplete behavior must integrate or deploy without becoming visible to users.
        **Deep dive**: `evolution/rules.md`
        
        ### Functional Core, Imperative Shell
        **Definition**: Favour pure functions. Referential transparency means you can replace a function call with its result without changing behaviour — the ultimate abstraction. Pure functions compose well and are easy to unit test. Push them to the core; keep side effects in an outer shell.
        **Use when**: Structuring a new module or refactoring existing logic.
        **Deep dive**: `decomposition/rules.md`
        
        ### Complementary Communication
        **Definition**: Use types, schemas, and tests for enforceable contracts; names for readable intent; comments and commit history for non-obvious rationale; documentation for system mission and usage.
        **Use when**: Deciding where to put an explanation.
        **Deep dive**: `api-design/rules.md`
        
        ### Justify Exceptions from the Rule
        **Definition**: Good rules work most of the time, but sometimes a rule is in the way. It's OK to deviate — but justify and document the reason. Get a second opinion first; a co-worker may see a way to follow the rule that you missed.
        **Use when**: You're tempted to break a team rule.
        **Deep dive**: `teamwork-git/rules.md`
        
        ### Parse, Don't Validate
        **Definition**: Your code receives data as JSON, XML, CSV, or other formats with few integrity guarantees. Convert less-structured data to more-structured data as soon as possible. Think of this as parsing, even if you don't parse plain text.
        **Use when**: Data enters your system from the outside.
        **Deep dive**: `encapsulation/rules.md`
        
        ### Explicit Boundary Parsing
        **Definition**: Parse untrusted data into explicit supported forms. Accept compatibility only when deliberate and tested; reject malformed or invented shapes instead of silently coercing them.
        **Use when**: Designing trust-boundary inputs and compatibility behavior.
        **Deep dive**: `encapsulation/rules.md`
        
        ### Red Green Refactor
        **Definition**: The TDD loop as a checklist: (1) write a failing test — did it run, did it fail, did it fail on an assertion, on the last assertion? (2) make all tests pass with the simplest thing that could possibly work, (3) refactor while tests stay green, (4) repeat.
        **Use when**: Practising test-driven development.
        **Deep dive**: `outside-in-tdd/rules.md`
        
        ### Regularly Update Dependencies
        **Definition**: Don't let your code base fall behind its dependencies. Check for updates on a regular schedule — if you fall too far behind, catching up becomes difficult.
        **Use when**: Maintaining a long-lived project.
        **Deep dive**: `evolution/rules.md`
        
        ### Reproduce Defects as Tests
        **Definition**: If at all possible, reproduce bugs as one or more automated tests before fixing them.
        **Use when**: A bug has been reported and can be triggered reliably.
        **Deep dive**: `troubleshooting/rules.md`
        
        ### Review Code
        **Definition**: Apply independent review proportional to risk. Material architecture, security, data, and contract changes require accountable human approval; automated review is screening rather than ownership.
        **Use when**: Reviewing production changes under repository risk policy.
        **Deep dive**: `teamwork-git/rules.md`
        
        ### Semantic Versioning
        **Definition**: Consider using Semantic Versioning — MAJOR.MINOR.PATCH — to signal compatibility of a release.
        **Use when**: Publishing a library or shared API.
        **Deep dive**: `evolution/rules.md`
        
        ### Separate Refactoring of Test and Production Code
        **Definition**: Make it possible to distinguish behavior changes, test-oracle changes, and mechanical test refactoring. Separate commits are useful but not mandatory when they would create broken intermediate states.
        **Use when**: Any refactoring session.
        **Deep dive**: `outside-in-tdd/rules.md`
        
        ### Slice
        **Definition**: Build through coherent vertical behavior and verifiable checkpoints. Large systematic work is acceptable when architecture and acceptance criteria remain clear.
        **Use when**: Planning how to deliver a feature.
        **Deep dive**: `outside-in-tdd/rules.md`
        
        ### Strangler
        **Definition**: Establish the new implementation beside the old, migrate callers, and remove the old path when side-by-side change reduces blast radius or improves verification and rollback.
        **Use when**: In-place replacement would create excessive compatibility, deployment, or recovery risk.
        **Deep dive**: `evolution/rules.md`
        
        ### Threat-Model
        **Definition**: Take deliberate security decisions. For non-experts, the STRIDE model is manageable: Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege. Involve IT and stakeholders — mitigation weighs business concerns against security risks.
        **Use when**: Designing a feature that handles auth, user data, or trust boundaries.
        **Deep dive**: `security/rules.md`
        
        ### Transformation Priority Premise
        **Definition**: Prefer transformations with clear verified checkpoints. Small steps are useful when they reduce uncertainty; larger systematic transformations are valid when executable constraints make them safer and clearer.
        **Use when**: Planning a non-trivial edit.
        **Deep dive**: `outside-in-tdd/rules.md`
        
        ### X-driven Development
        **Definition**: Use a driver for the code you write — static analysis, a unit test, a built-in refactoring tool, and so on. It's OK to deviate, but the closer you adhere, the less you tend to go astray.
        **Use when**: Starting any piece of code.
        **Deep dive**: `outside-in-tdd/rules.md`
        
        ### X Out Names
        **Definition**: Replace method names with Xs (in your head — you don't have to edit the code) to examine how much information the signature alone communicates. In a statically typed language, types can carry much of the meaning if you let them.
        **Use when**: Reviewing an API for clarity.
        **Deep dive**: `api-design/rules.md`
        
    • security
      • checklist.md 4.5 KB
        # STRIDE Threat-Model Checklist
        
        Use this when adding a new endpoint, feature, or service. Walk every STRIDE letter. For each threat, answer: did we consider it, what is the mitigation, and does it live in code or infra? Process steps live in `workflows/threat-model.md`.
        
        ## S - Spoofing
        
        - [ ] Is authentication required? If not, is that an explicit, documented decision?
        - [ ] Established identity provider (OAuth / OIDC / JWT), not a hand-rolled password check?
        - [ ] Token validation complete (signature, issuer, audience, expiry)?
        - [ ] Integration test: unauthenticated calls get `401`?
        - [ ] **Mitigation:** ______________________________________
        - [ ] **Lives in:** [ ] code  [ ] infra  [ ] both
        
        ## T - Tampering
        
        - [ ] HTTPS mandatory (no plaintext fallback)?
        - [ ] All SQL queries parameterised (no string concatenation of user input)?
        - [ ] Server re-validates every client-supplied value (no blind trust in request bodies)?
        - [ ] If resource URLs are capability tokens, are IDs unguessable (GUIDs / 128-bit)?
        - [ ] **Mitigation:** ______________________________________
        - [ ] **Lives in:** [ ] code  [ ] infra  [ ] both
        
        ## R - Repudiation
        
        - [ ] Every state-changing operation writes an audit log (who, what, when, before/after)?
        - [ ] Caller's identity attributable in the log (not just "anonymous")?
        - [ ] Audit logs append-only / shipped to a store the service cannot rewrite?
        - [ ] Real-world consequences (payment, contract) → stronger signal needed (signed submission, pre-auth)?
        - [ ] **Mitigation:** ______________________________________
        - [ ] **Lives in:** [ ] code  [ ] infra  [ ] both
        
        ## I - Information Disclosure
        
        - [ ] Response is the minimum projection the caller needs (no extra PII "just in case")?
        - [ ] Authn + authz required for PII/sensitive endpoints (`403` test for wrong-role)?
        - [ ] Secrets (passwords, tokens, API keys, raw JWTs) never logged?
        - [ ] Sensitive URLs kept out of logs, referer headers, and error messages?
        - [ ] **Mitigation:** ______________________________________
        - [ ] **Lives in:** [ ] code  [ ] infra  [ ] both
        
        ## D - Denial of Service
        
        - [ ] Rate limit on this endpoint (per IP, per token, or per account)?
        - [ ] Every outbound call (DB, HTTP, queue) has a bounded timeout?
        - [ ] Maximum payload size / maximum array length for bulk operations?
        - [ ] Fully distributed DoS raised with IT / infra when applicable?
        - [ ] **Mitigation:** ______________________________________
        - [ ] **Lives in:** [ ] code  [ ] infra  [ ] both
        
        ## E - Elevation of Privilege
        
        - [ ] Service runs with minimum privileges (non-root / non-admin DB user)?
        - [ ] Authentication and authorisation are separate, explicit steps?
        - [ ] Role/scope claims read only from a signed, validated token — never from body/header the client controls?
        - [ ] Dangerous database features (e.g. `xp_cmdshell`) disabled; SQL injection paths closed?
        - [ ] **Mitigation:** ______________________________________
        - [ ] **Lives in:** [ ] code  [ ] infra  [ ] both
        
        ## Red Flags
        
        Stop and address before shipping if you see:
        
        - A SQL query built by string concatenation or interpolation of request data.
        - An endpoint returning PII with no auth check, or with auth deferred entirely to infra.
        - Secrets, tokens, or full request bodies written to application logs.
        - A service container running as root, or a database user with admin/sysadmin rights.
        - A state-changing action that writes no audit log.
        - Unlimited payloads, unbounded outbound calls, or no rate limit on a public write endpoint.
        - A role or permission read from the request body instead of a signed token claim.
        - Sequential integer IDs used as capability tokens.
        - HTTP allowed alongside HTTPS with no forced redirect.
        
        ## Outcome
        
        For each STRIDE letter you should be able to answer one of:
        
        1. **Mitigated in code** - describe how, link tests.
        2. **Mitigated in infra** - describe how, name the owner.
        3. **Knowingly deferred** - describe the residual risk and who signed off.
        
        A threat identified and explicitly accepted is a valid outcome. A threat that was never considered is not.
        
        ## Quick Reference
        
        | Letter | One-line check | Typical layer |
        |--------|----------------|---------------|
        | S | Auth required? Real identity provider? | Code |
        | T | Parameterised SQL + HTTPS + server-side re-validation? | Code + Infra |
        | R | Attributable audit log on state changes? | Code |
        | I | Minimal response + auth on PII + no secrets in logs? | Code |
        | D | Rate limits + timeouts + payload caps? | Code + Infra |
        | E | Least privilege + authn/authz separate + no SQL injection? | Infra + Code |
        
      • knowledge.md 5.7 KB
        # Security Knowledge
        
        Core concepts for threat-modelling software with STRIDE. Security is part of design, not an ops afterthought that gets bolted on.
        
        ## Overview
        
        Software security is like insurance: you don't want to pay for it, but if you don't, you'll be sorry. There is no such thing as a completely secure system; security engineering is about finding an appropriate balance of risks, mitigations, and cost. Programmers own a specific slice of that balance (code-level defences) and share the rest with IT professionals and business stakeholders.
        
        ## STRIDE
        
        **Definition**: A threat-modelling acronym developed at Microsoft, used as a checklist to enumerate threats against a system. Each letter names one category of threat. You walk through each letter and ask "is this system vulnerable to this?" then decide on a mitigation (or an explicit decision to accept the risk).
        
        STRIDE is a thought exercise or workshop, informal or systematic. It brings programmers, IT professionals, and business owners together because mitigations live in different layers: some in code, some in network configuration, and some are business decisions.
        
        ## The Six Threat Categories
        
        ### Spoofing
        
        **Definition**: Attackers pose as someone they are not to gain unauthorised access.
        
        **Example**: A reservation API that accepts any name means a caller can make a booking under "Keanu Reeves". Whether that matters depends on whether the system makes decisions based on identity.
        
        **Typical mitigation**: Authentication. Use an established identity provider (OAuth, OpenID Connect, JWT) rather than a home-grown password check.
        
        ### Tampering
        
        **Definition**: Attackers modify data in transit or at rest without authorisation.
        
        **Example**: A man-in-the-middle intercepts an HTTP response containing a resource URL and uses it to `DELETE` someone else's record. Or an attacker injects SQL through an unsanitised query to rewrite rows.
        
        **Typical mitigation**: Integrity checks (HTTPS/TLS, hashes, digital signatures), parameterised SQL queries, never trusting client-side state.
        
        ### Repudiation
        
        **Definition**: Attackers (or regular users) deny performing an action, with no evidence to refute them.
        
        **Example**: A user makes a restaurant reservation and then never shows up, later claiming they never booked. Doctors, hairdressers, and ticketing systems all suffer this.
        
        **Typical mitigation**: Audit logs attributable to an authenticated identity; digital signatures; pre-authorisation charges. Mitigations must be balanced against user friction.
        
        ### Information Disclosure
        
        **Definition**: Attackers read data they should not have access to.
        
        **Example**: A schedule endpoint returns names and email addresses of all guests for a day. Without authentication, anyone could harvest that PII. Resource URLs that double as capability tokens are also sensitive - possession is authorisation.
        
        **Typical mitigation**: Encryption in transit (HTTPS), access control on sensitive endpoints, treating resource URLs as secrets, parameterised SQL, explicit PII handling.
        
        ### Denial of Service
        
        **Definition**: Attackers make the system unavailable for legitimate users.
        
        **Example**: A distributed flood of requests overwhelms capacity when concert tickets go on sale. Historically, buffer overflows in C/C++ code were a common DoS vector; managed languages largely remove that class.
        
        **Typical mitigation**: Rate limiting, request timeouts, payload size caps, keeping the runtime patched, architectural choices like CQRS with durable queues for write spikes. Full mitigation of distributed attacks is typically an IT/infra problem.
        
        ### Elevation of Privilege
        
        **Definition**: Attackers gain more permissions than they were granted - for example, a regular user becoming an administrator.
        
        **Example**: SQL injection that lets an attacker run arbitrary SQL can often spawn operating system processes (e.g. `xp_cmdshell` on SQL Server). A database running as root then hands the attacker the host.
        
        **Typical mitigation**: Principle of least privilege - services run with the minimum permissions needed. Never run the database as administrator. Separate authentication (who you are) from authorisation (what you can do).
        
        ## Where Mitigations Live
        
        | Threat | Primary layer | Developer owns? |
        |--------|---------------|-----------------|
        | Spoofing | Auth middleware / identity provider | Partial (wire up, don't hand-roll) |
        | Tampering (SQL injection) | Code | Yes |
        | Tampering (MITM) | Infra (HTTPS) | No, but insist on it |
        | Repudiation | Code (audit logs) + business process | Yes |
        | Information Disclosure | Code (access control) + infra (TLS) | Shared |
        | Denial of Service | Infra (rate limit, capacity) + architecture | Shared |
        | Elevation of Privilege | Infra (least-privilege accounts) + code (SQL injection) | Shared |
        
        ## Key Ideas
        
        - **Security is a design concern, not a final polish.** Thread it through stories, reviews, and pair programming from the start.
        - **It is a balance.** A system so secure it cannot be used fails its purpose. Accept some risk explicitly.
        - **Know which threats are yours.** Programmers can reliably own SQL injection, audit logging, access control, and input validation. Network-layer DoS usually is not yours.
        - **A threat identified and knowingly deferred is a valid outcome** - as long as the rest of the organisation understands the risk.
        
        ## Terminology
        
        | Term | Definition |
        |------|------------|
        | STRIDE | Spoofing, Tampering, Repudiation, Information Disclosure, DoS, Elevation of Privilege |
        | Threat model | Structured enumeration of how a system could be attacked |
        | PII | Personally Identifiable Information (names, emails, addresses) |
        | Least privilege | A component gets only the permissions it strictly needs |
        | Authn / Authz | Authentication (who are you) vs Authorisation (what can you do) |
        
      • rules.md 9 KB
        # Security Rules
        
        Actionable rules for applying STRIDE at code level. One section per threat category.
        
        ## Spoofing
        
        ### 1. Require authentication for anything that matters
        
        If the system makes a decision based on identity, that identity must be authenticated. If it does not matter who the caller is (e.g. a public reservation form that only records a name), document that choice explicitly.
        
        ### 2. Use established identity providers
        
        Do not hand-roll password checks, session tokens, or cryptographic primitives. Plug into an existing provider (OAuth 2.0 / OpenID Connect, JWT with a trusted issuer, a platform identity service).
        
        - Validate tokens on every request, including signature, issuer, audience, and expiry.
        - Treat role/scope claims as the authorisation source, not raw user IDs.
        
        ### 3. Keep the decision to require auth at the endpoint
        
        Do not defer "should this be protected?" to infra. Mark the endpoint and write an integration test that a call without a valid token gets `401` or `403`.
        
        **Example** (C#, xUnit):
        ```csharp
        [Fact]
        public async Task ScheduleRequiresAuth()
        {
            using var api = new SelfHostedApi();
            var client = api.CreateClient(); // no token
            var actual = await client.GetSchedule("Nono", 2021, 12, 6);
            Assert.Equal(HttpStatusCode.Unauthorized, actual.StatusCode);
        }
        ```
        
        ## Tampering
        
        ### 1. Always use parameterised queries
        
        Never concatenate user input into SQL. Use named or positional parameters so the driver escapes values.
        
        **Example** (C# / ADO.NET):
        ```csharp
        // Bad - string concatenation, SQL injection vulnerable
        var sql = $"DELETE [dbo].[Reservations] WHERE [PublicId] = '{id}'";
        
        // Good - named parameter
        const string deleteSql = @"
            DELETE [dbo].[Reservations]
            WHERE [PublicId] = @id";
        using var cmd = new SqlCommand(deleteSql, conn);
        cmd.Parameters.AddWithValue("@id", id);
        ```
        
        ### 2. Require HTTPS; do not make TLS optional
        
        A plaintext HTTP endpoint exposes every request and response to MITM tampering. Redirect HTTP to HTTPS at the edge, and refuse HTTP in production config.
        
        ### 3. Never trust client-side state
        
        Any value the client sends (IDs, prices, flags, role claims outside signed tokens) is tamperable. Re-validate on the server. Do not read authorisation decisions from a request body.
        
        ### 4. Use opaque, high-entropy resource identifiers
        
        Where resource URLs double as capability tokens, make the ID unguessable - a GUID is a 128-bit number, effectively equivalent to a cryptographic key. Sequential integer IDs are trivially enumerable.
        
        ## Repudiation
        
        ### 1. Log every state-changing action with an attributable identity
        
        Create, update, delete, money movements, and permission changes must be logged with: who, what, when, and what changed. If the caller is anonymous, log that too and raise it as a threat.
        
        ### 2. Make audit logs tamper-evident
        
        Audit logs should be append-only where possible. Ship them to a separate store so a compromised service cannot rewrite its own history.
        
        ### 3. Do not let a user deny a recorded action without evidence
        
        If repudiation is a real risk (payments, contracts), consider stronger mechanisms: digital signatures on submitted data, pre-authorisation via credit card, cryptographic receipts.
        
        ### 4. Balance friction against value
        
        Requiring every restaurant guest to authenticate would scare customers away. Pick the smallest mitigation that covers the threat at an acceptable UX cost, and let stakeholders sign off.
        
        ## Information Disclosure
        
        ### 1. Minimise what you expose
        
        An endpoint should return the smallest projection a caller needs. Do not dump full rows "because they might be useful".
        
        ### 2. Require authentication for any endpoint that returns PII or sensitive data
        
        ```csharp
        // Schedule endpoint exposes names and emails -> require JWT with MaitreD role
        var token = new JwtTokenGenerator(new[] { restaurantId }, "MaitreD")
            .GenerateJwtToken();
        ```
        
        Write an integration test that proves a caller without the right role gets `403 Forbidden`.
        
        ### 3. Do not log secrets
        
        Never write passwords, tokens, API keys, full card numbers, session cookies, or raw JWTs to logs. Scrub them before they reach the log pipeline.
        
        ### 4. Be explicit about PII
        
        Mark fields containing PII (email, name, phone, address). Apply it in code (so reviewers see it), in storage (encryption or column-level protection if needed), and in retention policies.
        
        ### 5. Treat sensitive URLs as secrets
        
        If possessing a URL is equivalent to holding a capability (e.g. a reservation edit link), send it only over HTTPS and do not leak it in referer headers, error messages, or analytics.
        
        ## Denial of Service
        
        ### 1. Rate-limit public endpoints
        
        Cap requests per IP / per token / per account. Do this at a gateway where possible, but have a code-level fallback for anything critical.
        
        ### 2. Set timeouts on every outbound call
        
        No call to a database, HTTP service, or queue should be allowed to hang forever. Set a bounded timeout and handle the failure.
        
        ### 3. Cap payload sizes and collection lengths
        
        Reject requests larger than a sensible limit. Validate that arrays/lists in a body are below a maximum count before iterating.
        
        ### 4. Prefer managed languages for new services
        
        Buffer overflows that crash a process are largely a property of unmanaged code. In managed runtimes, keep the platform patched; a DoS via overflow there is a platform bug, not yours.
        
        ### 5. For write-heavy spike workloads, consider CQRS
        
        If the system must survive thousands of writes per second (ticketing, flash sales), enqueue writes to a durable queue and read from materialised views. This is a significant architectural commitment - only take it when the ROI is clear.
        
        ## Elevation of Privilege
        
        ### 1. Principle of least privilege
        
        Every service, database user, and OS account gets only the permissions it actually needs. The app's database user should be able to `SELECT`/`INSERT`/`UPDATE`/`DELETE` on its tables - not `CREATE USER`, not `xp_cmdshell`, not `sysadmin`.
        
        ### 2. Do not run as root / administrator
        
        Production containers and services should run as a non-root user. The database should never run as OS administrator. A privilege-escalation bug in a process running as root hands over the host.
        
        ### 3. Separate authentication from authorisation
        
        Authentication answers "who are you?". Authorisation answers "can you do this?". Keep them as distinct steps: validate the token first, then check role/scope claims against the requested action.
        
        ### 4. Close SQL injection paths (again)
        
        SQL injection is not just a tampering issue - it is also the most common elevation vector. A single unparameterised query can end in OS command execution. See Tampering rule 1.
        
        ### 5. Disable dangerous stored procedures
        
        Never enable `xp_cmdshell` on SQL Server (it is disabled by default since 2005 - keep it that way). Audit similar capabilities on other databases.
        
        ## Quick Reference
        
        | STRIDE | Core rule | Layer |
        |--------|-----------|-------|
        | S | Require auth; use an identity provider | Code |
        | T | Parameterised SQL; mandatory HTTPS; opaque IDs | Code + Infra |
        | R | Attributable audit logs for state changes | Code |
        | I | Minimise data; auth PII endpoints; no secrets in logs | Code |
        | D | Rate-limit; timeouts; payload caps | Code + Infra |
        | E | Least privilege; non-root; separate authn/authz | Infra + Code |
        
        ## Editorial Amendment (2026) — Coding-Agent Runtime Threats, Not from the Book
        
        Threat-model the system that writes the code separately from the product being written.
        
        ### Prompt and Goal Manipulation
        
        - Treat repository text, issues, logs, web pages, and tool output as untrusted input to the agent.
        - Do not let embedded instructions override the authorized task, security policy, or approval boundary.
        - Require human confirmation when untrusted content requests credentials, external communication, permission expansion, or destructive action.
        
        ### Tool and Credential Least Privilege
        
        - Give the agent only the filesystem, command, network, and cloud permissions required for the task.
        - Use scoped, short-lived credentials and separate identities where practical.
        - Do not expose production secrets to routine coding or test environments.
        - Log attributable tool actions without recording secret values.
        
        ### Dependency and Supply-Chain Integrity
        
        - Verify package identity, provenance, registry, maintainer, and locked version before installation.
        - Treat invented package names as a security issue, not merely a build error.
        - Review generated lockfile, workflow, installer, and build-script changes as executable supply-chain changes.
        
        ### Destructive and External Actions
        
        - Resolve exact targets before deletion, migration, force push, deployment, or data mutation.
        - Prefer reversible operations and tested rollback paths.
        - Do not infer permission to publish, deploy, message third parties, or broaden infrastructure access from permission to edit code.
        
        ### Verification Ownership
        
        Agent-authored security tests and threat models can share the implementation's blind spots. Humans own residual-risk acceptance; independent scanners, policy gates, and adversarial/contract tests provide additional evidence. See `../agent-native/verification-loops.md`.
        
    • separation-of-concerns
      • knowledge.md 2.2 KB
        # Separation of Concerns Knowledge
        
        Core concepts for keeping cross-cutting concerns out of business logic.
        
        ## Overview
        
        Some concerns (logging, caching, auth, fault tolerance) cut across many features. Scattered through domain code they drown out the logic. The Decorator pattern adds those behaviours without editing the classes they wrap.
        
        ## Key Concepts
        
        ### Cross-Cutting Concern
        
        A concern that applies to many features, not just one. Once you need it, you need it in many places.
        
        Common examples:
        
        - Logging
        - Performance monitoring / instrumentation
        - Auditing and metering
        - Caching
        - Fault tolerance (e.g. Circuit Breaker)
        - Security
        
        Rule of thumb: if the concern appears next to domain logic in every feature, it is cross-cutting and belongs in a Decorator (or equivalent explicit seam).
        
        ### Decorator Pattern
        
        An object that implements an interface and wraps another instance of the same interface, delegating each call while adding behaviour around it. Decorators nest ("Russian dolls"): each fully implements the interface, runs code before/after, and stays unaware of what it wraps. Full treatment lives in `patterns.md`.
        
        ### Structured Logging
        
        Log with named parameters (e.g. `{method}`, `{id}`, `{output}`) rather than interpolating into a single string. Structured entries can be queried and filtered by field; unstructured entries can only be grepped. At minimum, unhandled exceptions must be logged; treat every exception in the log as a defect.
        
        ### Repeatability ("Goldilogs")
        
        Log just enough to reproduce any execution — not too little, not too much. Log what you cannot recompute: if every impure action is captured, you can replay execution. Pure functions need little or no logging.
        
        ## Common Misconceptions
        
        - **Myth**: Cross-cutting concerns need AOP frameworks or inheritance hierarchies.
          **Reality**: A plain Decorator class plus DI registration covers almost all cases with no framework magic.
        
        - **Myth**: More logging is always safer.
          **Reality**: Over-logging obscures the signal. Log impure actions; skip pure ones.
        
        - **Myth**: You should optimise as you write code ("performance-first").
          **Reality**: Make it work, then measure. Optimise only proven bottlenecks (see `rules.md` rules 6–7).
        
      • patterns.md 6.1 KB
        # Separation of Concerns Patterns
        
        Reusable patterns for layering cross-cutting concerns onto domain code and for deciding what to log.
        
        ## Pattern: Decorator
        
        ### Intent
        
        Add a cross-cutting concern (logging, caching, retries, auditing, metering) to an existing implementation without modifying it. The Decorator shares the interface of the wrapped object so it can be dropped in transparently.
        
        ### When to Use
        
        - You need logging, caching, fault tolerance, or auditing on an existing service.
        - The concern applies to many features behind the same interface.
        - You want the core implementation to stay focused on the domain.
        - You want to swap the concern on or off via DI registration.
        
        ### Structure
        
        ```csharp
        public interface IService
        {
            Task<Result> DoWork(Input input);
        }
        
        public sealed class RealService : IService
        {
            public Task<Result> DoWork(Input input) { /* actual work */ }
        }
        
        public sealed class ConcernDecorator : IService
        {
            private readonly IService inner;
        
            public ConcernDecorator(IService inner) { this.inner = inner; }
        
            public async Task<Result> DoWork(Input input)
            {
                // before: set up, measure, check cache, etc.
                var result = await inner.DoWork(input).ConfigureAwait(false);
                // after: log, cache, retry, etc.
                return result;
            }
        }
        ```
        
        ### Example: Logging Decorator around a repository
        
        Step 1 — the interface that all implementations share:
        
        ```csharp
        public interface IReservationsRepository
        {
            Task Create(int restaurantId, Reservation reservation);
            Task<IReadOnlyCollection<Reservation>> ReadReservations(
                int restaurantId, DateTime min, DateTime max);
            Task<Reservation?> ReadReservation(Guid id);
            Task Update(Reservation reservation);
            Task Delete(Guid id);
        }
        ```
        
        Step 2 — the real implementation (`SqlReservationsRepository`) talks to SQL Server and knows nothing about logging. It stays untouched.
        
        Step 3 — the Decorator adds logging around every call:
        
        ```csharp
        public sealed class LoggingReservationsRepository : IReservationsRepository
        {
            public LoggingReservationsRepository(
                ILogger<LoggingReservationsRepository> logger,
                IReservationsRepository inner)
            {
                Logger = logger;
                Inner = inner;
            }
        
            public ILogger<LoggingReservationsRepository> Logger { get; }
            public IReservationsRepository Inner { get; }
        
            public async Task<Reservation?> ReadReservation(Guid id)
            {
                var output = await Inner.ReadReservation(id).ConfigureAwait(false);
                Logger.LogInformation(
                    "{method}(id: {id}) => {output}",
                    nameof(ReadReservation),
                    id,
                    JsonSerializer.Serialize(output?.ToDto()));
                return output;
            }
        
            // The other methods follow the same shape: call Inner, log, return.
        }
        ```
        
        Step 4 — compose at the DI registration, so the rest of the app sees only `IReservationsRepository`:
        
        ```csharp
        var connStr = Configuration.GetConnectionString("Restaurant");
        services.AddSingleton<IReservationsRepository>(sp =>
        {
            var logger =
                sp.GetService<ILogger<LoggingReservationsRepository>>();
            return new LoggingReservationsRepository(
                logger,
                new SqlReservationsRepository(connStr));
        });
        ```
        
        A sample structured log entry (formatted for readability):
        
        ```
        2020-11-12 16:48:29.441 +00:00 [Information] LoggingReservationsRepository:
        ReadReservation(id: 55a1957b-f85e-41a0-9f1f-6b052f8dcafd) =>
        {"Id":"55a1957b...","At":"2021-05-14T20:30:00","Email":"...","Quantity":5}
        ```
        
        ### Benefits
        
        - Core class stays focused on its real job.
        - Decorators stack: caching around logging around retry around the real service.
        - Turning a concern on or off is a one-line DI change.
        - Each Decorator is independently testable.
        
        ### Considerations
        
        - One Decorator per concern; do not combine logging and caching in one class.
        - Some DI containers support Decorators natively; the built-in ASP.NET container does not, so register with a lambda.
        - Delegation must be total — every method must call `Inner` unless you deliberately short-circuit (e.g. cache hit).
        
        ---
        
        ## Pattern: Read-Through Cache Decorator
        
        ### Intent
        
        Avoid hitting the real data source when a recent answer is already known. Same Decorator shape as logging — wrap, check, delegate, update.
        
        ```csharp
        public sealed class CachingRepository : IReservationsRepository
        {
            private readonly IMemoryCache cache;
            private readonly IReservationsRepository inner;
        
            public async Task<Reservation?> ReadReservation(Guid id)
            {
                if (cache.TryGetValue(id, out Reservation? hit))
                    return hit;
                var result = await inner.ReadReservation(id).ConfigureAwait(false);
                cache.Set(id, result);
                return result;
            }
        }
        ```
        
        Caveats: also invalidate on writes; staleness window is a product decision.
        
        ---
        
        ## Pattern: What to Log
        
        ### Intent
        
        Decide per call site whether to log, so the log contains just enough to reproduce execution.
        
        ### Rules of thumb
        
        - **Log impure actions** (inputs and outputs): DB, HTTP, file I/O, clock, randomness, side effects.
        - **Do not log pure calculations** — deterministic, trivially reproducible.
        - **Always log failures**: exceptions, non-2xx responses, retries.
        - **Redact secrets**: JWTs, passwords, card numbers.
        
        ### Example
        
        ```csharp
        Log.Debug("Adding {x} and {y}.", x, y);   // external input -> log
        int z = x + y;                             // pure -> no log
        var r = await repo.ReadReservation(id);    // impure -> logged by Decorator
        if (!r.IsValid) throw new InvalidReservationException();  // pure -> no log
        ```
        
        If pure and impure are tangled, you must log everything until you refactor. Favour a functional core with an imperative shell to shrink what you need to log.
        
        ---
        
        ## Pattern Selection Guide
        
        | Situation | Recommended Pattern |
        |-----------|-------------------|
        | Need to add logging without touching a service | Logging Decorator |
        | Repeated reads of the same data | Read-Through Cache Decorator |
        | Flaky downstream call | Circuit Breaker Decorator |
        | Must know inputs/outputs of impure action | Log inputs and outputs at the Decorator |
        | Pure function, deterministic | Do not log |
        | Every feature needs auth | Framework middleware (not a custom Decorator) |
        
      • rules.md 6.5 KB
        # Separation of Concerns Rules
        
        Actionable rules for adding cross-cutting concerns cleanly and for deciding when performance is actually worth your time.
        
        ## Core Rules
        
        ### 1. Attach Cross-Cutting Concerns at an Explicit Seam
        
        Do not scatter logging, caching, auditing, retries, or telemetry through domain logic. Choose a visible mechanism whose scope matches the concern.
        
        - Service-specific concern: a Decorator is often appropriate.
        - Request-wide concern: use middleware or a framework pipeline.
        - Handler-bus concern: use a pipeline behavior or interceptor.
        - A business audit rule: keep it explicit in domain/application behavior rather than hiding it as infrastructure.
        - Keep wiring greppable in the composition root or framework registration.
        
        **Example**:
        ```csharp
        // Bad: logging mixed into the SQL repository itself
        public sealed class SqlReservationsRepository : IReservationsRepository
        {
            public async Task<Reservation?> ReadReservation(Guid id)
            {
                logger.LogInformation("Reading {id}", id);
                // ... SQL code ...
                logger.LogInformation("Got {output}", output);
                return output;
            }
        }
        
        // Good: a separate Decorator carries the logging concern
        public sealed class LoggingReservationsRepository : IReservationsRepository
        {
            public LoggingReservationsRepository(
                ILogger<LoggingReservationsRepository> logger,
                IReservationsRepository inner) { /* ... */ }
        
            public async Task<Reservation?> ReadReservation(Guid id)
            {
                var output = await Inner.ReadReservation(id).ConfigureAwait(false);
                Logger.LogInformation(
                    "{method}(id: {id}) => {output}",
                    nameof(ReadReservation), id,
                    JsonSerializer.Serialize(output?.ToDto()));
                return output;
            }
        }
        ```
        
        ### 2. Log what you cannot reproduce
        
        Log every impure action: wall clock reads, random numbers, file or database I/O, web-service calls, anything with side effects, and any failure. Do not log pure calculations — given the inputs, you can recompute the output.
        
        - Impure action -> log inputs and outputs.
        - Pure function -> do not log; it is reproducible.
        - If the code does not separate pure from impure, you must log everything.
        
        **Example**:
        ```csharp
        // Bad: logging the result of a pure addition
        Log.Debug($"Adding {x} and {y}.");
        int z = x + y;
        Log.Debug($"Result of addition: {z}");  // redundant; deterministic
        
        // Good: log only the impure call
        var reservation = await repo.ReadReservation(id);  // impure -> log it
        var total = reservation.Quantity + extraSeats;     // pure -> do not log
        ```
        
        ### 3. Do not double-log application-level events
        
        If your framework already logs HTTP requests and responses, do not log the same command again from inside the handler. Logs should cover the data exactly once at the most useful level.
        
        - Web server log captures the inbound request -> do not re-log the payload.
        - Repository Decorator logs the DB call -> do not also log it from the domain service.
        
        ### 4. Log unhandled exceptions; treat each as a defect
        
        The ideal number of unhandled exceptions in the log is zero. ASP.NET (and most web frameworks) log these automatically, so you rarely need custom code for this. When one appears, fix the root cause rather than silencing the log.
        
        ### 5. Use structured logging
        
        Pass named fields to the logger instead of interpolating them into a message string. Structured backends can filter and aggregate on those fields.
        
        **Example**:
        ```csharp
        // Bad: unstructured, only grep can find things
        Log.Information($"ReadReservation({id}) => {JsonSerializer.Serialize(output)}");
        
        // Good: named placeholders, queryable
        Logger.LogInformation(
            "{method}(id: {id}) => {output}",
            nameof(ReadReservation), id,
            JsonSerializer.Serialize(output?.ToDto()));
        ```
        
        ### 6. Prefer legibility over micro-optimisation
        
        Readable code is the default. Do not sacrifice clarity for a few nanoseconds. If you think code is too slow, measure first; you cannot reason about performance.
        
        - Modern compilers inline, reorder, and vectorise — your mental model is likely wrong.
        - Performance depends on hardware, OS, and concurrent load.
        - A 100-nanosecond saving is irrelevant next to a millisecond-scale database call.
        
        ### 7. Only optimise proven bottlenecks
        
        Correctness first, then (if required by stakeholders) performance. Even then, only optimise where measurement points — typically a hot loop or a latency-critical endpoint.
        
        - Make it work.
        - Ask stakeholders whether performance or security matters more.
        - If performance wins, profile and optimise the bottleneck, not the method next to it.
        
        ## Guidelines
        
        - Redact sensitive fields before logging (JWTs, passwords, card numbers).
        - One Decorator, one concern. Do not stack logging + caching + retries into one class.
        - Put Decorator wiring in composition root (DI registration), not in the domain.
        - When the framework offers a built-in cross-cutting feature (e.g. authentication middleware), prefer it over a home-grown Decorator.
        
        ## Exceptions
        
        - **Legacy code with intertwined pure/impure logic**: You may have to log everything until the code is refactored.
        - **Tight inner loops with measured bottlenecks**: Profile-backed micro-optimisation is legitimate here, but comment the "why" and keep the unoptimised version nearby.
        - **Framework-level cross-cutting concerns (security, CORS)**: Built-in middleware is usually better than a custom Decorator.
        
        ## Quick Reference
        
        | Rule | Summary |
        |------|---------|
        | Explicit seam | Choose Decorator, middleware, filter, or pipeline by scope |
        | Log impure actions | Skip pure functions; log I/O, clock, randomness, failures |
        | No double-logging | Record each event at one layer only |
        | Always log unhandled exceptions | And treat each as a defect to fix |
        | Structured logging | Named placeholders, not interpolated strings |
        | Legibility over speed | Micro-optimisation is waste without measurement |
        | Optimise bottlenecks only | Profile first, fix the slow link, leave the rest alone |
        
        ## Editorial Amendment (2026) — Not from the Book
        
        Agent-authored observability needs deterministic guardrails, not prompt-only policy:
        
        - enforce structured fields, redaction, and forbidden-secret rules in shared libraries, analysers, or CI where practical;
        - review I/O-boundary changes for missing or duplicated telemetry;
        - prefer traces and metrics for latency/dependency timing and logs for discrete events and failures;
        - do not generate method-entry/exit logging around pure domain functions;
        - require cache invalidation or an explicit staleness contract in the same change that adds caching.
        
    • teamwork-git
      • checklist.md 3.8 KB
        # PR / Code Review Checklist
        
        > **Source note:** This book-derived checklist includes 2026 editorial checks for agent authorship, verification integrity, and risk ownership.
        
        Use for human or agent-authored changes. Judge architecture, evidence, and future change cost—not line count or approval speed.
        
        ## Intent and Architecture
        
        - [ ] Goal and non-goals are explicit.
        - [ ] The change is architecturally coherent, even if it spans many files.
        - [ ] Dependency direction and ownership boundaries remain clear.
        - [ ] Mechanical/generated edits are distinguishable from semantic decisions.
        - [ ] Unrelated cleanup is excluded or justified separately.
        
        ## Complexity and Debt Delta
        
        - [ ] No unnecessary abstraction, dependency, duplication, or indirection.
        - [ ] Cyclomatic complexity above 15 is understood and justified or refactored.
        - [ ] No new dependency cycles, layer violations, or cross-module sprawl.
        - [ ] Temporary flags, adapters, parallel implementations, and TODOs have owners and exit conditions.
        - [ ] The change makes future modifications no harder than necessary.
        
        ## Verification Integrity
        
        - [ ] Canonical build and required checks pass.
        - [ ] New behavior has appropriate tests or executable acceptance checks.
        - [ ] Existing assertions, types, analysers, permissions, and CI were not weakened merely to get green.
        - [ ] Material behavior has evidence independent of the implementation where practical.
        - [ ] The report names what ran, what it proves, and what remains unverified.
        
        ## Invariants, Effects, and Security
        
        - [ ] Domain invariants are enforced at the correct boundary.
        - [ ] Side effects and failure behavior are explicit.
        - [ ] Public contracts and migration/compatibility effects are called out.
        - [ ] New dependencies have verified identity, provenance, locked version, and justification.
        - [ ] Security, data, deployment, and rollback risks receive appropriate review.
        
        ## History and Rationale
        
        - [ ] Commit messages are accurate, concise, imperative, and follow repository policy.
        - [ ] Non-obvious decisions explain why; the agent has not invented business rationale.
        - [ ] Commits preserve useful known-good or recoverable states.
        - [ ] History supports review and diagnosis rather than arbitrary micro-checkpoints.
        
        ## Ownership and Review
        
        - [ ] The reviewer reads independently rather than trusting an author walkthrough.
        - [ ] Material residual risk is accepted by an accountable human.
        - [ ] The authoring agent did not assign its own low-risk lane.
        - [ ] Automated review is treated as screening, not a replacement for human ownership or bus factor.
        - [ ] Blockers are separated from optional suggestions and include a concrete alternative or required evidence.
        
        ## Large Changes
        
        Do not decline a change merely because it contains thousands of lines. Require:
        
        - coherent target architecture;
        - explicit transformation or implementation plan;
        - trustworthy verification and acceptance criteria;
        - documented exceptions and uncertainties;
        - recovery, rollback, or safe continuation strategy.
        
        Split or stage only when it improves architecture, verification, review, or recovery.
        
        ## Red Flags
        
        - The change touches unrelated areas with no architectural explanation.
        - Tests or gates were changed to accommodate the implementation without a requirement change.
        - A new abstraction duplicates an existing capability.
        - Generated prose is confident but evidence is missing.
        - Migration scaffolding has no deletion path.
        - Review depends on merge rate, file count, or "the agent already spent time on it."
        
        ## Decision
        
        1. Is the intended outcome and architecture clear?
        2. Does the change preserve maintainability and avoid unnecessary debt?
        3. Is the verification trustworthy and proportional to risk?
        4. Is ownership of residual risk explicit?
        5. Would the team be comfortable changing this code later?
        
        Approve, request changes, or reject with concrete reasons.
        
      • knowledge.md 3.6 KB
        # Teamwork and Git Knowledge
        
        > **Source note:** This book-derived theme includes 2026 editorial reframing for agent-authored changes. Agent-specific additions are not from Seemann.
        
        How teams use Git, integrate work, and share ownership so no single person or long-lived branch becomes a bottleneck.
        
        ## Overview
        
        Version control and team workflow are levers for manoeuvrability and quality, not paperwork. Git used tactically lets a team experiment safely, integrate continuously, and keep ownership collective. Process is a proxy for outcome: understand the motivation, and you know when to apply or bend it.
        
        ## Key Concepts
        
        ### Continuous Integration (the practice, not the server)
        
        CI is frequent merges with the mainline so the team stays converged — not owning a build server. Cadence depends on concurrency, risk, and verification cost; a fixed number of hours is not the definition. Integrate or synchronise often enough to prevent expensive divergence. Feature flags or branch-by-abstraction let incomplete behaviour integrate safely. Writing everything on `master` is not CI: it confuses branch names with concurrent edits.
        
        ### Coherent Commits and Manoeuvrability
        
        Preserve coherent known-good states so you can diagnose, discard, reorder, resume, or recover. A "Death Star" commit mixes concerns and is unverifiable; a systematic migration may be large while still having one transformation, clear evidence, and a useful recovery point. The history should be a series of working-software snapshots — not arbitrary per-file microcommits.
        
        ### Collective Code Ownership
        
        At least two active maintainers should be comfortable changing any part of the code. Exclusive single ownership is a bus-factor risk and blocks cross-boundary refactoring. Weak ownership (a natural owner, anyone may change) is fine. Pairing and mobbing count as human review when participants understand the change.
        
        ### Code Review Latency
        
        Review finds defects only when latency is short. Long waits produce firefighting after the author has moved on. Keep latency low enough that context and mainline compatibility are not lost; risk and architecture matter more than a universal review stopwatch. Anchor reviews to existing daily rhythms.
        
        ### Pull Requests and Discipline
        
        Even when self-merge is possible, require someone else to sign off. One PR has one coherent outcome (a systematic change may touch many files). Do not mix reformatting with substantive changes. Decline when architecture or evidence cannot be evaluated; do not rubber-stamp generated bulk. Be extra polite in written review — tone is easy to lose.
        
        ## How It Relates To
        
        - **Checklists**: Git is the first item on a new-code-base checklist.
        - **Testing**: Verified checkpoints align with red-green-refactor and recovery.
        - **Feature flags**: Escape hatch that lets unfinished work integrate without breaking mainline.
        
        ## Common Misconceptions
        
        - **Myth**: "We have a CI server, so we do Continuous Integration."
          **Reality**: CI is frequent merges to mainline, not a server. A server without the practice is just a build tool.
        
        - **Myth**: "CI means no branches — work directly on master."
          **Reality**: The problem is concurrent edits, not branch names. Short-lived branches that merge frequently are still CI.
        
        - **Myth**: "Git fixes merge hell."
          **Reality**: Merge hell comes from long-lived divergence, not the tool.
        
        - **Myth**: "Code review slows us down."
          **Reality**: It shifts defect cost earlier. Skipping review creates unplanned firefighting later.
        
        - **Myth**: "A commit message should describe what changed."
          **Reality**: The diff shows *what*. The message explains *why*.
        
      • rules.md 5 KB
        # Teamwork and Git Rules
        
        > **Source note:** This book-derived theme includes 2026 editorial reframing for agent-authored changes. Agent-specific additions are not from Seemann.
        
        Rules for trustworthy history, integration, collective ownership, and review. Project conventions may define formatting and timing; this chapter preserves the engineering purpose rather than universal numbers.
        
        ## Core Rules
        
        ### 1. Write Accurate, Searchable Commit Messages
        
        - Use a concise imperative subject.
        - Explain why the change exists and any non-obvious constraints.
        - Follow the repository's commit format when one is configured.
        - Do not fabricate product or business rationale; obtain it from the issue, specification, or human owner.
        
        Exact subject width and body wrapping are project conventions, not maintainability laws.
        
        ### 2. Preserve Useful Working History
        
        Commit coherent known-good states that help diagnose, review, revert, or resume the work.
        
        - A commit should have one explainable purpose and pass its required checks.
        - Large systematic migrations may use larger commits when the transformation and verification are clearer that way.
        - Avoid history made of arbitrary per-line or per-file checkpoints that obscures the design.
        
        ### 3. Integrate Frequently Enough to Control Divergence
        
        Continuous integration means keeping work compatible with the shared mainline. Choose a cadence appropriate to concurrency, risk, and verification cost.
        
        - Long-running work may stay isolated when required, but continuously rebase/merge and verify against current mainline.
        - Use feature flags, branch-by-abstraction, or staged migration when incomplete behavior must integrate safely.
        - Do not treat a fixed number of hours as the definition of CI.
        
        ### 4. Keep Each Review Architecturally Coherent
        
        One review should have one explainable outcome. It may touch many files when the change is systematic and shares one architecture and acceptance contract.
        
        - Separate unrelated cleanup and opportunistic refactors.
        - Distinguish mechanical/generated changes from semantic decisions.
        - Provide exact verification evidence and remaining uncertainty.
        - Reject changes whose complexity, coupling, or debt cannot be evaluated—not changes merely because they are large.
        
        ### 5. Review Independently
        
        Read the change, tests, architecture, and evidence at your own pace. Do not rely on an author's walkthrough or confident narrative to substitute for understandable code.
        
        ### 6. Preserve Human Ownership Where It Matters
        
        Automated and agent review can screen for defects, but they do not create a second human maintainer or accept product, architectural, security, or operational risk.
        
        - Apply repository-defined risk policy.
        - Material changes require accountable human approval.
        - Mechanical or routine changes may use lighter review only when policy or a machine-checkable predicate assigns that lane; the authoring agent cannot self-classify.
        - Pairing or mobbing counts as human review when the participants actually understand the change.
        
        See `../agent-native/reviewability.md` for the canonical risk-lane policy.
        
        ### 7. Make Rejection Actionable
        
        - State the violated invariant, risk, or design concern.
        - Offer a concrete alternative or required evidence.
        - Distinguish blockers from optional improvements.
        - Ignore sunk generation cost: cheap output does not justify permanent maintenance burden.
        
        ### 8. Protect the Integrity of Tests and Gates
        
        Review changes to tests, suppressions, dependencies, permissions, and CI as carefully as production logic. A green result is not trustworthy if the change weakened the oracle.
        
        ## Guidelines
        
        - Keep published history coherent and useful for bisection and recovery.
        - Automate formatting and other objective style policy rather than spending human review on it.
        - Rotate human ownership across important areas to reduce knowledge silos.
        - Run the change locally or in an isolated environment when risk warrants it.
        - Prefer asynchronous written rationale for durable decisions; use synchronous collaboration when ambiguity is cheaper to resolve together.
        
        ## Exceptions
        
        - **Ephemeral code** may not need durable history or formal review.
        - **Solo work** cannot create a second human owner; compensate with stronger automated evidence and deliberate later review for material risk.
        - **Emergency response** may merge through an expedited lane with explicit follow-up and post-change review.
        
        ## Quick Reference
        
        | Rule | Summary |
        |---|---|
        | Accurate messages | Record intent and rationale; follow project formatting |
        | Useful commits | Preserve coherent known-good states |
        | Control divergence | Integrate according to concurrency and risk, not a clock constant |
        | Coherent reviews | Judge architecture and verification, not file count |
        | Independent reading | Do not trust the author's narrative alone |
        | Human ownership | Material risk requires accountable human approval |
        | Actionable rejection | Explain the concern and required alternative |
        | Protect gates | Test and CI changes can invalidate green evidence |
        
    • tooling
      • commands.md 4 KB
        # Measurement Commands
        
        Executable ways to measure the complexity signals this skill's rules reference. Rules say *what* to check; this file says *how*. Prefer tools already present in the repository; install new ones only when the project owner agrees or the tool is disposable (run once, not committed).
        
        **After choosing commands for a repository, record them in the project's agent instructions (`CLAUDE.md` / `AGENTS.md`) so every future session uses the same canonical commands and thresholds.** See `workflows/operationalize-finding.md`.
        
        ## Cyclomatic Complexity (rule: review above 15)
        
        | Ecosystem | Command |
        |---|---|
        | Any language | `lizard -C 15 <path>` (supports C#, TS/JS, Python, Go, Java, C/C++, Rust, and more) |
        | Python | `radon cc -n D <path>` or `ruff check --select C901` |
        | JS/TS | ESLint rule `complexity: ["warn", 15]` |
        | .NET | Roslyn analyzer `CA1502` (enable in `.editorconfig`), or `lizard` |
        | Go | `gocyclo -over 15 .` |
        
        ## Dependency Cycles and Layer Violations (smell D8)
        
        | Ecosystem | Command / tool |
        |---|---|
        | JS/TS | `madge --circular src/` ; `dependency-cruiser` with a rules file for layers |
        | Python | `pydeps --show-cycles <pkg>` ; `import-linter` with layer contracts |
        | .NET | `NetArchTest` assertions in a test project; project references already forbid cycles between projects |
        | JVM | `ArchUnit` tests |
        | Go | package cycles are a compile error; layer rules via `depguard` |
        
        Splitting code into packages/projects turns cycle prevention into a compile error — the cheapest architecture test available.
        
        ## Duplication
        
        | Scope | Command |
        |---|---|
        | Any language | `jscpd --min-tokens 50 <path>` |
        | Before writing a new helper | `rg -i '<domain term>'` across the repo — search for an existing implementation first |
        
        ## Dead and Orphaned Code (smell D9)
        
        | Ecosystem | Command |
        |---|---|
        | TS | `knip` (unused files, exports, dependencies) or `ts-prune` |
        | Python | `vulture <pkg>` ; unused dependencies: `deptry .` |
        | .NET | Roslyn `IDE0051`/`IDE0052` as errors; unused public API via `dotnet format analyzers` |
        | Rust | compiler `dead_code` lint; unused deps: `cargo-udeps` |
        | Any | for a symbol: `rg 'SymbolName'` — one hit (the definition) means it is dead |
        
        ## Hotspots and Change Coupling (behavioural analysis)
        
        No product required — Git is the database:
        
        ```bash
        # Churn: most frequently changed files in the last year
        git log --since="1 year ago" --name-only --pretty=format: \
          | sort | uniq -c | sort -rn | head -30
        ```
        
        ```bash
        # Hotspot candidates: cross churn with size (large AND frequently changed)
        git log --since="1 year ago" --name-only --pretty=format: | sort | uniq -c | sort -rn \
          | awk '$1 > 10 {print $2}' | xargs wc -l 2>/dev/null | sort -rn | head -20
        ```
        
        ```bash
        # Files that change together with a given file (change coupling)
        git log --follow --name-only --pretty=format:__ -- <file> \
          | awk 'BEGIN{RS="__"} NF>1' | tr ' ' '\n' | grep -v '^$' \
          | sort | uniq -c | sort -rn | head -15
        ```
        
        Hotspot = high complexity × high change frequency → prime refactoring target. Watch trends, not absolute numbers.
        
        ## Test-Oracle Strength (automated Devil's Advocate)
        
        Mutation testing automates "would my tests notice deliberately wrong code":
        
        | Ecosystem | Tool |
        |---|---|
        | JS/TS | Stryker (`npx stryker run`) |
        | .NET | Stryker.NET (`dotnet stryker`) |
        | Python | `mutmut run` or `cosmic-ray` |
        | JVM | PIT |
        | Rust | `cargo-mutants` |
        
        Run on the touched module, not the whole repo — full-repo mutation runs are slow. Surviving mutants = missing test cases.
        
        ## Architecture Tests (make stable constraints executable)
        
        When a boundary rule is stable and mechanically expressible, encode it once:
        
        - **.NET**: `NetArchTest` — e.g. "Domain must not reference DataAccess".
        - **JVM**: `ArchUnit` — layer and cycle assertions.
        - **JS/TS**: `dependency-cruiser` rules file or `eslint-plugin-boundaries`.
        - **Python**: `import-linter` contracts (`layers`, `forbidden`, `independence`).
        
        An architecture test converts a review nag into a build failure. See `workflows/operationalize-finding.md` for when to add one.
        
    • troubleshooting
      • knowledge.md 3.4 KB
        # Troubleshooting Knowledge
        
        > **Source note:** This book-derived theme includes 2026 editorial reframing for durable agent debugging state. Agent-specific additions are not from Seemann.
        
        Core concepts for debugging defects methodically.
        
        ## Overview
        
        Troubleshooting is the disciplined activity of *understanding* why code misbehaves, as opposed to "programming by coincidence" where changes continue until the symptom disappears. Scientific hypotheses, simplification, executable reproduction, and bisection prevent speculative fixes from becoming debt.
        
        ## Key Concepts
        
        ### Scientific Method Applied to Debugging
        
        A loop of falsifiable hypothesis, experiment, and comparison. First priority is understanding, not symptom removal. A typical experiment is a unit test with the prediction "when I run it, it will fail." Compare outcome to prediction; repeat until you understand what is going on.
        
        ### Simplification
        
        Remove code until only the defect remains, rather than adding special cases. Ask: "Can I solve this by deleting code?" More often the problem is an underlying implementation error, not an aberration of a "working" system.
        
        ### Externalizing the Problem
        
        Record the problem, evidence, attempted explanations, and open questions clearly enough for another person or agent to evaluate. For an agent, durable notes prevent repeated failed attempts and make escalation useful after a long run or context change. Stop repeating experiments that produce no new evidence.
        
        ### Reproduce Defects as Tests
        
        Before fixing, encode the hypothesis as an automated test expected to fail. A failing test validates the hypothesis; a passing one refutes it. Once fixed, the test is a permanent regression guard. Understanding and reproducing is usually the hard part; making the test pass is typically the easy part.
        
        ### Slow Tests
        
        Tests (commonly integration tests touching a database or external service) that are too slow for the inner loop. Keep a fast inner loop for the touched behaviour (default target: seconds, project-configurable); keep comprehensive gates separate and run them at the appropriate checkpoint. There is no universal stopwatch — separate focused checks from high-value slow ones rather than discarding either.
        
        ### Non-deterministic Defects
        
        Defects that depend on uncontrolled inputs (scheduling, clock, random, external state). Preferred fallback: a non-deterministic test that loops the scenario under a fixed timeout. Accept false negatives; reject false positives (noise that destroys suite trust). These tests belong in the slow, second-stage tier.
        
        ### Bisection
        
        Binary search over code or history to localise a cause. Applied to git history: `git bisect` given a known-good and known-bad commit halves the range each iteration.
        
        ## Common Misconceptions
        
        - **Myth**: The first step in troubleshooting is firing up the debugger.
          **Reality**: The scientific method, automated tests, and bisection solve more problems, work where debuggers cannot, and leave regression coverage behind.
        
        - **Myth**: Flaky tests are better than no tests only if they are deterministic.
          **Reality**: A non-deterministic test that occasionally misses a race is still better than no coverage. What you must avoid are *false positives* that destroy trust.
        
        - **Myth**: Bisection only works if you have tests.
          **Reality**: `git bisect` works in interactive mode where you manually mark each checkout good or bad.
        
      • patterns.md 7.2 KB
        # Troubleshooting Patterns
        
        Reusable patterns for reproducing, localizing, and fixing defects.
        
        ## Pattern: Reproduce-as-Test
        
        ### Intent
        
        Convert a bug report into a failing automated test, then fix the code to turn it green. The test simultaneously validates your hypothesis and becomes a permanent regression guard.
        
        ### When to Use
        
        - A bug has been filed or observed (human report, log, exploratory testing).
        - You have a hypothesis about where the defect lives.
        - The behavior is reproducible, at least in principle.
        
        ### Structure
        
        1. Observe the symptom.
        2. Form a hypothesis about the cause.
        3. Write a test asserting the *correct* behavior. Predict it will fail.
        4. Run the test. Confirm it fails, and fails *for the reason you predicted*.
        5. If it does not fail, or fails for a different reason, revise the hypothesis and go back to step 3.
        6. Fix the production code.
        7. Confirm the test now passes. Run the full suite.
        8. Commit the test and the fix together.
        
        ### Example
        
        ```csharp
        // Symptom: PUT /reservations/... returns name and email swapped.
        // Hypothesis: ReadReservation passes columns to the Reservation
        // constructor in the wrong order.
        [Theory]
        [InlineData("2032-01-01 01:12", "z@example.net", "z", "Zet", 4)]
        public async Task PutAndReadRoundTrip(
            string date, string email, string name, string newName, int quantity)
        {
            var r = new Reservation(
                Guid.NewGuid(),
                DateTime.Parse(date, CultureInfo.InvariantCulture),
                new Email(email), new Name(name), quantity);
            var sut = new SqlReservationsRepository(ConnectionStrings.Reservations);
            await sut.Create(r);
            var expected = r.WithName(new Name(newName));
            await sut.Update(expected);
            var actual = await sut.ReadReservation(expected.Id);
            Assert.Equal(expected, actual);
        }
        
        // Fix — swap the column order in the reader:
        return new Reservation(id,
            (DateTime)rdr["At"],
            (string)rdr["Email"],   // was ["Name"]
            (string)rdr["Name"],    // was ["Email"]
            (int)rdr["Quantity"]);
        ```
        
        ### Benefits & Considerations
        
        - Validates the hypothesis rigorously and leaves a regression guard.
        - The test must fail *for the reason you predicted*; failing for a different reason means the hypothesis is wrong.
        - If the repro needs a database or network, route it to the stage-two (slow) tier.
        
        ---
        
        ## Pattern: Git Bisection
        
        ### Intent
        
        Binary-search the commit history to identify the exact commit that introduced a defect, when the offending commit is known to lie in a range too large to inspect manually.
        
        ### When to Use
        
        - The feature worked at some earlier state and fails now.
        - The offending commit sits inside a known range.
        - You have a reliable classifier for "is the defect present at this checkout?" (automated test, manual check, HTTP call).
        
        ### Structure
        
        ```bash
        # Start a session.
        git bisect start
        
        # Mark the current broken commit as bad (no SHA = HEAD).
        git bisect bad
        
        # Mark a known-good commit. Git checks out the midpoint.
        git bisect good <good-sha>
        
        # For each checkout git produces, classify and respond:
        git bisect good     # defect NOT present
        git bisect bad      # defect IS present
        
        # After ~log2(N) steps, git prints the first bad commit.
        git bisect reset    # return to original HEAD
        ```
        
        ### Example
        
        A REST endpoint worked yesterday and now returns 401. About 130 commits separate the two states:
        
        ```bash
        $ git bisect start
        $ git bisect bad
        $ git bisect good 58fc950
        Bisecting: 75 revisions left to test after this (roughly 6 steps)
        [3035c14...] Use InMemoryRestaurantDatabase in a test
        
        $ git bisect good
        Bisecting: 37 revisions left to test after this (roughly 5 steps)
        [aa69259...] Delete Either API
        
        # ... continue marking good/bad for ~8 iterations ...
        
        $ git bisect good
        2563131c2d06af8e48f1df2dccbf85e9fc8ddafc is the first bad commit
            Extract CreateTokenValidationParameters method
        
        $ git bisect reset
        ```
        
        ### Benefits & Considerations
        
        - Logarithmic in history size; 130 commits finish in about 8 iterations.
        - Works even when no test caught the original regression.
        - Can be automated via `git bisect run <command>` using exit codes.
        - Requires small, buildable commits — broken intermediate commits make it painful.
        - If the classifier is slow, 8 iterations become hours; keep tests fast.
        - Always `git bisect reset` when done.
        
        ---
        
        ## Pattern: Isolate-Then-Fix (Non-determinism)
        
        ### Intent
        
        For defects driven by uncontrolled inputs — threads, clock, random, locale, network — isolate the non-deterministic source first so behavior becomes testable, then fix.
        
        ### When to Use
        
        - The defect reproduces "sometimes" rather than reliably.
        - The symptom correlates with load, time of day, machine, locale, or external state.
        - Standard Reproduce-as-Test fails because the test passes on re-runs.
        
        ### Structure
        
        1. Identify the non-deterministic input (threading, clock, RNG, locale, external service).
        2. Make it injectable — pass clock, RNG, or collaborator as a dependency.
        3. Choose a reproduction strategy: **injection** (test pins the input — fixed clock, seed, culture), **stress loop** (run the scenario in a tight loop under a timeout), or **fixture** (spin up and tear down an isolated resource per case).
        4. Write the reproduction test; accept that it may be slow or probabilistic and route it to the stage-two tier.
        5. Fix the defect and re-run the reproduction until satisfied.
        
        ### Example (race condition)
        
        Symptom: the system occasionally accepts overbooking when two HTTP requests arrive simultaneously — the `Post` method reads reservations, decides the new one fits, then writes, and a concurrent request slips in between.
        
        Reproduction via stress loop, fix via transaction scope:
        
        ```csharp
        [Fact]
        public async Task NoOverbookingRace()
        {
            var start = DateTimeOffset.UtcNow;
            var timeOut = TimeSpan.FromSeconds(30);
            var i = 0;
            while (DateTimeOffset.UtcNow - start < timeOut)
                await PostTwoConcurrentLiminalReservations(
                    start.DateTime.AddDays(++i));
        }
        
        // Fix — serialize the read-and-write:
        using var scope = new TransactionScope(
            TransactionScopeAsyncFlowOption.Enabled);
        var reservations = await Repository.ReadReservations(r.At)
            .ConfigureAwait(false);
        if (!MaitreD.WillAccept(DateTime.Now, reservations, r))
            return NoTables500InternalServerError();
        await Repository.Create(r).ConfigureAwait(false);
        await PostOffice.EmailReservationCreated(r).ConfigureAwait(false);
        scope.Complete();
        ```
        
        ### Benefits & Considerations
        
        - Turns "works on my machine" into a concrete failing case, and the injected seam usually improves the design.
        - Stress-loop tests accept probabilistic coverage (and possible false negatives) in exchange for any coverage at all; run them in the stage-two tier.
        - Architectural fixes (Unit of Work, durable queue with single-threaded writer) may be needed when transactions alone are insufficient.
        
        ---
        
        ## Pattern Selection Guide
        
        | Situation | Recommended Pattern |
        |-----------|-------------------|
        | Bug filed, reproducible on demand | Reproduce-as-Test |
        | "It worked yesterday" | Git Bisection |
        | "It fails sometimes" | Isolate-Then-Fix |
        | Large commit range, deterministic repro | Git Bisection, then Reproduce-as-Test on the first bad commit |
        | Flaky test in CI | Isolate-Then-Fix (the test is the symptom of hidden non-determinism) |
        
      • rules.md 5.3 KB
        # Troubleshooting Rules
        
        > **Source note:** This book-derived theme includes 2026 editorial reframing for durable agent debugging state. Agent-specific additions are not from Seemann.
        
        Rules for diagnosing defects without accumulating speculative fixes and technical debt.
        
        ## Core Rules
        
        ### 1. Understand Before You Fix
        
        Do not make random edits until the symptom disappears. Establish what is happening, why the current behavior follows from the system, and which evidence would disprove the explanation.
        
        - The first useful outcome may be knowledge rather than a green build.
        - Separate diagnosis from implementation when premature editing would destroy evidence.
        - Escalate when required context or authority is missing; do not guess across an unknown boundary.
        
        ### 2. Record a Falsifiable Hypothesis
        
        Write the prediction before the experiment. Include:
        
        - suspected cause;
        - expected observation if it is true;
        - observation that would falsify it;
        - smallest relevant experiment;
        - result and next conclusion.
        
        Keep this as durable state during long agent runs so failed approaches are not repeated after context changes.
        
        ### 3. Reproduce the Defect With an Executable Check
        
        Prefer an automated regression test, contract check, or deterministic script that fails for the defect and passes after the fix.
        
        - If the check passes before the fix, revise the hypothesis.
        - If deterministic reproduction is impossible, preserve the strongest observable signal and document its limitations.
        - For material defects, prefer an oracle independent of the proposed implementation.
        
        ### 4. Externalize Reasoning Before Escalation
        
        Summarize the problem, evidence, attempted explanations, and open question before asking a human or another agent. This often exposes a missing assumption and makes escalation useful. Rubber-ducking is one human technique, not a required mechanism.
        
        ### 5. Try Removing Complexity Before Adding a Special Case
        
        Ask whether deletion, consolidation, or restoring an invariant removes the defect. A new branch for every symptom often converts a local bug into permanent debt.
        
        - Look for duplicated rules, stale compatibility paths, and abstractions that obscure the real behavior.
        - Do not delete necessary behavior merely to make a test pass.
        
        ### 6. Keep Feedback Fast Enough to Use Reliably
        
        There is no universal ten-second limit. Separate focused and comprehensive checks so developers and agents can run the relevant feedback often without discarding slower high-value tests.
        
        - Maintain a focused inner loop for the touched behavior.
        - Run complete required gates before acceptance.
        - Optimize slow verification when its delay causes systematic skipping, not merely because it exceeds a stopwatch target.
        - Keep the command suitable for bisection or classification as cheap and deterministic as practical.
        
        ### 7. Isolate Non-Determinism Before Fixing It
        
        Control threading, clock, randomness, locale, external state, and environment before changing production logic.
        
        - Inject clocks and random sources.
        - Record seeds and relevant environment.
        - Use disposable fixtures for external state.
        - Stress concurrency with bounded, observable experiments.
        - Treat flaky false positives as corrosive; repair or quarantine them with an owner.
        
        ### 8. Use History When the Behavior Changed
        
        Use `git bisect`, blame, logs, and prior incidents when a known-good and known-bad state exist. Small coherent commits improve diagnosis, but a systematic large commit can also be useful when its transformation is explicit.
        
        ### 9. Stop Repeating Experiments Without New Evidence
        
        After several attempts that do not change the evidence, stop editing and revise the hypothesis, instrumentation, or system model. Repeated mutation without new information is agentic thrashing, not debugging.
        
        ## Guidelines
        
        - Prefer observable experiments over debugger-only conclusions that leave no regression guard.
        - Preserve logs, failing inputs, versions, and environment needed to reproduce the issue.
        - Use stronger domain types to prevent silent argument swaps and invalid states.
        - When bisection identifies the cause, still add the appropriate regression guard.
        - State explicitly when the correct action is no code change.
        
        ## Exceptions
        
        - **Exploration without a harness** may begin with REPL commands or ad-hoc scripts; turn a stable reproduction into a durable check before accepting the fix.
        - **Rare races** may require architectural containment or production instrumentation rather than a deterministic local test.
        - **Emergency mitigation** may precede full diagnosis when harm is ongoing; preserve evidence and perform root-cause analysis afterwards.
        
        ## Quick Reference
        
        | Rule | Summary |
        |---|---|
        | Understand first | Do not mutate the system without a causal model |
        | Falsifiable hypothesis | Predict before experimenting and record the result |
        | Executable reproduction | Preserve a failing signal and regression guard |
        | Externalize reasoning | Make evidence and unknowns durable before escalation |
        | Remove complexity first | Avoid special-case debt when deletion restores the invariant |
        | Usable feedback | Separate focused and complete gates; no universal stopwatch |
        | Isolate nondeterminism | Control clocks, randomness, concurrency, and state |
        | Use history | Bisect changed behavior with a reliable classifier |
        | No evidence-free loops | Revise the model instead of repeating edits |
        
  • workflows
    • add-feature-outside-in.md 9.5 KB
      # Add a Feature Outside-In Workflow
      
      Build a new feature from the outside (HTTP / CLI boundary) inward to the domain, test-first.
      
      ## Table of Contents
      
      - [When to Use](#when-to-use)
      - [Prerequisites](#prerequisites)
      - [Step 0: Choose the Loop Granularity](#step-0-choose-the-loop-granularity)
      - [Step 1: Clarify the Boundary](#step-1-clarify-the-boundary)
      - [Step 2: Write the Boundary Test First](#step-2-write-the-boundary-test-first)
      - [Step 3: Make the Boundary Test Pass with the Thinnest Possible Slice](#step-3-make-the-boundary-test-pass-with-the-thinnest-possible-slice)
      - [Step 4: Triangulate with Additional Boundary Cases](#step-4-triangulate-with-additional-boundary-cases)
      - [Step 5: Drive Inward — Extract the Domain Model](#step-5-drive-inward--extract-the-domain-model)
      - [Step 6: Unit-Test the Domain](#step-6-unit-test-the-domain)
      - [Step 7: Decompose If Needed](#step-7-decompose-if-needed)
      - [Step 8: Add Cross-Cutting Concerns](#step-8-add-cross-cutting-concerns-if-needed)
      - [Step 9: Threat-Model If Publicly Exposed](#step-9-threat-model-if-publicly-exposed)
      - [Step 10: Commit in Small, Reversible Steps](#step-10-commit-in-small-reversible-steps)
      - [Quick Checklist](#quick-checklist)
      - [Common Mistakes](#common-mistakes)
      - [Exit Criteria](#exit-criteria)
      
      ## When to Use
      
      - Adding a new endpoint, command, or user-visible capability
      - Starting a new feature from scratch
      - You want the fastest credible path to a deployable slice
      
      ## Prerequisites
      
      - A code base with a basic walking skeleton (CI, tests runnable, a deployable build)
      - A concrete description of the feature (user story, example input/output)
      - Understanding of the existing boundaries (what's the outermost layer?)
      
      **Primary references**: `references/outside-in-tdd/`, `references/encapsulation/`, `references/api-design/`
      
      For large features or migrations, define the target architecture, dependency direction, acceptance and non-regression criteria, verification strategy, and recovery path before implementation. Do not reduce scope merely to satisfy a line or file limit; stage work only where it improves architecture, verification, or rollback.
      
      ---
      
      ## Workflow Steps
      
      ### Step 0: Choose the Loop Granularity
      
      **Goal**: Resolve how fine-grained the red-green loop should be for *this* change — not every change needs fake-it theatre.
      
      - [ ] Prefer the **fine-grained** fake-it / triangulate loop (Steps 3–4 as written) when:
        - requirements are ambiguous,
        - the domain is unfamiliar, or
        - the oracle is weak (few executable constraints).
        The micro-loop exists to *discover* the specification.
      - [ ] Prefer **direct implementation with coherent checkpoints** when:
        - the contract is explicit, and
        - executable constraints (types, schemas, acceptance tests) are strong.
        Write the full implementation, verify at coherent checkpoints (see `../references/agent-native/verification-loops.md`), and do **not** perform fake-it theatre for a fully specified change.
      - [ ] Record the choice briefly (fine-grained vs checkpointed) so review knows which loop you ran.
      
      **Ask**: "Am I discovering the spec, or executing a known one?"
      
      **Reference**: `../references/agent-native/verification-loops.md`, `../references/outside-in-tdd/rules.md`
      
      ---
      
      ### Step 1: Clarify the Boundary
      
      **Goal**: Name the outermost entry point — where the feature begins.
      
      - [ ] Identify the boundary: HTTP route, CLI subcommand, queue handler, etc.
      - [ ] Sketch the external contract: input shape, output shape, errors
      - [ ] Decide status codes / error shapes if HTTP; exit codes if CLI
      
      **Ask**: "What does the user type / send, and what do they get back?"
      
      **Reference**: `references/outside-in-tdd/knowledge.md` (walking skeleton, vertical slice)
      
      ---
      
      ### Step 2: Write the Boundary Test First
      
      **Goal**: A test at the outermost layer that fails.
      
      - [ ] Write one test at the boundary (e.g. `HttpClient.PostAsync`) covering the happy path
      - [ ] Use AAA structure
      - [ ] Keep assertions light at this level (status code, key response field)
      - [ ] Run the test — verify it FAILS (and fails for the right reason)
      
      **If the test passes without any production code**: your test isn't testing what you think. Fix it.
      
      **Reference**: `references/outside-in-tdd/rules.md` (AAA, see tests fail)
      
      ---
      
      ### Step 3: Make the Boundary Test Pass with the Thinnest Possible Slice
      
      **Goal**: Hard-coded response that makes the test green.
      
      - [ ] Return a hard-coded response or always-success
      - [ ] Add only the wiring needed to reach the handler
      - [ ] No database, no domain logic yet — just plumb the call
      - [ ] Run: all tests green
      
      **Reference**: `references/outside-in-tdd/patterns.md` (Walking Skeleton), `references/outside-in-tdd/examples.md`
      
      ---
      
      ### Step 4: Triangulate with Additional Boundary Cases
      
      **Goal**: Force the implementation to generalise.
      
      - [ ] Add a second test case that fails with the hard-coded version
      - [ ] Use parametrised tests when the cases share structure
      - [ ] Apply the Devil's Advocate: "what input would break my current code?"
      - [ ] Add tests until the implementation is the simplest generalisation
      
      **Reference**: `references/outside-in-tdd/rules.md` (Devil's Advocate, red-green-refactor, when enough tests)
      
      ---
      
      ### Step 5: Drive Inward — Extract the Domain Model
      
      **Goal**: Move logic from the controller into typed domain objects.
      
      - [ ] Identify the invariants (what must always be true for a valid input?)
      - [ ] Parse input at the boundary into a domain type with those invariants guaranteed
      - [ ] Follow parse-don't-validate: return `null` / `Result` from the parse function
      - [ ] Move branching logic out of the controller; let types do the work
      
      **Reference**: `references/encapsulation/rules.md`, `references/encapsulation/examples.md`
      
      ---
      
      ### Step 6: Unit-Test the Domain
      
      **Goal**: Inner units have their own fast unit tests.
      
      - [ ] Write unit tests for each domain method (AAA, one assertion concept)
      - [ ] Use parametrised tests for invariants ("any negative quantity rejected")
      - [ ] Use a Fake (not a mock) for I/O dependencies
      - [ ] Keep domain tests fast (milliseconds) — they're the feedback loop
      
      **Reference**: `references/outside-in-tdd/patterns.md` (Fake Object), `references/outside-in-tdd/examples.md`
      
      ---
      
      ### Step 7: Decompose If Needed
      
      **Goal**: Keep responsibilities and effects coherent.
      
      - [ ] Any method exceeds cyclomatic complexity 15, or mixes responsibilities/effects even below that threshold? Review and decompose when it clarifies the design.
      - [ ] Any new cycles, duplicated rules, cross-module coupling, or temporary paths without cleanup ownership?
      - [ ] Prefer sequential composition over nested
      - [ ] Extract pure helpers; keep side effects at the shell
      - [ ] Re-run tests after each extraction
      
      **Reference**: `references/decomposition/rules.md`, `references/decomposition/patterns.md`
      
      ---
      
      ### Step 8: Add Cross-Cutting Concerns (if needed)
      
      **Goal**: Logging, metrics, caching — without mixing them into domain logic.
      
      - [ ] Attach the concern at an explicit seam: Decorator, middleware, filter, pipeline, or domain behavior according to scope
      - [ ] Log at the boundary of I/O (requests, failures), not on pure functions
      - [ ] Keep wiring visible in the composition root or framework registration
      
      **Reference**: `references/separation-of-concerns/patterns.md`, `references/separation-of-concerns/rules.md`
      
      ---
      
      ### Step 9: Threat-Model If Publicly Exposed
      
      **Goal**: New endpoint → STRIDE check.
      
      - [ ] Run through `workflows/threat-model.md` if the feature is reachable by untrusted input
      - [ ] Add authn / authz / input limits / audit logging as needed
      
      **Reference**: `workflows/threat-model.md`
      
      ---
      
      ### Step 10: Preserve Coherent, Recoverable History
      
      **Goal**: Each commit is a viable state.
      
      - [ ] Preserve known-good checkpoints appropriate to the architecture and recovery plan
      - [ ] Use concise imperative subjects that follow repository policy
      - [ ] Make behavior, oracle, and mechanical refactoring changes distinguishable without forcing broken intermediate states
      
      **Reference**: `references/teamwork-git/rules.md`
      
      ---
      
      ## Quick Checklist
      
      ```
      [ ] Step 0: Loop granularity chosen (fine-grained discovery vs checkpointed direct)
      [ ] Step 1: Boundary identified
      [ ] Step 2: Boundary test fails
      [ ] Step 3: Thinnest slice makes it pass (if fine-grained)
      [ ] Step 4: Triangulated with Devil's Advocate (if fine-grained)
      [ ] Step 5: Domain types with invariants
      [ ] Step 6: Domain unit-tested
      [ ] Step 7: Decomposition OK
      [ ] Step 8: Cross-cutting concern attached at the right explicit seam
      [ ] Step 9: Threat-modelled if exposed
      [ ] Step 10: Small, clean commits
      ```
      
      ---
      
      ## Common Mistakes
      
      | Mistake | Why It's Bad | Do Instead |
      |---------|--------------|------------|
      | Starting with the database schema | House-building thinking; locks design | Start at the boundary; infer schema later |
      | Writing all tests first, then all code | Big-bang; no feedback loop | One test → one slice → refactor → repeat |
      | Mocking everything | Tests decouple from reality | Use Fakes for I/O, real objects for domain |
      | Skipping the "see it fail" step | Green-from-start tests assert nothing | Always watch the test fail first |
      | Inflating the first slice | Never reach green | Hard-code until the next test forces generalisation |
      
      ---
      
      ## Exit Criteria
      
      Feature is done when:
      - [ ] All boundary tests pass
      - [ ] Domain unit tests pass
      - [ ] Decomposition rules satisfied
      - [ ] Threat model considered if applicable
      - [ ] Commit history is accurate, coherent, and useful for recovery
      - [ ] Material behavior has independent acceptance evidence where practical
      - [ ] Deployable end-to-end slice works
      
    • debug-defect.md 6.8 KB
      # Debug a Defect Workflow
      
      Methodical, scientific-method debugging — from bug report to regression-proofed fix.
      
      ## Table of Contents
      
      - [When to Use](#when-to-use)
      - [Prerequisites](#prerequisites)
      - [Step 1: Understand the Report](#step-1-understand-the-report)
      - [Step 2: Reproduce the Defect](#step-2-reproduce-the-defect)
      - [Step 3: Simplify to the Smallest Failing Case](#step-3-simplify-to-the-smallest-failing-case)
      - [Step 4: Hypothesise, Then Test](#step-4-hypothesise-then-test)
      - [Step 5: If It's a Regression, Bisect](#step-5-if-its-a-regression-bisect)
      - [Step 6: Fix with the Test as a Guard Rail](#step-6-fix-with-the-test-as-a-guard-rail)
      - [Step 7: Understand Root Cause](#step-7-understand-root-cause)
      - [Step 8: Commit and Document](#step-8-commit-and-document)
      - [Quick Checklist](#quick-checklist)
      - [Common Mistakes](#common-mistakes)
      - [Exit Criteria](#exit-criteria)
      
      ## When to Use
      
      - A bug report or failing test arrives
      - A test is flaky and needs investigating
      - You need to find when a regression was introduced
      
      ## Prerequisites
      
      - Ability to run the code / test suite locally (or in CI)
      - Access to git history
      - A reproducible or semi-reproducible failure (if not, Step 2 is where you fight)
      
      **Primary references**: `references/troubleshooting/`, `references/outside-in-tdd/rules.md`
      
      ---
      
      ## Workflow Steps
      
      ### Step 1: Understand the Report
      
      **Goal**: Restate the defect in your own words.
      
      - [ ] Read the full bug report (or the failing test output)
      - [ ] Identify: What was the user doing? What did they expect? What did they get?
      - [ ] Write a concise explanation with evidence, attempted hypotheses, and unknowns
      - [ ] Ask yourself: could this be user error / environment / config rather than a defect?
      
      **Ask**: "What's the single sentence description of the wrong behaviour?"
      
      **Reference**: `references/troubleshooting/knowledge.md` (externalizing the problem)
      
      ---
      
      ### Step 2: Reproduce the Defect
      
      **Goal**: Make the bug happen on demand, ideally in an automated test.
      
      - [ ] Try to trigger the bug in your local environment with the reported inputs
      - [ ] If it reproduces → convert it into a failing unit / integration test (reproduce-as-test)
      - [ ] If it doesn't reproduce → simplify the reported scenario progressively
      - [ ] If non-deterministic → isolate sources of non-determinism (clock, random, threads, external state)
      
      **If you can't reproduce**: do not "fix" it. Escalate for more info (steps, environment, timestamps).
      
      **Reference**: `references/troubleshooting/patterns.md` (Reproduce-as-Test, Isolate-Then-Fix)
      
      ---
      
      ### Step 3: Simplify to the Smallest Failing Case
      
      **Goal**: Remove everything that's not the defect.
      
      - [ ] Keep halving the input / setup until the bug disappears; then re-add only the last piece
      - [ ] Remove unrelated code from the repro
      - [ ] Aim for a test that takes milliseconds and isolates a single assertion
      
      **Reference**: `references/troubleshooting/rules.md` (simplification)
      
      ---
      
      ### Step 4: Hypothesise, Then Test
      
      **Goal**: Use the scientific method — don't guess-and-change.
      
      - [ ] Write down your hypothesis: "I think X causes Y because Z"
      - [ ] Design the smallest experiment that would disprove or confirm it
      - [ ] Run the experiment; record the result
      - [ ] If disproven → new hypothesis; do NOT tweak code randomly
      
      **Ask**: "What would I expect to see if my hypothesis is right? If it's wrong?"
      
      **Reference**: `references/troubleshooting/knowledge.md` (scientific method)
      
      If repeated experiments produce no new evidence, stop editing and revise the hypothesis, instrumentation, or system model.
      
      ---
      
      ### Step 5: If It's a Regression, Bisect
      
      **Goal**: Find the exact commit that introduced the defect.
      
      - [ ] Identify a known-good commit (or tag)
      - [ ] Identify a known-bad commit (usually `HEAD`)
      - [ ] Run `git bisect start; git bisect bad; git bisect good <commit>`
      - [ ] At each step, run your reproduction test → `git bisect good` or `git bisect bad`
      - [ ] Git announces the first bad commit
      
      **If the suite is too slow for bisection**: first make it fast (cf. `references/troubleshooting/rules.md` on slow tests).
      
      **Reference**: `references/troubleshooting/patterns.md` (Git Bisection)
      
      ---
      
      ### Step 6: Fix with the Test as a Guard Rail
      
      **Goal**: Fix makes the failing test pass; don't break any other test.
      
      - [ ] Apply the simplest change that restores the violated invariant without adding symptom-specific debt
      - [ ] Run the full test suite — all green
      - [ ] Review: does the fix handle related cases, or only the exact repro?
      - [ ] Add a few more test cases (Devil's Advocate) to cover the generalisation
      - [ ] For material impact, confirm behavior against an independent oracle where practical
      
      **Reference**: `references/outside-in-tdd/rules.md` (Devil's Advocate)
      
      ---
      
      ### Step 7: Understand Root Cause
      
      **Goal**: Don't ship a fix without knowing why the defect existed.
      
      - [ ] Explain in one paragraph: why did this bug exist? What invariant was violated?
      - [ ] Could the bug have existed because of a missing type constraint? (Parse-don't-validate miss.)
      - [ ] Could tests have caught it earlier? (What test was missing?)
      - [ ] Are there similar defects lurking elsewhere?
      
      **Reference**: `references/encapsulation/rules.md` (invariants, parse-don't-validate)
      
      ---
      
      ### Step 8: Commit and Document
      
      **Goal**: Leave the history readable.
      
      - [ ] Commit the failing test + the fix
      - [ ] Commit subject is concise, imperative, and follows repository policy ("Fix off-by-one in date parser")
      - [ ] Body explains: what was the symptom, what was the root cause, how the fix works
      - [ ] If warranted, add a comment at the fix site — but ONLY if the why is non-obvious
      
      **Reference**: `references/teamwork-git/rules.md`
      
      ---
      
      ## Quick Checklist
      
      ```
      [ ] Step 1: Report understood and restated
      [ ] Step 2: Bug reproduced (as a test)
      [ ] Step 3: Failing case simplified
      [ ] Step 4: Hypothesis → experiment → result
      [ ] Step 5: Bisected if regression
      [ ] Step 6: Minimum fix + suite green
      [ ] Step 7: Root cause explained
      [ ] Step 8: Commit with clear message
      ```
      
      ---
      
      ## Common Mistakes
      
      | Mistake | Why It's Bad | Do Instead |
      |---------|--------------|------------|
      | "Fixing" without reproducing | You don't know you fixed it | Reproduce first, always |
      | Random changes + re-run test | Wastes time, adds noise | Write a hypothesis; test it |
      | Patching the symptom (add `if` around crash) | Bug re-appears as a different symptom | Find the root cause |
      | Fixing without a test | Regression next sprint | Every defect fix includes a regression test |
      | Bisecting with a slow suite | Wall-clock pain | Fix the slow tests first |
      
      ---
      
      ## Exit Criteria
      
      Defect investigation is done when:
      - [ ] A test reliably reproduces the original failure
      - [ ] The test now passes with the fix applied
      - [ ] Root cause is understood and documented in the commit message
      - [ ] No other tests regressed
      - [ ] (Optional) Similar areas of code scanned for the same root-cause pattern
      
    • operationalize-finding.md 4.2 KB
      # Operationalize a Finding Workflow
      
      Convert a recurring review finding or design rule into an executable gate, then record it in the project's agent instructions. This is how the skill's discipline survives beyond the session that applied it: a machine-enforced rule is harder to erode under delivery pressure than a prose convention, and a rule recorded in project memory reaches every future session.
      
      ## When to Use
      
      - The same class of finding appeared in review twice or more (complexity, layering, logging, naming, duplication).
      - A design decision was made that future changes must respect (dependency direction, boundary parsing, forbidden imports).
      - You just finished applying this skill to a repository and want the thresholds and commands to persist.
      
      ## Prerequisites
      
      - Write access to the repository's lint/build/CI configuration.
      - `references/tooling/commands.md` for tool options per ecosystem.
      
      ---
      
      ## Workflow Steps
      
      ### Step 1: Name the Finding Class
      
      - [ ] State the rule in one falsifiable sentence ("Domain must not import DataAccess", "no method above cyclomatic complexity 15", "no unparameterised SQL").
      - [ ] Confirm it is stable — a constraint that will change next sprint is not worth automating.
      - [ ] Confirm it is mechanically expressible. Intent and architecture judgment stay with review; only objective constraints become gates.
      
      ### Step 2: Pick the Cheapest Enforcing Layer
      
      In order of preference — earlier layers give faster feedback:
      
      | Layer | Use for |
      |---|---|
      | Formatter | Style — never spend review or prose rules on it |
      | Type system / strict mode | Nullability, invalid states, contract drift |
      | Linter rule | Complexity thresholds, forbidden patterns, naming |
      | Architecture test | Dependency direction, cycles, layer rules |
      | Unit/property test | Behavioral invariants |
      | CI-only check | Slow or cross-cutting checks (duplication, dead code, mutation) |
      
      ### Step 3: Implement the Gate Narrowly
      
      - [ ] Enable one rule, not a whole rule pack — packs create noise and invite blanket suppression.
      - [ ] Brownfield: apply the ratchet (`references/codebase-setup/rules.md`) — fix existing violations in one slice, then flip the rule to error so it cannot regress.
      - [ ] Verify the gate fails on a deliberate violation before trusting it (same principle as seeing a test fail).
      
      ### Step 4: Record in Project Agent Instructions
      
      Add to the repository's `CLAUDE.md` / `AGENTS.md` (create the section if missing):
      
      - [ ] The canonical verification command(s) — build, test, lint — exactly as CI runs them.
      - [ ] Project-specific thresholds that override this skill's defaults (e.g. "CC review trigger here is 10").
      - [ ] Accepted exceptions and their rationale, so future agents do not re-litigate or silently violate them.
      - [ ] Chosen measurement commands from `references/tooling/commands.md` that fit this repository.
      
      Keep the section short — a map to authoritative config files, not a duplicate of them.
      
      ### Step 5: Retire the Prose Version
      
      - [ ] Stop flagging the now-automated rule in reviews — the gate owns it.
      - [ ] If a document listed the rule as a manual check, replace the text with a pointer to the gate.
      
      ---
      
      ## Quick Checklist
      
      ```
      [ ] Rule stated in one falsifiable sentence, stable, mechanical
      [ ] Cheapest enforcing layer chosen
      [ ] Gate implemented narrowly; seen failing on a violation
      [ ] Brownfield violations ratcheted, then rule flipped to error
      [ ] Canonical commands + thresholds + exceptions recorded in project agent instructions
      [ ] Prose/manual version retired
      ```
      
      ## Common Mistakes
      
      | Mistake | Why It's Bad | Do Instead |
      |---------|--------------|------------|
      | Enabling a full analyzer pack at once | Noise flood → blanket suppressions | One rule at a time, ratcheted |
      | Automating a judgment call | False positives destroy trust in gates | Automate objective constraints only |
      | Gate added but instructions not updated | Next agent re-derives or fights the gate | Always do Step 4 |
      | Keeping the manual check alongside the gate | Double cost, drift | Retire the prose version |
      
      ## Exit Criteria
      
      - [ ] The gate fails the build/CI on violation and passes on current mainline.
      - [ ] Project agent instructions name the gate, its threshold, and the canonical commands.
      
    • review-code.md 6.9 KB
      # Code Review Workflow
      
      End-to-end process for reviewing a pull request or a diff, grounded in the book's heuristics.
      
      ## Table of Contents
      
      - [When to Use](#when-to-use)
      - [Prerequisites](#prerequisites)
      - [Step 1: Understand the Change Set](#step-1-understand-the-change-set)
      - [Step 2: Read the Tests First](#step-2-read-the-tests-first)
      - [Step 3: Check Decomposition](#step-3-check-decomposition)
      - [Step 4: Check Encapsulation](#step-4-check-encapsulation)
      - [Step 5: Check API Design](#step-5-check-api-design-if-the-pr-changes-public-surface)
      - [Step 6: Check Cross-Cutting & Security](#step-6-check-cross-cutting--security-if-applicable)
      - [Step 7: Check Commit Hygiene](#step-7-check-commit-hygiene)
      - [Step 8: Write the Feedback](#step-8-write-the-feedback)
      - [Quick Checklist](#quick-checklist)
      - [Common Mistakes](#common-mistakes)
      - [Exit Criteria](#exit-criteria)
      
      ## When to Use
      
      - A user asks to review a PR, a diff, or a set of changes
      - A user asks "is this code good?" or "what's wrong with this code?"
      - Before approving/merging a change set
      
      ## Prerequisites
      
      - Access to the diff (or the full file if the change is small)
      - Ability to run tests (ideally)
      - Some sense of the code base's conventions
      
      **Primary references**: `references/teamwork-git/checklist.md`, `references/decomposition/rules.md`, `references/api-design/rules.md`
      
      ---
      
      ## Workflow Steps
      
      ### Step 1: Understand the Change Set
      
      **Goal**: Know what the change is trying to achieve before judging how it achieves it.
      
      - [ ] Read the PR description / commit message. Is it clear *why*?
      - [ ] Is the change architecturally coherent, even if it is large?
      - [ ] Are unrelated cleanup and semantic changes separated?
      - [ ] For systematic migrations, are the transformation, exceptions, and recovery path explicit?
      
      **Ask**: "If I had to summarise this PR in one sentence, what is it doing?"
      
      **Reference**: `references/teamwork-git/rules.md`, `references/agent-native/reviewability.md`
      
      ---
      
      ### Step 2: Read the Tests First
      
      **Goal**: Tests encode intent — read them before the production code.
      
      - [ ] For each new/changed behaviour, is there a test?
      - [ ] Do the tests follow AAA structure (Arrange / Act / Assert)?
      - [ ] Would you believe the tests cover the stated behaviour?
      - [ ] Did the change weaken, skip, or rewrite an existing oracle merely to get green?
      - [ ] For material behavior, is any acceptance evidence independent of the implementation?
      - [ ] Any flaky patterns (time, random, network without isolation)?
      
      **If no tests for a behaviour change**: block and ask why (reference `references/outside-in-tdd/rules.md`).
      
      **Reference**: `references/outside-in-tdd/rules.md`, `references/code-navigation/rules.md`
      
      ---
      
      ### Step 3: Check Decomposition
      
      **Goal**: Verify each unit fits in a head.
      
      Measure, don't estimate: complexity/cycle/duplication commands in `../references/tooling/commands.md`.
      
      - [ ] Cyclomatic complexity > 15 anywhere, or lower complexity with opaque path interaction?
      - [ ] Any method mixes unrelated responsibilities, effects, or abstraction levels?
      - [ ] Too many unrelated values, mutable states, or dependencies interact at once?
      - [ ] Any feature envy (method uses another class's data more than its own)?
      - [ ] Cohesion OK (methods in a class share fields / serve one responsibility)?
      - [ ] Any new cycles, layer violations, duplicated rules, broad coupling, or stale migration paths?
      
      **Reference**: `references/decomposition/rules.md`, `references/decomposition/smells.md`
      
      ---
      
      ### Step 4: Check Encapsulation
      
      **Goal**: Confirm invariants are protected at type level.
      
      - [ ] Can any domain object be constructed in an invalid state?
      - [ ] Is validation done once at parse time (parse-don't-validate) or scattered?
      - [ ] Are DTOs and domain types distinct?
      - [ ] Any `null!` or bypassed nullability? Ask why.
      - [ ] 400 vs 500 distinction clear at API boundaries?
      
      **Reference**: `references/encapsulation/rules.md`, `references/encapsulation/examples.md`
      
      ---
      
      ### Step 5: Check API Design (if the PR changes public surface)
      
      **Goal**: The API should make wrong things hard to do.
      
      - [ ] Does each method obey CQS (command OR query, never both)?
      - [ ] Do names carry the design (X-Out test: blank names — does code still read)?
      - [ ] Are affordances clear (what actions does this interface suggest)?
      - [ ] Any comments that could be replaced by a better method/type name?
      
      **Reference**: `references/api-design/rules.md`
      
      ---
      
      ### Step 6: Check Cross-Cutting & Security (if applicable)
      
      **Goal**: Catch concerns that cut across layers.
      
      - [ ] New logging/telemetry attached at an explicit seam appropriate to its scope?
      - [ ] Any secrets, PII, or sensitive data risk of being logged?
      - [ ] New endpoint → did we STRIDE it? (Optional deeper dive: `workflows/threat-model.md`)
      - [ ] New dependency → identity, provenance, locked version, and necessity verified?
      
      **Reference**: `references/separation-of-concerns/rules.md`, `references/security/checklist.md`
      
      ---
      
      ### Step 7: Check Commit Hygiene
      
      **Goal**: The history is the record — make it readable.
      
      - [ ] Subjects are concise and imperative, following repository policy?
      - [ ] Messages accurately explain non-obvious rationale?
      - [ ] Commits preserve coherent known-good states useful for review or recovery?
      - [ ] No drive-by refactors mixed with behaviour changes?
      
      **Reference**: `references/teamwork-git/rules.md`
      
      ---
      
      ### Step 8: Write the Feedback
      
      **Goal**: Give the author everything they need to respond.
      
      - [ ] Separate blockers (must fix) from suggestions (nice to have)
      - [ ] For each blocker: state the rule violated + point to the reference
      - [ ] Offer a concrete alternative when possible
      - [ ] Keep the total review under ~8 items; if more, pick the most important
      
      **Ask**: "If I were the author, would I understand what to change?"
      
      ---
      
      ## Quick Checklist
      
      ```
      [ ] Step 1: Understood the change's intent
      [ ] Step 2: Read the tests first
      [ ] Step 3: Complexity/debt delta understood (local + system structure)
      [ ] Step 4: Encapsulation OK (invariants, validation)
      [ ] Step 5: API design OK (CQS, naming)
      [ ] Step 6: Cross-cutting & security OK
      [ ] Step 7: History is accurate, coherent, and useful
      [ ] Step 8: Feedback written (blockers vs suggestions)
      ```
      
      ---
      
      ## Common Mistakes
      
      | Mistake | Why It's Bad | Do Instead |
      |---------|--------------|------------|
      | Reviewing line-by-line before understanding intent | Miss the forest for the trees | Read PR description + tests first |
      | Flagging style nits as blockers | Drowns the real issues | Auto-format; reserve review for design |
      | Approving without running the tests | Bugs slip through | Run locally or verify CI |
      | Asking open-ended "why?" on everything | Review takes forever | Be specific: "rule X is violated here" |
      
      ---
      
      ## Exit Criteria
      
      Review is done when:
      - [ ] Every reviewer comment is either a blocker or marked as a suggestion
      - [ ] Every blocker cites a rule / reference file
      - [ ] Decision is explicit: approve / request changes / reject
      
    • threat-model.md 8.1 KB
      # Threat Model Workflow (STRIDE)
      
      Walk a new endpoint or service through the six STRIDE threats and decide mitigations.
      
      ## Table of Contents
      
      - [When to Use](#when-to-use)
      - [Prerequisites](#prerequisites)
      - [Step 1: Describe the Component](#step-1-describe-the-component)
      - [Step 2: Spoofing — Who's Calling?](#step-2-spoofing--whos-calling)
      - [Step 3: Tampering — Can Data Be Modified?](#step-3-tampering--can-data-be-modified)
      - [Step 4: Repudiation — Can Actions Be Denied?](#step-4-repudiation--can-actions-be-denied)
      - [Step 5: Information Disclosure — What Leaks?](#step-5-information-disclosure--what-leaks)
      - [Step 6: Denial of Service — Can It Be Overwhelmed?](#step-6-denial-of-service--can-it-be-overwhelmed)
      - [Step 7: Elevation of Privilege — Can Someone Gain Access They Shouldn't?](#step-7-elevation-of-privilege--can-someone-gain-access-they-shouldnt)
      - [Step 8: Record Decisions](#step-8-record-decisions)
      - [Quick Checklist](#quick-checklist)
      - [Common Mistakes](#common-mistakes)
      - [Exit Criteria](#exit-criteria)
      
      ## When to Use
      
      - Adding a new HTTP endpoint, RPC, or any externally reachable service
      - Reviewing a PR that exposes new attack surface
      - Before shipping anything that handles untrusted input or holds sensitive data
      - Periodically on a running service (e.g. yearly audit)
      
      ## Prerequisites
      
      - A description of the component under review (endpoint, service, integration)
      - Awareness of what data the component handles and who can reach it
      
      **Primary references**: `references/security/knowledge.md`, `references/security/rules.md`, `references/security/checklist.md`
      
      ---
      
      ## Workflow Steps
      
      ### Step 1: Describe the Component
      
      **Goal**: One paragraph: what is this, who reaches it, what does it touch?
      
      - [ ] Name the component and its boundary (URL / topic / queue)
      - [ ] List callers: who reaches it (public users? internal services? admins?)
      - [ ] List data: what does it read / write / return? Any PII or secrets?
      - [ ] List integrations: what does it call downstream?
      
      If a coding agent or agent runtime is in scope, separately list:
      
      - [ ] Untrusted instructions/content it can read (repository, issues, web, logs, tool output)
      - [ ] Filesystem, shell, network, cloud, and external-communication capabilities
      - [ ] Credentials and whether they are scoped and short-lived
      - [ ] Dependency installation and executable supply-chain paths
      - [ ] Destructive or irreversible actions and their approval/rollback boundary
      
      **Ask**: "If this component is compromised, what gets lost?"
      
      ---
      
      ### Step 2: Spoofing — Who's Calling?
      
      **Goal**: Can the component verify identity?
      
      - [ ] Is authentication required? (If not, justify why.)
      - [ ] Is it using an established identity provider (OAuth, OIDC, cloud IAM), not a home-grown check?
      - [ ] Are credentials sent over TLS only?
      - [ ] For service-to-service: mTLS or signed tokens?
      
      **Mitigation examples**: OAuth2 / OIDC, mutual TLS, API keys rotated via secret manager, SPNEGO.
      
      **Reference**: `references/security/rules.md` (Spoofing section)
      
      ---
      
      ### Step 3: Tampering — Can Data Be Modified?
      
      **Goal**: Ensure no attacker can silently change data in transit or at rest.
      
      - [ ] TLS for all external communication?
      - [ ] Client-side state (cookies, JWTs) — signed / HMAC'd?
      - [ ] Database writes — access control checked server-side (never trust the client)?
      - [ ] Binary artefacts (uploads, downloads) — checksummed / signed?
      - [ ] SQL queries — parameterised (no string concatenation)?
      
      **Mitigation examples**: Signed JWTs, parameterised queries, prepared statements, content-hash checks.
      
      **Reference**: `references/security/rules.md` (Tampering section)
      
      ---
      
      ### Step 4: Repudiation — Can Actions Be Denied?
      
      **Goal**: Log enough to prove who did what.
      
      - [ ] Security-relevant events logged (auth success/failure, privilege changes, admin actions)?
      - [ ] Logs are append-only / tamper-evident / shipped off-host quickly?
      - [ ] Timestamps are server-side and from a trusted clock?
      - [ ] User identity in every audit line (not just session ID)?
      
      **Mitigation examples**: Structured audit log stream, centralised log store with WORM semantics.
      
      **Reference**: `references/security/rules.md` (Repudiation section), `references/separation-of-concerns/patterns.md` (Decorator for audit logging)
      
      ---
      
      ### Step 5: Information Disclosure — What Leaks?
      
      **Goal**: Minimise exposure of sensitive data.
      
      - [ ] What fields does the endpoint return — any over-fetching of PII?
      - [ ] Error responses: do they leak internals (stack traces, SQL)?
      - [ ] Logs: any secrets / tokens / PII written to them?
      - [ ] Responses to unauthenticated requests — any info about the system (e.g. "user X exists")?
      - [ ] Data at rest encrypted? Data in transit encrypted?
      
      **Mitigation examples**: Response DTOs with explicit fields (not domain objects), generic error pages, log scrubbing, at-rest encryption.
      
      **Reference**: `references/security/rules.md` (Information Disclosure section)
      
      ---
      
      ### Step 6: Denial of Service — Can It Be Overwhelmed?
      
      **Goal**: Survive or gracefully degrade under load / abuse.
      
      - [ ] Request rate limits (per-IP / per-key)?
      - [ ] Payload size limits (body size, collection sizes, nesting depth)?
      - [ ] Query timeouts end-to-end?
      - [ ] Downstream circuit breakers / retry budgets?
      - [ ] Any expensive operations exposed without auth (regex, crypto, image processing)?
      
      **Mitigation examples**: WAF, rate limiter, timeouts, circuit breakers, request body size caps, query cost limits.
      
      **Reference**: `references/security/rules.md` (Denial of Service section)
      
      ---
      
      ### Step 7: Elevation of Privilege — Can Someone Gain Access They Shouldn't?
      
      **Goal**: Principle of least privilege throughout.
      
      - [ ] The process runs as the minimum privileged user (not root)?
      - [ ] Every authorisation check happens server-side (never trust "I'm an admin" from the client)?
      - [ ] Auth checks happen on every action, not just login?
      - [ ] Role / permission boundaries enforced at the data layer, not only in the UI?
      - [ ] Secrets scoped to just what this service needs?
      
      **Mitigation examples**: Per-request authZ middleware, row-level security in DB, scoped service accounts.
      
      **Reference**: `references/security/rules.md` (Elevation of Privilege section)
      
      ---
      
      ### Step 8: Record Decisions
      
      **Goal**: Make the threat model reviewable and revisitable.
      
      - [ ] For each of the six threats, record: mitigated / accepted / deferred, and why
      - [ ] Link to the code locations that implement each mitigation
      - [ ] Name owners for any deferred items
      - [ ] Schedule a re-review for next major change
      
      **Reference**: `references/security/checklist.md`
      
      ---
      
      ## Quick Checklist
      
      ```
      [ ] Step 1: Component described (boundary, callers, data, integrations)
      [ ] Step 2: Spoofing — authn in place
      [ ] Step 3: Tampering — integrity, TLS, parameterised queries
      [ ] Step 4: Repudiation — audit logs
      [ ] Step 5: Information Disclosure — minimise, encrypt, scrub
      [ ] Step 6: Denial of Service — rate limits, timeouts, caps
      [ ] Step 7: Elevation of Privilege — least privilege, server-side authZ
      [ ] Step 8: Decisions recorded with owners
      [ ] Agent runtime scope reviewed separately when applicable
      ```
      
      ---
      
      ## Common Mistakes
      
      | Mistake | Why It's Bad | Do Instead |
      |---------|--------------|------------|
      | "We have a WAF, we're safe" | A WAF doesn't replace authZ or input validation | Defence in depth; fix at each layer |
      | Rolling custom crypto / auth | Almost always broken | Use vetted libraries, standard protocols |
      | Writing "TODO: fix later" and shipping | Almost always ships without the fix | Deferred decisions get an owner + date |
      | Threat-modelling in isolation | Misses context | Include infra + product people who know callers |
      | Treating STRIDE as a form to fill | Missed real risks | Use it as a lens; dig into concrete scenarios |
      
      ---
      
      ## Exit Criteria
      
      Threat model is done when:
      - [ ] All six STRIDE categories have a recorded decision (mitigated / accepted / deferred)
      - [ ] Every "mitigated" has a code pointer
      - [ ] Every "deferred" or "accepted" has an owner and a date
      - [ ] The document is stored where PRs that touch this area can re-reference it
      - [ ] If an agent runtime was in scope, its permissions, untrusted inputs, dependency provenance, and destructive-action boundaries have explicit decisions
      
  • guidelines.md 11.3 KB
    # Guidelines — Task Routing for `code-that-fits-in-your-head`
    
    This is the routing layer for the skill. Find the user's task or symptom below, then load **only** the specific files listed.
    
    **Rule of thumb**: one primary file + at most 1-2 secondary files per task. If you find yourself wanting to load four or more, re-read the task — you may be combining two tasks.
    
    **Note on editorial content**: `agent-native/` contains four canonical amendments for code agents. Specialized agent-era additions in book-derived themes are explicitly marked and are not attributable to Seemann. See `references/agent-native/knowledge.md`.
    
    ---
    
    ## Table of Contents
    
    - [By Task](#by-task)
    - [By Symptom / Smell](#by-symptom--smell)
    - [By Named Practice](#by-named-practice)
    - [Agent-Native Amendments](#agent-native-amendments-not-from-the-book)
    
    ---
    
    ## By Task
    
    ### Code Review
    
    | What you're reviewing | Load these files |
    |-----------------------|-------------------|
    | A pull request (general) | `workflows/review-code.md`, `references/teamwork-git/checklist.md` |
    | A large agent-authored migration/change | `references/agent-native/reviewability.md`, `references/agent-native/verification-loops.md` |
    | Function/method complexity | `references/decomposition/rules.md`, `references/decomposition/smells.md`, `references/tooling/commands.md` |
    | API surface (public interface) | `references/api-design/rules.md`, `references/api-design/examples.md` |
    | Naming quality | `references/api-design/rules.md` (X-Out Names section) |
    | Type/class encapsulation | `references/encapsulation/rules.md`, `references/encapsulation/examples.md` |
    | Tests | `references/outside-in-tdd/rules.md`, `references/teamwork-git/checklist.md` |
    | Commit messages | `references/teamwork-git/rules.md` (accuracy, rationale, repository policy) |
    | Security posture of an endpoint | `workflows/threat-model.md`, `references/security/checklist.md` |
    | Logging / cross-cutting | `references/separation-of-concerns/rules.md`, `references/separation-of-concerns/patterns.md` |
    
    ### Writing New Code
    
    | What you're writing | Load these files |
    |---------------------|-------------------|
    | A brand-new feature | `workflows/add-feature-outside-in.md`, `references/outside-in-tdd/knowledge.md` |
    | A new domain type with invariants | `references/encapsulation/rules.md`, `references/encapsulation/examples.md` |
    | A new public API | `references/api-design/knowledge.md`, `references/api-design/rules.md` |
    | A unit test (first for this SUT) | `references/outside-in-tdd/rules.md`, `references/outside-in-tdd/examples.md` |
    | Additional test cases | `references/outside-in-tdd/rules.md` (Devil's Advocate section) |
    | Cross-cutting concern (logging, caching, auth) | `references/separation-of-concerns/patterns.md` |
    | A commit message | `references/teamwork-git/rules.md` |
    
    ### Refactoring
    
    | What you're changing | Load these files |
    |----------------------|-------------------|
    | A long/complex function | `references/decomposition/rules.md`, `references/decomposition/patterns.md` |
    | A class that has feature envy | `references/decomposition/smells.md`, `references/decomposition/examples.md` |
    | Splitting monolithic logic | `references/decomposition/patterns.md`, `references/separation-of-concerns/patterns.md` |
    | Replacing a legacy subsystem | `references/evolution/patterns.md` (Strangler), `references/evolution/examples.md` |
    | Tests without touching prod code | `references/outside-in-tdd/rules.md` (separate-refactor section) |
    | Hardening validation | `references/encapsulation/rules.md` (parse-don't-validate) |
    
    ### Debugging & Troubleshooting
    
    | What you're investigating | Load these files |
    |---------------------------|-------------------|
    | A reproducible defect | `workflows/debug-defect.md`, `references/troubleshooting/patterns.md` |
    | A flaky/non-deterministic test | `references/troubleshooting/rules.md`, `references/troubleshooting/patterns.md` |
    | A regression (when did it break?) | `references/troubleshooting/patterns.md` (bisection section) |
    | Slow test suite | `references/troubleshooting/rules.md` (slow tests section) |
    
    ### Security Review
    
    | What you're threat-modelling | Load these files |
    |------------------------------|-------------------|
    | A new endpoint/service | `workflows/threat-model.md`, `references/security/checklist.md` |
    | A STRIDE-specific concern | `references/security/rules.md` |
    
    ### Setting Up / Onboarding
    
    | Situation | Load these files |
    |-----------|-------------------|
    | Starting a new code base | `references/codebase-setup/checklist.md`, `references/codebase-setup/rules.md` |
    | Retrofitting discipline into a legacy base | `references/codebase-setup/rules.md` (gradual improvement section) |
    | Onboarding to an unfamiliar code base | `references/code-navigation/knowledge.md`, `references/code-navigation/rules.md` |
    
    ### Evolution & Release
    
    | Situation | Load these files |
    |-----------|-------------------|
    | Deploying a risky change | `references/evolution/rules.md` (feature flag section), `references/evolution/patterns.md` |
    | Versioning a library / breaking change | `references/evolution/rules.md` (semver section) |
    | Updating dependencies | `references/evolution/rules.md` (regular updates section) |
    
    ### Measurement & Operationalization
    
    | Situation | Load these files |
    |-----------|-------------------|
    | Measure complexity, cycles, duplication, dead code, hotspots | `references/tooling/commands.md` |
    | Turn a recurring finding into a lint/CI gate; persist thresholds in project memory | `workflows/operationalize-finding.md` |
    
    ---
    
    ## By Symptom / Smell
    
    | If you notice... | Load these files |
    |------------------|-------------------|
    | Cyclomatic complexity > 15 or opaque branching | `references/decomposition/rules.md`, `references/decomposition/smells.md` (D1) |
    | Small methods but architecture remains tangled | `references/decomposition/smells.md` (D8 system-wide sprawl) |
    | Unused helpers / leftover parallel implementations | `references/decomposition/smells.md` (D9) |
    | Method envies another object's data | `references/decomposition/smells.md` (D4 feature envy) |
    | Vocabulary drift between layers | `references/decomposition/smells.md` (D5 lost in translation) |
    | Boolean `IsValid()` scattered everywhere | `references/encapsulation/rules.md` (parse-don't-validate) |
    | Invariants checked in many places | `references/encapsulation/rules.md`, `references/encapsulation/examples.md` |
    | `null!` or `?` sprinkled to silence compiler | `references/encapsulation/rules.md` |
    | A method both returns and mutates | `references/api-design/rules.md` (CQS section) |
    | Names don't carry meaning | `references/api-design/rules.md` (X-Out Names) |
    | Comments explaining what code does | `references/api-design/rules.md` (naming-over-comments) |
    | Logging duplicated across layers | `references/separation-of-concerns/rules.md` |
    | Inheritance for cross-cutting | `references/separation-of-concerns/patterns.md` (Decorator) |
    | Big-bang replacement plan | `references/evolution/patterns.md` (Strangler) |
    | Live schema change | `references/evolution/patterns.md` (Expand-Contract) |
    | Weak tests pass wrong code | `references/outside-in-tdd/rules.md` (Devil's Advocate + mutation testing) |
    | Tests are flaky | `references/troubleshooting/rules.md`, `references/troubleshooting/patterns.md` |
    | Can't reproduce a bug | `references/troubleshooting/patterns.md` (reproduce-as-test) |
    | Dependency cycles between namespaces | `references/code-navigation/rules.md`, `references/tooling/commands.md` |
    | Input validation after construction | `references/encapsulation/rules.md` |
    | Commit history lacks rationale or useful checkpoints | `references/teamwork-git/rules.md` |
    | PR sits unreviewed for days | `references/teamwork-git/rules.md` (review latency) |
    
    ---
    
    ## By Named Practice
    
    For any retained practice referenced by name (for example Strangler or CQS), first check `references/practices-glossary/knowledge.md` for a short definition and deep-dive pointer, then load the referenced theme file.
    
    | Practice | Deep dive |
    |----------|-----------|
    | Arrange-Act-Assert (AAA) | `references/outside-in-tdd/rules.md` |
    | Bisection | `references/troubleshooting/patterns.md` |
    | Command Query Separation (CQS) | `references/api-design/rules.md` |
    | Cyclomatic Complexity | `references/decomposition/rules.md` |
    | Decorator (cross-cutting) | `references/separation-of-concerns/patterns.md` |
    | Devil's Advocate | `references/outside-in-tdd/rules.md`, `references/outside-in-tdd/patterns.md` |
    | Expand-Contract | `references/evolution/patterns.md` |
    | Feature Flag | `references/evolution/rules.md`, `references/evolution/patterns.md` |
    | Functional Core, Imperative Shell | `references/decomposition/patterns.md` |
    | Complementary Communication | `references/api-design/rules.md` |
    | Parse, Don't Validate | `references/encapsulation/rules.md` |
    | Explicit Boundary Parsing | `references/encapsulation/rules.md` |
    | Red Green Refactor | `references/outside-in-tdd/rules.md` |
    | Reproduce Defects as Tests | `references/troubleshooting/patterns.md` |
    | Semantic Versioning | `references/evolution/rules.md` |
    | Strangler | `references/evolution/patterns.md` |
    | STRIDE / Threat Model | `references/security/knowledge.md`, `references/security/checklist.md` |
    | Transformation Priority Premise | `references/encapsulation/rules.md` |
    | X Out Names | `references/api-design/rules.md` |
    
    Full retained list in `references/practices-glossary/knowledge.md`.
    
    ---
    
    ## Agent-Native Amendments (NOT from the book)
    
    Load one of these when the user's concern is specifically how an agent should do something differently from Seemann's 2021 guidance. These files are editorial additions, not summaries of the book.
    
    | Concern | File |
    |---------|------|
    | Verification checkpoints, independent oracles, and protection against weakened gates | `references/agent-native/verification-loops.md` |
    | Invented API, version drift, package hallucination, or dependency provenance | `references/agent-native/hallucination-debugging.md` |
    | Choosing practical types, schemas, tests, and executable constraints | `references/agent-native/types-as-guardrails.md` |
    | Reviewing agent-authored work, including very large systematic changes | `references/agent-native/reviewability.md` |
    | Overview / disclaimer that these files are not book content | `references/agent-native/knowledge.md` |
    
    When to prefer an `agent-native/` file over a book theme:
    
    | User's question | Book theme | Agent-native | Choose |
    |-----------------|-----------|---------------|--------|
    | "Why is this test flaky?" | `references/troubleshooting/` | — | Book |
    | "This test has been failing since I changed library version" | `references/troubleshooting/` | `references/agent-native/hallucination-debugging.md` | Agent-native (version drift) |
    | "How should this long-running change be verified?" | `references/outside-in-tdd/rules.md` | `references/agent-native/verification-loops.md` | Agent-native (checkpoint and oracle integrity) |
    | "Should I enable strict mode?" | `references/codebase-setup/rules.md` | `references/agent-native/types-as-guardrails.md` | Both — strongest practical policy with a brownfield ratchet |
    | "How do I write a good commit message?" | `references/teamwork-git/rules.md` | — | Book |
    | "How do I keep this PR reviewable?" | `references/teamwork-git/checklist.md` | `references/agent-native/reviewability.md` | Both — book for mechanics, ours for agent-specific framing |
    
  • README.md 3.8 KB
    # code-that-fits-in-your-head
    
    Cross-agent skill for keeping software understandable and inexpensive to change. It helps prevent accidental complexity, architectural erosion into a Big Ball of Mud, and silent technical-debt accumulation—especially when coding agents can generate code faster than people can review and own it.
    
    The skill is based on Mark Seemann's *Code That Fits in Your Head: Heuristics for Software Engineering* (2021), with clearly labelled editorial amendments for agent-driven development.
    
    Use it when a task is about:
    
    - designing complex code, APIs, domain types, validation boundaries, and invariants
    - decomposing tangled functions, classes, workflows, or subsystems
    - reviewing code for readability, cohesion, coupling, encapsulation, and testability
    - adding features outside-in with tests and a walking skeleton
    - debugging defects with reproducible tests, bisection, and tighter verification loops
    - evolving legacy systems with feature flags, Strangler-style migration, reversible stages, and explicit verification
    - threat-modelling endpoints or services with STRIDE
    - reviewing agent-generated changes for architectural fit, verification integrity, dependency provenance, and new debt
    
    Large tasks are not rejected because they are large. Agents can successfully implement changes spanning tens of thousands of lines when the work has coherent architecture, a clear plan, trustworthy acceptance criteria, and verifiable checkpoints. The skill targets unstructured complexity and unverifiable change—not scope by itself.
    
    ## How It Works
    
    `SKILL.md` contains the trigger description and top-level index. `guidelines.md` is the routing layer: it maps tasks, symptoms, and named practices to the smallest useful reference files.
    
    Code examples are C#-first; every rule and the tooling tables are language-neutral.
    
    The skill uses progressive disclosure. Load one primary file and at most one or two secondary files for the current task instead of reading the whole skill. When a rule proves repeatedly useful in a repository, operationalize it (`workflows/operationalize-finding.md`) so it persists as a gate and in project agent instructions.
    
    ## Structure
    
    ```text
    SKILL.md                 # Trigger, philosophy, chapter index
    guidelines.md            # Task/symptom/practice routing
    workflows/               # Step-by-step workflows for common engineering tasks
    references/              # Focused reference packs by theme
    references/tooling/      # Executable measurement commands (complexity, cycles, dead code, …)
    ```
    
    Core themes include decomposition, encapsulation, API design, outside-in TDD, separation of concerns, teamwork and Git discipline, software evolution, troubleshooting, security, and measurement tooling. Workflows cover review, outside-in features, debugging, threat modelling, and operationalizing findings into gates.
    
    ## Source Boundaries
    
    Most reference folders summarize or operationalize ideas from Seemann's book. The `references/agent-native/` folder is different: it contains local editorial additions for coding agents—verification integrity, hallucination and dependency grounding, executable guardrails, and accountable review. Editorial amendments placed in book-derived themes are explicitly marked. Do not attribute them to Seemann.
    
    ## Governing Principles
    
    - Prefer simple, cohesive designs over clever or speculative abstractions.
    - Keep dependencies, side effects, and ownership boundaries explicit.
    - Judge complexity across the system, not only inside individual methods.
    - Require verification proportional to risk and independent of the implementation where it matters.
    - Never weaken tests, types, CI, or security controls merely to make generated code pass.
    - Treat generated code volume, merge rate, and approval speed as poor proxies for maintainability.
    
  • SKILL.md 6.8 KB
    ---
    name: code-that-fits-in-your-head
    description: Software-engineering heuristics based on Mark Seemann's Code That Fits in Your Head (2021), updated for agent-driven development. Use when writing or reviewing code, refactoring accidental complexity or a Big Ball of Mud, controlling technical or architectural debt in generated code, designing APIs and invariants, adding a feature through a walking skeleton and acceptance tests, debugging a defect with reproducible tests or bisection, threat-modelling endpoints and trust boundaries with STRIDE, planning a legacy or Strangler migration with rollback, or setting up a maintainable codebase. Covers decomposition and cyclomatic complexity, cohesion, encapsulation, outside-in TDD, separation of concerns, Git/review discipline, safe evolution, and troubleshooting. Not for language syntax, framework tutorials, production incident response, or performance profiling.
    ---
    
    # Code That Fits in Your Head
    
    Engineering heuristics for sustainable software, based on Mark Seemann's 2021 book and clearly labelled agent-era amendments.
    
    Code examples are C#-first; every rule and the tooling tables are language-neutral.
    
    ## Philosophy (Why This Skill Exists)
    
    Software development is principally a **design activity**, not construction. An agent may produce most of the text, but people still review, operate, extend, and own the resulting system. These heuristics make software sustainable: understandable, resistant to architectural erosion, and cheap to change after thousands of decisions.
    
    Core mental model from Chapter 1:
    
    | Metaphor | What it gets right | What it misses |
    |----------|-------------------|-----------------|
    | **Building a house** | Plans, structure | Software endures; there's no construction phase (compiling is free); dependencies can start anywhere |
    | **Growing a garden** | Pruning, refactoring, tending | Code does not improve by itself; generated code still needs stewardship |
    | **Art / craft** | Skill, mastery, situational knowledge | Doesn't scale; leaves newcomers without guidance |
    | **Engineering** (the target) | Heuristics, review, sign-off, checklists | We're not there yet — physical-construction calculations don't apply |
    
    > "The act of describing a program in unambiguous detail and the act of programming are one and the same." — Kevlin Henney
    
    **Practical implications for a code agent:**
    
    1. **Successful software endures.** Prefer changes that preserve clear boundaries and keep future change affordable.
    2. **Complexity is the enemy, not task size.** Agents can complete changes spanning tens of thousands of lines when architecture, plan, and acceptance criteria are sound. Reject needless coupling, duplication, hidden effects, and unverifiable bulk—not large scope by itself.
    3. **Heuristics, not laws.** Understand the purpose of a rule before applying or relaxing it. Project policy overrides generic formatting and workflow conventions.
    4. **Verification is part of design.** Types, tests, schemas, architecture checks, observability, and explicit acceptance criteria constrain both human- and agent-written code.
    5. **Code is a liability.** Generated volume is not progress. Prefer the smallest coherent design that solves the problem without accumulating debt.
    
    See `references/foundations/` for more on sustainability, readability, and brain-limited design.
    
    ## How to Use This Skill
    
    1. Identify the user's task (writing, reviewing, debugging, security review, setting up, etc.)
    2. Read `guidelines.md` — it maps tasks and symptoms to specific reference files
    3. Load only the reference files relevant to the current task (progressive disclosure)
    4. Apply the rules; when in doubt, consult `references/practices-glossary/` for cross-references
    5. When a rule proves repeatedly useful in a repository, operationalize it (`workflows/operationalize-finding.md`) so it persists as a gate and in project agent instructions.
    
    ## Chapter Index
    
    | Topic | Use when... |
    |-------|-------------|
    | `references/foundations/` | Sustainability, readability, complexity control, and code as liability |
    | `references/codebase-setup/` | Starting or inheriting a code base — git, build automation, warnings-as-errors |
    | `references/outside-in-tdd/` | Writing new features test-first; walking skeleton, AAA, triangulation, devil's advocate, editing tests |
    | `references/encapsulation/` | Designing types with invariants; DTO vs Domain Model, always-valid, Postel's law, parse-don't-validate |
    | `references/decomposition/` | Controlling method and system complexity; cyclomatic complexity, cohesion, coupling, feature envy, fractal architecture |
    | `references/api-design/` | Designing a public API; affordance, poka-yoke, CQS, hierarchy of communication, naming over comments |
    | `references/separation-of-concerns/` | Adding cross-cutting concerns; Decorator pattern, logging, what to log, performance vs legibility |
    | `references/teamwork-git/` | Writing commits, reviewing changes, continuous integration, collective ownership |
    | `references/evolution/` | Changing running systems; feature flags, Strangler pattern, versioning, regular dependency updates, Conway's law |
    | `references/troubleshooting/` | Debugging a defect; scientific method, rubber ducking, reproduce-as-test, bisection, non-deterministic defects |
    | `references/security/` | Threat modelling; STRIDE (spoofing, tampering, repudiation, info disclosure, DoS, elevation) |
    | `references/code-navigation/` | Onboarding to a code base; big picture, file organisation, cycles, property-based testing, behavioural code analysis |
    | `references/tooling/` | Executable measurement commands: complexity, cycles, duplication, dead code, hotspots, mutation testing |
    | `references/practices-glossary/` | Looking up a named book practice and its current status |
    
    ### ⚠️ Editorial amendments (NOT from the book)
    
    The folder below is NOT content from Seemann's book. It contains our own additions covering agent-specific concerns the 2021 book does not address. Do not attribute these files to Seemann. See `references/agent-native/knowledge.md`.
    
    | Topic | Use when... |
    |-------|-------------|
    | `references/agent-native/` | Agent-specific verification integrity, hallucination and dependency grounding, executable guardrails, and accountable review |
    
    ## Workflows
    
    Composite step-by-step processes live in `workflows/`:
    
    | Task | Workflow |
    |------|----------|
    | Review a pull request / piece of code | `workflows/review-code.md` |
    | Add a new feature from scratch | `workflows/add-feature-outside-in.md` |
    | Investigate and fix a defect | `workflows/debug-defect.md` |
    | Threat-model a new endpoint | `workflows/threat-model.md` |
    | Turn a recurring finding into an executable gate and persist it in project memory | `workflows/operationalize-finding.md` |
    
    See `guidelines.md` for the full routing layer (task → file, symptom → file).
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related