feat: ADR-0035 4i(d) — merge shared-entry-word completions

In advanced mode an entry word like `create`/`drop` has several candidate
nodes (the SQL forms + the DSL fallback), but the walker commits to one,
so completion offered only that node's continuations — `drop ` showed
just `table`, and `drop rel` dead-ended at an empty list even though the
DSL drops parse via fallback.

At the entry-word boundary (advanced mode), walk every candidate, keep the
viable (Incomplete) ones, and union their next-keyword continuations:
`drop ` → table·index·column·relationship·constraint; `drop rel` →
relationship; `create ` → table·unique·index. Deeper positions keep the
committed walk untouched (no change to insert/update/delete/select).

Each continuation is classified by producing category (Both/Advanced/
Simple) and block-ordered Both → Advanced → Simple, so they read as
contiguous groups (the foundation for the 4i(e) colour, landing next).
CompletionProbe carries a parallel expected_modes; the parse path is
unchanged (the merge is completion-only).

Tests: completion merge + partial + block-order cases; the two tests that
encoded the old single-node behaviour updated. Full suite 1911 passing /
0 failing / 1 ignored; clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-26 11:36:18 +00:00
parent ca64434a1c
commit 1afcf4ed29
3 changed files with 361 additions and 18 deletions
+157
View File
@@ -0,0 +1,157 @@
# 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".