9.0 KiB
Session handoff — 2026-06-06 (58)
Fifty-eighth handover. Continues from handoff-57, whose next job
was H1a. This session did exactly that, end to end: H1a
(strong syntax-help in parse errors) is now done and marked [x]
in requirements.md, via a new ADR-0042 systematic pass. The arc
was audit → ADR → test-first matrix → four gap fixes → two
adversarial review passes that each caught a real defect. See §2.
§1. State at handoff
Branch: main. HEAD c305dc7. origin/main at 10f8c2a
(pushed mid-session) → 4 commits unpushed (649fdcb, 1d4923b,
d6e229f, c305dc7). Push is the user's step.
Tests: 2158 passing / 0 failing / 1 ignored (lib 1578, it 388,
typing_surface_matrix 192; the 1 ignored is the pre-existing one).
Clippy clean (nursery, all targets, -D warnings).
This session's commits (since handoff-57's a8d0138):
c305dc7 docs: mark H1a done via the ADR-0042 systematic pass
d6e229f feat: H1a CROSS JOIN ON teaching message; advanced-SQL gaps re-verified (ADR-0042)
1d4923b fix: H1a G3 advanced usage shows all valid forms; complete near-miss matrix (ADR-0042)
649fdcb feat: H1a parse-error gaps G2–G4 + advanced near-miss matrix (ADR-0042)
10f8c2a test: H1a near-miss matrix + friendlier `add 1:n relationship` label (ADR-0042)
0e6f767 docs: ADR-0042 — continue H1a parse-error pedagogy on the grammar tree
§2. What happened this stretch — H1a, start to finish
Audit first (the big correction)
The handoff-57 §6 pointer said "read ADR-0021 + ADR-0020". Both
are obsolete. They specify a chumsky-over-tokens mechanism
(UsageEntry registry, parse.token.* keys, a lexer) that
ADR-0024 deleted — chumsky is not a dependency; the parser is
the scannerless unified grammar-tree walker. ADR-0020/0021 are now
marked Superseded (status notes + README), kept as memory.
ADR-0021's intent survived and was already ~80% shipped via the
grammar tree. Read docs/adr/0042-h1a-parse-error-pedagogy-grammar-tree.md,
not 0020/0021, for the live H1a design.
ADR-0042 + the user's three scope decisions
Wrote ADR-0042 (continues H1a against the grammar tree). Three forks escalated and decided by the user: (1) ADR hygiene = superseded-notes + new ADR; (2) scope = matrix-verify + friendlier literal labels; (3) advanced-SQL in scope.
The near-miss matrix (the definition of done)
tests/it/parse_error_pedagogy.rs now holds a per-command
near-miss matrix, built test-first from an empirical baseline
capture. Covers, in both modes: every entry word's bare /
missing-clause / wrong-token cases, the app-lifecycle trailing-junk
cases, and the committed multi-forms (add index,
add constraint, add 1:n relationship, drop index/constraint/ relationship, show table, change column, create index,
alter table add/drop). Tests: near_miss_matrix_simple_mode,
near_miss_matrix_advanced_mode, near_miss_matrix_committed_multiforms,
plus per-gap tests.
Four gap fixes (each test-first)
- G1 — bare
1cardinality literal →`1:n relationship`informat_expectation(render-only; completion still offers1). - G2 — bare
select's 14-item expression first-set → "a projection:*, a column, or an expression", detected by thedistinct+allquantifier pair (unique to a projection start; empirically verified non-misfiring). Render-only informat_walker_error. - G3 — usage block was mode-blind. Added
usage_*_in_mode(src/dsl/grammar/mod.rs) + mode threading throughrender_usage_block(app.rs) and the ambient usage (input_render.rs). Decision (user, after review): advanced mode shows all forms valid in the mode, SQL-first then the DSL fallback forms — DSL forms (create table … with pk,drop column …) remain valid input in advanced mode (verified), so a usage hint must not hide them. Simple mode = DSL only. - G4 —
withgot its ownparse.usage.withCTE template (was borrowingselect's). - CROSS JOIN ON —
parse.cross_join_no_onteaches "a CROSS JOIN has no ON clause …" when the failing token isonand the most recent consumed join is a CROSS join.is_cross_join_oninparser.rs; render-only.
Two adversarial review passes earned their keep
- Pass 1 caught G3 over-correction: an initial "SQL-only"
advanced usage block hid valid DSL fallback forms. Escalated →
user chose "show all valid forms" → fixed (
1d4923b). - "Verify, don't trust the survey" reversed two of three advanced-SQL "gaps": INSERT…SELECT count and RETURNING scope were already handled (the Explore-survey list was wrong, twice). Only CROSS JOIN ON was real.
- The matrix itself caught a regression mid-work (advanced
insert/update/delete falling back to available-commands because
the SQL nodes have empty
usage_ids; fixed with a union fallback).
Deferred by decision (low-priority residual)
At submit time, a non-projection expression position (bare
where , returning , having , set col=) still renders the raw
~14-item expression first-set; only the SELECT projection is glossed
(G2). Low-impact because typing-time completion already offers the
right candidates there. User chose to leave it. Documented in
ADR-0042 + requirements.md.
Plus one pre-existing caveat (not this session's work, noted in
ADR-0042): insert into T (one_col) select * from Multi isn't
pre-caught for arity — SELECT * isn't expanded; the engine rejects
it at execution (brushes ADR-0019 §OOS-2 engine-error territory).
§3. ⚠️ Where parse-error pedagogy lives now (read before touching)
- Usage templates:
parse.usage.*insrc/friendly/strings/en-US.yaml;usage_idson eachCommandNode(src/dsl/grammar/mod.rs). Mode-aware selection:usage_keys_for_input_in_mode/usage_key_for_input_in_mode. - Structural error wording:
format_walker_error+format_expectationinsrc/dsl/parser.rs(this is where the G1 label, G2 projection gloss, and CROSS JOIN message live — render-only, they do not mutate theExpectationset the completion/hint layer consumes). - Catalog discipline (ADR-0019): every new key goes in
en-US.yamlandsrc/friendly/keys.rs::KEYS_AND_PLACEHOLDERS(thekeys_validate_against_catalogtest enforces it). - Tests: integration parse-error tests live in
tests/it/per the handoff-57 §3 rule — drop the file intests/it/+ add amodline totests/it/main.rs. Schema-aware diagnostics are tested at the walker level with aSchemaCache(vschemahelper intests/it/sql_insert.rs).
§4. Carried / unchanged
- arboard decisions (handoff-55 §3): X11-only on Linux;
copyreproduces[system]tags. One-line changes if revisited. - No open GitHub issues (
gh issue listempty; the project's issue tracker is GitHub, not the gitea hostteais configured for).
§5. Other tracks (from requirements.md)
Unchanged: Track 2 Iter 6 leftovers (history.log input-history
hydration polish, migration-framework exercise); C3a modify
relationship; C4 m:n convenience; H1 done (ADR-0019), H1a
done (ADR-0042); H2 hint; H3 help (partial — general
reference + help <command> still missing); V4 session-log /
Markdown export; I1/I1b multi-line + readline; I3 tab completion /
I4 syntax highlighting (the walker already exposes the hooks); TU1
tutorial (needs ADR); TT5 CI (not configured).
§6. Next job — pick one
No single forced next step. Candidates, roughly by readiness:
- H3
helpcompletion — the grammar tree already iterates the REGISTRY for the command list; the missing pieces are a general reference andhelp <command>per-command detail. Thehelp_idperCommandNodeis the hook. Small-to-medium. - I3 tab completion — the walker's expected-set + completion candidates already exist (used by ambient hints); I3 is the UI/UX (cursor handling, menu, accept). Needs its own ADR.
- The deferred H1a residual (§2) — generalise the projection gloss to other expression positions. Low value (completion already covers typing-time); only if it bugs you.
- CI (TT5) — test infra is solid (2158 green); no workflow yet.
§7. How to take over
- Read handoffs 56 → 57 → 58, then
CLAUDE.md, thendocs/requirements.md(H1 and H1a now[x]),docs/adr/README.md. - For anything parse-error/pedagogy: read ADR-0042, not ADR-0020/0021 (those are superseded; chumsky is gone).
- Codebase on
mainatc305dc7, clean, 4 unpushed. - Process pins that paid off this arc, again: audit before assuming (ADR-0021 was obsolete; H1a was mostly already shipped), verify empirically — don't trust the docs/survey (the advanced-SQL gap list was wrong twice; a regression hid in "looks fine"), escalate genuine forks (the G3 all-forms decision was the user's, not mine), and test-first + matrix as a regression lock (it caught a regression I introduced). Commits user-confirmed, append-only, no AI attribution.