# 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 <self>` 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<ModeClass>` (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".
