# Session handoff — 2026-05-19 (18) Eighteenth handover. A long **implementation run** that built two ADRs end to end: - **ADR-0026 (complex WHERE expressions)** — steps 1–4 + 6; see handoff-17 for its detail. - **ADR-0027 (input-field validity indicator)** — all six build-order steps, **with ADR-0026's deferred step 5 (the §7 expression flagging) folded in** as the WARNING severity's first triggers, exactly as the user directed. **Headline: the validity indicator works.** A debounced `[ERR]` / `[WRN]` marker at the input row's right edge tells a learner — before they press Enter — whether the command would run. `S6` and `C5a` are both satisfied; nothing from ADRs 0026/0027 is left unimplemented. ## State at handoff **Branch:** `main`. Working tree clean. **12 commits** since handoff-16 (`ac41938`), all local — push asynchronously, not blocking. handoff-17 (`dfd3c51`) is mid-chain; it documents the ADR-0026 landing and is superseded by this file. ``` 151ed08 hint: show the matching usage template for multi-form commands a326849 ADR-0027: existing-cases sweep + docs (step F) 9e10997 runtime: debounce the validity indicator (ADR-0027 step E) 1a9d950 ui: validity indicator rendering + warning theme colour (ADR-0027 step D) 73c7470 walker: expression WARNING diagnostics (ADR-0027 step C, folds ADR-0026 §7) 827b47f walker: schema-existence ERROR diagnostics (ADR-0027 step B) e22f933 walker: diagnostics-severity model + input_verdict (ADR-0027 step A) dfd3c51 chore: handoff 17 — ADR-0026 complex WHERE expressions implemented a50c6cd WHERE expressions: matrix cells + predicate_tail grammar fix (ADR-0026 step 6) f75f71b WHERE expressions: wire into update/delete/show data + SQL gen (ADR-0026 steps 3-4) 59e6a54 grammar: WHERE-expression fragment + Expr AST + build_expr (ADR-0026 step 2) f0b2043 walker: add Subgrammar node + recursion-depth cap (ADR-0026 step 1) ``` **Tests:** **1100 passing, 0 failing, 1 ignored** (`cargo test` — up from 1039 at handoff-16). The ignored test is the long-standing `` ```ignore `` doc-test in `src/friendly/mod.rs`. Typing-surface matrix: **161 cells**. **Clippy:** clean (`cargo clippy --all-targets -- -D warnings`, nursery group). ## §1. ADR-0026 — recap (detail in handoff-17) Complex WHERE expressions work end to end: `update` / `delete` / `show data` take a full boolean expression (`AND`/`OR`/`NOT`, the six comparisons, `LIKE`/`IN`/`BETWEEN`/ `IS [NOT] NULL`, parentheses); it compiles to parameterised SQL; `show data` gained `where` and `limit`. The §3 builder realization (option 1) and the as-built deviations are in ADR-0026's "As-built notes". **Step 5 (the §7 diagnostic flagging) was deferred at handoff-17 and is now done — see §2.** ## §2. ADR-0027 — the validity indicator Six steps, one commit each (`e22f933`..`a326849`): - **A — diagnostics model.** `Severity` (Error/Warning, ordered) and `Diagnostic { severity, span, message }` in `walker::outcome`; a `diagnostics` field on `WalkResult`. `walker::input_verdict(source, schema)` is the indicator's entry point — the highest severity across the parse outcome and the diagnostics, or `None` for clean / empty input. - **B — schema-existence ERRORs.** `MatchedKind::Ident` now carries its `IdentSource`. A post-walk pass over a structural `Match` flags an unknown table (`Tables` ident) or unknown column (`Columns` ident, scoped to the table in scope) as an ERROR — *new behaviour*: such names used to parse cleanly and fail only at execution. - **C — expression WARNINGs (ADR-0026 §7 folded in).** A type-mismatched comparison, or `= NULL` / `!= NULL`, in a WHERE expression yields a WARNING — computed post-walk from the built `Command`'s `Expr` against the table's column types. The command still parses and runs (§7 permissive posture unchanged). - **D — rendering.** `[ERR]` / `[WRN]` at the input row's right edge; new amber `theme.warning`. The rightmost six columns are reserved unconditionally so the typed command never shifts when the indicator appears. - **E — debounce.** The runtime event loop time-boxes `recv` while a recompute is owed: a keystroke hides the indicator and arms a 1 s window; once typing pauses that long the verdict is computed and shown. `update()` stays pure. - **F — sweep + docs.** `input_verdict` tests confirm the schema check fires across the identifier-taking commands. ## §2a. Post-ADR-0027 — a manual-testing pass After ADR-0027 landed, a round of manual testing surfaced three things; all are resolved. - **Bug, fixed (`151ed08`).** A parse error in `add index …` showed the `add column` usage. `add` / `drop` are multi-form commands and both the ambient hint and the submit-time usage block picked the *first* form unconditionally. New `grammar::usage_key_for_input` disambiguates by the form word after the entry keyword; the hint shows the committed form, `render_usage_block` too (bare `add` / `drop` still shows the whole family). - **`add index` syntax — reviewed, deliberately kept.** The inconsistency is real: `add column [to] [table] : ()` vs `add index [as ] on ()`. It was weighed and **left as-is** — `add index … on ()` is intentionally SQL-idiomatic (`CREATE INDEX … ON t (cols)`), the colon shape was built for "one column *with a type*" and an index has neither. A future agent should **not** re-flag this as a bug; if the owner later wants the entity-command separators unified, that is a deliberate ADR-0025 amendment / design pass, not a drive-by fix. - **No index parameters / no `unique` — by design.** ADR-0025 scoped `add index` to plain non-unique indexes; there is no `unique`-index syntax and no constraint syntax at all (`NOT NULL` / `UNIQUE` / `CHECK` / `DEFAULT` are the pending `C3`). ADR-0018's `unique` handling only *detects + displays* an existing constraint — nothing creates one. Not a gap in the help; the help is accurate. ## §3. Architectural delta (vs. handoff-17) ### Diagnostics - `walker::outcome::{Severity, Diagnostic}`; `WalkResult::diagnostics: Vec` (re-exported as `walker::{Severity, Diagnostic}`). - `walker::input_verdict(source, schema) -> Option` — the indicator entry point. - `MatchedKind::Ident` gained `source: IdentSource`. - Diagnostics are computed **post-walk** in `walk()` — `schema_existence_diagnostics` over the matched path, `expr_warnings` over the built `Expr` — not emitted incrementally (keeps them off the speculative-rollback paths). See ADR-0027 "As-built notes". ### App / runtime / ui - `App::input_indicator: Option` — the indicator's visible state; `App::input_validity_verdict()` — the pure verdict (simple mode only; advanced mode is raw SQL). - `runtime::run` event loop: a `tokio::time::timeout`-based debounce (`INDICATOR_DEBOUNCE`, 1 s). - `ui::render_input_panel` reserves a fixed six-column right strip and renders the `[ERR]`/`[WRN]` label. - `theme.warning` — amber, light + dark. ### Catalog - New `diagnostic:` section — `unknown_table`, `unknown_column`, `type_mismatch`, `eq_null`. ## §4. Known follow-ups (none blocking) - **Diagnostic highlight / hint wiring.** ADR-0027 §2 says highlighting and the hint panel "read the individual diagnostics". The indicator and the model ship; routing a diagnostic's span into the per-byte highlight overlay, and its message into the hint panel beyond the existing parse-error path, is not yet done — the build order did not enumerate it as a step. WARNING spans are currently coarse (the whole WHERE clause) because the `Expr` AST carries no source spans; precise per-literal spans would come with that wiring. Recorded in ADR-0027 "As-built notes". - **Debounce timing is integration-level, not unit-tested** — it is async event-loop glue. The pure pieces (`input_verdict`, `input_validity_verdict`, the indicator rendering) are covered. - **LIKE-on-a-numeric-column** is not flagged — `LIKE` is a text-pattern test and "mismatch" there is fuzzier; a future WARNING-model extension. Compare / Between / In type-mismatch and `= NULL` are flagged. ## §5. What's next - **ADR-0028 — query plans (`explain`).** The last of the handoff-16 design trio, still unimplemented. `show data … where` (now real) is the filtered query whose plan flips between a full scan and an index search; the diagnostics model and the `OutputLine` span-styling ADR-0028 describes are both now easier to reach. - The §4 follow-ups, if the user wants the diagnostic highlight/hint detail wired now rather than later. Other open clusters are unchanged from handoff-16/17 §3 (snapshot/undo `U`-series; constraints `C3` — `CHECK` can reuse ADR-0026's expression grammar; `C4` m:n; `C3a`; `C1` table rename; `H1`; `SD1`; `TT5` CI; `V4`; `I1`; `TU1`). Prioritisation is a user product decision — ask. ## §6. How to take over 1. **Read this file, then handoff-17** (ADR-0026 detail), then handoff-16 (the design trio). 2. **Read `CLAUDE.md`** — working-style rules. 3. **Read `docs/adr/0027-input-validity-indicator.md`** — especially its "As-built notes" (post-walk diagnostics, the pre-rendered message, the `timeout` debounce, coarse spans, the deferred highlight/hint wiring). 4. **Run `cargo test`** — 1100 passing, 0 failing, 1 ignored. 5. **Run `cargo clippy --all-targets -- -D warnings`** — clean. 6. **Pick the next work from §5** — ADR-0028 is the natural pick; prioritisation is a user decision, so ask. ### Note on the typing-surface matrix `tests/typing_surface/` is **161 cells**. The validity indicator is *not* matrix-covered — the matrix harness (`assess`) drives parse / hint / completion, not the debounced indicator; `input_verdict` is covered directly by walker unit tests instead. After any grammar/walker change the matrix-snapshot discipline from handoff-17 still applies.