Skip to content

Prelude to #210: port #248's persistability-from-source contract cross-port, and prove each fan-out no-ops on a sourceless projection #271

Description

@dmealing

Summary

#248 established the contract that an
object's participation in persistence derives from a declared or inherited source.* child,
never from a hardcoded object-subtype check. It shipped npm-only (migrate-ts + codegen-ts),
because schema is TS-owned per ADR-0015 and no other port has a migrate engine.

But the contract is not only about migrate. Every port's projection fan-out dispatches on
source kind — view-kind projections get a read-only DTO, proc-kind projections get FR-015
proc-callables. None of the four non-TS ports has ever been handed a projection with zero
source.* children, because nothing produces one today.

Why this matters

#210 will make sourceless projections
a first-class, commonly-authored shape: prompt payloads become projections with no source. On the
day that lands, four ports' fan-out meets an input it has never seen. The failure mode is not a
clean error — it is a @kind switch falling through, or a generator emitting a DTO that assumes a
table.

Verifying and fixing the dispatch before the breaking change, as its own non-breaking unit, is
what keeps #210's risk in the diff rather than in the fan-out.

Scope

For Java, Kotlin, C# and Python (TS already has it via #248):

  1. Establish what each port's projection/entity fan-out currently keys on — an explicit
    object-subtype check, a @kind lookup, or the presence of a source.
  2. Make persistability derive from the declared/inherited source.* child, matching Persistability is decided by a hardcoded subType === "value" compare — any custom object subtype silently becomes a table #248's
    contract.
  3. Prove each port cleanly no-ops on a projection with zero sources: no DDL, no table-assuming
    DTO, no silent fall-through. A sourceless projection should generate its read shape and nothing
    else.

Gate

  • A shared conformance fixture declaring a sourceless object.projection, exercised by all five
    ports. Existing output must be byte-identical for every model that has a source (this is
    additive tolerance, not a behaviour change).
  • Follow the ADR-0039 own-accessor discipline when reading the source.* child — a projection may
    inherit its source through extends, so the read must be resolving, not own-only.

Falsification signal for #210

If any port cannot no-op on a sourceless projection without new vocabulary — e.g. a marker
attribute distinguishing a wire-assembled projection from a database-backed one — that is a
different cost class under ADR-0023 and reopens the #210 ruling. Report it there rather than
inventing the attribute.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:metamodelMetamodel vocabulary / loader

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions