ADR-0028 (query plans / `explain`) is fully implemented; the handoff-16 design trio (ADR-0026 / 0027 / 0028) is now closed. - handoff-21: session summary, the two deliberate deviations from handoff-20's plan, test coverage, open clusters. - requirements.md: QA1 / QA2 ticked. - CLAUDE.md: the `EXPLAIN QUERY PLAN` deferred-items line updated to "implemented per ADR-0028".
7.0 KiB
Session handoff — 2026-05-19 (21)
Twenty-first handover. This session finished ADR-0028 —
query plans / explain. Steps 2–5 (planned in full in
handoff-20 §3) are implemented, tested and committed; step 1
landed in the previous session. ADR-0028 is complete, and
with it the handoff-16 design trio (ADR-0026 / ADR-0027 /
ADR-0028) is fully implemented.
State at handoff
Branch: main. Working tree clean. 3 feature commits
this session (plus this handoff); all local — push
asynchronously, not blocking.
<this file> docs: handoff 21 — ADR-0028 complete
ae99276 explain: typing-surface matrix cells (ADR-0028 step 5)
a7d459f explain: styled plan tree + annotation taxonomy (step 4)
d17addd explain: `explain` command end to end (steps 2–3)
c1fcf28 docs: handoff 20 — ADR-0028 step 1 done, 2-5 planned
03d8a09 ui: styled-output-line mechanism (ADR-0028 step 1)
Tests: 1172 passing, 0 failing, 1 ignored (cargo test). The ignored test is the long-standing ```ignore
doc-test in src/friendly/mod.rs. Typing-surface matrix:
174 cells (was 161 — +13 for explain).
Clippy: clean (cargo clippy --all-targets -- -D warnings, nursery group).
§1. What ADR-0028 delivered
The explain prefix command — explain show data …,
explain update …, explain delete from … — captures a
query's plan via EXPLAIN QUERY PLAN and renders it as an
annotated, span-styled tree. EXPLAIN QUERY PLAN never
executes the wrapped statement, so explaining a destructive
update / delete is safe and changes nothing.
- Grammar (
src/dsl/grammar/data.rs): a newEXPLAINCommandNodewhose shape is aChoiceover the three explainable query shapes, reached throughSubgrammar— the inner command is parsed, completed and hinted exactly as it is standalone.Command::Explain { query: Box<Self> }. - Worker (
src/db.rs): SQL construction split out ofdo_query_data/do_update/do_deleteintobuild_query_data_sql/build_update_sql/build_delete_sql, soEXPLAIN QUERY PLANruns the exact same statement.Request::ExplainPlan/do_explain_plancapture the plan;QueryPlan/ExplainRowcarry it back. - Display SQL: the executed statement with
?Nparams inlined as standard-SQL literals (<>for inequality, double-quoted idents, the implicitORDER BY <pk>thatlimitadds). - Render (
src/output_render.rs):render_explain_plandraws the box-drawing tree;PLAN_TAXONOMYclassifies each node'sdetailand only the category-bearing keyword run carries a semantic colour (efficient / expensive / automatic-index). An automatic-index node also gets the← add an index?advice tag. - Catalog:
parse.usage.explainplushelp.data.explain—explainappears in the in-apphelplisting.
§2. Decisions / deviations from handoff-20's plan
handoff-20 §3 was followed closely. The three gotchas it
flagged all held — static wrappers (EXPLAIN_SHOW_DATA
etc.) for the Subgrammar &'static requirement; the
role-based build_show_data extraction; steps 2+3 combined
into one commit because the Command::Explain variant breaks
every exhaustive match Command. Two deliberate deviations:
- Display SQL via param-inlining, not a parallel
compile_operand. handoff-20 suggested a second literal-rendering variant of the WHERE compiler. Instead,inline_params_for_display(src/db.rs) takes the executed SQL and substitutes each?Nwith its bound literal in a single quote-aware scan. This is structurally guaranteed to match the executed statement and avoids a second expression compiler that could drift from the first. (<>, double-quoted idents and the implicitORDER BYall come for free — they are already in the executed SQL.) help_id: Some("data.explain"), notNone. handoff-20 planned no help entry; at the user's requestexplainnow has ahelp.data.explaincatalog entry and appears inhelplike every other command.
Other notes:
render_explain_planreturnsVec<OutputLine>(styled). Commit A built the tree with plain lines; commit B enriched the same function with the taxonomy + styled runs — no duplicated tree-walking logic.explainis not written tohistory.log(do_explain_plantakes nosource) — it is a read-only diagnostic, andEXPLAIN QUERY PLANdoes not execute.- The plan tree renders wholly in the neutral foreground
colour (not the
Systemgreen) so connectors / names stay neutral per ADR-0028 §6; only category keywords are coloured. Every plan line therefore carries astyled_runspayload, including the display-SQL line.
§3. Test coverage added
- Parse (
src/dsl/grammar/data.rsexplain_tests): each wrapped form parses toCommand::Explain;explainof an incomplete inner command is the same parse error;explain show tableis rejected (explaincoversshow dataonly). - Worker (
src/db.rs): scan vs. index-search plans;explain delete/explain updateleave data untouched; display SQL inlines literals, quotes idents, shows the implicitORDER BY, writes<>; missing-table errors. - Render (
src/output_render.rs): the taxonomy classes, the automatic-index tag, neutral fallback, a cyclic-parent guard. - App (
src/app.rs): theDslExplainSucceededhandler renders the[ok]header, display SQL and tree. - Typing-surface matrix: 13 cells in
tests/typing_surface/explain.rs.
§4. What's next
ADR-0028 closes the handoff-16 design trio. No feature is in flight. Open clusters, unchanged from handoff-16/17/18/19/20 (prioritisation is a user decision — ask):
- Snapshot / undo / replay
U-series (designed in ADR-0006). - Constraints
C3; m:n convenienceC4; modify-relationshipC3a; column drop/rename/type-change grammarC1(the rebuild primitive exists). - Friendly-error layer
H1(partial); strong syntax-help in parse errorsH1a. - Session-log / Markdown export
V4— would be the first real reuse of theOutputLinestyled-runs mechanism beyond the plan renderer. SD1; CI workflowTT5; readline shortcutsI1b; multi-line inputI1;TU1.
§5. How to take over
- Read this file, then
CLAUDE.md(working-style rules), thendocs/requirements.md(per-item progress —QA1/QA2now ticked). - Run
cargo test— 1172 passing, 0 failing, 1 ignored. - Run
cargo clippy --all-targets -- -D warnings— clean. - Pick the next cluster with the user — §4 has no default.
Note on the typing-surface matrix
tests/typing_surface/ is 174 cells. The matrix-snapshot
discipline from handoff-17/18/19/20 stands: a failing cell
with correct new behaviour → update its snapshot; with
wrong behaviour → the cell earned its keep. cargo insta
is not installed on this machine — regenerate snapshots with
INSTA_UPDATE=always cargo test --test typing_surface_matrix <filter> and review the written .snap files before
committing.