# Boundary Mechanisms

Load when: every boundary check — this is the core reference. The technology packs under `packs/` instantiate these mechanisms with exact detect signals and gotchas; `index.md` routes to the packs the touched code exhibits.

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 litmus is **"would it ship green?"** — change one side, everything compiles, lints, and tests pass, and the other side breaks at runtime. An edge the compiler or an existing test already guards is not a boundary.

Every boundary instantiates one of seven mechanisms. The mechanism tells you what to grep for, what the universal fix is, and how bad a break is — including for technologies no pack covers. When touched code exhibits a contract that matches no pack entry, diagnose it from its mechanism.

---

## 1 · rendezvous-string

Two sides meet on a literal identifier: a column name in raw SQL, an event/job/topic name, a route path, an env key, a cache key, a channel template, a DI token, a flag key, a metric name, a port number, a binding name. The compiler sees two unrelated literals.

- **Ask:** where else does this exact string (or its template) appear — including config, IaC, CI, dashboards, and other repos?
- **Failure:** the sides silently stop meeting — publishes into the void, reads `undefined`, routes 404, handler never fires, alert goes quiet.
- **Trace:** grep the literal and its prefix/template variants repo-wide; the other side is often not code (a deploy manifest, a saved dashboard query, a provider allowlist).
- **Universal fix:** centralize the identifier in one shared constant or module. On rename, serve both old and new until the old side drains — in-flight jobs, cached entries, sent emails, deployed clients.
- **Severity:** HIGH by default; CRITICAL when the string scopes trust (a tenant segment in a channel name, a redirect-URI allowlist entry).

## 2 · serialized-shape

A payload crosses a gap in space or time — a process boundary, a deploy window, persistence — and the reader is not the writer. Old data and mixed-version peers must still parse. The defining sub-case is the **mixed-version window**: rolling deploys, in-flight queue items, long-lived clients, persisted events, browser storage — the old shape exists somewhere and outlives the change.

- **Ask:** who reads this shape that doesn't build from this commit — old rows, queued messages, deployed clients, replayed histories?
- **Failure:** the reader crashes or misreads with no error at the writer; persisted old shapes fail **forever**, not just during the deploy.
- **Trace:** find every deserializer of the shape (parsers, `JSON.parse` + property reads, ORM rehydration, client unwrappers) and every store where old instances persist.
- **Universal fix:** additive-only changes (add optional fields, never rename/retype/remove); carry an explicit version and read all live versions; readers tolerate unknown fields and ignore unknown discriminators.
- **Severity:** CRITICAL when the shape is persisted or replayed (the break is historical, not just live); HIGH for live-traffic shapes.

## 3 · generated-artifact

A derived artifact must stay in sync with its source: ORM client from schema, types from codegen, lockfile from package.json, bundler aliases from tsconfig, seed SQL from a CSV. The artifact is trusted as if it were the source — when they drift, the artifact **lies** to everything that consumes it.

- **Ask:** what generates this, what does it generate, and would CI notice if they drifted?
- **Failure:** code compiles against stale types and mismatches at runtime; installs resolve a tree nobody tested; hand-edits are clobbered on the next generation.
- **Trace:** find the generator invocation (scripts, `prepack`, CI steps) and every consumer of the output; check whether generation is enforced or manual.
- **Universal fix:** regenerate in the same commit as the source change; CI-gate drift (`git diff --exit-code` after generate); never hand-edit generated output.
- **Severity:** HIGH by default; CRITICAL when the artifact governs installs or schema (lockfile, migrations).

## 4 · dual-authority

Two independent writers own the same state: two migration tools targeting one database, a framework that owns its own tables, an app route table and an ingress rule, hand-written DDL and an ORM mirror. Each authority is internally consistent; the state they share is not.

- **Ask:** who else writes this — and does either writer know the other exists?
- **Failure:** one authority clobbers or drifts from the other; the loser's consumers break with no signal on the winner's side (a schema push drops the auth library's tables; login breaks).
- **Trace:** list every tool/config with write authority over the state (migration runners, CLIs, IaC, admin dashboards) and what each believes the state is.
- **Universal fix:** designate one canonical authority and make the others read-only mirrors reconciled from it — or byte-align them and CI-check the parity.
- **Severity:** CRITICAL when the shared state is persisted data or auth; HIGH otherwise.

## 5 · config-elsewhere

A value is declared in one place and consumed in another — env vars, platform bindings, secret paths, ports, build-time defines. Reader and provider never reference each other statically, and the provider is often per-environment, so the break can exist in exactly one environment.

- **Ask:** for each value this code reads, where is it declared — in **every** environment, not just the one you can see?
- **Failure:** `undefined` at request time, boot failure in one env only, a stale build-time literal where a runtime value was expected, a secret baked into a public artifact.
- **Trace:** map each read (`process.env.X`, `env.BINDING`, `import.meta.env.Y`) to its declaration in every env file, deploy manifest, CI secret store, and dashboard.
- **Universal fix:** validate the full key set at boot and fail fast; keep a tracked example/contract file (`.env.example`); change reader and every provider in one commit.
- **Severity:** HIGH by default; CRITICAL when the flow direction is wrong (a secret reaching a client bundle) — that is also a trust-invariant break.

## 6 · trust-invariant

An auth or isolation guarantee that **fails open**: tenancy scoping, RLS policies, cookie flags, CSRF defenses, redirect-URI allowlists, JWT audience checks, public-env prefixes. Unlike the other mechanisms, breaking it doesn't stop the system — everything keeps working, plus an attacker path or a cross-tenant leak.

- **Ask:** if this check were deleted, would anything visibly fail — or would the system happily serve data it shouldn't?
- **Failure:** silent grant or disclosure. There is no error to observe; tests that only exercise the happy path stay green by construction.
- **Trace:** find every enforcement point of the invariant and every path that bypasses it (new endpoints, new tables, raw queries, `SECURITY DEFINER`-style escape hatches, spoofable inputs like `req.body.tenantId`).
- **Universal fix:** enforce centrally (middleware, policy, schema default) so a new code path inherits the invariant instead of having to remember it; default-deny on any missing input.
- **Severity:** CRITICAL, categorically. Loud auth failures (everyone locked out) cap at HIGH; it's the quiet successes that are CRITICAL.

## 7 · lifecycle-protocol

An ordering or sequencing agreement with a platform or across deploys: expand-then-contract migrations, deploy order between producer and consumer, graceful-shutdown draining, replay determinism, retry/idempotency budgets, timezone/DST assumptions, append-only version tags.

- **Ask:** does correctness depend on *when* or *in what order* this runs — and who else assumes that order?
- **Failure:** correct code applied in the wrong order: a migration that strands a rolling deploy, a retry that double-charges, a rename that orphans live stateful objects, a cron that fires on the wrong day twice a year.
- **Trace:** reconstruct the sequence the change requires (what must exist before what) and compare against how deploys actually roll out — including crash/retry paths.
- **Universal fix:** make each step independently safe (idempotent handlers, additive-first migrations, append-only version markers); many boundary violations dissolve by **reordering the plan**, not changing what ships.
- **Severity:** CRITICAL when a wrong order corrupts persisted state or duplicates money-adjacent side effects; HIGH otherwise.

---

## Domain contracts — stack-independent, always checked

Two contracts come from the product's information architecture, not from any technology, and are checked on every run:

- **entity-model — CRITICAL.** The entity vocabulary and topology (what the entities are, how they relate) that every module, migration, wire type, and doc agrees on. Re-relating or renaming an entity is a change to *every* mechanism at once. Sourced from `docs/architecture/` (domain model, ADRs), a contracts package, and the schema — never from a single module's view of it. Safe change: map every consumer before re-relating; coordinate schema + wire + client in one planned sequence.
- **tenancy — CRITICAL · trust-invariant.** The invariant that every request carries a non-spoofable tenant identity and all data access is scoped by it, at every layer. Its enforcement points are technology-specific (a JWT claim, a channel-name segment, an RLS policy, a scoped query helper) but the invariant is one contract — a single unscoped path anywhere breaks it.

---

## Severity recap

- **CRITICAL** — can corrupt persisted data, or open an auth/money path. Trust-invariants and persisted/replayed shapes live here.
- **HIGH** — silent cross-module breakage that ships green.
- **MEDIUM** — drift-prone coupling (string-matched, duplicated constants) not yet broken.
- **LOW** — smell. Loud, immediately-visible runtime breakage caps at HIGH; statically-guarded edges are out of scope entirely.

Severity is intrinsic to the finding, not curved: three genuine CRITICALs in a small repo is correct signal, not inflation.
