# ADR-0005: Contract-authority split — Pydantic SSOT for engine entities, TS+zod SSOT for UI-owned types

**Status:** Accepted (Phase 2; SYNTHESIS Locked Decision #8)

## Context

Some shapes belong to the engine (Project, Beat, Take, GenerationReceipt,
EvalScoreCard) — they are produced by Python code, persisted to the engine's
on-disk format, and shipped over the wire. Other shapes belong to the UI
(workspace state envelopes, tweaks, parked-tab metadata, viewport positions)
— they live only as long as the console runs and have no engine semantics.
Owning all shapes in one language forces awkward seams: either Pydantic
models that serialize UI ephemera, or TypeScript types the engine has to
re-derive.

## Decision

Split the contract authority by data ownership. Engine entities are SSOT in
Pydantic; the codegen at Phase 16 (`pydantic-to-typescript`) emits
`packages/contracts/src/generated.ts`. UI-owned types are SSOT in TypeScript
+ zod, hand-authored under `packages/contracts/src/manual.ts`. The
workspace-state server stores UI shapes opaquely as JSON — it does not
re-derive their structure.

## Consequences

Engine refactors propagate to TS automatically via codegen; UI refactors
never bleed into the engine. The cost: developers have to know which side
owns which shape (the README in `packages/contracts/` lists this). A shape
that genuinely lives in both worlds must be promoted to engine ownership
and codegenned — no dual hand-typing.
