---
phase: 65.1-phase-65-code-review-remediation
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - test/integration/test_drop_on_fresh_readonly_clear_error.py
  - test/integration/test_load_extension_twice_idempotent.py
  - test/sql/phase651_yaml_filesystem_access_gating.test
  - test/sql/phase651_null_name_inputs.test
  - test/sql/phase651_bind_probe_hard_errors.test
  - test/sql/TEST_LIST
  - tests/registration_error_surfaces.rs
autonomous: true
requirements:
  - REM-WR-03
  - REM-WR-09
  - REM-CR-01
  - REM-IN-05
  - REM-WR-07
  - REM-WR-08
  - REM-WR-02

must_haves:
  truths:
    - "Wave 0 test stubs exist and are wired into the runners (sqllogictest TEST_LIST + cargo test discovery)"
    - "Each stub fails or is `#[ignore]` until the implementing plan lands"
  artifacts:
    - path: test/integration/test_drop_on_fresh_readonly_clear_error.py
      provides: "WR-03 stub (D-18) — empty test body + PEP-723 header"
    - path: test/integration/test_load_extension_twice_idempotent.py
      provides: "WR-09 stub (D-21) — empty test body + PEP-723 header"
    - path: test/sql/phase651_yaml_filesystem_access_gating.test
      provides: "CR-01 sqllogictest stub (enable_external_access=false rejection)"
    - path: test/sql/phase651_null_name_inputs.test
      provides: "IN-05 (D-25) null name vector sqllogictest stub"
    - path: test/sql/phase651_bind_probe_hard_errors.test
      provides: "WR-07/WR-08 (D-16) hard-error sqllogictest stub"
    - path: tests/registration_error_surfaces.rs
      provides: "WR-02 force-fail registration test stub"
    - path: test/sql/TEST_LIST
      contains: "phase651_yaml_filesystem_access_gating.test"
  key_links:
    - from: test/sql/TEST_LIST
      to: test/sql/phase651_*.test
      via: "registration list — runner skips unlisted files"
      pattern: "phase651_"
---

<objective>
Create the eight Wave 0 test scaffolds enumerated in `65.1-VALIDATION.md` so that downstream implementation plans (07, 08, 09, 10, 11, 12) can be verified by appending assertions to pre-existing files rather than creating them mid-plan. Every stub here is a placeholder: a `statement error` block that intentionally fails, a Python `@pytest.mark.skip("populated by plan NN")` body, or a Rust `#[ignore]` test. Each stub embeds a comment referencing the implementing plan and the decision ID (D-XX) it will satisfy.

Purpose: Eliminate the "I need to create the test file AND populate it AND wire it into TEST_LIST in one plan" friction. Splits scaffolding from semantics so each downstream plan has a single concern.

Output: 6 new test files + 1 modified TEST_LIST. No source code changes.
</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/STATE.md
@.planning/phases/65.1-phase-65-code-review-remediation/65.1-CONTEXT.md
@.planning/phases/65.1-phase-65-code-review-remediation/65.1-VALIDATION.md
@.planning/phases/65.1-phase-65-code-review-remediation/65.1-PATTERNS.md
@test/integration/test_concurrent_reads_per_call_conn.py
@test/sql/TEST_LIST
@test/sql/65_pk_error.test
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create Python integration test stubs (3 files)</name>
  <read_first>
    - test/integration/test_concurrent_reads_per_call_conn.py (template for PEP-723 header + open_master fixture)
    - .planning/phases/65.1-phase-65-code-review-remediation/65.1-PATTERNS.md (lines 640-724, the three test shape sketches)
    - .planning/phases/65.1-phase-65-code-review-remediation/65.1-CONTEXT.md (D-18, D-19, D-20, D-21 — what each test must assert)
  </read_first>
  <files>
    test/integration/test_drop_on_fresh_readonly_clear_error.py,
    test/integration/test_load_extension_twice_idempotent.py
  </files>
  <action>
Create two Python integration test stub files (NOT test_concurrent_writes_per_call_conn.py — that one is owned by Plan 05 and lives entirely in Plan 05's scope). Each stub must:

1. Carry the PEP-723 header copied verbatim from `test/integration/test_concurrent_reads_per_call_conn.py` lines 1-5 (`# /// script` block, `dependencies = ["duckdb==1.5.2"]`, `requires-python = ">=3.10"`).
2. Import the helpers `from test_ducklake_helpers import get_ext_dir, get_extension_path` after the `sys.path.insert` block matching lines 43-44 of the reads-test.
3. Define a single top-level test function:
   - `test_drop_on_fresh_readonly_clear_error.py` → `def test_drop_on_fresh_readonly_clear_error():`
   - `test_load_extension_twice_idempotent.py` → `def test_load_extension_twice_idempotent():`
4. Body must be `pytest.skip("populated by Plan 09 (WR-03)")` or `pytest.skip("populated by Plan 12 (WR-09)")` respectively (use `import pytest` at top).
5. Header docstring naming the decision ID (D-18 for WR-03, D-21 for WR-09) and the implementing plan number.

DO NOT create `test_concurrent_writes_per_call_conn.py` here — Plan 05 owns it standalone (no stub needed; that plan creates AND populates it in one task).

These stubs are not added to a runner manifest — `uv run test/integration/<file>.py` is invocation-by-path per CLAUDE.md so no TEST_LIST equivalent exists for `test/integration/`.
  </action>
  <verify>
    <automated>test -f test/integration/test_drop_on_fresh_readonly_clear_error.py &amp;&amp; test -f test/integration/test_load_extension_twice_idempotent.py &amp;&amp; grep -q "D-18" test/integration/test_drop_on_fresh_readonly_clear_error.py &amp;&amp; grep -q "D-21" test/integration/test_load_extension_twice_idempotent.py</automated>
  </verify>
  <acceptance_criteria>
    - Both files exist with PEP-723 inline metadata as line 1-5
    - Each contains exactly one top-level `def test_*():` function matching the filename stem
    - Each test function body invokes `pytest.skip(...)` referencing the implementing plan
    - Each file's module-level docstring contains the decision ID (D-18 or D-21)
    - `uv run test/integration/test_drop_on_fresh_readonly_clear_error.py` exits 0 (skipped, not failed)
  </acceptance_criteria>
  <done>Two Python integration stubs exist, skip-marked, referencing D-18 and D-21 in module docstrings.</done>
</task>

<task type="auto">
  <name>Task 2: Create sqllogictest stubs (3 files) + register in TEST_LIST</name>
  <read_first>
    - test/sql/TEST_LIST (registration pattern — paths are relative `test/sql/...`)
    - test/sql/65_pk_error.test (template for `statement error` block-form pattern per CLAUDE.md)
    - test/sql/65_metadata_via_sql.test (template for scalar null-handling assertions)
    - .planning/phases/65.1-phase-65-code-review-remediation/65.1-PATTERNS.md (lines 728-746, sqllogictest patterns)
  </read_first>
  <files>
    test/sql/phase651_yaml_filesystem_access_gating.test,
    test/sql/phase651_null_name_inputs.test,
    test/sql/phase651_bind_probe_hard_errors.test,
    test/sql/TEST_LIST
  </files>
  <action>
Create three sqllogictest stub files and append them to `test/sql/TEST_LIST`.

For each `.test` file:
1. First line: SQL comment header `# Phase 65.1 stub — populated by Plan NN (decision D-XX). DO NOT delete; runner will skip this file if removed from TEST_LIST.`
2. Body: A minimal `statement ok` line that always passes (e.g. `statement ok\nSELECT 1;`) so the runner does not error on an empty file. DO NOT add the real assertions — those land in the implementing plan.
3. End of file: a fenced comment block listing the assertions the implementing plan must add, copied from CONTEXT.md decisions.

File-specific headers:
- `test/sql/phase651_yaml_filesystem_access_gating.test` — header references Plan 07 (CR-01 D-01..D-03) and notes the future assertion: setting `enable_external_access=false` then attempting `CREATE SEMANTIC VIEW v FROM YAML FILE '/tmp/x.yaml'` must error.
- `test/sql/phase651_null_name_inputs.test` — header references Plan 09 (IN-05 D-25) and notes the future assertions for `read_yaml_from_semantic_view(NULL) IS NULL`, `get_ddl(NULL, 'x') IS NULL`, `get_ddl('SEMANTIC_VIEW', NULL) IS NULL`.
- `test/sql/phase651_bind_probe_hard_errors.test` — header references Plan 11 (WR-07/WR-08 D-14, D-15, D-16) and notes the future assertions: deliberately-broken FACTS expression → `statement error` block expecting substring `failed to infer column types`; existing valid `semantic_view(...)` query still passes.

Then append three lines to `test/sql/TEST_LIST` (preserving existing trailing newline behaviour — match the surrounding line endings):
```
test/sql/phase651_yaml_filesystem_access_gating.test
test/sql/phase651_null_name_inputs.test
test/sql/phase651_bind_probe_hard_errors.test
```

These three lines must appear after the existing `test/sql/65_read_bridge_spike.test` line (which is the current tail per the file inspection).
  </action>
  <verify>
    <automated>just build &gt; /tmp/claude/wave0_build.log 2>&amp;1; RC=$?; tail -50 /tmp/claude/wave0_build.log; test $RC -eq 0 &amp;&amp; just test-sql &gt; /tmp/claude/wave0_sql.log 2>&amp;1; RC2=$?; tail -50 /tmp/claude/wave0_sql.log; test $RC2 -eq 0</automated>
  </verify>
  <acceptance_criteria>
    - Three new `phase651_*.test` files exist under `test/sql/`
    - Each contains at least one `statement ok` so the runner does not report a parse error
    - Each contains an SQL comment header naming the implementing plan and the decision ID(s)
    - `test/sql/TEST_LIST` ends with the three new lines (appended after `test/sql/65_read_bridge_spike.test`)
    - `just test-sql` passes — the stubs do not fail any assertion
    - No existing test file was modified
  </acceptance_criteria>
  <done>Three sqllogictest stubs exist, all wired into TEST_LIST, `just test-sql` green.</done>
</task>

<task type="auto">
  <name>Task 3: Create Rust integration test stub for WR-02 surfacing</name>
  <read_first>
    - tests/no_long_lived_conn.rs (template for top-level `tests/*.rs` integration test layout — no module wrapper)
    - .planning/phases/65.1-phase-65-code-review-remediation/65.1-RESEARCH.md (lines 749-754 — registration_error_surfaces.rs test shape)
    - .planning/phases/65.1-phase-65-code-review-remediation/65.1-CONTEXT.md (D-08, D-09 — what error surfacing must look like)
  </read_first>
  <files>tests/registration_error_surfaces.rs</files>
  <action>
Create `tests/registration_error_surfaces.rs` as a top-level Rust integration test stub. Pattern matches `tests/no_long_lived_conn.rs` (no `mod` wrapper; tests are direct `#[test] fn` items at file scope).

File contents:
1. Module-level doc comment naming the implementing plan (Plan 02) and decision IDs (D-08, D-09): describes that the test will force `sv_register_table_function` to fail by passing a deliberately-bad signature (e.g. all-null required args once D-05 lands) and assert the error buffer surfaces the underlying message containing "null required argument".
2. A single `#[test]` function `fn init_extension_surfaces_registration_error_buf()` whose body is `eprintln!("STUB: populated by Plan 02 (WR-02 D-08/D-09)"); /* deliberately empty — pass */`.
3. The test must NOT be `#[ignore]` — it should pass as a no-op so `cargo test` discovery confirms the file compiles. Plan 02 will replace the body with the real assertion.

Do NOT add any FFI imports yet. Plan 02 will add `use libduckdb_sys as ffi;` and the actual force-fail invocation.
  </action>
  <verify>
    <automated>cargo test --test registration_error_surfaces &gt; /tmp/claude/wave0_rust.log 2>&amp;1; RC=$?; tail -30 /tmp/claude/wave0_rust.log; test $RC -eq 0</automated>
  </verify>
  <acceptance_criteria>
    - `tests/registration_error_surfaces.rs` exists with module-level doc comment naming Plan 02 + D-08 + D-09
    - Contains one `#[test] fn init_extension_surfaces_registration_error_buf()` at file scope
    - `cargo test --test registration_error_surfaces` exits 0 with `1 passed; 0 failed`
    - No `#[ignore]` attribute
  </acceptance_criteria>
  <done>Rust integration test stub exists, compiles, passes as no-op pending Plan 02 population.</done>
</task>

</tasks>

<verification>
  <automated>just test-all &gt; /tmp/claude/wave0_all.log 2>&amp;1; RC=$?; tail -100 /tmp/claude/wave0_all.log; test $RC -eq 0</automated>
</verification>

<success_criteria>
- 6 new test files exist (3 Python + 3 sqllogictest)
- 1 Rust integration test stub exists
- `test/sql/TEST_LIST` updated with 3 new lines
- `just test-all` green — all stubs either pass as no-ops or skip gracefully
- No production source code (`src/` or `cpp/src/`) modified
- Each stub's header comment names the implementing plan number and decision ID(s)
</success_criteria>

<output>
Create `.planning/phases/65.1-phase-65-code-review-remediation/65.1-01-SUMMARY.md` when done.
</output>
