Skip to content

DR-138 — A runtime validator is minted for untrusted input, not for symmetry

DR-138 — A runtime validator is minted for untrusted input, not for symmetry

Section titled “DR-138 — A runtime validator is minted for untrusted input, not for symmetry”

lib/validation/owner-kind.ts (ID-151, bl-412) shipped a TypeScript union and a Zod enum for each of three discriminator domains. Two of the three Zod enums had no consumer at all.

The reason is legible in the task, not in the code. ID-151’s goal line reads: “Introduce a shared OwnerKind/CitedKind type + Zod enum and re-point call sites for compile-time safety.” The deliverable was specified as “type + Zod enum” uniformly, so all three domains got both halves — while the stated purpose, compile-time safety, is delivered entirely by the unions. bl-412’s triage rationale is explicitly “Bare string literals at ~20 call sites with DB CHECKs as backstop … explicitly non-regression.” The authoring commit is a crash rescue (“executor parked pre-finalize”), and the finalisation that would have pruned or wired the unused halves never ran.

S544 swept app/api/**, lib/**, scripts/**, the Python pipeline, migrations, seeds, .rpc( sites, e2e/** and mcp-apps/ for a path where an owner_kind or cited_kind value reaches a write without being one of our own literals. None exists. No SQL function takes such a parameter. The single client-supplied kind field on the API surface is ReviewActionBodySchema.owner_kind — whose validator, FacetOwnerKindSchema, is wired and stays.

Mutation confirmed the unions are load-bearing: typo’ing a literal at lib/intelligence/pipeline.ts:176 fails bun run typecheck, even though the generated Insert type declares the column as bare string.

A Zod enum is minted for a discriminator column when a value can reach it from outside our own source — a request body, a query param, an MCP or tool-call argument, a file import. Where every call site writes a literal, the TypeScript union is the whole guard and no runtime validator is added.

Deleted: RecordEmbeddingsOwnerKindSchema, CitedKindSchema. Retained: FacetOwnerKindSchema (request-body input), and both TS unions.

Keep them as defence in depth. Rejected: an unreachable validator is not depth, it is a claim of protection that no test exercises and no caller invokes. It also invites the inverse error — a reviewer seeing a Zod enum in the module may assume the column is validated at some boundary when it is not.

Keep them for symmetry across the three domains. Rejected: symmetry is what produced them. The three domains differ in the way that matters — one takes untrusted input and two do not — so uniform treatment obscures the distinction the module exists to draw.

  • Adding an untrusted-input path to record_embeddings.owner_kind or citations.cited_kind in future requires re-minting the validator at that boundary. Neither column is validated at runtime today, by design.
  • A related claim in the same module was measured and found false, and is corrected in place rather than relied upon: satisfies readonly CitedKind[] was documented as making a future ALTER TYPE … ADD VALUE “drift loudly”. It does not — adding a bogus label fails, but removing a real one passes silently, because satisfies is a subset check, not an exhaustiveness one. Do not treat it as a drift alarm.
  • The exhaustiveness that does hold is at the consumer: citedTargetForDraftItem’s never arm stops compiling if the drafting-grain union grows a third member (verified — adding 'concept' errors at draft-response.ts:195).
  • ID-151 is archived; this amends its output without reopening it.