docs: session handoff 46 — DSL→SQL echo mid-feature (skeleton done, renderer pending)
This commit is contained in:
@@ -0,0 +1,187 @@
|
|||||||
|
# Session handoff — 2026-05-27 (46)
|
||||||
|
|
||||||
|
Forty-sixth handover. **We are mid-feature** — the DSL → SQL teaching
|
||||||
|
echo (ADR-0038) is partially built (a working walking skeleton). Read
|
||||||
|
this carefully before continuing; §4 and §5 are the load-bearing parts.
|
||||||
|
|
||||||
|
## §1. State at handoff
|
||||||
|
|
||||||
|
**Branch:** `main`. **HEAD `04c8e42`.** **Tests: 1970 passing, 0 failing,
|
||||||
|
0 skipped, 1 ignored.** **Clippy: clean** (`--all-targets -D warnings`,
|
||||||
|
nursery).
|
||||||
|
|
||||||
|
This session designed the **DSL → SQL teaching echo** (ADR-0030 §10) as a
|
||||||
|
`/runda`'d design set and began building it. Commits since handoff-45's
|
||||||
|
`2bcc55f`:
|
||||||
|
|
||||||
|
```
|
||||||
|
04c8e42 feat: DSL→SQL teaching echo — channel + create-table slice (ADR-0037 + ADR-0038)
|
||||||
|
9a23e28 fix: update … --all-rows falls back to the DSL instead of misparsing (ADR-0033 Am4)
|
||||||
|
338dc8a feat: advanced ALTER COLUMN SET/DROP NOT NULL & DEFAULT, SET DATA TYPE (ADR-0035 Am2)
|
||||||
|
9f15f38 docs(adr): design the DSL→SQL teaching echo (ADR-0038) + dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
## §2. The feature & its build plan
|
||||||
|
|
||||||
|
**Goal:** when a **DSL-form** command runs **in advanced mode**, echo the
|
||||||
|
equivalent SQL beneath `[ok]`, so a learner reads off the SQL spelling
|
||||||
|
(ADR-0030 §10). Authoritative spec: **ADR-0038** (read it — the catalogue
|
||||||
|
in §7, the copy-paste contract §1, the three-category framework §6).
|
||||||
|
|
||||||
|
Five ADRs, all **Accepted/Proposed and `/runda`'d** (the design `/runda`
|
||||||
|
verdict was PASS with fixes applied — see the ADRs):
|
||||||
|
|
||||||
|
| ADR | What | Build status |
|
||||||
|
|---|---|---|
|
||||||
|
| **ADR-0035 Am2** | standard-first dialect + `ALTER COLUMN SET/DROP NOT NULL`, `SET/DROP DEFAULT`, ISO `SET DATA TYPE` | ✅ **done** (`338dc8a`) |
|
||||||
|
| **ADR-0033 Am4** | `update … --all-rows` falls back to DSL (was a misparse) | ✅ **done** (`9a23e28`) |
|
||||||
|
| **ADR-0037** | `EffectiveMode` execution-time side-channel (the echo's gate) | ✅ channel **done** (`04c8e42`) |
|
||||||
|
| **ADR-0038** | the echo + full catalogue | 🚧 **skeleton only** — `create table` echoes; rest of the catalogue pending |
|
||||||
|
| **ADR-0039** | EXPLAIN over advanced SQL | ⏸ **deferred follow-up** — decision recorded, NOT this feature |
|
||||||
|
|
||||||
|
Build order was Am2 → Am4 → (0037+0038 merged). **0037 and 0038 were
|
||||||
|
merged into one build step** because the channel is dead code without its
|
||||||
|
consumer (`-D warnings` rejects it) — see ADR-0037 Implementation notes.
|
||||||
|
|
||||||
|
## §3. The doc-drift fix (already committed, FYI)
|
||||||
|
|
||||||
|
`9f15f38` also reconciled stale notes: **simple-mode column ops (B2/C2)
|
||||||
|
are implemented**, not pending. `requirements.md` C2/B2 were already
|
||||||
|
`[x]`; the project `CLAUDE.md` was corrected. (Don't re-flag this.)
|
||||||
|
|
||||||
|
## §4. The echo architecture — how it works NOW (read before editing)
|
||||||
|
|
||||||
|
Data flow for the **interactive** path (replay is separate — see below):
|
||||||
|
|
||||||
|
1. **`app.rs::submit`** computes the three-way **`EffectiveMode`**
|
||||||
|
(`Simple` / `AdvancedPersistent` / `AdvancedOneShot`) — *reusing the
|
||||||
|
pre-existing `EffectiveMode` enum*, NOT a new `SubmissionMode` (ADR-0037
|
||||||
|
was corrected to reuse it). Passes it to `dispatch_dsl`.
|
||||||
|
2. **`app.rs::dispatch_dsl(input, submission_mode: EffectiveMode)`** parses
|
||||||
|
with `submission_mode.as_mode()` (the two-way `Mode` the walker needs),
|
||||||
|
and emits **`Action::ExecuteDsl { command, source, submission_mode }`**
|
||||||
|
(new field, `action.rs`).
|
||||||
|
3. **`runtime.rs` Action::ExecuteDsl handler** → `spawn_dsl_dispatch(…,
|
||||||
|
submission_mode)`.
|
||||||
|
4. **`runtime.rs::spawn_dsl_dispatch`**: computes
|
||||||
|
`echo = crate::echo::echo_for(&command, submission_mode)` →
|
||||||
|
`Option<String>`, executes the command, and attaches `echo` to the
|
||||||
|
**`AppEvent::DslSucceeded { …, echo }`** (new field, `event.rs`).
|
||||||
|
5. **`app.rs` update() DslSucceeded arm** stashes `self.pending_echo = echo`
|
||||||
|
just before calling `handle_dsl_success`.
|
||||||
|
6. **`app.rs::note_ok_summary`** (the shared `[ok]` pusher, called by every
|
||||||
|
success handler) pushes `[ok] …` then, if `pending_echo` is `Some`,
|
||||||
|
pushes the echo line `Executing SQL: <sql>` (i18n key
|
||||||
|
`echo.executing_sql`). The stash is set+consumed within one synchronous
|
||||||
|
`update()`, so it's safe (no interleaving).
|
||||||
|
|
||||||
|
**The renderer:** `src/echo.rs`. `echo_for(command, mode)` gates (advanced
|
||||||
|
effective mode + DSL-form) and calls `command_to_sql(command) ->
|
||||||
|
Option<String>`. **`command_to_sql` currently handles only
|
||||||
|
`Command::CreateTable`** → `CREATE TABLE T (id serial PRIMARY KEY)`
|
||||||
|
(single inline / compound table-level PK, playground type vocabulary).
|
||||||
|
Everything else returns `None` (no echo yet).
|
||||||
|
|
||||||
|
**Replay is silent for free:** `run_replay` calls `execute_command_typed`
|
||||||
|
**directly**, bypassing `spawn_dsl_dispatch`, so replayed lines never
|
||||||
|
reach the echo path. No `interactive` flag needed.
|
||||||
|
|
||||||
|
**Where the echo is built (ADR correction):** NOT in the db.rs worker —
|
||||||
|
the worker gets *decomposed* calls, not the `Command`. The runtime's
|
||||||
|
dispatch is where `Command` + mode + result converge. ADR-0037 §3 /
|
||||||
|
ADR-0038 §4 were corrected to say so.
|
||||||
|
|
||||||
|
## §5. What's next — expanding the renderer (the actual work)
|
||||||
|
|
||||||
|
Per ADR-0038 §8 phasing. **The catalogue (ADR-0038 §7) is the spec** —
|
||||||
|
each row is a round-trip test (parse the echo in advanced mode → a
|
||||||
|
same-effect command; the §1 contract). Order:
|
||||||
|
|
||||||
|
### Phase 1 — rest of Bucket A (single-statement)
|
||||||
|
Expand `command_to_sql` for: `add column`, `drop column` (non-cascade),
|
||||||
|
`rename column`, `change column` (→ `ALTER COLUMN c SET DATA TYPE ty` —
|
||||||
|
**now runnable, Am2 shipped**), `add constraint` (NotNull→`SET NOT NULL`,
|
||||||
|
Default→`SET DEFAULT v`, Unique→`ADD UNIQUE (c)`, Check→`ADD CHECK (e)`),
|
||||||
|
`drop constraint` (NotNull/Default only; Unique/Check column-level →
|
||||||
|
Bucket C), `show data` (→ `SELECT * FROM T [WHERE …] [ORDER BY <pk> LIMIT
|
||||||
|
n]`), and the `delete … --all-rows` / `update … --all-rows` fall-throughs
|
||||||
|
(**now fall back, Am4 shipped**).
|
||||||
|
|
||||||
|
For EACH new command, also:
|
||||||
|
- add an `echo: Option<String>` field to its success event
|
||||||
|
(`DslAddColumnSucceeded`, `DslDropColumnSucceeded`,
|
||||||
|
`DslChangeColumnSucceeded`, `DslDataSucceeded`, `DslUpdateSucceeded`,
|
||||||
|
`DslDeleteSucceeded`),
|
||||||
|
- set it from `echo` in `spawn_dsl_dispatch`'s matching arm,
|
||||||
|
- stash `self.pending_echo = echo` in the App's matching update() arm.
|
||||||
|
(`note_ok_summary` already renders it for all.)
|
||||||
|
|
||||||
|
**Needs Expr→SQL + Value→SQL-literal renderers** (ADR-0038 §5 literal
|
||||||
|
table): for `show data` WHERE filters, `add constraint check`/`default`
|
||||||
|
values, etc. `blob` has no literal (moot — no blob literal syntax). Auto-
|
||||||
|
gen columns omitted from INSERT echoes.
|
||||||
|
|
||||||
|
### ⚠️ CRITICAL gotcha for Bucket B + category-3
|
||||||
|
The skeleton computes `echo` **BEFORE** `execute_command_typed` (it only
|
||||||
|
needs the `Command`). **Bucket B (resolved auto-names) and category-3
|
||||||
|
(generated shortids, conversion counts) need the *execution result*.** So
|
||||||
|
when you implement those, **move/augment the echo build to AFTER
|
||||||
|
execution**, reading the `CommandOutcome`/result (the resolved index name,
|
||||||
|
the `client_side.*` notes — ADR-0017 §6 / ADR-0018 §9). The runtime has
|
||||||
|
both (it builds the event from command + outcome). This is the one place
|
||||||
|
the current skeleton structure must change.
|
||||||
|
|
||||||
|
### Phase 2 — Bucket B
|
||||||
|
Resolved names (`drop index on T(cols)` → `DROP INDEX <resolved>`, drop/
|
||||||
|
add relationship, auto-named `add index`) and **multi-line** echoes (one
|
||||||
|
statement per line: `drop column --cascade`, `add relationship
|
||||||
|
--create-fk`). ADR-0038 §6 category 2. The echo payload likely becomes
|
||||||
|
`Vec<String>` (one per statement) — generalise `Option<String>`.
|
||||||
|
|
||||||
|
### Phase 3 — category-3 prose expansion
|
||||||
|
shortid generation, type-conversion transforms, and the `change column
|
||||||
|
--dont-convert` **caveat** (ADR-0038 §6). De-emphasised prose from the
|
||||||
|
worker's `client_side.*` notes.
|
||||||
|
|
||||||
|
### Polish (not yet done)
|
||||||
|
The echo line is currently a **plain `[system]` line** via `note_system`.
|
||||||
|
ADR-0038 §4 wants it **de-emphasised** (styled-runs, ADR-0028). Refine the
|
||||||
|
rendering (an `OutputLine` with `styled_runs`, dimmed `Executing SQL:`
|
||||||
|
prefix) — a TODO, deferred so the skeleton stayed small.
|
||||||
|
|
||||||
|
## §6. Catalogue cheat-sheet (authoritative: ADR-0038 §7)
|
||||||
|
|
||||||
|
- **Echoes** (DSL-only spellings in advanced mode): all DDL forms +
|
||||||
|
`show data` + `delete/update … --all-rows`. DSL column-op syntax is
|
||||||
|
`<verb> column in/from/to T: c (type)` (**colon + parens**); index/rel
|
||||||
|
naming is `as N` (not `named`).
|
||||||
|
- **NOT echoed**: `insert`/`update … where`/`delete … where` (SQL-first →
|
||||||
|
`Sql*` = already SQL), `drop table`, `drop index <name>` (→ `Sql*`),
|
||||||
|
`show table`, `explain`, `replay`, app commands, column-level
|
||||||
|
UNIQUE/CHECK *drop* (residual gap, ADR-0035 Am2). `change column
|
||||||
|
--dont-convert` → echoes the headline + a category-3 **caveat**.
|
||||||
|
|
||||||
|
## §7. Process pins (unchanged)
|
||||||
|
|
||||||
|
- **Confirm every commit** (propose message, wait). **No AI attribution.**
|
||||||
|
- **Test-first**; green + clippy-clean is the only acceptable end state;
|
||||||
|
baseline **1970 / 0 / 1**.
|
||||||
|
- **Keep docs lockstep**: ADR + `README.md` index + `requirements.md`.
|
||||||
|
Amend ADRs, don't re-litigate. **Raise implementation deviations and
|
||||||
|
update the ADR** (this session corrected ADR-0035 Am2's SET DEFAULT
|
||||||
|
executor, ADR-0037's enum + build-layer, ADR-0038 §4 in-place).
|
||||||
|
- **Round-trip every catalogue row** (the §1 copy-paste contract).
|
||||||
|
|
||||||
|
## §8. How to take over
|
||||||
|
|
||||||
|
1. **Read, in order:** this file → **ADR-0038** (§1 contract, §6
|
||||||
|
categories, §7 catalogue, §8 phasing) → **ADR-0037** (channel +
|
||||||
|
Implementation notes) → the four commits above → **`src/echo.rs`** (the
|
||||||
|
renderer to expand).
|
||||||
|
2. **Baseline:** `cargo test` (1970 / 0 / 1) + `cargo clippy --all-targets
|
||||||
|
-- -D warnings` (clean).
|
||||||
|
3. **Continue** with §5 Phase 1 (rest of Bucket A), test-first, one
|
||||||
|
command at a time, round-tripping each echo. Heed the §5 ⚠️ gotcha
|
||||||
|
before touching Bucket B / category-3.
|
||||||
|
4. ADR-0039 (EXPLAIN over advanced SQL) is a **separate deferred
|
||||||
|
follow-up**, not this feature.
|
||||||
Reference in New Issue
Block a user