16 KiB
Session handoff — 2026-05-29 (51)
Fifty-first handover. Continuation of handoff-50's bug-fix loop. The user steered this session twice with strategic judgement that proved correct both times (see §2). Three issues resolved — two bug fixes and one substantial cross-mode unification — plus two follow-up trackers spawned from #6's earlier /runda pass were tackled (#18 closed benign-but-hardened, #17 fixed). ADR-0022 gained Amendment 3; ADR-0036 gained Amendment 2.
§1. State at handoff
Branch: main. HEAD 10e5197. Tests: 2051 passing, 0
failing, 0 unexpected skips, 1 pre-existing ignored (the same
ignored doctest as prior baselines). Clippy: clean. Nothing
pushed (per the user's rules — push is the user's step).
Commits since handoff-50's abc9bb6:
10e5197 feat: bring simple-mode insert arity diagnostics to parity with advanced
7cccf4e refactor: make completion-drop gate schema-aware for consistency
fa5d0dc fix: insert VALUES between-values hint points at comma not close-paren
Test count moved 2040 → 2051 (+11 net across the three commits).
§2. The two user steers that shaped the session
This session is worth reading for how the user's judgement redirected the work — both calls were right and both uncovered real problems a naive pass would have missed.
-
"Most of the open issues were reported at the same time — the fixes we already applied may have changed the outcome of other reports." This re-validation instinct was correct. Acting on it, every issue picked up got a confirm-it-still-reproduces first step. Result: #2's reported root cause was wrong (the report blamed string literals; it was not literal-type-specific at all), and #18 turned out benign (the divergence it described cannot reach the code path it gates). Neither would have surfaced without re-probing against current HEAD first. This is now the standing agreement: the first step of every bug pickup is to reproduce it on current code before designing a fix.
-
On #17, the user rejected the "easy way out." When implementation of the chosen Approach A revealed a larger blast radius, the lead floated reverting to the lighter Approach B. The user pushed back: "this feels like mistakes were made in the past which resulted in this discrepancy, and approach B is the easy way out … I'd rather stick to A and get this sorted really cleanly — we're still in the original dev phase, it'd be good to create a strong basis for future maintenance, even if it means more work now." This was exactly right: there genuinely was a past discrepancy (issue #1's simple-mode submit teaching note was a workaround for the very gap #17 names), and Approach A — done properly — unified both modes onto one model rather than piling a third bespoke surface on top. Sticking with A also surfaced (and closed) a genuine execution-safety regression that the lighter path would have left buried.
§3. Resolved issues
#2 — VALUES between-values hint (commit fa5d0dc)
Re-characterised on investigation. The report's hypothesis (per-
column inference abandoned when the first literal doesn't match the
first column's type role) was wrong — probing values ('Oli',
values (42, values (3.5 all reproduced identically. Not literal-
type-specific.
Real root cause: the ambient-hint ladder's bottom rung
(ambient_hint_core_in_mode, src/input_render.rs) parsed
schemalessly (parse_command_in_mode) while every higher rung was
already schema-aware. Type-blind, the grammar closes an insert … values (…) tuple after one value, so the "Next:" hint pointed at )
when the schema-aware walk (knowing the remaining columns) expects ,.
The same schemaless parse also reported wrong-arity closed tuples as
complete → the hint read "submit with Enter" for an input the real
parse rejects.
Fix: the fallback rung now calls
parse_command_with_schema_in_mode(input, cache, mode). One-line
change (the cache was already in scope). Verified the friendly arity
diagnostic is not masked — in advanced mode it fires at a higher
rung and still wins (guard test
advanced_mode_wrong_arity_insert_keeps_friendly_diagnostic_over_fallback).
Three typing_surface snapshots re-baselined (form_a one-value
Next: )→,; form_b too-few and form_c wrong-count: the misleading
"Submit with Enter" → the accurate parse error). ADR-0022
Amendment 3 records the schema-aware fallback.
#18 — completion-drop schemaless gate (commit 7cccf4e, closed benign-but-hardened)
The ticket flagged completion.rs:336's input_parses_complete gate
using the schemaless parse (sibling of #2). Investigated as the
ticket asked: it's benign with the current grammar. The gate drives
exactly one thing — dropping a candidate that exactly equals the
trailing partial (don't re-suggest pk at the end of create table T with pk). The schemaless/schema-aware parse does diverge (e.g.
insert into T values (1, 1, 'x') — int into a bool column), but the
divergence is confined to value type/arity, while the drop only
ever removes a re-offered keyword == partial — and those positions
never coincide. Probed every plausible co-occurrence; candidate output
was identical with and without the fix.
Disposition (user chose "keep as hardening"): applied the one-line schema-aware fix anyway (matches #2, strictly safe — can only ever retain a candidate the schemaless gate would have dropped). No behavioural test possible (no observable bug); the suite staying green is the regression cover. Rationale recorded in a code comment.
#17 — simple-mode arity diagnostic parity (commit 10e5197)
The substantial one. Simple-mode wrong-count inserts showed a bare
expected ,/)`` while advanced mode showed a friendly "N column(s)
… M value(s)" message. Root cause: simple insert and advanced
insert are different grammar nodes (ADR-0033 Am1). Advanced routes a
wrong-count tuple to a type-blind fallback (tuple_value_list) so it
structurally matches and dml_insert_arity_diagnostics fires; the
simple DSL value list (column_value_list) built a fixed-length typed
Seq, so a wrong-count tuple Mismatched and the diagnostic never
ran.
Fix — unified onto the one ADR-0027 model (structural parse + ERROR diagnostic), advanced left byte-for-byte unchanged:
- Grammar: new
dsl_insert_value_listgate (data.rs) routes a wrong-count DSL insert tuple to the sharedFALLBACK_VALUE_LISTso it matches and the diagnostic fires. Gated toMode::Simple— in advanced the DSL node stays strict (otherwise a non-SQL shape like Form Cinsert into T (1,2)would spuriously match and be accepted in advanced;sql_insert.rsowns advanced inserts).count_tuple_values(moved toshared.rs) and a newinsert_target_columns(extracted fromcolumn_value_list) are now shared by both grammars so the gate and the typed slots never disagree. - Diagnostic is mode-aware:
dml_insert_arity_diagnosticsgained amodeparam. Advanced Form B expects all columns (auto-fills nothing); simple Form B/C expects the user-fillable columns (serial/shortidauto-fill, ADR-0018 §3). It now also counts the DSL Form A role (insert_first_item, distinct from SQLinsert_column) and scans the keyword-less Form C tuple. New catalog keysdiagnostic.insert_arity_mismatch_form_b_simple(names the fillable and the auto-generated columns) anddiagnostic.insert_arity_mismatch_all_auto. - Submit safety (the regression that A surfaced): a wrong-count
DSL insert now parses
Ok+ carries the ERROR diagnostic, so it would otherwise have dispatched and executed. A unified Ok-arm pre-flight (dsl_insert_count_mismatch_notes,input_render.rs) now blocksExecuteDsland shows the teaching note — mirroring the advanced Ok-arm pre-flight. The issue #1 Err-arm note retires.advanced_alternative_note's gate now reads the validity verdict (not justDefiniteErrorAt) so the cross-mode pointer still fires for the new parse-Ok-with-ERROR shape, but only when the line is genuinely valid in advanced (Form B 3-val → pointer; Form C / too-few → no pointer).
Scope guard (important for future readers): this is arity-
diagnostic UX parity only. It does not consolidate value-handling,
execution, or serial/shortid auto-fill across modes — ADR-0036's
deliberate mode-distinctness stands. The per-mode count difference is a
consequence of the auto-fill difference, not a violation of it.
Documented as ADR-0036 Amendment 2 (cross-ref ADR-0033 §8.1) + README index + requirements.md H1a.
§4. ADR / docs work
- ADR-0022 Amendment 3 (in
fa5d0dc): the ambient-hint fallback rung is now schema-aware. Records that Amendment 1's listing ofparse_command_in_modeas the fallback was an oversight (every other rung was schema-aware), and that the friendly arity diagnostic still wins at its higher rung. README index updated same-edit. - ADR-0036 Amendment 2 (in
10e5197): simple-mode arity-diagnostic parity (full detail in §3 #17). README index updated same-edit. - requirements.md H1a gained citations for both #2 and #17 contributions.
- #18 is a code-comment-only decision (benign hardening); no ADR.
§5. What's open
Bug reports still open (filed handoff-50)
| # | Title | Label |
|---|---|---|
| 7 | Advanced mode: explain not yet supported |
enhancement |
| 8 | Advanced-mode syntax highlighting: identifiers and type keywords share the teal colour | bug |
| 9 | [ok] explain <Table> is terse; reconsider the [ok]/SQL-echo duplication |
bug |
| 10 | Output panel: [error] tag colour identical to [system] (ADR-0037 gap) |
enhancement |
| 11 | Copy output panel contents to the system clipboard | enhancement |
| 12 | Long input hints overflow horizontally | bug |
| 13 | Undo confirmation dialog: too narrow + language polish | bug |
| 14 | --resume should restore the last-used input mode |
enhancement |
| 15 | Tab completion: offer common SQL function names in expression positions | enhancement |
| 16 | Restore typing-time column-typo hint for SQL expressions via known-function list | enhancement |
Closed this session: #2, #17, #18. Spawned + closed within the session: none new (#17/#18 were spawned in handoff-50 from #6's /runda pass).
Categorisation (carried from handoff-50, still current)
- Bounded code fixes: #8 (identifier vs type colour), #13 (undo dialog).
- Spec-heavy (discuss first): #9 (
[ok] explainredundancy), #10 ([error]tag colour — needs ADR-0037 amendment), #14 (--resumerestore mode — ties to ADR-0015 Iter 6). - Substantial scope: #7 (advanced
explain— feature gap; ADR-0039 design agreed, impl deferred), #11 (clipboard copy), #12 (long-hint overflow — design call: wrap vs cap vs resize). Note: #12 is adjacent to this session's hint work — the field-value hints it cites are exactly the arity / per-column prose touched in #2 and #17, which are now longer (the #17 messages name multiple columns). #12 may be more visible now; worth checking the hint-panel width handling when picked up. - Function-list cluster: #15 + #16 — do together (shared curated SQL-function-list infrastructure; both spawned from #6).
Other tracks (unchanged, from requirements.md)
- Track 2 project storage Iter 5/6 — export/import +
--resume+ persistent input history + migration framework. - C3a (modify relationship), C4 (m:n convenience).
- H1 friendly DB-error layer (partial); H1a syntax-help (this session added the #2 + #17 contributions).
- Tutorial / lesson system (needs its own ADR).
- V4 session log + Markdown export; I1/I1b multi-line + readline; I3 tab-completion polish; I4 syntax highlighting beyond input echo.
- ADR-0039 "explain over advanced SQL" — design agreed, impl deferred (this is issue #7).
§6. Process pins (carried forward, with this session's additions)
- NEW — reproduce-first. Per the user's §2.1 steer: the first step of every bug pickup is to confirm it still reproduces on current HEAD before designing a fix. Several handoff-50 reports were filed in one batch; earlier fixes may have changed or eliminated others.
- NEW — no easy-way-out on architectural discrepancies. Per §2.2: when implementation reveals a fix touches a past discrepancy (a workaround papering over a deeper gap), prefer the unifying fix that removes the discrepancy over a lighter patch that deepens it — the project is still in its original dev phase and a strong basis is worth the extra work. Escalate the trade-off; don't silently take the lighter path.
- Confirm every commit (propose message, wait). No AI
attribution. No issue numbers in commit messages (issue bodies
- close comments may reference freely).
- Test-first; green + clippy-clean is the only acceptable end state; current baseline 2051 / 0 / 1.
- Follow CLAUDE.md solo-mode phases explicitly.
/rundais the standard DA tool between/after phases on non-trivial work — this session ran it over the #2 fix and the #17 design (the design pass caught the DSL-Form-A-role mismatch and surfaced the message-wording fork before any code was written). The second-round DA discipline (the/rundare-prompt) caught real issues each time. - cargo-insta is NOT installed as a subcommand. Accept pending
snapshots by
mv -f <file>.snap.new <file>.snap(review the diff first — these are behaviour changes to verify, not rubber-stamp). - GitHub issue tracking (
ghCLI) is in use; migrating to Gitea (tea) later.
§7. How to take over
- Read, in order: this file →
requirements.mdfor open tracks →docs/adr/README.mdfor the ADR index → the relevant ADR for whatever you pick up. For insert/value-list work, ADR-0036 (esp. Amendment 2) and ADR-0033 §8.1 are now the authority on the arity-diagnostic model — both modes share it. - Baseline:
cargo test(2051 / 0 / 1) +cargo clippy --all-targets(clean). - For a bug fix: reproduce it first on current HEAD (process
pin above). The
panic!()probe-test technique (write a throwaway test that prints the actual behaviour, run, observe, delete) was the fast path all session — see how #2 and #17 were investigated. Then failing test → fix → green → /runda → commit-with-approval → close-with-summary. - Pick the next ticket from §5's categorisation. #8 or #13 are the bounded quick wins. #15 + #16 should be done together. If picking a hint-related ticket (#12), note this session lengthened the field-value hint strings.
- Architectural note for future insert work: the insert value-list
path now has a clean shared spine —
count_tuple_valuesandinsert_target_columnsingrammar::shared, consumed by both the DSL gate (data.rs::dsl_insert_value_list, simple-only) and the SQL gate (sql_insert.rs::tuple_value_list). The arity diagnostic (dml_insert_arity_diagnostics) is mode-aware. Keep that symmetry if you extend either side.