Context

ClientManifest is created by build/, then implicitly mutated by dev/, then consumed by render/ — with no documented ownership or mutation contract. Agent and tool registries are similarly shared without defined ownership. This makes it impossible to reason about correctness across module boundaries, and quietly allows races in dev mode.

Decision

Every shared data structure has exactly one owner, one mutation window, and one freeze point. After the freeze, any write throws in strict mode (Object.freeze). When a structure must change (e.g. dev island rebuild), the owner creates a new instance and atomically replaces the reference — never mutating the currently-serving frozen instance. See data ownership for the full table.

Consequences

State transitions are visible in the type system: consumers receive Readonly<ClientManifest>; the freeze is structural, not just documented. Swap-on-change means request handlers always see a consistent snapshot, not a partially-updated manifest. Cost: dev/ must build a full new manifest on each island rebuild rather than patching the old one — acceptable given that manifests are small.

Patties design specifications · a design exploration · All decisions