docs: session handoff 48 — DSL→SQL echo Phases 1-3 done, polish only

This commit is contained in:
claude@clouddev1
2026-05-28 10:32:31 +00:00
parent 5cb105b74b
commit df5c4e2a55
+256
View File
@@ -0,0 +1,256 @@
# Session handoff — 2026-05-28 (48)
Forty-eighth handover. **ADR-0038 is feature-complete except for the §4
styled-runs rendering polish.** Buckets A + B and the §6 category-3 prose
all ship, every catalogue row round-trips per line, docs are in sync,
and a /runda Devil's-Advocate pass landed cleanup. The session built
the renderer out from the handoff-46 walking skeleton through Phases
1, 2, and 3 in one go; if you continue, the polish is the only open
item. If you start a different feature, ADR-0038 won't surprise you.
## §1. State at handoff
**Branch:** `main`. **HEAD `5cb105b`.** **Tests: 2015 passing, 0 failing,
0 skipped, 1 ignored.** **Clippy: clean** (`--all-targets -D warnings`,
nursery).
Commits since handoff-47's `c60026c`:
```
5cb105b docs(adr): /runda DA cleanup — reflect Phases 1-3 done, pin Bucket C
e6ad1ae feat: DSL→SQL teaching echo — Phase 3 cat-3 caveat (ADR-0038)
275c726 feat: DSL→SQL teaching echo — Bucket B renderer (ADR-0038 Phase 2)
558cfae docs(adr): promote ADR-0037 + ADR-0038 to Accepted (Phase 1 shipped)
```
(Plus handoff-47 itself, `c60026c`, immediately prior.)
## §2. ADR status
| ADR | What | Build status |
|---|---|---|
| **ADR-0035 Am2** | standard-first dialect + `ALTER COLUMN` gap-fill | ✅ done (handoff-46 `338dc8a`) |
| **ADR-0033 Am4** | `update … --all-rows` falls back to DSL | ✅ done (handoff-46 `9a23e28`) |
| **ADR-0037** | `EffectiveMode` execution-time side-channel | ✅ **Accepted** — channel done (handoff-46 `04c8e42`), validated end-to-end by all four ADR-0038 commits |
| **ADR-0038** | the echo + full catalogue | ✅ **Accepted** — Phases 1-3 done (`90479cb` / `275c726` / `e6ad1ae`); **§4 styled-runs polish remains** |
| **ADR-0039** | EXPLAIN over advanced SQL | ⏸ deferred follow-up — separate feature |
ADR-0037 and ADR-0038 were promoted to **Accepted** with implementation
notes in `558cfae`; the /runda cleanup (`5cb105b`) refreshed both
entries to reflect every shipping commit.
## §3. What landed since handoff-47
### §3a. Phase 2 — Bucket B renderers (`275c726`, +1049 / 53 across 4 files)
Two new echo-build paths the handoff-46 ⚠️ gotcha foreshadowed:
- **`collect_echo_lookups`** (pre-execution, runtime) — resolves names
the dropped thing or not-yet-created column would erase post-
execution: positional `drop index` (describe the table, find the
index by exact column-set match), `drop relationship` in both
selector forms (`Endpoints` via child describe + matching by
endpoints; `Named` via a small `list_tables` scan acceptable for
teaching-playground schemas), and the `--create-fk` pre-state
(whether the child column already existed + the parent's PK type to
derive the new column type via `Type::fk_target_type``serial →
int`, `shortid → text`).
- **`build_schema_echo`** (post-execution, runtime) — subsumes the
Bucket A pure-`Command` schema cases (`create table`, `rename
column`, `add`/`drop constraint` — delegates to `command_to_sql`)
*and* renders Bucket B from the description (`add index` resolved
from `desc.indexes` by column set; `add relationship` from the
parent's `inbound_relationships` by endpoints) and the lookups.
Plus a **payload refactor**`Option<String>``Option<Vec<String>>`
on all 7 success events + arms + render path, motivated by the
category-2 multi-statement echoes (`drop column --cascade`, `add
relationship --create-fk`). Each catalogue row's pure renderer (and
its round-trip) lives in `src/echo.rs`; the runtime composes them.
The `DropColumn` arm gained `build_drop_column_cascade_echo`, which
reads `DropColumnResult::dropped_indexes` to emit the multi-line
cascade echo; non-cascade falls through to the pre-execution Bucket A
echo unchanged.
### §3b. Phase 3 — category-3 prose (`e6ad1ae`, +130 / 8 across 5 files)
Phase 3 turned out to be small: the existing `client_side.*` notes
already covered **shortid generation** (`auto_fill_add_shortid`,
`auto_fill_transition`) and **type-conversion transforms**
(`transformed`, `transformed_lossy`), and they already render in the
right position (between the echo and the structure). Only the **`change
column … --dont-convert` caveat** was actually missing — the one
Bucket A caveat (every other category-3 line is *illuminating*).
Wiring: a new `dont_convert_caveat: bool` on `DslChangeColumnSucceeded`,
set by the runtime when `submission_mode.is_advanced() && matches!(cmd,
ChangeColumnType { mode: DontConvert, .. })`, rendered by
`handle_dsl_change_column_success` between the client-side notes and
the structure. Gated on advanced mode because the caveat references
"the line above" — the echo, which only fires in advanced mode. New
i18n key `client_side.dont_convert_caveat` (registered in
`keys::KEYS_AND_PLACEHOLDERS`).
### §3c. /runda DA cleanup (`5cb105b`, +128 / 32 across 4 files)
Devil's-Advocate audit at the end of the session caught three doc-drift
bugs the per-phase commits left behind (requirements.md M4 and both
ADR-0038 Status block + README index entry still said "Phase 2 / Phase
3 remain") and one test gap — Bucket C cases (`show table`, `explain`,
`replay`, every `Command::App` variant) flowed through the catch-all
`_ => None` without explicit no-echo coverage. The new
`bucket_c_no_echo_commands_all_return_none` test pins all four; a
future renderer arm at the wrong place can no longer silently leak.
## §4. The architecture in shape now
Two echo construction paths now run in `spawn_dsl_dispatch`, side by
side:
- **Pre-execution** (`echo::echo_for``echo::command_to_sql`) — the
outer `echo` var, used by the **non-Schema** arms (`Update`,
`Delete`, `AddColumn`, `DropColumn` non-cascade, `ChangeColumn`).
Pure function of the `Command`. Returns `Option<Vec<String>>`
(single-element for the Bucket A single-line cases).
- **Post-execution** — three builders, each in the arm matching its
outcome:
- `build_schema_echo(command, mode, description, lookups)` for
`Schema` outcomes (subsumes Bucket A pure-Command schema +
Bucket B `add index` / `drop index` / `add`/`drop relationship`).
- `build_show_data_echo(database, command, mode)` for `Query`
outcomes (only `show data` echoes; the runtime describes the table
when the query is limited to source the `ORDER BY` PK).
- `build_drop_column_cascade_echo(command, mode, result)` for the
`DropColumn` arm when `cascade: true` (reads
`DropColumnResult::dropped_indexes`); non-cascade falls through to
the pre-execution echo.
The pre-execution lookup helper `collect_echo_lookups` runs once at the
top of the dispatch, captures whatever drops or `--create-fk` pre-state
will need post-execution, and passes through to `build_schema_echo`.
Replay still bypasses the whole spawn (`run_replay` calls
`execute_command_typed` directly) so no echo fires there — per
ADR-0038 §3.
Rendering: `note_ok_summary` loops over `pending_echo: Vec<String>`,
emitting one `Executing SQL: <line>` per entry via `note_system`.
Plain `[system]` lines today — the polish replaces this with
ADR-0028 styled runs.
## §5. The one open item — §4 styled-runs polish
ADR-0038 §4: "*The App renders it as one or more **de-emphasised**
`OutputLine`s beneath the `[ok]` summary, using the ADR-0028 styled-
runs mechanism (a dimmed `Executing SQL:` prefix; the SQL in a code-ish
run). One statement per line (§6 category 2).*"
Today the echo + the DontConvert caveat surface as plain `note_system`
lines (a single style). The polish needs:
- Read **ADR-0028** for the styled-runs `OutputLine` shape — there is
already a mechanism (used by the explain plan tree rendering — see
`output_render::render_explain_plan` for the established pattern of
pushing pre-built `OutputLine`s via `push_output`).
- Replace `note_system(crate::t!("echo.executing_sql", sql = line))` in
`note_ok_summary` with a styled `OutputLine`: dimmed `Executing
SQL:` prefix run + a code-ish run for the SQL itself. Decide the
exact style (italic? dim? distinct fg colour?) consistent with
ADR-0028's existing styled-runs surface; check both `theme::light()`
and `theme::dark()` for legibility.
- The DontConvert caveat is also a category-3 prose line — apply the
same de-emphasised treatment (or a complementary one — caveat could
warrant a `WARNING`/yellow accent, since it flags non-equivalence).
- Multi-statement echoes: each line in the Vec becomes its own styled
`OutputLine`. Today the rendering already emits one line per entry;
the polish is per-line styling, not per-line splitting.
Tests will need adjustment: the App-level tests (`bucket_a_success_*`,
`bucket_b_multi_line_*`, `change_column_dont_convert_renders_*`)
currently assert `texts[…].contains("Executing SQL: …")` from
`note_system` output. After the polish, the same content will live in
styled-runs — the tests should still find the substring within the
output line text but may need to look at `OutputLine` styled-run
content rather than the flat `text` field. Decide style-vs-text
assertion shape early.
The work is rendering-only — no Command-level changes, no event-shape
changes, no worker changes. Estimated scope: small. The biggest care
point is consistency with ADR-0028's existing styled-runs usage.
## §6. Catalogue cheat-sheet (authoritative: ADR-0038 §7)
Unchanged since handoff-47. **Every Bucket A and Bucket B row is now
implemented and round-tripped** (`echo::tests::*` + the runtime
end-to-end tests `bucket_b_resolved_name_echoes_against_real_worker` +
`bucket_b_multi_statement_echoes_against_real_worker`). Bucket C
(no-echo) is explicitly pinned by
`bucket_c_no_echo_commands_all_return_none`. Category-3 lines flow
through the existing `client_side.*` infrastructure plus the new
`dont_convert_caveat`.
## §7. Known follow-ups outside the ADR-0038 scope
These are **not** the styled-runs polish — they are separate items
the /runda surfaced or that emerged during the session, captured here
so they aren't lost.
- **Unquoted-identifier round-trip caveat.** The echo style (`render_*`
in `src/echo.rs`) emits bare identifiers — matching the existing
`render_create_table` aesthetic. A user-created column or table
named after a SQL keyword (e.g. `ORDER`, `SELECT`, `WHERE`) would
produce an echo whose round-trip is fragile. Pre-existing limitation
(the create-table echo had it from handoff-46); the §1 contract is
only formally violated for this edge case. If it ever bites a
learner, the fix is to add identifier quoting (probably backticks or
double-quotes per the advanced grammar's accepted spelling, plus a
conservative "needs quoting" predicate).
- **No full-spawn integration test for the echo pipeline.** Coverage
today is layered: pure renderers + runtime-helper unit tests (with
real `Database`) + App-level rendering tests. The unwired glue is
small (~3 lines per arm in `spawn_dsl_dispatch`). A Tier-3 test
that drives the full submit → runtime → App pipeline would complete
the picture; not blocking.
- **Pure-Command Schema commands double-compute echo.** `echo_for` is
called for every command, and the Schema arm then computes
`build_schema_echo` (whose catch-all redelegates to
`command_to_sql`). Minor inefficiency, no behaviour bug. Could be
optimized later; not worth the code complexity now.
- **ADR-0039 (EXPLAIN over advanced SQL)** — separate deferred
follow-up, not part of ADR-0038. Recorded 2026-05-27 (handoff-45/46);
pick up when prioritised.
## §8. Process pins (unchanged)
- **Confirm every commit** (propose message, wait). **No AI attribution.**
- **Test-first**; green + clippy-clean is the only acceptable end
state; current baseline **2015 / 0 / 1**.
- **Keep docs lockstep**: ADR + `README.md` index + `requirements.md`.
This session's `5cb105b` was the corrective pass when the prior
per-phase commits left M4 / ADR-0038 status drifted.
- **Round-trip every catalogue row** (the §1 copy-paste contract; for
multi-statement, *per line*).
- **Pre-execution lookups** for any future Bucket-B-style additions
whose echo needs info the dropped/created thing erases. Mirror the
`collect_echo_lookups` shape (best-effort, fail-soft) and consume
in the post-execution arm.
## §9. How to take over
1. **Read, in order:** this file → **ADR-0038** (§4 styled-runs
reference is the relevant bit for the polish) → **ADR-0028**
(`OutputLine` styled-runs mechanism; `output_render::render_*` for
established patterns) → **`src/echo.rs`** (the renderer surface
itself is settled — but read to understand the catalogue shape if
you'll touch rendering) → **`src/app.rs`**'s `note_ok_summary` and
`handle_dsl_change_column_success` (the two consumers of the echo
+ caveat strings — these are what the polish replaces).
2. **Baseline:** `cargo test` (2015 / 0 / 1) + `cargo clippy
--all-targets -- -D warnings` (clean).
3. **If continuing on ADR-0038**: the polish is the only open item.
See §5. Decide the App-test assertion shape early (style-vs-text).
4. **If picking up something else**: ADR-0038 is done modulo the
polish; the §4 line of the ADR remains the formal outstanding
item but does not block other work. ADR-0039 (EXPLAIN over
advanced SQL) is a self-contained deferred follow-up if you want
the next feature.