---
phase: 67-expansion-sql-coverage-and-tech-debt-cleanup
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - test/sql/phase47_semi_additive.test
  - test/sql/phase48_window_metrics.test
  - test/sql/phase46_fact_query.test
  - test/sql/phase67_qualified_emission.test
  - test/sql/TEST_LIST
autonomous: true
requirements: []
must_haves:
  truths:
    - "Each of the five rewritten expand paths (main, FACTS, semi-additive, window, materialization) has at least one shape assertion against `explain_semantic_view(...)` that pins the qualified 3-part `FROM \"<db>\".\"<schema>\".\"<table>\"` emission, ABOVE the existing behavioural assertions in the same file."
    - "A new fixture `test/sql/phase67_qualified_emission.test` exercises the five expand paths × two conditions (non-default-schema via `CREATE SCHEMA staging`, attached-DB via `ATTACH ':memory:' AS db2`) and is registered in `test/sql/TEST_LIST` so the sqllogictest runner picks it up."
    - "Every shape assertion in this plan uses the canonical idiom `SELECT count(*) > 0 FROM explain_semantic_view(...) WHERE explain_output LIKE '%<fragment>%'` returning `true` — never full-line equality, never inline regex, never negative absence assertions (per D-07/D-08)."
    - "The new fixture also runs each constructed semantic view through `semantic_view(...)` (not just `explain_semantic_view`) and asserts the query returns at least one row — fail-closed smoke check that rewritten SQL resolves (D-05). This is a structural check, NOT a value-correctness test."
    - "All shape fragments pin signal-bearing emission tokens visible in current production output: `OVER (PARTITION BY` and `NULLS LAST` for window; `ROW_NUMBER() OVER` and `__sv_rn` for semi-additive; the qualified `FROM \"<db>\".\"<schema>\".\"<table>\"` substring for all five paths."
    - "`just test-sql` exits 0 with the new fixture registered and all four edited fixtures green."
  artifacts:
    - path: "test/sql/phase47_semi_additive.test"
      provides: "Shape assertion(s) over `explain_semantic_view` output pinning the semi-additive snapshot CTE shape (`ROW_NUMBER() OVER`, `__sv_rn`) + the qualified `FROM` reference"
      contains: "explain_semantic_view"
    - path: "test/sql/phase48_window_metrics.test"
      provides: "Shape assertion(s) over `explain_semantic_view` output pinning the OVER clause and NULLS LAST emission for window metrics + the qualified `FROM` reference"
      contains: "OVER (PARTITION BY"
    - path: "test/sql/phase46_fact_query.test"
      provides: "Tightened FACTS-path shape assertion replacing the current `count(*) > 0` existence check with a fragment-LIKE pin on the FACTS inner-aggregation structure + the qualified `FROM` reference"
      contains: "explain_semantic_view"
    - path: "test/sql/phase67_qualified_emission.test"
      provides: "New fixture covering five expand paths × two conditions (non-default schema + ATTACHed DB), each shape-pinned via LIKE and smoke-tested via semantic_view(...) success"
      contains: "ATTACH ':memory:' AS db2"
    - path: "test/sql/TEST_LIST"
      provides: "Registration entry `phase67_qualified_emission.test`"
      contains: "phase67_qualified_emission.test"
  key_links:
    - from: "test/sql/phase67_qualified_emission.test"
      to: "test/sql/TEST_LIST"
      via: "explicit registration entry (sqllogictest runner gates on TEST_LIST membership, NOT directory scan)"
      pattern: "phase67_qualified_emission.test"
    - from: "test/sql/phase67_qualified_emission.test scenarios"
      to: "explain_semantic_view + semantic_view"
      via: "each scenario runs the same view through both — explain pins shape, semantic_view pins execution success"
      pattern: "explain_semantic_view\\(.*\\).*WHERE explain_output LIKE"
    - from: "phase47/48/46 added shape assertions"
      to: "src/expand/{semi_additive,window,sql_gen}.rs migration sites"
      via: "shape assertion fails if Phase 64 `qualify_and_quote_table_ref` wiring is regressed back to `quote_table_ref`"
      pattern: "FROM \"memory\".\"main\""
---

<objective>
Add sqllogictest-layer regression guards over rewritten expansion SQL — the layer Phase 66 retrofitted but only smoke-tested at the ADBC boundary. The Phase 64/66 `qualify_and_quote_table_ref` wiring has zero sqllogictest coverage of the qualified emission shape, and three of five rewritten paths (FACTS stub-only, semi-additive missing, window missing) lack any shape-level fixture. This plan closes the test gap WITHOUT introducing new test infrastructure — uses the established `explain_semantic_view(...) WHERE explain_output LIKE '%pattern%'` idiom (D-02) demonstrated in `test/sql/phase4_query.test:191-197` and `test/sql/phase28_e2e.test:139-156`.

Purpose: per CONTEXT.md D-01, the dissolved-by-Phase-65 root cause means the EXPAND-CTX failure mode no longer reproduces in-process, but the test gap is genuine. Adding shape assertions here means any future silent regression to unqualified `quote_table_ref` would surface immediately at `just test-sql`, not require an ADBC integration run to detect.

Output:
- Shape assertions added alongside existing behavioural tests in three existing fixtures (D-06: co-located, not centralized): `phase47_semi_additive.test`, `phase48_window_metrics.test`, `phase46_fact_query.test`
- One new fixture `test/sql/phase67_qualified_emission.test` (D-03): five paths × two conditions, each with a qualified-FROM shape assertion + a `semantic_view(...)` smoke check
- One-line registration in `test/sql/TEST_LIST` (D-03 — runner gates on membership)
- No production code changes; no `src/` files touched
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/67-expansion-sql-coverage-and-tech-debt-cleanup/67-CONTEXT.md
@.planning/phases/67-expansion-sql-coverage-and-tech-debt-cleanup/SCOPE.md
@.planning/phases/66-expansion-qualification-adbc-tests/66-CONTEXT.md
@.planning/phases/66-expansion-qualification-adbc-tests/66-02-SUMMARY.md

# Canonical idiom reference — copy this shape, do not invent a new one
@test/sql/phase4_query.test
@test/sql/phase28_e2e.test

# Fixtures being edited
@test/sql/phase47_semi_additive.test
@test/sql/phase48_window_metrics.test
@test/sql/phase46_fact_query.test

# Registration manifest — new fixture name must be appended
@test/sql/TEST_LIST

# Project rules — Rules 1 & 2 non-negotiable; TEST_LIST registration mandatory
@CLAUDE.md

<interfaces>
<!-- Canonical shape-assertion idiom (per D-02, D-07; verbatim from test/sql/phase4_query.test:204) -->
<!-- Returns `true` when the emitted SQL contains the fragment; `LIKE`-based, not regex, not full-line. -->

```
query I
SELECT count(*) > 0 FROM explain_semantic_view('view_name', dimensions := ['d1'], metrics := ['m1']) WHERE explain_output LIKE '%<fragment>%';
----
true
```

<!-- Emission fragments to pin (verified against src/expand/*.rs current production output) -->

Semi-additive snapshot CTE (src/expand/semi_additive.rs:116-187):
- `ROW_NUMBER() OVER` (the windowing primitive — fragile to whitespace; use `LIKE '%ROW_NUMBER() OVER%'`)
- `__sv_rn` (the alias the expander assigns at line 119; single-group case)
- Qualified FROM: `FROM "memory"."main"."<table>"` when view is created in default schema

Window metrics (src/expand/window.rs current emission, see phase48_window_metrics.test:66-67 DDL surface for the kind of `OVER (PARTITION BY EXCLUDING ... ORDER BY ... ASC NULLS LAST)` that resolves at expansion to `OVER (PARTITION BY <dims> ORDER BY ... NULLS LAST)`):
- `OVER (PARTITION BY`
- `NULLS LAST`
- Qualified FROM as above

FACTS path (src/expand/sql_gen.rs:181, 224, 244 — already wired by Phase 66):
- The FACTS-distinctive inner-vs-outer aggregation. Phase 64+66 commits emit a structure recognisable by the qualified FROM appearing inside a subquery; safest fragment to pin is the qualified `FROM "memory"."main"."<facts_table>"` itself plus the surrounding `SELECT` keyword if needed for disambiguation.

Materialization routing target (src/expand/materialization.rs:157, wired Phase 66):
- Qualified FROM to the materialization-target table: `FROM "memory"."main"."<mat_table>"` (the test/sql/phase57_introspection.test:76 fixture already asserts this exact shape — pattern to mirror)

<!-- New fixture skeleton — single file covering 5 paths × 2 conditions; planner discretion on whether to fold B5 quoted-source-table fixture into this file (Plan 02 owns) -->

```sql
# test/sql/phase67_qualified_emission.test
# Phase 67 A1: sqllogictest-layer regression guard over qualify_and_quote_table_ref
# across all five rewritten expand paths × non-default-schema + ATTACHed-DB conditions.

statement ok
LOAD semantic_views;

# ----- Scenario A1.1: main expand path, non-default schema -----
statement ok
CREATE SCHEMA staging;

statement ok
CREATE TABLE staging.sales (region VARCHAR, amount DECIMAL(18,2));

statement ok
INSERT INTO staging.sales VALUES ('US', 100.00), ('EU', 200.00);

statement ok
CREATE SEMANTIC VIEW p67_main_sch AS
  TABLES (s AS staging.sales)
  DIMENSIONS (s.region AS s.region)
  METRICS (s.total AS SUM(s.amount));

query I
SELECT count(*) > 0 FROM explain_semantic_view('p67_main_sch', dimensions := ['region'], metrics := ['total']) WHERE explain_output LIKE '%FROM "memory"."staging"."sales"%';
----
true

# semantic_view smoke check (fail-closed per D-05)
query I
SELECT count(*) > 0 FROM semantic_view('p67_main_sch', dimensions := ['region'], metrics := ['total']);
----
true

# ----- Scenario A1.2: main expand path, ATTACHed DB -----
statement ok
ATTACH ':memory:' AS db2;

statement ok
CREATE TABLE db2.main.sales2 (region VARCHAR, amount DECIMAL(18,2));

statement ok
INSERT INTO db2.main.sales2 VALUES ('US', 50.00);

statement ok
CREATE SEMANTIC VIEW p67_main_db2 AS
  TABLES (s AS db2.main.sales2)
  DIMENSIONS (s.region AS s.region)
  METRICS (s.total AS SUM(s.amount));

query I
SELECT count(*) > 0 FROM explain_semantic_view('p67_main_db2', dimensions := ['region'], metrics := ['total']) WHERE explain_output LIKE '%FROM "db2"."main"."sales2"%';
----
true

query I
SELECT count(*) > 0 FROM semantic_view('p67_main_db2', dimensions := ['region'], metrics := ['total']);
----
true

statement ok
DETACH db2;
# ^ explicit DETACH per CONTEXT.md <risk_areas>: multi-DB ATTACH state may bleed into later scenarios

# ----- Subsequent scenarios A1.3 (FACTS), A1.4 (semi-additive), A1.5 (window), A1.6 (materialization) -----
# Each follows the same shape:
#   1. Create schema or ATTACH as appropriate
#   2. CREATE TABLE + INSERT (small fixture data)
#   3. CREATE SEMANTIC VIEW exercising the specific path
#   4. explain_semantic_view shape assertion pinning the qualified FROM + path-specific fragment
#   5. semantic_view(...) smoke check
#   6. DETACH if ATTACH was used
```
</interfaces>
</context>

<tasks>

<!--
  Sequencing within this plan (per CONTEXT.md D-16): A2 → A3 → A4 → A1.
  A2/A3/A4 are cheap, validate the LIKE-pattern shape-assertion strategy against
  the three existing fixtures before A1 builds the new multi-path fixture on
  proven ground.
-->

<task type="auto">
  <name>Task 1: A2 — Add semi-additive shape assertions to phase47_semi_additive.test</name>
  <files>test/sql/phase47_semi_additive.test</files>
  <read_first>
    - test/sql/phase47_semi_additive.test (all 200 lines — locate one of the existing semantic views that exercises a semi-additive metric, e.g. `p47_account_view` at line 133; identify its dimensions/metrics list so the new `explain_semantic_view` call can reuse a valid arg signature)
    - src/expand/semi_additive.rs (lines 116-187: confirm the emitted alias `__sv_rn` and the `ROW_NUMBER() OVER` literal; lines 446, 458, 585, 670, 797: existing Rust-side substring assertions over the same emission — these are the fragments we mirror at the sqllogictest layer)
    - test/sql/phase4_query.test (lines 191-204: canonical `explain_semantic_view ... WHERE explain_output LIKE '%...%'` shape — copy this idiom verbatim)
  </read_first>
  <action>
At a point in `phase47_semi_additive.test` AFTER one of the existing semi-additive semantic views has been created and AFTER any existing behavioural assertions against that view (to keep related tests in narrative order), append a small block of shape assertions. Pick the SIMPLEST existing semi-additive view in the fixture as the target — verify it lives in `memory.main` (default schema) so the qualified FROM is predictable.

Block to add (replace `<view>`, `<dim>`, `<metric>`, `<base_table>` with the real names from the chosen view):

```
# Phase 67 A2: shape assertions pinning semi-additive snapshot CTE emission
query I
SELECT count(*) > 0 FROM explain_semantic_view('<view>', dimensions := ['<dim>'], metrics := ['<metric>']) WHERE explain_output LIKE '%ROW_NUMBER() OVER%';
----
true

query I
SELECT count(*) > 0 FROM explain_semantic_view('<view>', dimensions := ['<dim>'], metrics := ['<metric>']) WHERE explain_output LIKE '%__sv_rn%';
----
true

query I
SELECT count(*) > 0 FROM explain_semantic_view('<view>', dimensions := ['<dim>'], metrics := ['<metric>']) WHERE explain_output LIKE '%FROM "memory"."main"."<base_table>"%';
----
true
```

Rules:
- Use `LIKE '%<fragment>%'` per D-07. Never full-line equality. Never inline regex (CLAUDE.md: sqllogictest does not support inline regex).
- Do NOT add a negative assertion (D-08 rejects asserting absence of unqualified `FROM "<table>"`).
- Do NOT centralise — keep co-located with existing behavioural tests (D-06).
- The percent-sign `LIKE` wildcards must escape any DuckDB-meta characters inside the fragment; none of the recommended fragments contain such characters, but if you swap in a different fragment, verify.
- If the chosen view does not include both a non-additive group and a regular dimension, the `__sv_rn` emission may not fire — confirm by reading `semi_additive.rs:116-122` and the existing assertion at line 446 (`sql.contains("ROW_NUMBER() OVER")`). If the target view emits `__sv_rn_1` not `__sv_rn`, adjust the second LIKE fragment accordingly.

After adding, run sqllogictest locally to confirm green.
  </action>
  <verify>
    <automated>just build > /tmp/claude/build_67_01_task1.log 2>&1; RC=$?; tail -40 /tmp/claude/build_67_01_task1.log; test "$RC" -eq 0 && just test-sql > /tmp/claude/test_sql_67_01_task1.log 2>&1; RC=$?; tail -80 /tmp/claude/test_sql_67_01_task1.log; test "$RC" -eq 0</automated>
  </verify>
  <acceptance_criteria>
    - `grep -c "explain_semantic_view" test/sql/phase47_semi_additive.test` returns at least 3 more than the baseline (3 new shape assertions added).
    - `grep -c "ROW_NUMBER() OVER" test/sql/phase47_semi_additive.test` returns at least 1 (was 0 before).
    - `grep -c "__sv_rn" test/sql/phase47_semi_additive.test` returns at least 1 (was 0 before).
    - `just build` succeeds (extension rebuilt to pick up no code changes — `just test-sql` requires a fresh build per CLAUDE.md). If sandbox blocks `mktemp`, retry with `dangerouslyDisableSandbox: true`.
    - `just test-sql` exits 0 with phase47_semi_additive.test green.
    - All three new shape assertions return `true` (i.e., none of the LIKE fragments fails to match — if any returns `false`, the chosen fragment is wrong; fix the fragment, NOT the assertion shape).
  </acceptance_criteria>
  <done>Three shape assertions added co-located with existing tests; `just test-sql` green.</done>
</task>

<task type="auto">
  <name>Task 2: A3 — Add window metric shape assertions to phase48_window_metrics.test</name>
  <files>test/sql/phase48_window_metrics.test</files>
  <read_first>
    - test/sql/phase48_window_metrics.test (all 265 lines — locate `p48_sales_view` defined around line 66; note its window-metric DDL surface `OVER (PARTITION BY EXCLUDING date ORDER BY date ASC NULLS LAST)`; identify a dimension+metric pair for a successful `semantic_view(...)` call)
    - src/expand/window.rs (lines 1-50 module doc + the emission point that produces `OVER (PARTITION BY ... ORDER BY ... NULLS LAST)` after EXCLUDING resolution; confirm `NULLS LAST` is emitted literally not `NULLS_LAST`)
    - test/sql/phase4_query.test:191-204 (idiom reference)
  </read_first>
  <action>
After an existing semantic view in `phase48_window_metrics.test` that uses a window metric has been created (e.g. `p48_sales_view`), append shape assertions co-located with the existing behavioural tests.

Block to add (replace `<view>`, `<dim>`, `<window_metric>`, `<base_table>` with real names):

```
# Phase 67 A3: shape assertions pinning window metric OVER frame emission
query I
SELECT count(*) > 0 FROM explain_semantic_view('<view>', dimensions := ['<dim>'], metrics := ['<window_metric>']) WHERE explain_output LIKE '%OVER (PARTITION BY%';
----
true

query I
SELECT count(*) > 0 FROM explain_semantic_view('<view>', dimensions := ['<dim>'], metrics := ['<window_metric>']) WHERE explain_output LIKE '%NULLS LAST%';
----
true

query I
SELECT count(*) > 0 FROM explain_semantic_view('<view>', dimensions := ['<dim>'], metrics := ['<window_metric>']) WHERE explain_output LIKE '%FROM "memory"."main"."<base_table>"%';
----
true
```

Rules:
- Same as Task 1 (LIKE-only, co-located, no negative assertions, no centralisation).
- Confirm via `grep -n 'OVER (PARTITION' src/expand/window.rs` (or equivalent emission helper) that the literal substring `OVER (PARTITION BY` appears in the rendered SQL — the existing DDL-surface tests at line 164 (`LIKE '%OVER (%'`) hint at the form but it is the EXPANSION output (post-EXCLUDING-resolution) that the explain_semantic_view returns. If the actual emitted form differs from the DDL-surface form (e.g. `OVER(PARTITION BY` with no space), pick the correct emitted fragment.

If the chosen view fires NULLS LAST only when ORDER BY is present, ensure the window metric DDL has the ORDER BY clause (`p48_sales_view`'s `avg_qty` does — line 66).
  </action>
  <verify>
    <automated>just build > /tmp/claude/build_67_01_task2.log 2>&1; RC=$?; tail -40 /tmp/claude/build_67_01_task2.log; test "$RC" -eq 0 && just test-sql > /tmp/claude/test_sql_67_01_task2.log 2>&1; RC=$?; tail -80 /tmp/claude/test_sql_67_01_task2.log; test "$RC" -eq 0</automated>
  </verify>
  <acceptance_criteria>
    - `grep -c "OVER (PARTITION BY" test/sql/phase48_window_metrics.test` returns 1 more than the baseline grep before this task (new shape assertion fragment added).
    - `grep -c "NULLS LAST" test/sql/phase48_window_metrics.test` returns at least 1 more than baseline.
    - `just build` + `just test-sql` exit 0.
  </acceptance_criteria>
  <done>Three shape assertions added to phase48; window OVER frame and NULLS LAST pinned at the expansion layer.</done>
</task>

<task type="auto">
  <name>Task 3: A4 — Tighten FACTS shape assertion in phase46_fact_query.test</name>
  <files>test/sql/phase46_fact_query.test</files>
  <read_first>
    - test/sql/phase46_fact_query.test (lines 178-200 — the existing `count(*) > 0` existence check at line 183 is the assertion this task TIGHTENS, not replaces; also identify the semantic view name and a valid (dimension, fact) arg pair)
    - src/expand/sql_gen.rs (lines 181, 224, 244 — the FACTS-path call sites that emit qualified FROM; reading the surrounding emission code helps identify a stable inner-aggregation fragment to pin)
    - .planning/phases/66-expansion-qualification-adbc-tests/66-RESEARCH.md (Pattern 1 / FACTS notes — if accessible, mirrors the same emission)
  </read_first>
  <action>
In `phase46_fact_query.test`, leave the existing `count(*) > 0` assertion at line 183 in place (it is a useful smoke check) and ADD shape assertions immediately after it.

Block to add (replace `<view>`, `<dim>`, `<fact>`, `<base_table>` with real names from the view at line 183):

```
# Phase 67 A4: tighten FACTS shape — pin qualified FROM + FACTS-distinctive structure
query I
SELECT count(*) > 0 FROM explain_semantic_view('<view>', facts := ['<fact>'], dimensions := ['<dim>']) WHERE explain_output LIKE '%FROM "memory"."main"."<base_table>"%';
----
true
```

If reading `src/expand/sql_gen.rs:181-244` reveals a FACTS-distinctive structural fragment (e.g. a subquery alias, a particular keyword sequence that only the FACTS path emits — possible candidates include the inner SELECT subquery wrapping or specific column-list shape; do NOT invent a fragment, only pin one observed in the production emission), add one more LIKE assertion pinning it.

Do NOT replace the existing `count(*) > 0` assertion — additive only. Do NOT add a negative assertion (D-08).

If the chosen FACTS view emits an unqualified FROM (i.e. `def.database_name` / `def.schema_name` are None), the qualified-FROM assertion will fail. In that case the test should be on a view whose def DOES populate `database_name` / `schema_name` (CREATE-time metadata via `current_database()` / `current_schema()` — Phase 65 P03 / P04 wires this on every CREATE through parser_override). All views created normally via `CREATE SEMANTIC VIEW` in the fixture will have both fields populated. If grep shows the fixture uses some other path (e.g. function-based DDL, which was retired in v0.5.2), use a different CREATE.
  </action>
  <verify>
    <automated>just build > /tmp/claude/build_67_01_task3.log 2>&1; RC=$?; tail -40 /tmp/claude/build_67_01_task3.log; test "$RC" -eq 0 && just test-sql > /tmp/claude/test_sql_67_01_task3.log 2>&1; RC=$?; tail -80 /tmp/claude/test_sql_67_01_task3.log; test "$RC" -eq 0</automated>
  </verify>
  <acceptance_criteria>
    - `grep -c "explain_semantic_view" test/sql/phase46_fact_query.test` returns at least 1 more than the baseline.
    - The original `count(*) > 0` assertion at line ~183 is preserved (verify via grep showing the line still exists post-edit).
    - `just build` + `just test-sql` exit 0.
  </acceptance_criteria>
  <done>FACTS path has a qualified-FROM shape assertion added without removing the existing smoke check.</done>
</task>

<task type="auto">
  <name>Task 4: A1 — Create phase67_qualified_emission.test + register in TEST_LIST</name>
  <files>test/sql/phase67_qualified_emission.test, test/sql/TEST_LIST</files>
  <read_first>
    - test/sql/phase57_introspection.test (lines 60-80 — the materialization-routing fixture; this is the closest analog and proves the qualified-FROM idiom works in the sqllogictest harness)
    - test/sql/TEST_LIST (read the whole file to confirm the format — one fixture name per line, no prefix, no suffix marker; alphabetical-ish but not enforced)
    - test/sql/phase4_query.test (lines 188-205 — canonical explain_semantic_view + LIKE idiom)
    - src/expand/sql_gen.rs, src/expand/semi_additive.rs, src/expand/window.rs, src/expand/materialization.rs (skim the call sites at the lines listed in CONTEXT.md <canonical_refs> to confirm each rewritten path actually emits the qualified FROM at runtime — they do, per Phase 66 verification — but reading the call site context helps you write the matching scenario)
    - .planning/phases/66-expansion-qualification-adbc-tests/66-CONTEXT.md (D-08/D-09: ADBC tests as catalog-resolution regression guards — parallels the role of this new fixture at the sqllogictest layer)
  </read_first>
  <action>
Create `test/sql/phase67_qualified_emission.test` covering five expand paths × two conditions (non-default-schema via `CREATE SCHEMA staging`, attached-DB via `ATTACH ':memory:' AS db2`).

File structure (10 scenarios — 5 paths × 2 conditions):

1. **Header comment block** — purpose, citation of CONTEXT.md D-03..D-08, citation of `test/sql/phase4_query.test:191-204` as the idiom reference.

2. **`LOAD semantic_views;`** — mirrors other phase*.test files; sqllogictest auto-loads in some cases but explicit `LOAD` is safest.

3. **Scenario A1.1 — Main expand path × non-default schema**:
   - `CREATE SCHEMA staging;`
   - `CREATE TABLE staging.sales (region VARCHAR, amount DECIMAL(18,2));`
   - `INSERT INTO staging.sales VALUES ('US', 100.00), ('EU', 200.00);`
   - `CREATE SEMANTIC VIEW p67_main_sch AS TABLES (s AS staging.sales) DIMENSIONS (s.region AS s.region) METRICS (s.total AS SUM(s.amount));`
   - Shape assertion: `LIKE '%FROM "memory"."staging"."sales"%'` returns `true`.
   - Smoke check: `SELECT count(*) > 0 FROM semantic_view('p67_main_sch', dimensions := ['region'], metrics := ['total'])` returns `true`.

4. **Scenario A1.2 — Main expand × ATTACHed DB**:
   - `ATTACH ':memory:' AS db2;`
   - `CREATE TABLE db2.main.sales2 (region VARCHAR, amount DECIMAL(18,2));`
   - `INSERT INTO db2.main.sales2 VALUES ('US', 50.00);`
   - `CREATE SEMANTIC VIEW p67_main_db2 AS TABLES (s AS db2.main.sales2) DIMENSIONS (s.region AS s.region) METRICS (s.total AS SUM(s.amount));`
   - Shape assertion: `LIKE '%FROM "db2"."main"."sales2"%'` returns `true`.
   - Smoke check via `semantic_view(...)`.
   - `DETACH db2;` — explicit per CONTEXT.md `<risk_areas>` state-bleed mitigation.

5. **Scenarios A1.3-A1.10** — analogous structure for FACTS, semi-additive, window, materialization, each in both the staging-schema and ATTACH conditions:
   - **FACTS** (`src/expand/sql_gen.rs:181, 224, 244`): a semantic view with at least one FACT entry; assertion pins qualified FROM inside the FACTS-aggregation subquery (e.g. `FROM "memory"."staging"."facts_table"`).
   - **Semi-additive** (`src/expand/semi_additive.rs:195, 220, 238`): a view with a `MIN_BY(...)` or `MAX_BY(...)` semi-additive metric (mirror the shape `inv_view` uses in `test_adbc_queries.py` scenario 4); assertion pins qualified FROM inside the snapshot CTE (i.e. `LIKE '%ROW_NUMBER() OVER%FROM "memory"."staging"."<table>"%'` may be too restrictive — use two separate LIKE assertions on the same query: one for `%ROW_NUMBER() OVER%` and one for `%FROM "memory"."staging"."<table>"%`).
   - **Window** (`src/expand/window.rs:156, 181, 199`): a view with a window metric using `OVER (PARTITION BY EXCLUDING <dim> ORDER BY <dim> NULLS LAST)`; assertion pins qualified FROM in the inner subquery feeding the OVER clause.
   - **Materialization** (`src/expand/materialization.rs:157`): a view with a `MATERIALIZATIONS (...)` clause and a routing-matchable aggregation; assertion pins qualified FROM to the materialization-target table (mirror `test/sql/phase57_introspection.test:76` shape `FROM "memory"."main"."p57_agg_region"`). Also create one scenario where routing falls back to raw expansion and assert the qualified FROM on the BASE table — this exercises the routing-fallback path.

6. **Between every ATTACH-using scenario**, run `DETACH db2;` before re-ATTACHing or moving on. Pre-emptive `DROP SCHEMA IF EXISTS staging CASCADE;` may also be needed between scenarios if the same schema name is reused — easier: use unique schema/table names per scenario (e.g. `staging_facts`, `staging_semi`, `staging_win`, `staging_mat`).

7. **End-of-file cleanup**: optional `DETACH db2;` and `DROP SCHEMA IF EXISTS staging CASCADE;` for hygiene.

Each scenario follows the rule:
- Exactly one `CREATE SEMANTIC VIEW` per scenario.
- Exactly one `explain_semantic_view(...)` shape assertion per scenario (or two LIKE assertions on the same query if multiple fragments need pinning per D-07).
- Exactly one `semantic_view(...)` smoke check per scenario returning `count(*) > 0 -> true` (D-05 fail-closed structural check, NOT value-correctness).

After creating the fixture, append `phase67_qualified_emission.test` to `test/sql/TEST_LIST` (CLAUDE.md non-negotiable rule: runner gates on TEST_LIST membership). One entry per line; no surrounding markers; place near other phase67_*.test entries (which Plan 02 may also add — sort order is informal).

Authoring notes:
- All `query I` blocks use the `----\ntrue` separator form, NOT inline regex (CLAUDE.md rule).
- `statement ok` for CREATE/INSERT/DROP/ATTACH/DETACH; `statement error` only if explicitly testing a failure path (this plan does not).
- If a scenario emits a different qualified-FROM than expected (e.g. the materialization-fallback emits the base table not the materialization table), debug by running `EXPLAIN` manually first and pinning whatever the production emission actually is. The shape assertion should pin observed emission, not aspirational emission.
- If a multi-DB scenario causes state bleed (later scenarios fail because `db2` is still attached), add an explicit `DETACH db2;` immediately after every ATTACH-using scenario.
  </action>
  <verify>
    <automated>echo "phase67_qualified_emission.test" >> /tmp/claude/expected_test_list_entry.txt; grep -c "^phase67_qualified_emission.test$" test/sql/TEST_LIST > /tmp/claude/test_list_check_67_01_task4.log; test "$(cat /tmp/claude/test_list_check_67_01_task4.log)" = "1" && just build > /tmp/claude/build_67_01_task4.log 2>&1 && just test-sql > /tmp/claude/test_sql_67_01_task4.log 2>&1; RC=$?; tail -120 /tmp/claude/test_sql_67_01_task4.log; test "$RC" -eq 0</automated>
  </verify>
  <acceptance_criteria>
    - `test/sql/phase67_qualified_emission.test` exists with the 10-scenario structure (5 paths × 2 conditions = 10 scenarios required per D-04). Any omitted cell requires a SUMMARY note with evidence the cell is impossible or redundant — executor preference is not sufficient justification.
    - `grep -c "^phase67_qualified_emission.test$" test/sql/TEST_LIST` returns exactly 1.
    - `grep -c "FROM \"memory\".\"staging\"" test/sql/phase67_qualified_emission.test` returns >= 1 (at least one staging-schema scenario pins the qualified form).
    - `grep -c "FROM \"db2\".\"main\"" test/sql/phase67_qualified_emission.test` returns >= 1 (at least one ATTACH-DB scenario pins the qualified form).
    - `grep -c "semantic_view(" test/sql/phase67_qualified_emission.test` returns >= 5 (smoke checks per D-05 — one per path).
    - `grep -c "explain_semantic_view(" test/sql/phase67_qualified_emission.test` returns >= 5 (shape assertions per D-04).
    - `just build` succeeds; `just test-sql` exits 0 with phase67_qualified_emission.test green (verify the new fixture ran by grepping the test-sql output for the fixture name).
    - If `mktemp` is blocked, retry with `dangerouslyDisableSandbox: true` per CLAUDE.md Rule 2.
  </acceptance_criteria>
  <done>New fixture covers 5 paths × 2 conditions with qualified-FROM shape pins + semantic_view smoke checks; TEST_LIST registered; `just test-sql` green.</done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| (none new) | This plan adds test fixtures only; no production code paths, no new untrusted input crossings, no new FFI surface. |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-67-01 | Tampering | `test/sql/phase67_qualified_emission.test` (developer-authored fixture) | accept | The fixture is checked into the repo; tampering surfaces in code review. Pre-commit clippy/fmt does not apply to .test files but the file is text-only with no executable code outside the sqllogictest harness. |
| T-67-02 | Information Disclosure | sqllogictest fixture | accept | Fixture data is fully synthetic (`('US', 100.00)`, `('EU', 200.00)`); no real PII or secrets. |

No new high-severity threats introduced by this plan. The Phase 67 changes are pure test scaffolding.
</threat_model>

<verification>
- `just build` succeeds at every task boundary (sqllogictest harness requires fresh extension build per CLAUDE.md).
- `just test-sql` exits 0 with all four edited fixtures (phase46/47/48 + phase67) green.
- The new `phase67_qualified_emission.test` is listed in `test/sql/TEST_LIST` (mandatory or runner silently skips it).
- Every shape assertion uses the `LIKE '%...%'` block-form idiom from `test/sql/phase4_query.test:204`. No inline regex. No full-line equality. No negative absence assertions.
- All shape assertions pin fragments observed in PRODUCTION emission (not aspirational shape); if any assertion fails, the fragment is wrong — fix the fragment, not the assertion form.
- The new fixture exercises both `explain_semantic_view` (shape) and `semantic_view` (execution-success smoke per D-05).
- Tail-buffer rule honored: all build/test commands redirect to `/tmp/claude/*.log` first, then `tail -N`. No bare `tail` piping (CLAUDE.md Rule 1).
</verification>

<success_criteria>
- 3 of 5 rewritten expand paths (semi-additive, window, FACTS) have at least one new shape assertion co-located with existing behavioural tests (D-06).
- All 5 rewritten expand paths (main, FACTS, semi-additive, window, materialization) have shape coverage × 2 conditions (non-default-schema + ATTACHed-DB) in the new `phase67_qualified_emission.test` fixture (D-04).
- `phase67_qualified_emission.test` is registered in `test/sql/TEST_LIST` (D-03).
- `just test-sql` exits 0.
- No production source files (`src/`) modified.
- No release-prep tasks (CHANGELOG, Cargo.toml, etc.) included.
</success_criteria>

<output>
Create `.planning/phases/67-expansion-sql-coverage-and-tech-debt-cleanup/67-01-SUMMARY.md` when done. Include:
- The exact view names + dimension/metric pairs chosen for A2/A3/A4 shape assertions
- The 5×2 scenario matrix for A1 (which scenarios pin which fragments)
- Any deviations from the canonical 10-scenario count (e.g., if one path×condition combo was omitted with rationale)
- Confirmation that `just test-sql` exited 0 with the test count delta visible (N more sqllogictest assertions than baseline)
</output>
