11 KiB
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<project>/.snapshots/(index.yaml+ per-snapshot payload dirs). Hybrid whole-project snapshot — db via the online backup API +project.yaml/data/*.csvcopied; restore is text-first, db-last (ADR-0015 §6).src/db.rs—snapshot_thenbrackets all 19 mutating dispatch arms (stage→run→finalise/discard), gated on a user commandsource(internal ops like open-time rebuild are not snapshotted);BeginBatch/EndBatch(areplay/batch = one undo step);Undo/Redo/PeekUndo/PeekRedohandled inworker_loopwith&mut conn.undo/redoapp commands +Modal::UndoConfirmconfirm flow;--no-undoCLI flag;.snapshots/git-ignored + export-excluded- temp-cleanup-allowlisted.
/rundafound + 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 offinalize), so a laterredodiscarded the new work. Nowclear_redoruns on that path (snapshot_then/end_batch); commitdf6aa69, regression-tested.- ADR-0006 marked implemented (Amendment 1 + Implementation note);
requirements.mdU1/U2 →[x];CLAUDE.mdupdated.
- ADR-0035 drafted (
docs/adr/0035-advanced-mode-sql-ddl.md, commita079200, 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.
- Own per-statement
Sql*DDL commands —SqlCreateTable,SqlAlterTable,SqlDropTable,SqlCreateIndex,SqlDropIndex(granularity mirrors the DMLSql*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, andSTRICT. ("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-FKCREATE TABLE) — clarity over forced refactoring. - Dispatch:
create/dropreuse ADR-0033 Amendment 1's category-grouped, mode-aware dispatch (SQL-first in advanced, simple fallback);alteris a new advanced-only entry word. - 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. - FK → named relationships: inline
REFERENCES+ table-levelFOREIGN KEYcreate the table and its relationship metadata in one command (= one undo step);ON DELETE/ON UPDATE→ ADR-0013 actions; constraint name → relationship name. - 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 onundoas the net (no force flag, no dropping to simple mode). - Table rename (
C1) — advanced-onlyALTER TABLE … RENAME TO; new low-level op (rename table + its CSV file + the relationship metadata rows). No simple-mode form. - Surface = full, no pre-emptive cuts; 9 sub-phases 4a–4i (ADR §13), each with exit + DA gates.
- 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 rightIdentSourceon schema-name slots, its hint/usage catalog keys, and DDL-specific walker diagnostics (the DDL peers of the DML ones ADR-0033 added). - Replay:
create/drop/alterare schema-write entry words, not in ADR-0034's app-lifecycle skip set, so SQL DDL replays as a write — no replay-filter change (unlikeundo/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/<date>-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 TABLEcore (columns + the §3 type map + column constraints + single/compoundPRIMARY KEY); no FK yet. - 4b — FK in
CREATE TABLE(inline + table-level) → relationships. - 4c —
DROP TABLE. - 4d —
CREATE [UNIQUE] INDEX/DROP INDEX. - 4e —
ALTER TABLEadd/drop/rename column. - 4f —
ALTER TABLE … ALTER COLUMN TYPE(the §7 conversion model). - 4g —
ALTER TABLEadd/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 newRequestvariant insrc/db.rsand must be wrapped withsnapshot_thenlike 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'treply.sendit raw.create/drop/alterneed nois_app_lifecycle_entry_wordchange (they're writes, §3.9). - Grammar lives in
src/dsl/grammar/(CommandNode+ theREGISTRYinmod.rs, taggedCommandCategory); mirror the ADR-0033 DML nodes (src/dsl/grammar/sql_*).CHECK/DEFAULTexpressions reuse the ADR-0031sql_exprfragment. 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 bykeys_validate_against_catalog): every newhelp_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-endtests/sql_ddl_e2e.rs(mirrorsql_dml_e2e.rs), plus typing-surface/matrix coverage. Add a DDL undo test (each statement = one undo step;CREATE TABLEwith FK = one step).
Open micro-calls to escalate when reached (not yet decided):
CREATE UNIQUE INDEX— ADR-0025's index model may need auniqueflag (ADR §4/§13 4d flags this).- Exact
ALTER COLUMN … TYPEspelling, and whetherIF [NOT] EXISTSis 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).
blobvalue literal (src/dsl/value.rs) — pre-existing gap.- Undo residual edge (ADR-0006 Implementation note): if the entire
.snapshots/dir is unwritable,clear_redocan 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
/rundaround 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.mdin the same edit; behaviour changes updaterequirements.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
- 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). - Baseline:
cargo test # expect 1698 passing / 0 failing / 0 skipped / 1 ignored cargo clippy --all-targets -- -D warnings # clean - 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.
- Escalate anything not settled in ADR-0035; the user wants mismatches and scope calls surfaced, not silently decided.