Open Mission Entity persistence will use the same normalized Object-Graph Model discipline already proven in /flying-pillow: canonical storage records carry only the Entity’s own persisted fields plus explicit relation references, while hydrated Entity read shapes materialize related Entities through the daemon-owned storage, factory, and Entity boundary rather than by embedding child Entity storage inside parent storage records.

This ADR defines the structural refactor needed to make that model canonical in Open Mission.

Problem

Current Open Mission schemas already distinguish <Entity>StorageSchema from <Entity>Schema, but several Entity families still persist hydrated child Entity material or duplicate an Entity’s own identity with compatibility-era names such as missionId or taskId.

That drift causes four failures:

  • storage records are wider than the canonical persisted row they should represent;
  • Entity identity is inconsistent across transport, storage, and relation fields;
  • SurrealDB relations cannot become the authoritative storage and retrieval seam because child graphs are embedded instead of modeled;
  • Entity contracts and daemon reads must compensate with compatibility-era self locators, filesystem-root fallback, and repeated hydration logic.

Decision

Entity modeling is anchored in a normalized OGM-backed storage model. These rules specialize, and do not replace, ADR-0001.01, ADR-0001.02, ADR-0001.04, ADR-0001.05, ADR-0001.07, and ADR-0003.03.

For every first-class Entity:

  • id is the only canonical self-identity field and must remain serialized as table:uniqueId.
  • Filesystem paths are operational location fields, not Entity identity. They may be stored when the Entity owns or needs a durable location, but they must not replace id, relationship ids, owner references, or invocation addressing.
  • <Entity>StorageSchema is the persisted row shape for that Entity only, extends the shared EntityStorageSchema, and carries the canonical zod-surreal table and field metadata for SurrealDB provisioning.
  • <Entity>StorageSchema may include explicit relation reference fields such as missionId, repositoryId, stageId, taskId, ownerId, or relation-table keys when those fields reference another Entity.
  • <Entity>StorageSchema must not embed first-class child Entity storage arrays or hydrated child Entity objects as canonical persisted state.
  • <Entity>StorageSchema may declare adapter-computed relation fields only when they are explicitly marked as computed/readback metadata, are not written by toStorage(), and exist to let the store hydrate <Entity>Schema through the canonical relation model. Such fields are not persisted parent state and must not become a writable child array.
  • <Entity>Schema remains the hydrated Entity boundary shape, extends the shared EntitySchema, and may include related Entities, derived workflow material, and command descriptors. It is also the schema that generic Entity hydration uses to apply serializable fields onto the live Entity instance and that toData() uses to serialize the instance back to boundary data.
  • When the owning Entity boundary exposes its related children as part of the complete Entity read, those child arrays belong directly on <Entity>Schema. A separate aggregate wrapper file for the same owning Entity is forbidden because it creates a second read authority for the same Entity boundary.
  • SurrealDB relation and reference metadata belong on the canonical storage schemas through @flying-pillow/zod-surreal metadata.
  • Schema aliases, schema-inferred type aliases, import aliases, remote schema maps, and method result wrappers are forbidden. A schema name exists only when it validates a distinct concept.
  • Partial composition schemas such as <Entity>PrimaryDataSchema, <Entity>BaseDataSchema, <Entity>CoreDataSchema, <Entity>CommonSchema, or <Entity>SharedSchema are forbidden when they merely group fields for reuse between storage, hydration, adapter construction, tests, or docs. Canonical persisted fields live directly on <Entity>StorageSchema; canonical hydrated fields live directly on <Entity>Schema; daemon-only construction details stay outside the Entity schema export surface.
  • Contract argument and result schemas must reuse existing canonical schemas whenever those schemas already describe the shape. Full-Entity reads return <Entity>Schema; methods whose only argument is target identity use EntityIdSchema directly, not method-specific locator payloads.
  • Projection-named schemas, types, transforms, files, methods, and contract results are forbidden in this refactor surface. Callers that need complete hydrated Entity data use <Entity>Schema.
  • Standalone aggregate wrappers such as <Entity>IndexSchema, <Entity>ReadModelSchema, or similarly named composite files are also forbidden when they merely restate the owning Entity plus hydrated child Entities that already belong on <Entity>Schema.
  • Every schema and field introduced or refactored for this model must keep the Zod .meta({ description }) discipline from ADR-0001.04; zod-surreal description metadata is required in addition for persisted storage fields, relation/reference fields, relation tables, indexes, and generated model definitions.

Storage Versus Hydrated View

Canonical storage is normalized. Hydrated reads are assembled by the Entity boundary or by a bounded factory-owned store/query layer. In this ADR, hydrated view is only a shorthand for the canonical hydrated <Entity>Schema returned from the Entity boundary; it is not a surface-owned view model, projection, snapshot, or UI state shape.

Hydration creates a live Entity instance, not a passive data wrapper. Generic Entity hydration applies fields declared by <Entity>Schema to the Entity instance. Related Entity data fields are materialized into related Entity instance properties when their schemas are registered in the Entity model catalogue. toData() serializes the current Entity instance through <Entity>Schema; it must not expose runtime capability properties and must not rely on duplicate mutable related-data copies.

Hydrated in this ADR is descriptive prose, not a naming license. It does not authorize exported aliases such as <Entity>HydratedSchema or <Entity>HydratedType when <Entity>Schema and <Entity>Type already name the canonical boundary shape. If the boundary exposes one complete Entity read, that read remains <Entity>Schema.

This means:

  • parent Entities persist parent-owned state and relation references;
  • child Entities persist child-owned state and the references that attach them to their owner or graph neighbors;
  • hydrated reads may materialize related children from SurrealDB reference fetches, relation traversals, or bounded store-owned query plans;
  • live Entity instances may expose those related children as related Entity instance properties while toData() serializes them back to <RelatedEntity>Schema data;
  • surfaces read hydrated Entity data or explicit read models published by daemon contracts, not raw storage rows;
  • daemon write paths still enter through Entity contracts and State store transactions, with SurrealDB acting as an adapter behind those seams rather than as a public write API.

The words storage and view are therefore anchored in the OGM model:

  • storage means canonical persisted Entity rows and relation rows;
  • hydrated view means the Entity boundary read shape, not surface-local UI state, projection state, or a transform output with separate Entity truth.

Likewise, public event families emitted for that Entity boundary remain Entity-owned contract material rather than registry-owned runtime vocabularies. A daemon registry may publish an Entity event, but it must publish the event family already defined by the Entity schema and contract.

Identity And Relation Rules

The identity law from ADR-0001.01 remains binding.

  • id is always the Entity’s own identity.
  • Fields such as missionId, repositoryId, stageId, taskId, or artifactId are valid only when they refer to another Entity.
  • If a domain concept needs a human-oriented key, slug, directory name, sequence number, or display token, it must be named for that concept rather than reusing missionId or taskId as a second self-identity.

For SurrealDB-backed storage:

  • the application boundary sees canonical string ids only;
  • SurrealDB-specific record id objects are adapter internals;
  • adapter code owns translation between canonical table:uniqueId strings and SurrealDB record identifiers.
  • Entity schemas must validate canonical string ids, not SurrealDB record id object shapes.

Relation Modeling Rule

Entity relationships are modeled in storage schemas, not invented later in ad hoc query code.

Use these patterns:

  • direct reference fields for one-to-one or many-to-one ownership and lookup paths; these are the default modeling seam for canonical owner and foreign-reference fields and must compile to typed SurrealDB record<...> references with explicit ON DELETE policy where lifecycle behavior matters;
  • relation tables when the relationship has its own identity, cardinality constraints, metadata, or traversal semantics;
  • zod-surreal reference field metadata and relation-table metadata as the canonical DDL seam.

FETCH-style hydration and direct record dereference are the primary read path for reference-modeled ownership and lookup fields. Graph traversal through relation tables is for first-class edges, not the default substitute for ordinary owner/reference fields.

SurrealDB relation-table ENFORCED semantics are optional hardening, not a normative requirement of this ADR. Open Mission may adopt them where a graph edge must not exist before both endpoint records exist, but the core model does not depend on ENFORCED.

Do not persist hydrated child arrays in parent storage merely because the hydrated Entity read shape exposes them.

Entity Contract Consequences

Entity contracts become uniformly addressable.

  • class methods operate on the Entity class and do not take an Entity id unless the method argument intentionally addresses an Entity by identity;
  • methods whose only argument is target identity use canonical id directly as the method argument;
  • relation references remain explicit in method arguments when the behavior truly depends on another Entity relationship.

This refactor therefore pairs storage normalization with a transport normalization: method targeting must not depend on compatibility-era locator payloads or hidden repository-root context. A method whose argument is simply the Entity identity takes that id directly. A method that needs more than identity receives a named input schema whose fields describe the operation rather than a generic locator wrapper.

Method-specific input or result schemas are allowed only when no existing Entity, shared, acknowledgement, event, descriptor, or value-object schema already validates the exact shape. In particular, a read method returning the complete Entity binds directly to <Entity>Schema and does not introduce <Entity>ReadResponseSchema, <Entity>ReadResultSchema, or another wrapper.

Registry Direction

Open Mission will converge on one canonical Entity model/contract registry entry per Entity family. The registry is a daemon-owned catalogue, not generic Entity infrastructure and not a second schema authority.

Each registry entry must provide:

  • Entity name;
  • table name;
  • Entity class;
  • input, storage, and hydrated schemas;
  • Entity contract;
  • zod-surreal model definition and relationship metadata;
  • store/factory registration metadata.

That registry becomes the shared source for daemon dispatch, EntityFactory registration, schema generation, relation-aware hydration, and SurrealQL provisioning. The registry is daemon-owned infrastructure consumed by the factory and dispatcher; generic Entity base modules must remain child-independent.

The registry must not define schemas inline, alias schema-inferred types, or select behavior independently of <Entity>Contract.ts. It only binds the canonical class, schema, contract, table, and zod-surreal model metadata already owned by the Entity family.

Database Topology

Open Mission is a clean-sheet implementation. There is no live production database to migrate and no compatibility persistence shape to preserve.

  • each tracked Repository owns one Open Mission database at <repository-root>/.open-mission/database/, provisioned by Repository initialization;
  • every first-class Entity, including Repository, is persisted as canonical rows in its own table within that database;
  • Repository and Mission ownership are modeled by canonical row fields and relation metadata, not by selecting different databases;
  • Repository initialization provisions that database before Mission work depends on it;
  • .open-mission/settings.json remains the authoritative Repository settings document but does not carry a database-path override in the first implementation wave.

This database location is Repository control state. Mission record state and Mission worktree paths do not create alternate Entity databases.

Refactor Scope

This is a structural refactor, not a local cleanup.

The first implementation wave must include:

  • canonical id cleanup where self-identity is duplicated under entity-specific field names;
  • normalization of the first targeted Entity families, starting with the current Mission-owned graph where embedded child storage is already present;
  • normalized schema and DDL ownership for the code-intelligence graph so its persisted shape is generated from canonical schemas instead of ad hoc table definitions;
  • relation modeling in storage schemas using zod-surreal metadata;
  • a bounded factory-owned SurrealDB read layer that can hydrate related Entity data from modeled relations;
  • an Entity registry that unifies contracts, classes, storage schemas, hydrated schemas, and Surreal model definitions;
  • transport cleanup so instance calls are addressed by canonical Entity id rather than compatibility-era locator payloads;
  • State store transaction discipline so Surreal writes do not become an unvalidated daemon-side shortcut around Entity contracts.

Consequences

  • Open Mission Entity storage becomes structurally consistent with the normalized OGM pattern proven in /flying-pillow.
  • SurrealDB can become the authoritative persistence and relation traversal seam for first-class Entities.
  • Entity storage stays narrow and durable.
  • Hydrated Entity reads stay rich without forcing storage schemas to become embedded document bags.
  • Entity contracts, stores, and factory registration can converge on one registry-backed model.

Risks:

  • the refactor touches identity, storage, contracts, hydration, and daemon transport at once;
  • because this is a clean-sheet implementation, temporary dual-model compatibility and persistence migration must not be introduced;
  • several current storage schemas and contracts will become invalid and must be rewritten mechanically, not patched piecemeal.

Validation Expectations

Implementation must prove at least:

  • storage schemas no longer persist hydrated child Entity arrays for first-class child Entities;
  • schema and contract audits prove no schema aliases, schema-inferred type aliases, import aliases, remote schema maps, partial composition schemas, unnecessary read response/result schemas, or Projection-named Entity shapes remain in the refactored surface;
  • description audits prove every schema, field, nested field, enum, union member, method argument/result schema, event schema, acknowledgement schema, descriptor schema, table, relation, reference, and index carries meaningful documentation metadata;
  • SurrealDB provisioning compiles reference and relation metadata from canonical storage schemas;
  • canonical string ids round-trip through the Surreal adapter;
  • hydrated Entity reads can materialize related child Entities from modeled relations;
  • Entity remote instance calls are addressed by canonical Entity id;
  • code-intelligence graph storage is expressed as canonical node and edge schemas with generated DDL rather than ordinary catch-all relation tables;
  • every refactored Entity family follows the same identity and relation rules.