---
phase: 25-sql-body-parser
plan: 04
type: execute
wave: 4
depends_on:
  - 25-03
files_modified:
  - test/sql/phase25_keyword_body.test
  - tests/parse_proptest.rs
  - .planning/phases/25-sql-body-parser/25-VALIDATION.md
autonomous: false
requirements:
  - DDL-01
  - DDL-02
  - DDL-03
  - DDL-04
  - DDL-05
  - DDL-07

must_haves:
  truths:
    - "just test-all passes (Rust unit tests, proptests, sqllogictest, DuckLake CI)"
    - "CREATE SEMANTIC VIEW with AS keyword body creates a queryable view end-to-end"
    - "All 7 DDL verbs work with the new AS keyword body syntax in sqllogictest"
    - "Error caret position in DuckDB CLI output points at clause typo (human verified)"
    - "parse_proptest AS-body round-trip test passes (validate_and_rewrite Ok(Some(_)))"
  artifacts:
    - path: "test/sql/phase25_keyword_body.test"
      provides: "Complete integration tests — all statements passing (not just stubs)"
      contains: "AS TABLES"
    - path: "tests/parse_proptest.rs"
      provides: "Updated TEST-06 with full validate_and_rewrite round-trip assertion"
      contains: "as_body_validate_and_rewrite_succeeds"
  key_links:
    - from: "test/sql/phase25_keyword_body.test"
      to: "src/ddl/define.rs::DefineFromJsonVTab"
      via: "sqllogictest drives full extension pipeline end-to-end"
      pattern: "AS TABLES"
    - from: "tests/parse_proptest.rs::as_body_validate_and_rewrite_succeeds"
      to: "src/parse.rs::validate_and_rewrite"
      via: "proptest drives validate_and_rewrite with random view names"
      pattern: "validate_and_rewrite"
---

<objective>
End-to-end integration verification: strengthen the sqllogictest file, upgrade the proptest block, run the full test suite, and gate the phase on human visual verification of caret error reporting.

Purpose: Plans 01-03 built the pipeline. This plan verifies it works end-to-end via sqllogictest (extension loaded, DDL parsed, view created, queried), confirms proptests pass with full round-trip assertions, and gates human verification of caret error position in the DuckDB CLI.

Output: Green `just test-all`, updated proptest with full assertions, human-verified caret output.
</objective>

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

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/25-sql-body-parser/25-CONTEXT.md
@.planning/phases/25-sql-body-parser/25-VALIDATION.md
@.planning/phases/25-sql-body-parser/25-03-SUMMARY.md
@test/sql/phase25_keyword_body.test
@tests/parse_proptest.rs

<interfaces>
<!-- What the sqllogictest runner expects and how validate_and_rewrite now works. -->

From just test-sql (runs sqllogictest against the built extension):
- Extension is loaded from the built binary
- Each `statement ok` expects success
- Each `statement error` expects DuckDB to return an error
- `query I rowsort` expects output rows (empty result = no rows printed)

From parse_proptest.rs existing patterns:
```rust
// Existing pattern to strengthen for AS body:
fn as_body_validate_and_rewrite_succeeds(...) {
    // Currently only checks detection -- upgrade to:
    let result = validate_and_rewrite(&query);
    prop_assert!(result.is_ok(), ...);
    let sql = result.unwrap().unwrap();
    prop_assert!(sql.starts_with("SELECT * FROM create_semantic_view_from_json("), ...);
    prop_assert!(sql.contains(&format!("'{name}'")), ...);
}
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Strengthen sqllogictest file and upgrade parse_proptest AS-body assertions</name>
  <files>test/sql/phase25_keyword_body.test, tests/parse_proptest.rs</files>
  <action>
**Update `test/sql/phase25_keyword_body.test`:**

Run `just test-sql` after Plan 03 to see which test cases pass and which fail. Fix any failing `statement ok` tests by adjusting syntax to match the actual parser behavior. Common adjustments:
- If multi-line SQL in sqllogictest needs special handling, collapse to single-line
- Adjust `query I rowsort` expected output to match actual query results from the test table
- The `DESCRIBE SEMANTIC VIEW` and `SHOW SEMANTIC VIEWS` expected output blocks need actual column counts

For the DESCRIBE expected output: DESCRIBE returns 6 columns (column_name, column_type, null, key, default, extra). Update the expected row count to match the actual dimensions + metrics created.

For SHOW SEMANTIC VIEWS: returns 2 columns (view_name, definition). After the CREATE, SHOW should return at least one row with the view name.

If the error test cases fail with the wrong error message, update the `statement error` blocks to match actual error messages from the implementation.

**Upgrade `tests/parse_proptest.rs` TEST-06 block:**

In the `as_body_validate_and_rewrite_succeeds` proptest, replace the weak detection-only assertion with the full round-trip check:
```rust
#[test]
fn as_body_validate_and_rewrite_succeeds(
    name in arb_view_name(),
) {
    let query = format!("CREATE SEMANTIC VIEW{}", build_as_body_suffix(&name));
    let result = validate_and_rewrite(&query);
    prop_assert!(
        result.is_ok(),
        "Expected Ok for valid AS-body DDL, got: {:?}",
        result
    );
    let sql = result.unwrap();
    prop_assert!(
        sql.is_some(),
        "Expected Some(sql) for valid AS-body DDL"
    );
    let sql = sql.unwrap();
    prop_assert!(
        sql.starts_with("SELECT * FROM create_semantic_view_from_json("),
        "Expected create_semantic_view_from_json route, got: {sql}"
    );
    prop_assert!(
        sql.contains(&format!("'{name}'")),
        "Expected view name in rewritten SQL, got: {sql}"
    );
}
```

Also strengthen `as_body_position_invariant_clause_typo`: once Plan 02/03 is complete, this test can assert the actual error and position:
```rust
#[test]
fn as_body_position_invariant_clause_typo(
    spaces in "[ ]{0,20}",
) {
    let query = format!("{spaces}CREATE SEMANTIC VIEW x AS TABLSE (t AS orders PRIMARY KEY (id)) DIMENSIONS (t.r AS r) METRICS (t.m AS SUM(1))");
    let err = validate_and_rewrite(&query).unwrap_err();
    let pos = err.position.unwrap();
    // Position must point at "TABLSE" in the original query
    prop_assert_eq!(
        &query[pos..pos + 6],
        "TABLSE",
        "Position {} does not point at 'TABLSE' in query: {:?}",
        pos, query
    );
    prop_assert!(
        err.message.contains("TABLES") || err.message.contains("tables"),
        "Expected 'did you mean TABLES' error, got: {}",
        err.message
    );
}
```
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo test parse_proptest 2>&1 | tail -10</automated>
  </verify>
  <done>
    - `cargo test parse_proptest` passes (all TEST-06 tests including full round-trip assertion)
    - sqllogictest file updated to match actual parser output
    - `as_body_position_invariant_clause_typo` asserts the error position points at the typo
  </done>
</task>

<task type="auto">
  <name>Task 2: Run just test-all and fix any failures</name>
  <files></files>
  <action>
Run the full test suite and fix any failures found:

```bash
cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && just test-all
```

The full suite runs:
1. `cargo test` -- Rust unit tests, proptests, doc tests
2. `just test-sql` -- sqllogictest via sqllogictest runner (requires fresh `just build`)
3. `just test-ducklake-ci` -- DuckLake integration tests

Expected failure modes to check:
- **sqllogictest failures:** If `phase25_keyword_body.test` has wrong expected output, update the test file. If a statement that should succeed returns an error, trace through the pipeline: parse.rs AS dispatch -> body_parser.rs -> define.rs JSON VTab.
- **DuckLake CI failures:** DuckLake tests use the extension with real file-backed databases. If the new `_from_json` VTabs have issues with the persist path, errors will surface here.
- **Proptest failures:** If the AS-body proptest generates a view name with special characters causing JSON escaping issues, fix the JSON escaping in `rewrite_ddl_keyword_body` (ensure `serde_json::to_string` handles all Rust string escaping correctly).

If `just test-sql` fails with "sqllogictest not found", run `just build` first to ensure the extension binary is fresh.

Fix any failures found. Do NOT mark the phase complete until `just test-all` returns 0 exit code.
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && just test-all 2>&1 | tail -20</automated>
  </verify>
  <done>
    - `just test-all` returns exit code 0
    - All Rust unit tests pass
    - All sqllogictest tests pass (including phase25_keyword_body.test)
    - DuckLake CI tests pass
    - No test failures or warnings about buffer truncation
  </done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 3: Human verification of end-to-end pipeline and caret error position</name>
  <files>N/A (checkpoint only -- no files modified by Claude)</files>
  <action>Pause for human to run the DuckDB CLI verification steps below.</action>
  <verify>Human confirms caret error position and successful view creation (see how-to-verify below).</verify>
  <done>Human types "approved" after verifying all 3 test scenarios pass.</done>
  <what-built>
Complete AS-body keyword clause DDL pipeline:
- C++ 64 KB buffer fix
- body_parser.rs: TABLES, RELATIONSHIPS, DIMENSIONS, METRICS clause parsers
- parse.rs: AS dispatch to JSON rewrite
- define.rs: create_semantic_view_from_json VTab
- All 7 DDL verbs working with new syntax

just test-all is green (verified by Task 2 above).
  </what-built>
  <how-to-verify>
Run in the DuckDB CLI with the built extension loaded:

```bash
cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views
just build
# Then in a new DuckDB session:
duckdb -cmd "LOAD 'target/debug/semantic_views.duckdb_extension';"
```

Test 1 -- Clause typo caret:
```sql
CREATE SEMANTIC VIEW v AS TABLSE (o AS orders PRIMARY KEY (id)) DIMENSIONS (o.r AS r) METRICS (o.m AS SUM(1));
```
Expected: Error with caret pointing at "TABLSE", message mentioning "did you mean TABLES".

Test 2 -- Valid keyword body create and query:
```sql
CREATE TABLE test_orders (id INTEGER, region VARCHAR, amount DOUBLE);
INSERT INTO test_orders VALUES (1, 'East', 100.0), (2, 'West', 200.0);

CREATE SEMANTIC VIEW order_view AS
TABLES (
    o AS test_orders PRIMARY KEY (id)
)
DIMENSIONS (
    o.region AS region
)
METRICS (
    o.total_revenue AS SUM(amount)
);

FROM semantic_view('order_view', dimensions := ['region'], metrics := ['total_revenue']);
```
Expected: Returns 2 rows with region (East/West) and total_revenue (100.0/200.0).

Test 3 -- All 7 DDL verbs:
```sql
CREATE OR REPLACE SEMANTIC VIEW order_view AS
TABLES (o AS test_orders PRIMARY KEY (id))
DIMENSIONS (o.region AS region)
METRICS (o.total_revenue AS SUM(amount));

CREATE SEMANTIC VIEW IF NOT EXISTS order_view AS
TABLES (o AS test_orders PRIMARY KEY (id))
DIMENSIONS (o.region AS region)
METRICS (o.total_revenue AS SUM(amount));

DESCRIBE SEMANTIC VIEW order_view;
SHOW SEMANTIC VIEWS;
DROP SEMANTIC VIEW IF EXISTS order_view;
DROP SEMANTIC VIEW IF EXISTS order_view;
```
Expected: Each statement succeeds (no errors, DESCRIBE returns columns, SHOW returns rows, DROP succeeds).
  </how-to-verify>
  <resume-signal>Type "approved" if all 3 tests pass including caret position. Describe any failures.</resume-signal>
</task>

</tasks>

<verification>
```bash
cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && just test-all 2>&1 | tail -5
```
Full suite green. Human checkpoint approved.
</verification>

<success_criteria>
- `just test-all` returns 0 (Rust tests + sqllogictest + DuckLake CI)
- Human checkpoint approved: error caret points at clause typo, valid AS-body DDL creates queryable view
- All 7 DDL verbs confirmed working with keyword body syntax in both automated tests and manual verification
- VALIDATION.md `nyquist_compliant: true` updated
</success_criteria>

<output>
After completion, create `.planning/phases/25-sql-body-parser/25-04-SUMMARY.md`

Also update `.planning/phases/25-sql-body-parser/25-VALIDATION.md`:
- Set `nyquist_compliant: true`
- Set `wave_0_complete: true`
- Mark all task statuses as green
</output>
