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
Commit 7d2aa5a
Browse filesBrowse the repository at this point in the historyBrowse files
An independent Fable-model review of the whole #208 stream found one MAJOR bug the two
prior Opus reviews missed, plus minor doc/coverage gaps. Fixes:
- [MAJOR] A write-through host (writable table + an @SQL read-view source, no derived
fields — legal, since R4 only forbids origin.* under @SQL) has NO extends anchors, so
collectSqlDependsOn returned [] — omitting the view's ONE certain dependency, the host's
own table. A later column-altering ALTER on that table would then fail at apply (Pass 2c
never drop+recreates the view around it). collectSqlDependsOn now also includes the
host's own writable table for a write-through host (distinct from the deferred D7 case,
where the missing dep is a JOINed table not declared in metadata). The write-through
@SQL branch had zero coverage; a regression test now pins dependsOn == [host table].
- [MINOR] The ADR/CHANGELOG claimed all six rules were conformance-gated by shared
fixtures; only R1/R2/R4 were. Added shared error-fixtures for R3 (empty @SQL →
ERR_BAD_ATTR_VALUE) and R5 (@filter + @SQL → ERR_ORIGIN_UNDER_SQL_BODY) — R5 being the
subtlest own-vs-resolving rule. Now R1–R5 (the five error rules) are cross-port gated;
the R6 WARN stays per-port unit-tested (the corpus has no warn-fixture mechanism). Docs
corrected to match, and the Java fail-fast-per-pass vs collect-all difference documented.
- [NIT] ADR-0043 now documents that @Unmanaged is effectively non-inheritable in the
migrate lowering (both diff halves use own-source detection) — declare it on the
concrete entity's own source.
All 5 ports green on the two new fixtures (TS conformance, Java ConformanceTest 470,
Python conformance, C# 501). codegen-ts 992 pass, typecheck clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGQ7oSuNcjhsMHWwZzhBwr
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,7 +14,7 @@ Two mutually-exclusive `source.rdb` attributes express *who owns a DB object's D
14
14
-**`@sql`** — a hand-written view body the tool **registers, fingerprints, and drift-checks but never authors or parses**. The value is the body inside `CREATE <kind> <name> AS …`. It lets a genuinely-irreducible view (recursive CTE, window function, set operation) carry an `extends`-bound identity/fields for row identity and shape *without* the tool mis-synthesizing a wrong base-table passthrough `SELECT` — the **suppression rule** classifies DDL-ownership *before* the derivation decision, closing a silent-wrong-synthesis hole. The `@sql` view rides the existing emit/fingerprint pipeline; a pre-existing unstamped view at its name is `replace-view`-blocked pending the one-time **`meta migrate --allow adopt-view`** adoption ceremony. `@sql` on a writable kind, combined with `@unmanaged`, empty, or combined with `origin.*`/`@filter` is a load error. v1 migrate lowering is `@kind: view` only (matview/proc → an actionable hard error).
15
15
-**`@unmanaged: true`** — this DB object (a view **or a table**) is managed elsewhere (Flyway / a hand-migration owns its DDL). `meta migrate` never creates, drops, or drift-checks it (excluded from both the expected and the introspected-actual sides across the online, offline, D1, and `verify` paths); `meta verify --db` reports it as *external (declared)*. An inbound FK from a managed table still resolves the external table's physical name.
16
16
17
-
**Registration + the six fail-closed loader-validation rules (R1–R6) ship in all five ports** (TS / C# / Java / Kotlin / Python), resolving `MetaSource` accessors following the `@role`/`effectiveKind` precedent (ADR-0039), conformance-gated by shared `fixtures/conformance/` error-fixtures — every port emits the same code per rule (`ERR_SQL_BODY_WITH_UNMANAGED` / `ERR_SQL_BODY_ON_WRITABLE_KIND` / `ERR_BAD_ATTR_VALUE` / `ERR_ORIGIN_UNDER_SQL_BODY` / `WARN_ORIGIN_UNDER_UNMANAGED`). All migrate/verify **lowering is TS-only** (ADR-0015). An `xhigh` review before merge caught a real offline-`migrate` DROP-for-external-table bug (the offline `planOffline` path did not thread the unmanaged-name set), fixed + regression-tested. Deferred follow-ups: `@dependsOn` for `@sql` views, the matview managed path, and opaque-body column-name verification. Designed in `docs/superpowers/specs/2026-07-17-issue-208-ddl-ownership-escape-valves-design.md`.
17
+
**Registration + the six fail-closed loader-validation rules (R1–R6) ship in all five ports** (TS / C# / Java / Kotlin / Python), resolving `MetaSource` accessors following the `@role`/`effectiveKind` precedent (ADR-0039). The five **error** rules (R1–R5) are conformance-gated by shared `fixtures/conformance/` error-fixtures — every port emits the same code per rule (`ERR_SQL_BODY_WITH_UNMANAGED` / `ERR_SQL_BODY_ON_WRITABLE_KIND` / `ERR_BAD_ATTR_VALUE` / `ERR_ORIGIN_UNDER_SQL_BODY`); the R6 `WARN_ORIGIN_UNDER_UNMANAGED` is per-port unit-tested. All migrate/verify **lowering is TS-only** (ADR-0015). An `xhigh` review before merge caught a real offline-`migrate` DROP-for-external-table bug (the offline `planOffline` path did not thread the unmanaged-name set), fixed + regression-tested. Deferred follow-ups: `@dependsOn` for `@sql` views, the matview managed path, and opaque-body column-name verification. Designed in `docs/superpowers/specs/2026-07-17-issue-208-ddl-ownership-escape-valves-design.md`.
Copy file name to clipboardExpand all lines: spec/decisions/ADR-0043-ddl-ownership-escape-valves.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -36,7 +36,7 @@ An `@sql`- or `@unmanaged`-marked read source **suppresses derivation-classifica
36
36
37
37
### 4. Cross-port scope split
38
38
39
-
-**All five ports** (TS / C# / Java / Kotlin / Python): register the two attrs on `source.rdb`; resolving `MetaSource` accessors (`sqlBody`/`isUnmanaged` etc., following the `@role`/`effectiveKind`*resolving* precedent — **not** the `@dbColumnType` own-only exception, ADR-0039); and the six fail-closed loader-validation rules (§5), conformance-gated by shared `fixtures/conformance/` error-fixtures.
39
+
-**All five ports** (TS / C# / Java / Kotlin / Python): register the two attrs on `source.rdb`; resolving `MetaSource` accessors (`sqlBody`/`isUnmanaged` etc., following the `@role`/`effectiveKind`*resolving* precedent — **not** the `@dbColumnType` own-only exception, ADR-0039); and the six fail-closed loader-validation rules (§5). The five **error** rules (R1–R5) are conformance-gated by shared `fixtures/conformance/` error-fixtures (every port emits the same code per rule); the R6 **WARN** is per-port unit-tested (the conformance corpus has no warn-fixture mechanism). Java's loader is fail-fast-per-pass (throws on the first violation) versus TS/C#/Python's collect-all — a pre-existing loader-architecture difference, invisible to the single-violation conformance fixtures.
40
40
-**TS-only** (ADR-0015 — schema is TS-owned): all migrate/verify *lowering* — the `@sql` verbatim-emit + fingerprint + adopt-view ceremony, and the `@unmanaged` skip + act-side silencing.
41
41
42
42
### 5. Loader validation — six fail-closed rules (cross-port)
@@ -64,6 +64,7 @@ The R4-error / R6-warn asymmetry is deliberate: `@sql` is a second body (two sou
64
64
-**Documented adopter caveats (not errors):**
65
65
- An FK *from a managed table into an `@unmanaged` table* has no migration-**ordering** guarantee versus the external tool — the external object must exist before the managed FK is applied.
66
66
- An `@unmanaged` view is **not** drop/recreated around a managed dependency's column-altering `ALTER` — the tool has no body for an external view and cannot recreate it; Postgres blocking the `ALTER` is the adopter's signal to coordinate.
67
+
-`@unmanaged` is effectively **non-inheritable** in the migrate lowering: both the expected-side skip and the act-side name-set use own-source detection (so the two halves agree — no split-brain where a table is not created yet is still proposed for drop). Declare `@unmanaged` on the concrete entity's *own*`source.rdb`, not on an `extends` parent's.
67
68
-**Deferred follow-ups (filed on the roadmap):**
68
69
-**`@dependsOn` for `@sql` views** — a `@sql` view's `dependsOn` is derived from `extends`-bound anchors only, so a table the opaque body JOINs but does not surface as a bound column is not tracked for auto-recreate around a column `ALTER` (§11 YAGNI — defer the explicit `@dependsOn` attr until an adopter hits it).
69
70
-**The matview managed path** — `@sql` on `@kind: materializedView` needs `pg_matviews` introspection + COMMENT-on-matview plumbing + a REFRESH story.
0 commit comments