---
phase: 62
plan: 01
type: execute
wave: 0
depends_on: []
files_modified:
  - cpp/include/parser_extension_compat.hpp
  - test/sql/error_caret_create.test
  - test/sql/error_caret_drop.test
  - test/sql/error_caret_alter.test
  - test/sql/error_caret_multiline.test
  - test/sql/error_caret_unicode.test
  - test/sql/lru_removed_isolation.test
  - test/sql/extension_reload.test
  - test/integration/test_caret_position.py
  - test/integration/test_multi_db_isolation.py
  - tests/parse_proptest.rs
autonomous: true
requirements: []
notes: |
  Wave 0 — test scaffolding + static_assert. ALL fixtures use `skipif true` (or
  the runner's `halt`) so they parse cleanly and run as no-ops; Plan 04 (Wave 3)
  populates them with the real expected error text once Plans 02–03 land. This
  keeps the suite green between waves while pinning every behavioural property
  from RESEARCH §6 in code now.

must_haves:
  truths:
    - "Every Wave 0 test fixture file exists on disk with valid sqllogictest/proptest syntax"
    - "cpp/include/parser_extension_compat.hpp pins ParserExtensionParseResult layout via static_assert"
    - "test/integration/test_caret_position.py extract_caret_position assertions are wired (skipped until Wave 3 populates)"
    - "test/integration/test_multi_db_isolation.py contains a >16-DB sequential open/close test"
    - "tests/parse_proptest.rs covers position-byte-offset preservation through validate_and_rewrite for arbitrary leading whitespace/comments"
  artifacts:
    - path: "cpp/include/parser_extension_compat.hpp"
      provides: "static_assert pinning ParserExtensionParseResult layout"
      contains: "static_assert(sizeof(ParserExtensionParseResult)"
    - path: "test/sql/error_caret_create.test"
      provides: "Caret rendering coverage for CREATE error paths"
    - path: "test/sql/error_caret_drop.test"
      provides: "Caret rendering coverage for DROP error paths"
    - path: "test/sql/error_caret_alter.test"
      provides: "Caret rendering coverage for ALTER error paths"
    - path: "test/sql/error_caret_multiline.test"
      provides: "Multi-line caret coverage (B4)"
    - path: "test/sql/error_caret_unicode.test"
      provides: "Unicode prefix caret coverage (B5)"
    - path: "test/sql/lru_removed_isolation.test"
      provides: "Multi-DB isolation pinned without LRU; >16 sequential opens"
    - path: "test/sql/extension_reload.test"
      provides: "Extension re-LOAD idempotency (B18)"
    - path: "test/integration/test_caret_position.py"
      provides: "Tightened caret column assertions for B1/B2/B3 + new B4/B5/B6/B7 placeholders"
    - path: "test/integration/test_multi_db_isolation.py"
      provides: "B15 >16 sequential DBs + B16 RSS-bounded loop"
    - path: "tests/parse_proptest.rs"
      provides: "ParseError::position byte-offset preservation proptest"
  key_links:
    - from: "test/integration/test_caret_position.py"
      to: "extract_caret_position helper"
      via: "Re-enabled assertions on caret column"
      pattern: "extract_caret_position\\("
    - from: "cpp/include/parser_extension_compat.hpp"
      to: "ParserExtensionParseResult struct"
      via: "static_assert on sizeof + offsetof(error_location)"
      pattern: "static_assert.*ParserExtensionParseResult"
---

<objective>
Wave 0 — install all test scaffolding and the layout `static_assert` BEFORE
any production code changes land. Every behavioural property in
`62-RESEARCH.md` §6 (B1–B19) gets a fixture file or a hook in an existing
fixture, so Plans 02 and 03 can move green→green→green without inventing new
test infrastructure mid-flight. Until Plan 04 populates the fixtures with
concrete expected text, each new sqllogictest file uses `halt` (no-op) and
the Python integration tests guard new assertions behind a feature flag /
`pytest.skip` so the existing suite stays green.

Purpose: Pin the contract — every test slot Phase 62 promises to fill exists
in code, named, indexed by VALIDATION.md.

Output: 9 new fixture files + 1 layout assertion + tightened helper hooks in
`test_caret_position.py` + extended `test_multi_db_isolation.py`.
</objective>

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

<context>
@.planning/STATE.md
@.planning/ROADMAP.md
@.planning/phases/62-caret-restoration-lru-removal/62-RESEARCH.md
@.planning/phases/62-caret-restoration-lru-removal/62-VALIDATION.md
@_notes/v0.8.0_phase_62_ultraplan.md
@_notes/v0.8.0_phase_62_sqllogictest_spike.md
@CLAUDE.md
@TECH-DEBT.md

<interfaces>
<!-- Layout to pin in static_assert (from cpp/include/parser_extension_compat.hpp) -->
```cpp
struct ParserExtensionParseResult {
    ParserExtensionParseResult();
    explicit ParserExtensionParseResult(string error_p);
    explicit ParserExtensionParseResult(unique_ptr<ParserExtensionParseData> parse_data_p);
    string error;
    unique_ptr<ParserExtensionParseData> parse_data;
    ParserExtensionResultType type;
    optional_idx error_location;  // line 84 — populated by Phase 62
};
```

<!-- Helper already present in test_caret_position.py:73-94 -->
```python
def extract_caret_position(error_text: str) -> int | None:
    """Return 0-based column of caret relative to query (subtracting 'LINE 1: ' prefix)"""
```

<!-- Validate_and_rewrite signature (src/parse.rs:996) -->
```rust
pub(crate) fn validate_and_rewrite(query: &str)
    -> Result<Option<RewriteOutcome>, ParseError>;
// ParseError::position is Option<usize> -- byte offset into the user's input.
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add ParserExtensionParseResult layout static_assert (Risk F)</name>
  <files>cpp/include/parser_extension_compat.hpp</files>
  <read_first>
    - cpp/include/parser_extension_compat.hpp (full file — locate the existing `static_assert(sizeof(ParserOptions) == 32, ...)` at line 150 and the `ParserExtensionParseResult` struct at lines 67–85)
    - .planning/phases/62-caret-restoration-lru-removal/62-RESEARCH.md §3 Risk F (Wave 0 compile guard rationale)
    - cpp/include/duckdb.cpp around line 347279 to confirm the upstream layout we're pinning against
  </read_first>
  <action>
    Add a `static_assert` block immediately after the closing brace of
    `struct ParserExtensionParseResult` (around line 86, before the
    `parse_function_t` typedef). Pin the size and the offset of
    `error_location`:

    ```cpp
    // Phase 62 layout guard — sv_parse_stub writes result.error_location after
    // construction (the constructor doesn't take it). If a future DuckDB bump
    // moves error_location to private or changes its type from optional_idx,
    // this assertion fires before silent breakage. See RESEARCH.md Risk F.
    static_assert(sizeof(ParserExtensionParseResult) <= 64,
                  "ParserExtensionParseResult layout drift — re-grep duckdb.cpp parser_extension.hpp");
    static_assert(std::is_same<decltype(ParserExtensionParseResult{}.error_location), optional_idx>::value,
                  "ParserExtensionParseResult::error_location type drift");
    ```

    Note: the `<= 64` upper bound is intentionally loose to tolerate ABI padding
    differences across compilers — its purpose is to catch a *jump* (e.g. new
    field added) not to lock the exact byte count. The type-check on
    `error_location` is the strict guard.

    If `<type_traits>` isn't already included via `duckdb.hpp`, add `#include <type_traits>` at the top of the file.
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && just build 2>&1 | tail -20</automated>
  </verify>
  <acceptance_criteria>
    - `cpp/include/parser_extension_compat.hpp` contains `static_assert(sizeof(ParserExtensionParseResult)`
    - `cpp/include/parser_extension_compat.hpp` contains `static_assert(std::is_same<decltype(ParserExtensionParseResult{}.error_location), optional_idx>`
    - `just build` exits 0 (the static_assert holds against the current vendored amalgamation)
  </acceptance_criteria>
  <done>Build succeeds with the new static_assert in place; assertion fails loudly on any future ABI drift.</done>
</task>

<task type="auto">
  <name>Task 2: Create sqllogictest scaffolding fixtures (7 files, halted no-ops)</name>
  <files>test/sql/error_caret_create.test, test/sql/error_caret_drop.test, test/sql/error_caret_alter.test, test/sql/error_caret_multiline.test, test/sql/error_caret_unicode.test, test/sql/lru_removed_isolation.test, test/sql/extension_reload.test</files>
  <read_first>
    - .planning/phases/62-caret-restoration-lru-removal/62-VALIDATION.md (Wave 0 Requirements list)
    - .planning/phases/62-caret-restoration-lru-removal/62-RESEARCH.md §6 (B4, B5, B6, B7, B8, B15, B18 rows + Wave 0 gap list)
    - test/sql/peg_compat.test (existing example of `LOAD semantic_views` + `statement error` patterns)
    - test/sql/phase2_restart.test (existing example of multi-statement DDL fixture; pin file pattern)
  </read_first>
  <action>
    Create 7 sqllogictest fixture files. Each file MUST start with the standard
    extension-load preamble matching the rest of `test/sql/`:

    ```
    require semantic_views

    statement ok
    LOAD semantic_views;
    ```

    After the preamble, each file emits a `halt` directive followed by a
    commented-out test plan that Plan 04 will activate. The `halt` directive
    causes sqllogictest to stop processing the file at that point — so the
    file is parsed, validated, and discovered by the runner, but no
    assertions run yet. This keeps the suite green until Wave 3.

    File-by-file content (after preamble):

    **`test/sql/error_caret_create.test`** (B1, B2, B3 sqllogictest-form coverage):
    ```
    halt

    # Phase 62 Plan 04 will populate these. Coverage:
    #   - missing '(' after TABLES
    #   - typo 'TBLES' instead of 'TABLES'
    #   - near-miss prefix 'CRETAE SEMANTIC VIEW'
    # Each statement error block expects substring of validator message AND
    # presence of "LINE 1:" + a caret line in the error text.
    ```

    **`test/sql/error_caret_drop.test`** (B7):
    ```
    halt

    # Phase 62 Plan 04: DROP SEMANTIC VIEW with bad form (missing name) →
    # caret on the trailing token; substring "expected" or similar.
    ```

    **`test/sql/error_caret_alter.test`** (B6):
    ```
    halt

    # Phase 62 Plan 04: ALTER SEMANTIC VIEW v RENAM TO w; → caret on RENAM.
    ```

    **`test/sql/error_caret_multiline.test`** (B4):
    ```
    halt

    # Phase 62 Plan 04: 3-line CREATE with malformed clause on line 2.
    # Pin DuckDB's "LINE N:" rendering for N != 1.
    ```

    **`test/sql/error_caret_unicode.test`** (B5):
    ```
    halt

    # Phase 62 Plan 04: CREATE SEMANTIC VIEW vüé AS TBLES (...) — pin
    # whatever DuckDB does today (byte vs char column counting) as the
    # contract. RESEARCH §6 notes DuckDB uses byte positions internally.
    ```

    **`test/sql/lru_removed_isolation.test`** (B15 sqllogictest companion):
    ```
    halt

    # Phase 62 Plan 04: ATTACH 17+ databases sequentially in a single
    # session; CREATE SEMANTIC VIEW v_N on each; DESCRIBE on the FIRST one
    # must succeed (pre-Phase-62 the LRU would have evicted it on the 17th
    # ATTACH). Verifies the LRU silent-eviction error class is gone.
    ```

    **`test/sql/extension_reload.test`** (B18):
    ```
    halt

    # Phase 62 Plan 04: LOAD semantic_views; CREATE v1; LOAD semantic_views;
    # DROP v1; — must succeed even if a second ParserExtensionInfo is
    # registered. RESEARCH §3 Risk A.
    ```
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && for f in test/sql/error_caret_create.test test/sql/error_caret_drop.test test/sql/error_caret_alter.test test/sql/error_caret_multiline.test test/sql/error_caret_unicode.test test/sql/lru_removed_isolation.test test/sql/extension_reload.test; do test -f "$f" || { echo "MISSING $f"; exit 1; }; grep -q "halt" "$f" || { echo "$f missing halt directive"; exit 1; }; grep -q "require semantic_views" "$f" || { echo "$f missing require directive"; exit 1; }; done && just build 2>&1 | tail -3 && just test-sql 2>&1 | tail -10</automated>
  </verify>
  <acceptance_criteria>
    - All 7 files exist under `test/sql/`
    - Each file contains `require semantic_views` AND `halt` directives
    - `just test-sql` exits 0 (new files parse and halt cleanly; suite stays green)
  </acceptance_criteria>
  <done>7 fixture files committed; sqllogictest discovers them and treats them as no-ops; suite green.</done>
</task>

<task type="auto" tdd="true">
  <name>Task 3: Wire caret-position helper assertions + multi-DB extensions + position proptest</name>
  <files>test/integration/test_caret_position.py, test/integration/test_multi_db_isolation.py, tests/parse_proptest.rs</files>
  <behavior>
    - `test_caret_position.py`: existing 3 tests (`test_caret_missing_paren`, `test_caret_clause_typo`, `test_caret_near_miss`) call `extract_caret_position(error_text)` and skip via `pytest.skip(...)` if it returns `None` — the skip is the temporary state until Plan 04 flips them to `assert ... is not None`. Add 4 NEW test functions: `test_caret_multiline_typo`, `test_caret_unicode_prefix`, `test_caret_alter_typo`, `test_caret_drop_missing_name`. Each new test runs the malformed query, captures the error text, and currently `pytest.skip("Plan 04 will populate")`. The test BODIES must already issue the malformed query and capture the exception so the skeleton is real, not a stub.
    - `test_multi_db_isolation.py`: add `test_seventeen_dbs_sequential_create` — opens 17 in-memory DuckDB connections sequentially, loads the extension into each, runs `CREATE SEMANTIC VIEW v AS TABLES(...)` on each, then DESCRIBE on the first connection's view. PRE-Phase-62 this would surface the LRU "catalog context evicted" error on opens 17+; POST-Phase-62 it succeeds. Add `test_fifty_db_open_close_rss_bounded` — opens+closes 50 in-memory DBs in a loop with the extension loaded, asserts process RSS delta stays under 50 MB (use `psutil` if already a dep, else `resource.getrusage` for max RSS). Both tests start `pytest.skip("Plan 04 — Phase 62 LRU removal verifies this")` so they're staged.
    - `tests/parse_proptest.rs`: new file (or new module added inside existing structure). Proptest verifies `ParseError::position` is preserved as byte offset into the user's input through `validate_and_rewrite` — generates arbitrary leading whitespace + line comments + block comments, prepends to a known-bad CREATE, asserts position equals `prefix_byte_len + bad_token_offset_in_canonical_query`.
  </behavior>
  <read_first>
    - test/integration/test_caret_position.py (full 240-line file — locate `extract_caret_position` at lines 73–94, the 3 test functions at 123/145/167, and the comment block at lines 14–21 we'll preserve until Plan 04)
    - test/integration/test_multi_db_isolation.py (full file — pin existing patterns for `LOAD semantic_views` + multiple databases)
    - tests/parse_proptest.rs (existing — pin proptest module conventions used in this codebase)
    - src/parse.rs lines 996–1063 (`validate_and_rewrite` — confirm position is byte offset into `query` argument)
    - src/errors.rs lines 11–16 (`ParseError::position` doc comment — byte offset contract)
    - .planning/phases/62-caret-restoration-lru-removal/62-RESEARCH.md §2 Q1 (positions ARE in user input — no remapping needed)
  </read_first>
  <action>
    **Sub-step 3a — `test/integration/test_caret_position.py`:**

    1. Leave the existing module-level docstring at lines 14–21 in place (Plan 04 rewrites it). Add a single line under the docstring:

       ```python
       # Phase 62 Wave 0: assertion bodies wired but skipped pending Plan 04.
       ```

    2. For each of the 3 EXISTING tests (`test_caret_missing_paren` line 123, `test_caret_clause_typo` line 145, `test_caret_near_miss` line 167): after the existing message-text assertion, append:

       ```python
       caret_col = extract_caret_position(error_text)
       if caret_col is None:
           # Phase 62 Wave 0 — Plan 04 will tighten this to: assert caret_col is not None
           import pytest
           pytest.skip("Phase 62 Plan 04: caret rendering not yet restored")
       # Plan 04 will add: assert caret_col == EXPECTED_COLUMN_FOR_THIS_TEST
       ```

       (If `pytest` is not currently a dep — check with `rg "import pytest" test/integration/`. If not, use `print("SKIP: ..."); return` and exit code path used elsewhere in the file. Pick whichever the existing helpers `run_test`/`fail`/`pass` use for staged-skip. Do NOT add a new dep just for this.)

    3. Add 4 new test functions following the existing pattern (call `make_connection()`, attempt malformed DDL, capture exception, extract message text):

       - `test_caret_multiline_typo` — query: `"CREATE SEMANTIC VIEW v AS\nTABLES (t)\nDIMENSIONS (TBLES x);"` (typo on line 3). Body issues the query, captures error text, currently pytest.skip / `print("SKIP")`.
       - `test_caret_unicode_prefix` — query contains a multibyte UTF-8 identifier like `"CREATE SEMANTIC VIEW vüé AS TBLES (t);"` — same skeleton.
       - `test_caret_alter_typo` — query: `"ALTER SEMANTIC VIEW v RENAM TO w;"` — same skeleton.
       - `test_caret_drop_missing_name` — query: `"DROP SEMANTIC VIEW;"` — same skeleton.

    4. Wire the 4 new tests into the `if __name__ == "__main__":` runner block at the bottom of the file (follow the pattern used for the existing 3).

    **Sub-step 3b — `test/integration/test_multi_db_isolation.py`:**

    1. Add at the bottom:

       ```python
       def test_seventeen_dbs_sequential_create():
           """B15 expanded — open 17 in-memory DBs sequentially, CREATE on each.
           Pre-Phase-62: LRU evicts DB #1 on the 17th open and the next CREATE
           on its handle surfaces 'catalog context for this database has been
           evicted'. Post-Phase-62: every CREATE succeeds because OverrideContext
           is attached per-DBConfig and never evicted.
           """
           # Phase 62 Wave 0 — Plan 04 will remove this skip
           print("SKIP: Phase 62 Plan 04 will activate this test")
           return
           # ---- body left here for Plan 04 to enable ----
           # connections = []
           # for i in range(17):
           #     conn = duckdb.connect(":memory:", config={...})
           #     conn.execute("LOAD semantic_views")
           #     conn.execute(f"CREATE TABLE t_{i}(id INTEGER PRIMARY KEY)")
           #     conn.execute(f"CREATE SEMANTIC VIEW v_{i} AS TABLES (t_{i})")
           #     connections.append(conn)
           # # Verify oldest still works
           # connections[0].execute("DESCRIBE SEMANTIC VIEW v_0").fetchall()
           # for c in connections: c.close()


       def test_fifty_db_open_close_rss_bounded():
           """B16 — sequentially open+close 50 in-memory DBs each running a CREATE.
           RSS delta < 50 MB across the loop. The catalog connection leaks one
           Connection object per DB (~few KB) — bounded by workload, not by an
           LRU cap. See RESEARCH.md Q2 for the leak rationale.
           """
           print("SKIP: Phase 62 Plan 04 will activate this test")
           return
           # body left for Plan 04
       ```

    2. Add to the bottom-of-file runner if there is one.

    **Sub-step 3c — `tests/parse_proptest.rs`:**

    Append (or create as a new file if `tests/parse_proptest.rs` is structured as multiple property tests already — pin to existing conventions):

    ```rust
    /// Phase 62 — `ParseError::position` MUST be a byte offset into the
    /// user's input string, NOT into the rewritten SQL. RESEARCH.md Q1
    /// proves this is already true; this proptest pins it as a contract
    /// across arbitrary leading whitespace + comments.
    #[test]
    fn parse_error_position_is_byte_offset_into_user_input_smoke() {
        // Smoke test (proptest variant below). The malformed CREATE has a
        // bad token at byte offset N; prepending whitespace shifts the
        // reported position by exactly that many bytes.
        use semantic_views::parse::validate_and_rewrite;

        let canonical = "CREATE SEMANTIC VIEW v AS TBLES (t);";
        let bad_token_offset = canonical.find("TBLES").unwrap();

        let err = validate_and_rewrite(canonical).expect_err("should fail");
        let pos = err.position.expect("position should be set for clause typo");
        assert_eq!(pos, bad_token_offset, "smoke: canonical case");

        // Whitespace prefix shifts the position by its byte length.
        let prefixed = format!("   \n\t{canonical}");
        let prefix_len = prefixed.len() - canonical.len();
        let err2 = validate_and_rewrite(&prefixed).expect_err("should fail");
        let pos2 = err2.position.expect("position should be set");
        assert_eq!(pos2, prefix_len + bad_token_offset, "smoke: with whitespace");

        // Line-comment prefix.
        let with_comment = format!("-- a comment\n{canonical}");
        let comment_len = with_comment.len() - canonical.len();
        let err3 = validate_and_rewrite(&with_comment).expect_err("should fail");
        let pos3 = err3.position.expect("position should be set");
        assert_eq!(pos3, comment_len + bad_token_offset, "smoke: with line comment");
    }

    proptest! {
        /// Position is preserved as user-input byte offset for arbitrary
        /// safe ASCII whitespace + comment prefix.
        #[test]
        fn position_byte_offset_preserved_for_arbitrary_prefix(
            ws in proptest::collection::vec(prop_oneof![Just(' '), Just('\t'), Just('\n')], 0..32),
            line_comment in r"-- [a-zA-Z0-9 ]{0,40}\n",
        ) {
            use semantic_views::parse::validate_and_rewrite;
            let prefix: String = ws.into_iter().collect::<String>() + &line_comment;
            let canonical = "CREATE SEMANTIC VIEW v AS TBLES (t);";
            let bad_offset = canonical.find("TBLES").unwrap();
            let full = format!("{prefix}{canonical}");
            let prefix_len = prefix.len();
            let err = validate_and_rewrite(&full).expect_err("must fail on TBLES");
            let pos = err.position.expect("position should always be set for clause typo");
            prop_assert_eq!(pos, prefix_len + bad_offset);
        }
    }
    ```

    NOTE: if `validate_and_rewrite` is `pub(crate)` and not externally accessible from the integration-test crate, expose it via a `#[cfg(test)]`-gated `pub` re-export in `src/parse.rs` (Plan 02 picks this up if needed; for now use whatever access pattern existing proptests in this file use).
  </behavior>
  <read_first>
    - tests/parse_proptest.rs (existing — replicate proptest config / use pattern, including how `proptest!` is imported)
    - tests/expand_proptest.rs (cross-reference for how proptest tests reach into crate-private items)
    - test/integration/test_caret_position.py lines 73–94 + 95–240 (helpers, run_test, all 3 existing test bodies)
    - test/integration/test_multi_db_isolation.py (existing structure)
    - src/parse.rs lines 940–1063 (detect_near_miss + validate_and_rewrite — `validate_and_rewrite` is the public-to-crate API; check visibility)
    - src/lib.rs `pub mod parse` declaration (confirms test-crate visibility path)
  </read_first>
  <action>
    Execute sub-steps 3a, 3b, 3c above. The `<behavior>` block defines the
    expected test surface; this `<action>` block is intentionally short
    because the test bodies are pinned in `<behavior>`.

    Critical: every new test (Python and Rust) must currently SKIP (or be a
    no-op smoke that succeeds), so the suite stays green between Wave 0 and
    Wave 3. Plan 04 flips the skips to assertions.

    The Rust proptest smoke test IS expected to pass right now — `validate_and_rewrite`
    already produces correct positions per RESEARCH §2 Q1. If it fails, that's a
    real Phase 62 finding (escalate; do not weaken the assertion).
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo test --no-default-features parse_error_position 2>&1 | tail -20 && cargo test --no-default-features position_byte_offset 2>&1 | tail -10 && just build 2>&1 | tail -3 && just test-caret 2>&1 | tail -15 && just test-multi-db 2>&1 | tail -10</automated>
  </verify>
  <acceptance_criteria>
    - `cargo test parse_error_position_is_byte_offset_into_user_input_smoke` exits 0 (smoke proves the contract holds today)
    - `cargo test position_byte_offset_preserved_for_arbitrary_prefix` exits 0 (proptest)
    - `test/integration/test_caret_position.py` contains `def test_caret_multiline_typo` AND `def test_caret_unicode_prefix` AND `def test_caret_alter_typo` AND `def test_caret_drop_missing_name`
    - `test/integration/test_multi_db_isolation.py` contains `def test_seventeen_dbs_sequential_create` AND `def test_fifty_db_open_close_rss_bounded`
    - `just test-caret` exits 0 (the 3 existing tests still pass; the 4 new tests skip without erroring)
    - `just test-multi-db` exits 0 (existing tests still pass; the 2 new tests skip)
  </acceptance_criteria>
  <done>All 4 new Python caret tests + 2 new multi-DB tests staged with skips; proptest enforcing position contract green; suite green end-to-end.</done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| Test fixture files → sqllogictest runner | Test infrastructure only; no production trust boundary changed in Wave 0. |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-62-W0-01 | Tampering | Wave 0 fixture files | accept | Fixture files are dev-only; checked into git; no runtime trust impact. |
| T-62-W0-02 | DoS | proptest case generation | accept | proptest uses bounded shrink budget (default 256 cases); no untrusted input. |

No high-severity threats in Wave 0 — pure test scaffolding + a compile-time static_assert. The static_assert is a defensive guard against future ABI drift, not a security mitigation. Phase-level threat model lives in Plans 02/03 where production code changes.
</threat_model>

<verification>
- `just build` exits 0 (static_assert holds)
- `just test-sql` exits 0 (7 new sqllogictest fixtures parse + halt cleanly)
- `cargo test` exits 0 (proptest + smoke test green)
- `just test-caret` exits 0 (3 existing tests pass; 4 new staged tests skip)
- `just test-multi-db` exits 0 (existing tests pass; 2 new staged tests skip)
- `just test-all` exits 0 (full suite stays green between waves)
</verification>

<success_criteria>
- 9 Wave 0 artefacts created (1 static_assert in compat header, 7 sqllogictest fixtures, 1 proptest)
- Test suite green end-to-end (`just test-all`)
- Every behavioural property B1–B19 has a fixture slot ready for Plans 02–04 to populate
- Multi-DB isolation tests staged for >16 sequential opens (B15/B16)
</success_criteria>

<output>
After completion, create `.planning/phases/62-caret-restoration-lru-removal/62-01-SUMMARY.md` documenting which fixture files were created, the static_assert text, and the proptest smoke result.
</output>
