Files

13 KiB

Session handoff — 2026-05-23 (33)

Thirty-third handover. This session implemented ADR-0033 sub-phase 3j (dispatch wiring — making insert/update/delete shared DSL/SQL entry words), and the implementation surfaced a chain of cross-cut interactions that were escalated, decided with the user, and resolved by ADR-0033 Amendment 3 (the command-identity / mode model

  • a new combined-error UX feature). A user-invoked /runda round at the end found and fixed two more issues.

The next session does sub-phase 3k — the Phase-3 verification sweep and phase-exit report. See §4.

§1. State at handoff

Branch: main. Tests: 1626 passing, 0 failing, 1 ignored. Clippy: clean (cargo clippy --all-targets -- -D warnings).

Latest commit (local-only):

d5c7f63 grammar+walker: 3j — shared insert/update/delete entry words (ADR-0033 §2 / Amendments 1 & 3)

origin/main is at 6b8888f (3h), so 8 commits are local-only (the six 3i commits, the handoff-32 doc commit c16196f, and this 3j commit d5c7f63). Unpushed commits are a normal working state; pushing is the user's step — do not prompt about it.

§2. Process lessons (read first)

  1. The "DSL is the Simple-mode surface" premise was wrong about the tests. The plan's 3j exit gate assumed the DSL insert/update/ delete tests run in Simple mode. They do not — the common helpers (ok/err/parse, parse_command, db parse_filter, the typing_surface assess) default to Advanced mode. Pre-3j that was harmless (those words were DSL-only, so Advanced still hit the DSL grammar). Once the words became shared, Advanced routes them to SQL. The fix was to mode-correct the DSL tests to Simple (inputs/assertions unchanged), not to rewrite assertions. Recorded as ADR-0033 Amendment 3.

  2. The dispatch model needed three escalation rounds with the user to get right (recorded as Amendment 3). The user's framing — which is now the authoritative model — is:

    • A command is the mode-rooted grammar-path outcome; its identity is intrinsic (Command::Insert vs Command::SqlInsert are distinct, both correct, both tested).
    • Execution is mode-independent today; the differences are "visual" (output/diagnostics), so a valid command replays / executes identically regardless of which variant is produced.
    • Simple mode commits the DSL candidate for a shared word (surfacing the real DSL error); "this is SQL" is reserved for entry words with no DSL form (select/with).
  3. /runda again earned its keep (last session it found 6 bugs; this session 2). Lesson re-confirmed: probe, don't reason — every suspicion was checked with a throwaway test that printed the actual parse_command / ambient_hint output before any conclusion. Reasoning alone had me wrong twice (the --all-rows "regression" that wasn't, and the replay "regression" that wasn't).

  4. Escalate ADR-vs-implementation mismatches; don't "improve" silently. The simple-mode behaviour, the replay mode, and the combined-pointer UX were all escalated to the user before coding and resolved by Amendment 3. The user values this; keep doing it.

§3. What shipped this session (detail)

All in commit d5c7f63; the authoritative record is ADR-0033 Amendment 3 (docs/adr/0033-sql-dml-grammar.md) — read it.

3j dispatch wiring (the planned work)

  • data.rs: SQL_INSERT/SQL_UPDATE/SQL_DELETE CommandNodes moved off the dev words (sqlinsert/sql_update/sql_delete) to the real entry words insert/update/delete, category Advanced, alongside the Simple DSL nodes. build_sql_{insert,update,delete} collapsed to source.trim() (the row-source extraction is span-based, so it was entry-word-agnostic already).
  • walker/mod.rs: the two REGISTRY entry-word listing sites (completion_probe_in_mode, expected_at_input_in_mode) now de-duplicate the shared primaries.
  • Dev-word inputs migrated to the real words across tests/sql_*.rs, the walker diagnostic tests, and completion.rs (advanced mode).

Dispatch model fixes (the cross-cut chain)

  • decide (walker/mod.rs)Simple mode now commits the DSL candidate for a shared word (was: emitted "this is SQL" on DSL-mismatch + SQL-match, which discarded the actionable DSL error and broke phase-D / completion). Advanced mode commits the first candidate that is Match or ValidationFailed (so an internal- table rejection on the SQL candidate is surfaced, not masked by the DSL fallback). Removed the now-dead scratch_full_match.
  • Mode-aware helpers so simple-mode hints stop leaking the advanced SQL view of a shared word: classify_input_with_schema_in_mode (input_render), invalid_ident_at_cursor_in_mode (completion).
  • Test-mode correction: DSL grammar / completion / phase-D / db tests parse in Simple mode (parse_command_in_mode(.., Simple), cands_simple, assess pinned to Simple). replay keeps its Advanced model (one stale "parse error" assertion relaxed to "rejected, state intact").

Combined DSL-error + advanced-SQL pointer (user-requested feature)

A Simple-mode definite DSL error whose line would run as SQL in advanced mode keeps its DSL prose and gains the advanced_mode.also_valid_sql suffix — e.g. "for Name: Type a quoted string … (valid as SQL in advanced mode — mode advanced or prefix :)". Lives in input_render::ambient_hint_in_mode (live typing) and App::dispatch_dsl (submit), both via the shared input_render::advanced_alternative_note. New catalog key advanced_mode.also_valid_sql (keys.rs + en-US.yaml).

/runda round (two findings)

  • Finding A (fixed): the combined pointer reached multi-row / UPSERT in the live hint but not delete … returning (live completion shadowed the error) and not the submit path at all. Fixed by adding the pointer to dispatch_dsl (submit) — now every Simple-mode definite-DSL-error-that's-valid-SQL gets it on submit.
  • Finding B (fixed): simple-mode insert into __rdbms_* … parsed to Command::Insert — the DSL data-command target slots had no reject_internal_table (advanced SQL rejected them; simple did not). Added the validator to the three DSL target slots (TABLE_NAME_EXISTING / _INSERT / _WRITES in data.rs) so insert/update/delete/show-data/show-table refuse __rdbms_* in both modes (ADR-0030 §6 — "every table-source slot").

§4. Sub-phase 3k — the NEXT job (verification sweep)

Read docs/plans/20260520-adr-0033-phase-3.md "Sub-phase 3k" and the "Cross-cut verification matrix" section.

Goal: the Phase-3 phase-exit verification. Per the plan:

  1. Author/extend the end-to-end (Tier-3) integration tests for the real-world DML shapes listed in 3k's exit gate (INSERT…SELECT cross-table; multi-row INSERT covering the ten types; UPDATE with subquery in SET; DELETE with cascade; UPSERT round-trip; RETURNING on all three; history.log replay of every Phase-3 form).
  2. Fill the cross-cut verification matrix (every "X comes for free" claim from ADR-0030/0031/0032/0033 → a passing test whose INPUT is SQL syntax for SQL claims). Mark each row's file::function.
  3. Final DA review — genuine, specific critiques first, verdict last; rubber-stamp PASS is a process failure.
  4. Produce the phase-exit report at docs/handoff/<date>-phase-3-verification.md.
  5. Confirm zero failures / zero skips / no regression from the Phase-2 baseline (1446/0/1; this session's count is 1626/0/1 and rising as tests were added per sub-phase). Clippy clean.

3k heads-up — re-run the full diagnostic + dispatch set. 3j changed how shared-word inputs route by mode; the new dispatch (Amendment 3) is covered but exercise the compound forms (INSERT…SELECT…ON CONFLICT… RETURNING) in both modes, and confirm the combined-pointer + internal- table behaviour on the awkward inputs (per §2 lesson 3, probe the output, don't assume).

§5. Decisions settled this session (do not re-litigate)

Recorded in ADR-0033 Amendment 3 (Accepted-in-spirit; the ADR is still "Proposed" overall pending 3k). Key points:

  • Simple mode = DSL surface; commit the DSL candidate for shared words. "This is SQL" (bare) only for select/with. A shared-word SQL construct in simple mode → DSL error + combined pointer.
  • Advanced mode = SQL-first, DSL fallback. Fallback fires on a structural mismatch (delete … --all-rows); a content rejection (internal table) on the SQL candidate is committed, not fallen back.
  • update … --all-rows in advanced does NOT fall back — the SQL SET expression absorbs --all-rows as 42 - -all - rows; harmless because the engine treats --all-rows as a SQL comment, so it runs as "all rows" anyway. (Only delete … --all-rows falls back.)
  • Replay parses in Advanced mode (the full surface, same parser as interactive) — run_replay is unchanged in mode; a valid log replays identically by the §6/§7 parity guarantees, so replay needs no per-line mode. The deferred M4 is not a replay prerequisite.
  • Execution-time mode side-channel = deferred (M4). Three-way Mode (simple / advanced / advanced-one-shot) threaded to the worker for mode-dependent output (e.g. simple create table echoing generated SQL in advanced). Tracked in requirements.md M4; gets its own ADR. Not a Phase-3 dependency.

§6. Tracked deferred items (nothing lost)

Pick up after Phase 3 (3k) per the user's standing "extra tasks after phase 3 is complete".

  • M4 — execution-time mode side-channel (new this session; requirements.md). See §5. Its own future ADR.
  • TASK #8 — implement ADR-0034 (history journal). history.log success-only ⇒ failed commands recallable in-session but lost across sessions. ADR-0034 (docs/adr/0034-…, Accepted): complete journal tagged ok/err; hydration reads all, replay reads ok only.
  • TASK #9 — fix broken replay + add the missing test. run_replay parses each whole line through the DSL parser with no understanding of the <ts>|<status>|<source> journal format, so replay history.log fails on line 1. (Note: 3j confirmed replay's mode is correct — Advanced; #9 is about the line format, orthogonal.)

Lower-priority observations (discussed, acceptable)

  • DDL internal-table rejection is still absent: drop table __rdbms_*, add column to __rdbms_* … etc. are not refused at parse (Finding B fixed only the data commands, which have SQL counterparts). No SQL DDL exists to compare against; flagged for a future pass.
  • The migrated test helpers run_sqlinsert / run_update / run_delete (tests/sql_*.rs) keep their dev-word-era names though they now parse the real words — cosmetic, low priority.
  • also_valid_sql is not appended to mid-typing (incomplete) hints, by design (avoids noise during normal DSL entry).

§7. Process pins (unchanged, still binding)

  • Confirm every commit. Propose the message; wait for the go-ahead. No auto-commit at sub-phase gates (the user confirmed each commit).
  • Push is the user's step. Never push; never prompt about it.
  • No AI attribution in commits (global rule).
  • Test-first / probe-then-assert. Reproduce with a failing (or printing) test before concluding. /runda's model: probe → confirm → fix → keep the test.
  • Core walker changes (walk_seq / walk_choice / walk_repeated) need explicit user OK. decide (the dispatcher) and new post-walk passes do not — but Amendment-worthy behaviour changes are escalated and recorded (as Amendment 3 was).
  • Escalate ADR-mechanism mismatches; never classify work "out of scope" without user confirmation. (Finding B was fixed only after the user said to; the DDL gap was flagged, not silently dropped.)

§8. How to take over

  1. Read, in order: this file → docs/adr/0033-sql-dml-grammar.md (Amendment 3 is the model this session settled; also Amendment 1 = dispatch, Amendment 2 = cascade) → docs/plans/20260520-adr-0033-phase-3.md "Sub-phase 3k"
    • "Cross-cut verification matrix" → CLAUDE.mddocs/requirements.md (note new M4).
  2. Baseline:
    cargo test    # expect 1626 passing / 0 failing / 1 ignored
    cargo clippy --all-targets -- -D warnings   # clean
    
  3. Start 3k per §4: end-to-end DML integration tests, fill the cross-cut matrix, final DA review, phase-exit report at docs/handoff/<date>-phase-3-verification.md.
  4. Escalate anything not settled in ADR-0033 / its amendments / the plan; the user wants mismatches surfaced, not silently fixed.