Summary
FR-024 §7 "view behavior on the entity" is doctrine-complete and loader-validated — an object.entity may carry a non-primary read-only source.rdb @kind:view plus derived (origin.*) fields; writes route to the table, reads to the view, derived fields are read-only + excluded from write codecs, and the design promises the view DDL is emitted ("one emitter, two hosts"). The loader even licenses it (ERR_DERIVED_FIELD_NO_READ_SOURCE). But the schema pipeline never implements it, in two places:
1. Derived fields leak into the write table. migrate-ts/src/expected-schema.ts:377 builds a column for every entity.fields() — it never excludes origin-bearing (derived) fields:
for (const field of entity.fields()) {
// … flattenObjectField / buildColumn — no check for an origin child
}
So a field whose data comes from a join (origin.passthrough @from:"Other.col" @via:"…") gets a spurious column on the write table. Worse: when that column name matches the read-view's own SELECT alias (SELECT E.*, O.col AS col …), the generated CREATE VIEW collides on a duplicate column.
2. The read-view is never emitted. isWriteThrough() (codegen-ts/src/projection/projection-detector.ts:24) is defined, exported (index.ts:83), and tested — but has no call site. buildProjectionViews only walks isProjection objects, so a write-through entity's replica-view DDL is never emitted or owned, despite FR-024 §7.
Impact
An adopter following the FR-024 §7 / entity-read-view on-ramp (model the join extras as derived fields on the entity, the intended clean modeling) gets a wrong table — derived join columns leak in, and can collide with the view — and no view is created. That is a silent-corruption class, more severe than a docs gap. It's the exact reason a downstream migration currently has to quarantine the view-only columns out of the entity's field set as a workaround instead of modeling them properly.
Proposal
- Exclude origin-bearing fields from the column loop in
expected-schema.ts (a field with an origin.* child is derived → read-only → not a write-table column).
- Wire
isWriteThrough entities into buildProjectionViews so a write-through entity's replica-view DDL is emitted and owned exactly like a projection's view — reuse the one canonical emitter ("one emitter, two hosts"). The dead-but-tested isWriteThrough is presumably the intended hook.
Not covered by the open issues
This is the schema-pipeline half of FR-024 §7. The entity-read-view docs issue is on-ramp docs only; the view-level-filter and join-type issues extend the view emitter but don't wire the write-through host. So without this, the whole "add the read source and the derived fields" story produces incorrect schema.
Summary
FR-024 §7 "view behavior on the entity" is doctrine-complete and loader-validated — an
object.entitymay carry a non-primary read-onlysource.rdb @kind:viewplus derived (origin.*) fields; writes route to the table, reads to the view, derived fields are read-only + excluded from write codecs, and the design promises the view DDL is emitted ("one emitter, two hosts"). The loader even licenses it (ERR_DERIVED_FIELD_NO_READ_SOURCE). But the schema pipeline never implements it, in two places:1. Derived fields leak into the write table.
migrate-ts/src/expected-schema.ts:377builds a column for everyentity.fields()— it never excludes origin-bearing (derived) fields:So a field whose data comes from a join (
origin.passthrough @from:"Other.col" @via:"…") gets a spurious column on the write table. Worse: when that column name matches the read-view's own SELECT alias (SELECT E.*, O.col AS col …), the generatedCREATE VIEWcollides on a duplicate column.2. The read-view is never emitted.
isWriteThrough()(codegen-ts/src/projection/projection-detector.ts:24) is defined, exported (index.ts:83), and tested — but has no call site.buildProjectionViewsonly walksisProjectionobjects, so a write-through entity's replica-view DDL is never emitted or owned, despite FR-024 §7.Impact
An adopter following the FR-024 §7 / entity-read-view on-ramp (model the join extras as derived fields on the entity, the intended clean modeling) gets a wrong table — derived join columns leak in, and can collide with the view — and no view is created. That is a silent-corruption class, more severe than a docs gap. It's the exact reason a downstream migration currently has to quarantine the view-only columns out of the entity's field set as a workaround instead of modeling them properly.
Proposal
expected-schema.ts(a field with anorigin.*child is derived → read-only → not a write-table column).isWriteThroughentities intobuildProjectionViewsso a write-through entity's replica-view DDL is emitted and owned exactly like a projection's view — reuse the one canonical emitter ("one emitter, two hosts"). The dead-but-testedisWriteThroughis presumably the intended hook.Not covered by the open issues
This is the schema-pipeline half of FR-024 §7. The entity-read-view docs issue is on-ramp docs only; the view-level-filter and join-type issues extend the view emitter but don't wire the write-through host. So without this, the whole "add the read source and the derived fields" story produces incorrect schema.