From 032a050f7b51b27b6dfc4c38a83542b885289b30 Mon Sep 17 00:00:00 2001 From: "claude@clouddev1" Date: Mon, 18 May 2026 20:46:06 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20add=20ADR-0027=20=E2=80=94=20input-fiel?= =?UTF-8?q?d=20validity=20indicator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A debounced `[ERR]` / `[WRN]` marker at the right edge of the input row, summarising — before submit — whether the current command would run. Backed by a small diagnostics-severity model: the walker emits severity- tagged diagnostics (parse outcome, schema-existence of table / column names) that the indicator summarises and the existing highlighting / hint layers detail. Advisory only — submission is never blocked. - docs/adr/0027-input-validity-indicator.md — the ADR. - docs/adr/README.md — index entry. - docs/requirements.md — new S6 (TUI shell). --- docs/adr/0027-input-validity-indicator.md | 241 ++++++++++++++++++++++ docs/adr/README.md | 1 + docs/requirements.md | 6 + 3 files changed, 248 insertions(+) create mode 100644 docs/adr/0027-input-validity-indicator.md diff --git a/docs/adr/0027-input-validity-indicator.md b/docs/adr/0027-input-validity-indicator.md new file mode 100644 index 0000000..ebd528c --- /dev/null +++ b/docs/adr/0027-input-validity-indicator.md @@ -0,0 +1,241 @@ +# ADR-0027: Input-field validity indicator + +## Status + +Accepted + +## Context + +While the user types, the app already flags problems in +two immediate ways (ADR-0022): per-token syntax +highlighting, and an ambient hint panel describing the slot +under the cursor. Both are *local* — they speak about the +spot being edited. + +What is missing is a *global* signal: a single, glanceable +answer to "if I press Enter now, will this command run?" A +learner can type something whose error is highlighted ten +columns back, move the cursor to the end, and submit it +without noticing. + +This ADR adds a **validity indicator** at the right edge of +the input row, and — because a meaningful indicator needs a +consistent notion of "what is wrong" — a small +**diagnostics-severity model** behind it. + +The indicator is **advisory**. It never blocks submission: +that posture is settled (ADR-0022 — the app indicates, it +does not refuse; ADR-0026 §7 — even a flagged WHERE still +runs if submitted). + +## Decision + +### 1. Two severities + +- **ERROR** — the input is *known* to fail. Either it does + not parse (incomplete, or a mismatched / invalid token), + or it parses but names something that does not exist (an + unknown table or column). +- **WARNING** — the input is valid and *will* run, but is + very likely not what a knowledgeable user wants: a + type-mismatched comparison, or `= NULL` (both from + ADR-0026 §7). + +The split is *certainty of failure* versus *likely +misleading*. The indicator shows the highest severity +present; clean input shows nothing at all. + +### 2. The diagnostics model + +The walk produces a set of **diagnostics** alongside its +`WalkOutcome`. A diagnostic is, roughly, +`{ severity, span, message_key }` — the severity drives the +indicator, the span drives highlighting, the message key +drives the hint text (ADR-0019 catalog). + +Diagnostics come from three sources, all reachable within +the single schema-aware walk: + +- **The parse outcome.** `Incomplete`, `Mismatch`, and + `ValidationFailed` each yield an ERROR diagnostic; a + plain `Match` yields none. +- **Schema existence.** A matched `IdentSource::Tables` or + `IdentSource::Columns` token whose name is absent from + the schema cache yields an ERROR diagnostic. *This is new + behaviour.* Today `walk_ident` returns `Matched` for any + identifier-shaped token and consults the schema only to + populate context (`current_table`, `current_column`); an + unknown table parses cleanly and fails only at execution. + The check is cheap — the schema cache is already in + `WalkContext` — but it is genuinely new code, and it must + run for every Tables / Columns ident, not only the + `writes_*` ones. +- **Expression flagging (ADR-0026).** A type-mismatched + comparison and `= NULL` yield WARNING diagnostics. + +The walker is the one schema-aware pass, so diagnostics are +emitted there, not in a separate re-resolution pass. +`WalkResult` gains a `diagnostics` field. The indicator +reads the highest severity across the outcome and the +diagnostics; highlighting and the hint panel read the +individual diagnostics for *where* and *why*. The indicator +is the summary; the existing layers remain the detail. + +### 3. "Would Enter succeed now?" — and the debounce + +The indicator answers exactly one question: *if you pressed +Enter right now, what would happen?* — runs clean (nothing +shown), runs but flagged (`[WRN]`), or will not run +(`[ERR]`). + +An incomplete, still-being-typed command is in the `[ERR]` +bucket: pressing Enter on it fails. The indicator does not +special-case "still typing". + +Flicker is prevented not by suppressing states but by a +**debounce**: + +- On every keystroke the indicator is hidden immediately. +- After roughly one second with no typing, it reappears, + showing the current verdict. + +So: typing → blank; a pause → the verdict. The user gets a +settled, glance-before-submit signal without a marker that +thrashes on every key. Highlighting and hints stay +immediate and unchanged — only the *indicator's display* is +debounced; diagnostics themselves are still computed every +keystroke to feed those immediate layers. + +The indicator's display is therefore time-gated. Per the +`update()`-is-pure invariant, the debounce timer lives in +the runtime / event loop; `App` holds the indicator's +visible state, which the runtime sets when the quiet +interval elapses or a keystroke arrives. The interval is a +single tunable constant (~1 s). + +### 4. Rendering + +The indicator is a fixed-width five-column label — +`[ERR]` or `[WRN]` — or nothing when the input is clean. A +positive "all good" state is deliberately omitted: absence +*is* the all-clear. + +It sits in the input row, at the right end. The rightmost +strip — five columns for the label plus one column of gap, +six in all — is **reserved unconditionally**: the text area +is always `width − 6`, whether or not the indicator is +currently visible. The label appears and disappears within +its already-reserved strip, so the text / horizontal-scroll +boundary never moves and the label never collides with the +typed command. + +A *conditionally* reserved strip was rejected: it would +make a long, horizontally-scrolled command jump up to six +columns sideways on every typing-pause transition. +Unconditional reservation costs ~6 columns of typing width +— negligible — for a stable layout. + +Border-mounting the indicator (as chrome on the field's +frame, like the mode label) was also considered and not +chosen: a right-edge border decoration reads differently +from a top-border label, and the in-row position is the +intended look. + +`[ERR]` uses the existing `theme.error`. `[WRN]` needs a +new **`warning`** theme colour — an orange / amber — +defined for both the light and dark themes (NFR-5: colour +conveys information; NFR-7: legible on either background). + +### 5. The indicator never blocks submission + +Enter always submits, whatever the indicator shows. A user +who wants to run a flagged command — or even a doomed one, +to see what the engine does — may; the error lands in the +output as usual. The indicator's job is to make "this will +not do what you think" visible *before* submission, not to +prevent it. This is the same advisory posture as ADR-0022 +and ADR-0026 §7. + +### 6. The existing-cases sweep, and discoverability + +Implementation includes a **sweep** of the current command +surface for failures that are detectable before submission +but not yet surfaced as diagnostics — chiefly unknown table +and column references, across every command that takes an +identifier. Each becomes an ERROR diagnostic via §2. (The +WARNING set is thin until ADR-0026 lands; type mismatch and +`= NULL` are its first members.) + +The diagnostics model is documented as *the* route for any +future "we can tell, before it runs, that this is wrong or +dubious" case. This ADR is cross-referenced from the +diagnostic-producing code and from related ADRs, so new +cases plug into the model rather than becoming one-off +checks. + +### 7. Out of scope + +- **Blocking submission** — never; the indicator is + advisory (§5). +- **A positive `[OK]` state** — clean input shows nothing. +- **Raw advanced-mode SQL** — there is no SQL parser yet + (`Q1`). The indicator covers simple-mode DSL and the + app-level commands the walker parses; when SQL parsing + lands, SQL diagnostics route through this same model. +- **Per-diagnostic display in the indicator itself** — the + indicator is a one-glyph summary; *where* and *why* stay + with highlighting and the hint panel. + +## Consequences + +- `WalkResult` gains a `diagnostics` field; the walker + emits diagnostics (parse-outcome and schema-existence) as + it walks. +- A new schema-existence check on `IdentSource::Tables` / + `Columns` matches. Small, but genuinely new — today an + unknown identifier parses cleanly and fails only at + execution. +- The event loop gains a debounce timer for the indicator's + display. It lives in the runtime, so `update()` stays + pure. +- A new `warning` theme colour; the input field reserves a + fixed six-column right strip. +- The indicator is advisory; submission is never gated. +- A reusable diagnostics-severity model that future + pre-submit checks — and, eventually, advanced-mode SQL — + extend. +- The WARNING severity has no triggers until ADR-0026 is + implemented. The indicator may ship ERROR-only first and + gain WARNING with C5a, or ship after C5a; the model + carries both severities regardless. + +## Implementation notes + +A sensible order, each step test-guarded: + +1. The `Diagnostic` type and the `diagnostics` field on + `WalkResult`; map the parse outcome to diagnostics. +2. The schema-existence check in `walk_ident` for + `Tables` / `Columns` idents. +3. The `warning` theme colour; the fixed six-column input + strip; the `[ERR]` / `[WRN]` rendering. +4. The debounce in the runtime, and the indicator's + visible state on `App`. +5. The sweep — confirm every identifier-taking command + surfaces unknown-name diagnostics; cross-reference this + ADR from the diagnostic sites. + +ADR-0026's expression diagnostics (type mismatch, `= NULL`) +land with that ADR's implementation and feed the WARNING +severity. + +## See also + +- ADR-0003 — input modes; the input field and its mode + label. +- ADR-0019 — the friendly-message catalog the diagnostic + message keys resolve through. +- ADR-0022 — ambient typing assistance: the immediate + highlighting and hint layers this indicator summarises. +- ADR-0026 — complex WHERE expressions; the type-mismatch + and `= NULL` WARNING diagnostics. diff --git a/docs/adr/README.md b/docs/adr/README.md index adde8ad..9717f0e 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -32,3 +32,4 @@ This directory contains the project's ADRs, recorded per - [ADR-0024 — Unified grammar tree: execution plan](0024-unified-grammar-tree-execution-plan.md) — **Accepted**, the executable spec — implemented (Phases A–F; Phase F shipped "minimal", `parser.rs` retained as the router — see the ADR's Phase F implementation note) - [ADR-0025 — Indexes](0025-indexes.md) — **Accepted**, `add index` / `drop index`, persistence, rebuild-table preservation, and items-list display (`C3` index portion + `S2`) - [ADR-0026 — Complex WHERE expressions](0026-complex-where-expressions.md) — **Accepted**, stratified recursive expression grammar (`AND`/`OR`/`NOT`, comparisons, `LIKE`, `IS NULL`, `IN`, `BETWEEN`) for `update` / `delete` / `show data` filters; `show data` gains `where` + `limit`; adds the `Subgrammar` node and a recursive `Expr` AST (`C5a`) +- [ADR-0027 — Input-field validity indicator](0027-input-validity-indicator.md) — **Accepted**, a debounced `[ERR]` / `[WRN]` marker at the input row's right edge, backed by a walker diagnostics-severity model (parse-outcome + schema-existence); advisory, never blocks submission (`S6`) diff --git a/docs/requirements.md b/docs/requirements.md index 7f035ec..88ed1df 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -59,6 +59,12 @@ group enabled. (Earlier reference points: 1006 after ADR-0024 for inspecting hints about the current input or last error. - [ ] **S5** Mode label and distinct border style on the input field communicate the current input mode at all times. +- [ ] **S6** Input-field validity indicator: a debounced + `[ERR]` / `[WRN]` marker at the right edge of the input row, + summarising — before submit — whether the current command + would run. Backed by a walker diagnostics-severity model + (ERROR / WARNING). Advisory only — never blocks submission. + Designed in ADR-0027; implementation pending. ## Input field