diff --git a/docs/handoff/20260524-handoff-36.md b/docs/handoff/20260524-handoff-36.md new file mode 100644 index 0000000..5ae30dd --- /dev/null +++ b/docs/handoff/20260524-handoff-36.md @@ -0,0 +1,211 @@ +# Session handoff — 2026-05-24 (36) + +Thirty-sixth handover. This session **implemented ADR-0006 +(undo/snapshot) end-to-end** (§8 steps 1–8 + a `/runda` data-loss +fix) and then **drafted ADR-0035 — advanced-mode SQL DDL** (Phase 4 +of the ADR-0030 roadmap, status **Proposed**). The next session +**begins implementing ADR-0035, starting at sub-phase 4a**. See §4. + +## §1. State at handoff + +**Branch:** `main`. **Tests: 1698 passing, 0 failing, 0 skipped, +1 ignored** (the unchanged `friendly/mod.rs` doctest). **Clippy:** +clean (`cargo clippy --all-targets -- -D warnings`). + +**Latest commit (local-only):** + +``` +a079200 docs: ADR-0035 — advanced-mode SQL DDL (Phase 4) +``` + +`origin/main` is at `df6aa69`; **1 commit is local-only** (a079200). +The undo/snapshot work (through `df6aa69`) is already pushed. +Unpushed commits are a normal working state; pushing is the user's +step — do not prompt about it. + +## §2. What shipped this session + +- **ADR-0006 (undo/snapshot) fully implemented** — the U1/U2 half; + the replay/journal half (U3/U4) had shipped via ADR-0034. Built + across §8 steps 1–8 of the plan + (`docs/plans/20260524-adr-0006-undo-snapshots.md`): + - `src/undo.rs` — `SnapshotStore`: a persisted undo ring + redo + stack under `/.snapshots/` (`index.yaml` + per-snapshot + payload dirs). Hybrid whole-project snapshot — db via the online + backup API + `project.yaml`/`data/*.csv` copied; restore is + text-first, db-last (ADR-0015 §6). + - `src/db.rs` — `snapshot_then` brackets all **19 mutating dispatch + arms** (stage→run→finalise/discard), gated on a **user command + `source`** (internal ops like open-time rebuild are *not* + snapshotted); `BeginBatch`/`EndBatch` (a `replay`/batch = one undo + step); `Undo`/`Redo`/`PeekUndo`/`PeekRedo` handled in + `worker_loop` with `&mut conn`. + - `undo`/`redo` app commands + `Modal::UndoConfirm` confirm flow; + `--no-undo` CLI flag; `.snapshots/` git-ignored + export-excluded + + temp-cleanup-allowlisted. + - **`/runda` found + fixed a silent data-loss bug**: a committed + mutation whose snapshot couldn't be *staged* left the redo stack + stale (redo-clear was a side effect of `finalize`), so a later + `redo` discarded the new work. Now `clear_redo` runs on that path + (`snapshot_then`/`end_batch`); commit `df6aa69`, regression-tested. + - ADR-0006 marked implemented (Amendment 1 + Implementation note); + `requirements.md` U1/U2 → `[x]`; `CLAUDE.md` updated. +- **ADR-0035 drafted** (`docs/adr/0035-advanced-mode-sql-ddl.md`, + commit `a079200`, **Proposed**) — the next job (§3/§4). + +## §3. ADR-0035 design — settled with the user (do not re-litigate) + +All user-confirmed through discussion this session. + +1. **Own per-statement `Sql*` DDL commands** — `SqlCreateTable`, + `SqlAlterTable`, `SqlDropTable`, `SqlCreateIndex`, `SqlDropIndex` + (granularity mirrors the DML `Sql*` set). **Clarifies ADR-0030 §4**: + DDL gets its own advanced commands, but **unlike DML they execute + *structurally*, not verbatim** — raw execution would lose the + playground's types, named relationships, and `STRICT`. ("Verbatim" + for DML was a convenience, not a rule.) **Simple mode is untouched** + (additive). Handlers **reuse the low-level schema/metadata helpers** + where the op matches simple mode, **stand alone where the SQL + surface is richer** (multi-FK `CREATE TABLE`) — clarity over forced + refactoring. +2. **Dispatch:** `create`/`drop` reuse ADR-0033 Amendment 1's + category-grouped, mode-aware dispatch (SQL-first in advanced, simple + fallback); **`alter` is a new advanced-only entry word.** +3. **Type vocabulary** (ADR-0030 §5): playground keywords + standard-SQL + aliases mapped (`integer`→`int`, `varchar`→`text`, `timestamp`→ + `datetime`, …); length args accepted-and-ignored; no engine type + names in/out. +4. **FK → named relationships:** inline `REFERENCES` + table-level + `FOREIGN KEY` create the table **and** its relationship metadata in + one command (= **one undo step**); `ON DELETE`/`ON UPDATE` → + ADR-0013 actions; constraint name → relationship name. +5. **Column-type conversion — unified** (ADR-0017 engine, mode policy): + clean auto-converts both modes; incompatible + own-type-static cases + refuse both modes; **lossy refuses-by-default in simple mode + (`--force-conversion`), but advanced mode performs it with a post-op + loss note and relies on `undo` as the net** (no force flag, no + dropping to simple mode). +6. **Table rename (`C1`)** — advanced-only `ALTER TABLE … RENAME TO`; + **new low-level op** (rename table + its CSV file + the relationship + metadata rows). No simple-mode form. +7. **Surface** = full, no pre-emptive cuts; **9 sub-phases 4a–4i** + (ADR §13), each with exit + DA gates. +8. **Integration is structural, not free of authoring** (ADR §11): + the walker *mechanisms* (highlighting, `[ERR]`/`[WRN]`, usage + skeleton, completion engine) come free; each node still **authors** + the right `IdentSource` on schema-name slots, its hint/usage catalog + keys, and DDL-specific walker diagnostics (the DDL peers of the DML + ones ADR-0033 added). +9. **Replay:** `create`/`drop`/`alter` are schema-write entry words, + **not** in ADR-0034's app-lifecycle skip set, so SQL DDL **replays + as a write — no replay-filter change** (unlike `undo`/`redo`). + +## §4. ADR-0035 implementation — the NEXT job + +**Begin Phase 4 at sub-phase 4a**, test-first, following the ADR-0033 +sub-phase model. **Recommended first action:** read ADR-0035 in full, +then write a short plan doc +(`docs/plans/-adr-0035-sql-ddl.md`) and confirm any open +micro-calls with the user before coding — exactly how ADR-0033/0034/ +0006 were run. + +**Sub-phases (ADR-0035 §13):** + +- **4a** — dispatch + `CREATE TABLE` core (columns + the §3 type map + + column constraints + single/compound `PRIMARY KEY`); no FK yet. +- **4b** — FK in `CREATE TABLE` (inline + table-level) → relationships. +- **4c** — `DROP TABLE`. +- **4d** — `CREATE [UNIQUE] INDEX` / `DROP INDEX`. +- **4e** — `ALTER TABLE` add/drop/rename column. +- **4f** — `ALTER TABLE … ALTER COLUMN TYPE` (the §7 conversion model). +- **4g** — `ALTER TABLE` add/drop constraint, add FK. +- **4h** — `ALTER TABLE … RENAME TO` (the §6 new low-level op). +- **4i** — verification sweep (typing-surface/matrix, engine-neutral + errors, undo parity, `help`/usage). + +**Concrete must-not-forget for the implementer:** + +- **Undo wiring for the new commands.** Each `Sql*` DDL command becomes + a **new `Request` variant in `src/db.rs`** and **must be wrapped with + `snapshot_then`** like the existing 19 mutating arms (so it's one undo + step). `handle_request`'s exhaustive match will force you to handle + each new variant — wrap it, don't `reply.send` it raw. `create`/ + `drop`/`alter` need **no** `is_app_lifecycle_entry_word` change + (they're writes, §3.9). +- **Grammar lives in `src/dsl/grammar/`** (`CommandNode` + the + `REGISTRY` in `mod.rs`, tagged `CommandCategory`); mirror the + ADR-0033 DML nodes (`src/dsl/grammar/sql_*`). `CHECK`/`DEFAULT` + expressions reuse the ADR-0031 `sql_expr` fragment. +- **`ALTER`/column ops build on the ADR-0013 rebuild-table primitive** + (already used by drop/rename/change-column). +- **Catalog + keys lockstep** (`src/friendly/strings/en-US.yaml` + + `src/friendly/keys.rs`, validated by `keys_validate_against_catalog`): + every new `help_id`/`usage_id`/diagnostic key needs both a catalog + body and a keys.rs entry; engine-neutral wording (the vocab audit + enforces it). +- **Tests:** four tiers (ADR-0008). Per-sub-phase Tier-3 like + `tests/sql_insert.rs`/`sql_update.rs`/`sql_delete.rs`, plus an + end-to-end `tests/sql_ddl_e2e.rs` (mirror `sql_dml_e2e.rs`), plus + typing-surface/matrix coverage. **Add a DDL undo test** (each + statement = one undo step; `CREATE TABLE` with FK = one step). + +**Open micro-calls to escalate when reached** (not yet decided): + +- `CREATE UNIQUE INDEX` — ADR-0025's index model may need a `unique` + flag (ADR §4/§13 4d flags this). +- Exact `ALTER COLUMN … TYPE` spelling, and whether `IF [NOT] EXISTS` + is admitted or an ordinary parse error (ADR §12 leans OOS). +- Status flip: ADR-0035 is **Proposed**; flip to **Accepted** once 4a + validates the path end-to-end (the ADR-0033 lifecycle). + +## §5. Other tracked deferred items (nothing lost) + +- **(A)** App-lifecycle-command *runtime*-failure journalling (small + ADR-0034 follow-up; recorded in ADR-0034's Implementation note). +- **M4** — execution-time mode side-channel (deferred by ADR-0033 + Amendment 3; needs its own ADR). +- **`blob` value literal** (`src/dsl/value.rs`) — pre-existing gap. +- **Undo residual edge** (ADR-0006 Implementation note): if the entire + `.snapshots/` dir is unwritable, `clear_redo` can also fail and a + stale redo could survive — accepted (whole undo subsystem is broken + then). +- **CI / TT5**, **DSL→SQL teaching echo** (ADR-0030 Phase 5), **DDL as + the last SQL phase before §6 polish**. + +## §6. Process pins (unchanged, still binding) + +- **Confirm every commit.** Propose the message; wait for the go-ahead. + (Every commit this session was user-approved.) +- **Push is the user's step.** Never push; never prompt about it. +- **No AI attribution** in commits (global rule). +- **Probe, don't reason.** The `/runda` round this session found a real + silent-data-loss bug by *running a throwaway probe*, not by + reasoning. Reproduce/print before concluding; delete probes before + committing. +- **Escalate ADR-vs-implementation mismatches and scope calls.** The + whole ADR-0035 design was settled by escalating (the + reuse-vs-own-command question, the conversion model) rather than + guessing — and corrected twice when the user pushed back. +- **Keep docs in lockstep.** ADR status flips update + `docs/adr/README.md` in the same edit; behaviour changes update + `requirements.md`. +- **Terminology:** the **DSL is the one unified command grammar**; + the real axis is **mode-availability** (simple / advanced / both), + not "DSL vs SQL". Avoid calling simple-mode commands "the DSL". + +## §7. How to take over + +1. **Read, in order:** this file → `docs/adr/0035-advanced-mode-sql-ddl.md` + (**the next job**) → `docs/adr/0030-advanced-mode-sql-surface.md` + (§4/§5 architecture) + `docs/adr/0033-sql-dml-grammar.md` (the + dispatch + sub-phase precedent to mirror) → `CLAUDE.md` → + `docs/requirements.md` (`Q4`/`C1`). +2. **Baseline:** + ``` + cargo test # expect 1698 passing / 0 failing / 0 skipped / 1 ignored + cargo clippy --all-targets -- -D warnings # clean + ``` +3. **Start ADR-0035** per §4: read the ADR, draft a short plan, confirm + the open micro-calls with the user, then implement 4a test-first. +4. **Escalate** anything not settled in ADR-0035; the user wants + mismatches and scope calls surfaced, not silently decided.