You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
FR: @sqlView escape valve + managed-elsewhere marker — register a hand-written DB object instead of aborting meta migrate #208
Formalizes the escape valve already chartered as "a separate FR to design with Fable" in the #195 follow-ups (spec/roadmap.md, #195 follow-up (a)) and the #195 design doc (2026-07-15-issue-195-projection-readmodel-origins-design.md:23,152). It is out of scope of #195 itself but blocks the same adoption path.
A genuinely irreducible view — recursive CTE, window function, set operation (UNION), or a base the metadata doesn't (yet) model — cannot be synthesized from origin.*. Today a single such view aborts the entire meta migrate run (extract-view-spec.ts:226-231), so an adopter with even one can't migrate anything. The current fallback (a hand-written, unmodeled view) is invisible to the tool: meta migrate can't own it and meta verify --db can't drift-check it.
Two distinct concepts (both must generalize to ANY DB object, not projections only)
1. @sqlView — a metadata-carried, hand-written view body the tool registers but does not author. A worst-case escape hatch inside the metadata file (keeps the SoR the single source of truth):
{ "source.rdb": { "@kind": "view", "@view": "v_report",
"@sqlView": "SELECT … FROM … WHERE … UNION ALL …" } }
meta migrate emits the CREATE VIEW verbatim, fingerprints it (reuse view-fingerprint.ts), and meta verify --db drift-checks the fingerprint — managed-but-opaque instead of invisible. The tool never parses/derives the SQL; it owns the object's lifecycle, not its contents.
2. A distinct "managed-elsewhere" marker — "this object exists but Flyway / a hand-migration owns its DDL; do not create, drop, or drift-check it." meta migrateskips it (rather than aborting), so one un-modeled object no longer blocks the tree.
The two are orthogonal: (1) is "I give you the DDL, you manage it"; (2) is "someone else manages it, stay out of the way." Both apply to entities and other DB objects, not just view projections.
Why in-metadata (not a side migration file)
Keeps the object visible in the model and drift-gated, and lets the tool make the create-vs-skip decision per marker — the alternative (raw DDL in a Flyway file) drops the object out of the SoR entirely and out of verify --db's sight.
Scope
The two escape attrs on source.rdb (and entities); migrate emits (@sqlView) or skips ("managed-elsewhere") per marker instead of the all-or-nothing abort; verify fingerprints the raw body; generalize across paradigms so the same escape works for any future backend. Design with Fable per the #195 owner ruling (2026-07-15).
One more requirement for the escape valve to be usable end-to-end: the external-body marker (@sqlView / managed-elsewhere) must also override derivation-classification. Today viewIsDerived flips a projection to "synthesize" the moment it carries an extends-bound field or identity. But an escape-valve view often needs to state its lineage — a borrowed identity.primary { extends: "Base.id" } (row identity / get-by-id) and/or extends-bound base fields — without the tool trying to synthesize a body it has been explicitly told is external. Please spec that an @sqlView / managed-elsewhere source suppresses synthesis even when identity/fields are extends-bound (ADR-0028's "self-declared under external assembly" already implies this). Without it, an escape-valve view can never declare its row identity — a real case is a hand-written view whose id IS the base entity's id.
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].
Activity
dmealing commentedon Jul 16, 2026
One more requirement for the escape valve to be usable end-to-end: the external-body marker (
@sqlView/ managed-elsewhere) must also override derivation-classification. TodayviewIsDerivedflips a projection to "synthesize" the moment it carries anextends-bound field or identity. But an escape-valve view often needs to state its lineage — a borrowedidentity.primary { extends: "Base.id" }(row identity / get-by-id) and/orextends-bound base fields — without the tool trying to synthesize a body it has been explicitly told is external. Please spec that an@sqlView/ managed-elsewhere source suppresses synthesis even when identity/fields are extends-bound (ADR-0028's "self-declared under external assembly" already implies this). Without it, an escape-valve view can never declare its row identity — a real case is a hand-written view whoseidIS the base entity'sid.docs(roadmap): track the projection/view-materialization epic (#206-212)
docs(#208): DDL-ownership escape valves design (@SQL body + @Unmanaged …
docs(#208): implementation plan — @sql/@Unmanaged DDL-ownership escap…
feat(#208): register @SQL + @Unmanaged on source.rdb (5-port coordina…
feat(#208): TS MetaSource @sql/@Unmanaged resolving accessors
feat(#208): TS loader validation for @sql/@Unmanaged (6 fail-closed r…
feat(#208): suppress derivation-classification for @sql/@Unmanaged so…
feat(#208): @SQL view — golden-DDL + D4 hard-error + real-PG adopt-vi…
feat(#208): @Unmanaged — skip create/drop/drift + introspection-side …
fix(#208): checkpoint code-review fixes — offline DROP suppression + …
feat(#208): C# @sql/@Unmanaged accessors + loader validation
feat(#208): Java @sql/@Unmanaged accessors + loader validation
feat(#208): Python @sql/@Unmanaged accessors + loader validation
test(#208): shared conformance error-fixtures for @sql/@Unmanaged (5-…
docs(#208): ADR-0043 + migration/verify docs for @sql/@Unmanaged
fix(#208): Fable-review fixes — write-through @SQL dependsOn + R3/R5 …
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(cli)+docs(tph): #208 verify --db @Unmanaged gate + TPH lane-cove…
fix(migrate): #239 — an opaque @SQL adopt keeps its non-destructive r…
fix(migrate): offline --allow adopt-view drop+creates a structural pr…