# Plan: ADR-0035 Phase 4, sub-phase 4i — verification sweep (completes Phase 4) The closing sub-phase. Canonical scope: **ADR-0035 §13 4i**. Items: - **(a)** Refresh the `CREATE TABLE` help/usage skeleton for the 4a.2 `DEFAULT`/`CHECK`/composite-`UNIQUE`, 4a.3 table-`CHECK`, and 4b FK forms (the only help/usage debt — 4d/4e/4f/4g/4h carry their own). - **(b)** `describe` display of table-level constraints (composite `UNIQUE` + table `CHECK`, incl. **named** CHECKs from 4g). - **(c)** 4b self-ref FK pre-submit indicator: stop false-flagging a `references ` parent as unknown. - **(d)** shared-entry-word completion merge. - **(e)** visually distinguish simple- vs advanced-mode completions. - **staples:** typing-surface + matrix coverage, engine-neutral error pass, undo-parity (one step per statement). **User-chosen sequencing (2026-05-26):** the **(d)/(e) design conversation first**, then build. (d)/(e) are designed (below); (a)/(b)/(c) + staples follow. ## 1. Baseline - HEAD `ca64434`; **1909 passing / 0 failing / 0 skipped / 1 ignored**; clippy clean. ## 2. (d) + (e) — settled design (user-confirmed 2026-05-26) ### (d) Shared-entry-word completion merge **Bug:** in advanced mode the walker's `decide()` (`walker/mod.rs`) commits **one** candidate node for a shared entry word, so only that node's continuations reach completion — `drop ` offers only `table`, and `drop rel` returns empty, even though the DSL drops parse via fallback. **Fix:** in `completion_probe_in_mode`, for an **advanced-mode** shared entry word (the entry word resolves to **>1 candidate**), walk **each** candidate node speculatively (the existing `scratch`/`walk_one_command` machinery on a fresh context), extract each one's expected continuations (the same `WalkOutcome`→`expected` match the single-walk path uses), and **union** them. Simple mode is unchanged (its single DSL node already offers all DSL continuations; SQL isn't offered). A candidate that mismatches the current input contributes an empty set, so as the input disambiguates (`drop table …`) the union naturally narrows to the surviving candidate. ### (e) Visual simple-vs-advanced distinction Each merged continuation is classified by **which categories produced it** — a new `ModeClass`: - `Both` — produced by ≥1 `Advanced` **and** ≥1 `Simple` candidate (a continuation valid either way, e.g. `drop table`). - `Advanced` — only `Advanced` candidates (SQL-only, e.g. `create index`). - `Simple` — only `Simple` candidates (DSL-only, e.g. `drop relationship`). **Colour + order apply only when the candidate list is *mixed*** (>1 distinct `ModeClass`) — the only place the signal is informative; a single-mode list (deep inside any command) keeps today's token-kind colours, no mode tint (avoids redundant noise duplicating the mode indicator). When mixed: - **Colour:** `Both` → today's token-kind colour (neutral); `Advanced` → `theme.mode_advanced` (orange); `Simple` → `theme.mode_simple` (cyan). These two mode colours already exist (used by the mode indicator). - **Order (user refinement):** group the continuations into contiguous colour **blocks** in the order **`Both` → `Advanced` → `Simple`**, so `Advanced` sits between the other two and each colour reads as one block rather than interleaving. ## 3. Architecture & change list (d/e) - **`src/dsl/walker/outcome.rs`** (or `CompletionProbe`): add a parallel `expected_modes: Vec` (same length/order as `expected`), defaulting to all `Both` (so the single-walk path is neutral). New `ModeClass` enum (likely in `completion.rs`, re-exported). - **`src/dsl/walker/mod.rs`** `completion_probe_in_mode`: factor the expected-extraction (lines 333-356) into a helper; add the advanced-mode multi-candidate branch that walks each candidate (mode `Advanced`), classifies each expectation by the producing category, and fills `expected` + `expected_modes`. - **`src/completion.rs`**: `Candidate` gains `mode: ModeClass`; the keyword-building (`Expectation::Word`) carries the parallel `ModeClass` through prefix filtering; when the keyword set is mixed, **block-order** by `ModeClass` (Both→Advanced→Simple) within the keyword group; non-keyword kinds and single-mode lists are all `Both`. - **`src/ui.rs`** `render_candidate_line` (≈907): when the candidate list contains >1 `ModeClass`, colour each by its `ModeClass` (Both=kind-colour, Advanced=`mode_advanced`, Simple=`mode_simple`); otherwise unchanged. - **Tests:** `tests/typing_surface/` — `drop ` (advanced) offers `table·index·column·relationship·constraint`; `drop rel` → `relationship`; `create ` → `table` (Both) + `index` (Advanced); block-order assertion (Both then Advanced then Simple); simple mode unchanged. A `ui.rs` snapshot for the mixed-mode coloured hint line. ## 4. (a)/(b)/(c) — outline (build after d/e) - **(a)** Extend the `sql_create_table` help/usage strings (`friendly/strings/en-US.yaml`) for `DEFAULT`/`CHECK`/composite-`UNIQUE` (4a.2), table-`CHECK` (4a.3), and inline + table-level `FOREIGN KEY` (4b). Catalog-lockstep + vocab-audit guard wording. - **(b)** `describe` (`do_describe_table` / the structure renderer): show table-level composite `UNIQUE` and table `CHECK` constraints (named CHECKs show their name). Tier-2 snapshot + Tier-3 describe assertions. - **(c)** The pre-submit schema-existence diagnostic: treat a FK parent equal to the in-statement `CREATE TABLE` target as valid (self-ref), so the `[ERR]`/`[WRN]` indicator stops lying. Tier-1/typing-surface test. ## 5. Phase 2/3 — candidates & selection (d/e) Settled via the design conversation (§2). The merge alternative ("modify `decide`/`walk` to merge in the core parser") was **rejected** — it would change the shared parser used everywhere; doing the merge in the completion-only `completion_probe_in_mode` is lower-risk. The colour alternatives (always-by-mode; marker tag; defer) were presented to the user, who chose **mode-colour-when-mixed + block ordering**. ## 6. Devil's Advocate review of this plan - **Forks escalated?** The (e) visual treatment was put to the user with four options + previews; they chose option 1 with the block-ordering refinement. (d)'s behaviour is ADR/handoff-specified. ✓ - **Core-parser risk?** The merge lives in `completion_probe_in_mode` (completion-only), not `walk`/`decide` — the parse path is untouched, so no risk to execution/dispatch. ✓ - **Simple mode unaffected?** The merge branch is advanced-only; simple-mode completion keeps the single-DSL-node path. A test pins it. ✓ - **Noise?** Colour/order apply only when the list is genuinely mixed; single-mode lists are visually unchanged. ✓ - **Perf?** Completion walks each candidate per keystroke — candidates per entry word are few (≤ ~3), and completion is already a per-keystroke walk; negligible. ✓ - **Ordering vs existing kind-order?** Block-ordering applies *within* the keyword group (where shared-entry continuations live); the identifiers-first kind ordering is preserved. ✓ ## 7. Implementation sequence (test-first) 1. **(d) merge** — `ModeClass` + `expected_modes`; the advanced-mode multi-candidate walk + union in `completion_probe_in_mode`; thread through `completion.rs` (functional merge, classes computed but not yet coloured). Typing-surface tests (merge + simple-mode-unchanged) → green. 2. **(e) order + colour** — block-ordering when mixed; `render_candidate_line` colours by `ModeClass`. Ordering test + a UI snapshot → green. 3. **(c)** self-ref FK indicator → test-first. 4. **(b)** describe table-level constraints → snapshot/e2e. 5. **(a)** CREATE TABLE help/usage skeleton + catalog lockstep. 6. **Staples** — typing-surface/matrix sweep, engine-neutral error pass, undo-parity spot-check. 7. **Full sweep** + finished-slice `/runda` → commit proposal(s). ## 8. Exit gate All §13 4i items done or explicitly deferred-with-user-confirmation; all tiers green, zero skips; no regression from 1909; clippy clean; written-DA / `/runda` PASS; ADR-0035 §13 4i + README + requirements lockstep. **4i completes ADR-0035 Phase 4** — flip the ADR Status from "4i pending".