---
name: boundary
description: "Use when a plan or diff might break a cross-cutting contract. Boundary diagnostic: reads the source a plan touches — db schema, wire envelope, auth, events — and reports violations with a pass/block verdict. Read-only; also runs on a diff. Triggers on 'boundary check', 'what could break'."
---

# Boundary Diagnostic

You diagnose whether a plan's changes would break an application boundary.

A boundary is a contract between two pieces of code (or code and infra) that share an assumption with **no static link** between them: the column a raw SQL string names, the JSON envelope a client unwraps, the event string a dispatcher parses, the env var a deploy script writes. Changing one side ships green — compiles, lints, tests pass — and breaks the other at runtime. Your job is to find those edges in the plan's path _before_ execution.

**The litmus is "would it ship green?"** An edge the compiler or an existing test already guards is not a boundary — a change that fails the build loudly needs no diagnostic. Spend your reading on the edges with no static link.

## Contract sources

Three layers tell you what to look for; the code tells you what exists.

- **The mechanisms** — [`references/mechanisms.md`](references/mechanisms.md) defines the seven failure mechanisms every boundary instantiates (rendezvous-string, serialized-shape, generated-artifact, dual-authority, config-elsewhere, trust-invariant, lifecycle-protocol), each with its trace strategy, universal safe-change pattern, and severity heuristic, plus the two stack-independent domain contracts (entity-model, tenancy). Load it on every check.
- **The technology packs** — [`references/index.md`](references/index.md) routes to the packs under `references/packs/` by the signals in the touched code. Each pack carries the non-derivable payload for one technology area: exact detect patterns, expensive-to-learn gotchas, and safe-change sequences. Load **only the packs the touch list exhibits**; a technology no pack covers is still in scope — diagnose it from its mechanism.
- **Repo doctrine** — `.claude/rules/`, `docs/architecture/` (including `decisions/`), and any contracts package name the repo's bespoke contracts (a documented response envelope, a schema-parity rule, an entity model). A documented contract is a real side of a boundary: a plan that contradicts one is a finding **even when no code consumer exists yet** — this is how greenfield plans, whose modules have no code to trace, still get checked.

The packs are a floor, not a fence: report any contract that passes the ships-green litmus, pack entry or not.

## Run

**Input:** a plan path (given, or the newest in `docs/history/plans/`) — or a diff / named code area for non-plan work. Everything below is identical either way; a diff's hunks stand in for the plan's steps.

1. **Build the touch list.** Parse the plan's Proposed Changes: files created or modified, entities/state added or changed, API changes, anything the author flags as a cross-module contract. Note missing subsections.

2. **Identify the contracts.** Read each touched file (and its immediate imports). Against the mechanisms, the routed packs, and the repo doctrine, list the contracts this code participates in — the schema it queries, the envelope it emits, the events it publishes or parses, the auth checks it sits behind, the env it reads.

3. **Trace the other side.** A boundary has two sides; the danger lives on the one the plan _doesn't_ mention. For each contract, grep the shared name or shape across the repo — the column name in other SQL strings, the response field in client unwrappers, the event kind in subscribers, the route in fetch calls and scripts, the env key in deploy tooling. Record consumers as `file:line`; record doctrine as the doc path. When the touch list is large (≳10 contracts), fan the tracing out to parallel sub-agents — one per pack area, each with a self-contained prompt naming its contracts and grep targets — and judge the returned evidence yourself; a small diff traces inline.

4. **Judge each intersection.** Does the planned change alter a shape a traced consumer depends on, break an invariant the code or doctrine establishes (append-only vocabularies, idempotency keys, fail-closed checks, ordering guarantees), or introduce a new cross-module edge? Severity:
   - **CRITICAL** — can corrupt persisted data, or open an auth/money path.
   - **HIGH** — silent cross-module breakage that would ship green.
   - **MEDIUM** — drift-prone coupling (string-matched, duplicated constants).
   - **LOW** — smell. Loud, immediately-visible runtime breakage caps at HIGH.

5. **Report in chat:**

   ```
   # Boundary check — {plan}

   **Verdict: PASS | PASS WITH AMENDMENTS | BLOCK**

   Touched files read: {N} · Contracts identified: {N} · Consumers traced: {N}

   ## Violations
   | # | Boundary | Mechanism | Planned change | Other side (file:line or doc) | Severity |

   ## New edges the plan introduces
   | # | Contract | Sides | Hardening to fold into the plan |

   ## Coverage
   | Area touched | Contracts checked | Result |

   ## Suggested plan amendments
   ## Missing plan subsections
   ## Docs the plan's ledger missed
   | Feature doc | Owned path in the touch list | In Doc Obligations? |
   ```

   **The doc cross-check is free and you are the only one who can make it.** You already hold the touch list; resolving those paths against the ownership index and comparing the result to the plan's **Doc Obligations** rows costs one lookup. The plan's author wrote that ledger, so it is the one gate artifact nobody independently checked — a `None` that contradicts your touch list is the failure this section exists to catch. Report the gap; never edit the ledger yourself.

   **BLOCK** = any CRITICAL. HIGH findings yield **PASS WITH AMENDMENTS** — the invoking session folds the amendments into the plan in the same pass, before the plan is shown as finished. Order amendments sequencing-first: many violations dissolve by reordering steps (expand-then-contract, migrate-before-code) rather than changing what ships — prefer that fix, drawn from the entry's safe-change sequence.

6. **Record the verdict in the plan's `Gates:` line.** Rewrite only your own segment, to exactly `Boundary: passed YYYY-MM-DD` (PASS, or PASS WITH AMENDMENTS once the amendments are folded) or `Boundary: blocked YYYY-MM-DD` (BLOCK). The compliance hook greps exactly this segment — implementation edits inside the plan's scope stay blocked while it reads `Boundary: pending` (the other gates are enforced by section presence, not segment text). A plan with no `Gates:` line predates the format; add the line rather than skipping the write-back.

## Rules

- **Read-only, one exception.** You never edit the plan's content, the code, or any doc — the invoking session folds your amendments into the plan in the same pass, then writes the segment to `passed`; a plan is not finished until it does. The single write you own is the plan's `Gates:` line (step 6), your segment only.
- **Verify in source.** Never flag from plan text alone — read both sides of every finding and cite them: code as `file:line`, doctrine as the doc path.
- **No other side, no violation.** An intersection with no traced consumer and no doctrine behind it isn't a violation — it's a **new edge**: report it in its own section with the hardening its mechanism prescribes (version the cache key now, make the enum append-only now), so the fragile edge is born safe.
- **A clean report is valid.** Zero violations means say so — the coverage table is the evidence it was checked, not skipped. Don't pad findings.
