Repository navigation
meta migrate: aggregate/monitoring read-model views can't be modeled as projections — 4 missing origin/field capabilities #195
Description
Summary
object.projection + origin.* currently synthesizes view DDL for passthrough and count/sum/avg/min/max aggregate shapes. But a common class of admin/monitoring read-model views can't be expressed, so an adopter is forced to keep them as hand-authored, unmodeled views — which meta migrate can't own and meta verify --db can't drift-check (they're invisible/unmanaged).
Because meta migrate processes the whole metadata tree, a single un-expressible view also can't just be "skipped": the projection either derives a base entity + synthesizable body, or the migrate/baseline run aborts with cannot derive the base entity — declare an extends-bound identity (FR-024) and blocks migration of every other entity in the tree. So an adopter with even a few aggregate views can't adopt meta migrate at all until each is either expressible or explicitly excludable.
Concretely, four capabilities are missing. Each is a real column in a shipping adopter's monitoring views:
1. Boolean-any / boolean-all aggregate (bool_or / bool_and)
origin.aggregate @agg supports count | sum | avg | min | max. There's no boolean rollup. The canonical case is a per-group "did any related row fail?" flag:
bool_or(NOT success) AS has_error -- one row per session/turnProposal: add @agg: any / @agg: all (→ bool_or / bool_and) with an optional @filter, or allow count(@filter) > 0 to lower to a boolean.
(generic column names below; the shapes are from real monitoring views but the names are anonymized.)
2. Computed-expression origin (per-row derived column)
No origin expresses a row-level computed column — only passthrough / aggregate / collection. The recurring case is substituting a heavy column with a cheap derived boolean:
(payload_json IS NOT NULL) AS has_payload -- avoid shipping the heavy JSON text in the list viewProposal: an origin.computed (or origin.expression) taking a portable, whitelisted expression over the base entity's own fields (IS NULL / IS NOT NULL, comparisons, COALESCE), emitted into the synthesized SELECT. Kept deliberately narrow (not a raw-SQL escape hatch).
3. Correlated latest-row origin (ORDER BY … LIMIT 1 per group)
No origin expresses "the latest related row's column X" — a correlated ORDER BY <ts> DESC LIMIT 1 / lateral. This is extremely common in session/entity summaries:
(SELECT label FROM child_a c WHERE c.parent_id = p.id AND c.is_active ORDER BY c.created_at DESC LIMIT 1) AS current_label
(SELECT score FROM child_b b WHERE b.parent_id = p.id ORDER BY b.created_at DESC LIMIT 1) AS latest_scoreProposal: an origin.latest (sibling of origin.aggregate) taking @via (relationship path) + @of (projected column) + @orderBy + optional @filter, lowering to a correlated subquery or DISTINCT ON.
4. Array-column typing for array_agg output
There's no field subtype/attr for a Postgres array column produced by array_agg (e.g. text[]), so an aggregate view that rolls values into an array column can't be projected and the column is dropped from the read model:
array_agg(DISTINCT category) AS categories -- text[](isArray: true models a stored array base-table column, but there's no way to declare that an aggregate origin produces an array column.) Proposal: allow origin.aggregate @agg: arrayAgg (with isArray: true on the projection field) to emit array_agg(...).
Why it matters
Together these are the difference between "the adopter's monitoring/admin read-models are metadata-managed + drift-gated" and "they stay hand-written SQL that verify --db is blind to." The passthrough + basic-aggregate support already shipped covers the simple views; these four cover the aggregate/correlated ones that are otherwise the long tail blocking full meta migrate adoption.
Happy to provide anonymized DDL for each shape and to help with the migrate-ts + per-port work if useful.
Activity
fix(codegen): a hand-authored view no longer aborts `meta migrate` fo…
docs(#195): design spec for four projection read-model origin capabil…
docs(#195): implementation plan for projection read-model origins
feat(#195): attr.expression closed node grammar (TS grammar core)
docs(#195): plan correction — vocab registration is coordinated cross…
feat(#195): register 4 projection read-model origin capabilities (coo…
feat(#195): per-capability origin validation (TS reference)
feat(#195): TS view-DDL synthesis for the four projection read-model …
feat(#195): precise projection read-schema nullability for the new or…
feat(#195): Python per-capability origin validation + native typing (…
feat(#195): Java per-capability origin validation + native typing (cr…
feat(#195): C# per-capability origin validation + native typing (cros…
feat(#195): Kotlin native typing for the four new origins (cross-port…
test(#195): origin-agg-any / origin-agg-all cross-port conformance fi…
docs(#195): CHANGELOG + roadmap follow-ups + agent-docs vocab
fix(#195): reject a non-object origin.computed @expr cross-port (clos…
fix(#204): projection view field carries isArray through extends (TS)
docs(roadmap): track the projection/view-materialization epic (#206-212)
feat(#207): view-level @filter DDL lowering — outer WHERE (emit layer)
feat(#214): entity read-view codegen READ half — Java port
feat(#214): entity read-view codegen READ half — Kotlin port
docs(#208): DDL-ownership escape valves design (@SQL body + @Unmanaged …
docs: accuracy pass for the 0.17.0/7.9.0 feature bundle (#195/#207/#214)
docs(skills): 0.17.0 agent-context skills overhaul — Fable-reviewed, …
dmealing commentedon Jul 18, 2026
Shipped in the coordinated 0.17.0 (npm / PyPI / NuGet) · 7.9.0 (Maven Central) release — live on all four registries (PR #215, tag
v0.17.0). Cross-port, conformance-gated. See CHANGELOG [0.17.0].test(conformance): shared #195 origin-validation error-fixtures (10)
test(integration): #195 origin real-engine value probes (PG + SQLite)
test(python): #195 origin read-model nullability (origin_guaranteed_n…