From 0e6f7678480b6b78b10a28616a5009001f5bd7c9 Mon Sep 17 00:00:00 2001 From: "claude@clouddev1" Date: Wed, 3 Jun 2026 14:05:09 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20ADR-0042=20=E2=80=94=20continue=20H1a?= =?UTF-8?q?=20parse-error=20pedagogy=20on=20the=20grammar=20tree?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR-0020/0021 specified a chumsky-based H1a; ADR-0024 replaced chumsky with the scannerless walker, leaving both obsolete. Mark them superseded (kept as institutional memory) and add ADR-0042, which restates H1a against the architecture as built. ADR-0042 records that H1a is substantially shipped already — per-command usage block, available-commands fallback, source-derived ident slot labels, curated parse.custom.* near-miss messages, and schema-aware [ERR] diagnostics — and defines the remaining work: a verified per-command near-miss matrix (the definition of done), friendlier literal expectation labels that add role context while keeping the exact literal visible, and advanced-mode SQL parse parity (RETURNING scope, CROSS JOIN ON, INSERT…SELECT count), kept distinct from ADR-0019 §OOS-2 engine-error sanitisation. - docs/adr/0020,0021: superseded notes + README entries - docs/adr/0042: new ADR - docs/adr/README.md: index upkeep (ADR-0000 rule) --- ...0-tokenization-layer-for-the-dsl-parser.md | 24 +- .../0021-parser-as-source-of-truth-for-h1a.md | 32 +- ...2-h1a-parse-error-pedagogy-grammar-tree.md | 298 ++++++++++++++++++ docs/adr/README.md | 5 +- 4 files changed, 355 insertions(+), 4 deletions(-) create mode 100644 docs/adr/0042-h1a-parse-error-pedagogy-grammar-tree.md diff --git a/docs/adr/0020-tokenization-layer-for-the-dsl-parser.md b/docs/adr/0020-tokenization-layer-for-the-dsl-parser.md index fab1700..58595cf 100644 --- a/docs/adr/0020-tokenization-layer-for-the-dsl-parser.md +++ b/docs/adr/0020-tokenization-layer-for-the-dsl-parser.md @@ -2,7 +2,29 @@ ## Status -Accepted. +**Superseded by ADR-0024** (2026-05-14). Accepted then superseded +without being implemented. + +> **Superseding note (2026-06-03).** This ADR was never built. It +> specifies a `chumsky`-over-tokens architecture — a separate lexer +> producing `Vec`, a `define_keywords!` macro, and chumsky +> grammar combinators consuming `&[Token]`. ADR-0024 (unified grammar +> tree) instead adopted a **scannerless hand-rolled walker** that +> operates directly on source bytes, and **removed chumsky from the +> project entirely** (it is no longer a dependency). The lexer, +> `keyword.rs`, and the token model described below do not exist. +> +> What this ADR got *right* survives in ADR-0024: the +> expected-set aggregation it wanted (one branch's report no longer +> swallowing the others) is delivered by the walker's structural +> `expected` derivation, and the I3 (completion) / I4 (highlighting) +> hooks it anticipated are served by the same walker. Read ADR-0024 +> for the architecture as built; this ADR remains as institutional +> memory of the path not taken and the reasoning that led there. + +--- + +*Original status (historical):* Accepted. Amends ADR-0001 (language and TUI framework) by adding a tokenization layer between the source string and the chumsky diff --git a/docs/adr/0021-parser-as-source-of-truth-for-h1a.md b/docs/adr/0021-parser-as-source-of-truth-for-h1a.md index 244f7f4..ff1e4c7 100644 --- a/docs/adr/0021-parser-as-source-of-truth-for-h1a.md +++ b/docs/adr/0021-parser-as-source-of-truth-for-h1a.md @@ -2,7 +2,37 @@ ## Status -Accepted. +**Mechanism superseded by ADR-0024; H1a scope continued in ADR-0042.** +Accepted then superseded. + +> **Superseding note (2026-06-03).** The *intent* of this ADR — surface +> the grammar of the command at the point of error, not just the next +> token — survived and is largely delivered. The *mechanism* did not. +> This ADR specifies a `chumsky`-based design: a separate `UsageEntry` +> registry in `src/dsl/usage.rs`, `parse.token.*` catalog keys driven +> by chumsky's `RichPattern` expected sets, and a renderer over +> chumsky output. ADR-0024 (unified grammar tree) replaced chumsky with +> a scannerless walker and **folded usage info onto the grammar nodes +> themselves**: `usage_ids` live on each `CommandNode`, the per-command +> `parse.usage.*` templates and the `parse.available_commands` fallback +> ship as designed here, and the expected-set vocabulary +> (`format_expectation` in `parser.rs`) renders directly from walker +> `Expectation` variants — no `UsageEntry` registry, no `parse.token.*` +> keys, no `src/dsl/usage.rs`. +> +> So: the §1 usage registry, §3 "deepest consumed keyword" mechanism, +> §4 `parse.token.*` catalog, and §7 validator details below describe +> code that does not exist. What shipped equivalently: §1's per-command +> templates (as `usage_ids` + `parse.usage.*`), §2's three-block render +> (echo+caret / structural error / usage), and §5's available-commands +> fallback. **ADR-0042 picks up H1a from here** — it records what is +> actually shipped and defines the remaining systematic-pass scope +> against the grammar-tree architecture. Read ADR-0042 for the live +> plan; this ADR remains as the design rationale for the pedagogy goal. + +--- + +*Original status (historical):* Accepted. Builds on ADR-0020 (tokenization layer). Addresses H1a from `requirements.md` — the parse-error pedagogy gap that diff --git a/docs/adr/0042-h1a-parse-error-pedagogy-grammar-tree.md b/docs/adr/0042-h1a-parse-error-pedagogy-grammar-tree.md new file mode 100644 index 0000000..6a84df6 --- /dev/null +++ b/docs/adr/0042-h1a-parse-error-pedagogy-grammar-tree.md @@ -0,0 +1,298 @@ +# ADR-0042: H1a parse-error pedagogy in the grammar-tree era + +## Status + +**Accepted** — 2026-06-03. + +Continues H1a (`requirements.md`) from **ADR-0021**, whose +chumsky-based mechanism was superseded by **ADR-0024** (unified +grammar tree). ADR-0021's *intent* — surface the grammar of the +command at the point of error, not just the next token — is +re-stated here against the architecture as actually built, with +an inventory of what already ships and a definition of done for +the remaining work. + +Cross-references ADR-0019 (friendly-error layer + i18n catalog +conventions; H1a output shares the catalog), ADR-0022 (ambient +typing assistance, which shares the walker's expected-set +machinery), ADR-0024 (the grammar tree), and ADR-0009 (DSL +surface conventions; usage templates render in the documented +surface form). + +## Context + +### Why a new ADR rather than amending ADR-0021 + +ADR-0021 specifies a `UsageEntry` registry in `src/dsl/usage.rs`, +`parse.token.*` catalog keys, and a renderer over chumsky's +`RichPattern` expected sets. None of that exists. ADR-0024 +removed chumsky from the project, deleted `usage.rs`, and folded +usage information onto the grammar nodes themselves. Amending +ADR-0021 in place would force every reader to mentally translate a +dead mechanism; a fresh ADR records the live state directly. +ADR-0020 and ADR-0021 keep their superseding notes and remain as +institutional memory. + +### What H1a is + +When a learner types something near-correct, the error should +*name the missing keyword or clause* and *show the shape of the +command*, rather than point a caret at the unexpected character. +The user-reported gap: typing `create` once produced +`parse error: after \`create\`, expected \`table\`` — structurally +true, pedagogically silent. + +### What already ships (the baseline — do not re-build) + +Verified against code on 2026-06-03. The grammar-tree migration +delivered most of ADR-0021's intent through different machinery: + +1. **Per-command usage block.** Every `CommandNode` carries + `usage_ids: &'static [&'static str]` + (`src/dsl/grammar/mod.rs`). On any parse error the renderer + emits a `usage:` block listing every form of the matched + command family — 38 templates under `parse.usage.*` + (`src/friendly/strings/en-US.yaml:499-571`), resolved by + `grammar::usage_keys_for_input` and rendered by + `render_usage_block` (`src/app.rs:2560`). + +2. **Available-commands fallback.** When no command keyword was + consumed, the block becomes + `available commands: …` (`parse.available_commands`, + `en-US.yaml:493`; `app.rs:2593`). + +3. **Structural error names the consumed prefix and expected + set.** `format_walker_error` (`src/dsl/parser.rs:289`) renders + `after \`\`, expected , found `, distinguishing incomplete-at-EOF (`at_eof = true`, + more input would help) from a definite mid-input mismatch. + +4. **Friendly slot labels for identifiers.** `format_expectation` + (`src/dsl/parser.rs:262`) renders `Ident` slots by source — + "table name", "column name", "relationship name", "index + name", "type" — instead of a bare "identifier" (ADR-0022 stage + 8c). + +5. **Curated custom messages** for high-value near-misses under + `parse.custom.*` (`en-US.yaml:443-478`): `create_table_needs_pk`, + `insert_form_a_missing_values` ("looks like Form A — add + `values (...)`"), `change_column_flags_exclusive`, + `bind_type_mismatch`, the redundant-constraint and + alter-add-primary-key cases, etc. + +6. **Schema-aware pre-flight diagnostics** that light the + `[ERR]` validity indicator *at typing time* (ADR-0027 / + ADR-0033 / ADR-0036): INSERT arity for Forms A/B/C, unknown + table/column, type mismatch, `= NULL`, NOT-NULL-missing, and — + on the advanced-SQL surface — `cte_arity_mismatch`, + `compound_arity_mismatch`, and `projection_alias_misplaced` + (`diagnostic.*`, `en-US.yaml:577-620`; walker logic in + `src/dsl/walker/mod.rs`). + +7. **Ambient "Next:" hints** and the **simple→advanced cross-mode + pointer** (ADR-0022 / `advanced_alternative_note`, + `src/input_render.rs`). + +So H1a is *substantially* delivered at the intent level. The +handoff's two canonical examples already behave: `insert into T +('Oli')` → custom Form-A message; `update T set x=1` → structural +"expected `where` or `--all-rows`" + usage block. + +### What remains — the genuine gap + +The remaining work is **systematic verification plus targeted +polish**, not a missing feature: + +- **No enumerated coverage guarantee.** Coverage is curated + case-by-case; nothing asserts that *every required slot in every + command* produces a pedagogically-sound near-miss message. +- **Literal expectations render terse.** `Word`/`Literal`/`Punct`/ + `Flag` slots come out as backticked literals (`` `where` ``, + `` `=` ``, `` `--all-rows` ``). Correct, but a learner is helped + more by a short prose gloss in select high-value positions. +- **Advanced-mode SQL parse pedagogy is thinner** than the DSL + surface (RETURNING scope, CTE-arity diagnostic positioning, + `CROSS JOIN … ON`, INSERT…SELECT column-count). No other ADR or + open issue covers this (ADR-0019 §OOS-2 covers advanced-SQL + *engine-error sanitisation* — a different layer). + +## Decision + +### 1. Definition of done — a verified near-miss matrix + +H1a is "done" when there is a test matrix that, for **every +command in the REGISTRY**, exercises its salient near-miss inputs +and asserts the rendered output reads pedagogically. "Salient +near-misses" per command means at minimum: + +- the bare entry keyword alone (`create`, `add`, `update`); +- each required clause omitted (e.g. `update T set x=1` with no + filter rail; `insert into T (cols)` with no `values`); +- a wrong token where a specific slot is expected (e.g. a number + where a table name belongs); +- the zero-prefix / unknown-command case (available-commands + fallback). + +The matrix lives in the existing surfaces — `tests/typing_surface/` +(snapshot-based, the standalone `typing_surface_matrix` binary) for +the typing-time hint/validity view, and +`tests/it/parse_error_pedagogy.rs` (the consolidated `it` binary) +for the submit-time rendered three-block output. New integration +tests go in `tests/it/` per the handoff-57 §3 layout rule — **not** +as new top-level `tests/*.rs`. + +Work is **test-first**: add the matrix entry, observe the current +rendering, and only then adjust wording/labels where it reads +poorly. A near-miss whose current rendering is already good is +locked by a snapshot, not rewritten. + +### 2. Friendlier literal expectation labels + +`format_expectation` gains, for high-value keyword/punct positions, +an optional prose gloss while **always keeping the exact literal +visible** — a learner must still see the precise token to type. +The principle: a label may *add* role context, never *replace* the +literal. + +Illustrative target (final wording settled per-case against the +matrix, as is normal for pedagogical text): + +- `expected \`where\` or \`--all-rows\`` → + `expected a filter clause: \`where …\` or \`--all-rows\`` +- `expected \`values\`` (after a Form-A column list) → + already covered by `parse.custom.insert_form_a_missing_values`; + the matrix confirms it fires. + +Mechanism (illustrative, finalised at implementation time): a +grammar `Word`/`Punct` node may carry an optional expectation-label +key, mirroring how `Ident` slots derive a label from +`IdentSource`. Absent an override, rendering is unchanged (the +backticked literal). This keeps the change additive and per-slot — +no blanket reword that would churn the anchor-phrase tests +needlessly. + +New glosses are catalog-sourced (`parse.expect.*` or reuse of +`parse.usage.*` fragments — chosen at implementation time) so +wording stays in `en-US.yaml`, not in code, consistent with +ADR-0019. + +### 3. Advanced-mode SQL parse pedagogy — in scope + +The same matrix discipline (§1) extends to the advanced-mode SQL +surface. Two of the relevant arity diagnostics **already exist** and +must not be re-built — `cte_arity_mismatch` and +`compound_arity_mismatch` (`en-US.yaml:590-591`); for these the work +is matrix coverage and, for CTE, auditing whether the diagnostic is +*positioned at the CTE name* (easiest to fix) rather than the body. +The genuine **absences** the pass adds are: `RETURNING` column +scope, `CROSS JOIN` rejecting an `ON` clause, and INSERT…SELECT +projection/target column-count (verified absent from the catalog +2026-06-03). Each gets a matrix entry; fixes land as walker +diagnostics or `parse.custom.*` messages following the existing +patterns. The `:` one-shot escape (a simple-mode line run once in +advanced mode) is part of the advanced surface and gets at least one +near-miss matrix entry. + +This stays clear of ADR-0019 §OOS-2 (advanced-SQL *engine-error* +sanitisation): §OOS-2 reworks errors raised by *executing* SQL; +H1a here concerns errors raised while *parsing* it. If a near-miss +turns out to be an engine error rather than a parse error, it is +out of H1a scope and noted against §OOS-2 instead. + +### 4. Catalog and anchor-phrase discipline + +All new or reworded user-facing strings go through the i18n catalog +(`en-US.yaml`) and the `KEYS_AND_PLACEHOLDERS` validator, per +ADR-0019. No engine vocabulary in any string (CLAUDE.md). + +Two anchor styles constrain §2's glosses and both are preserved by +its "literal always visible" rule: + +- The **substring assertions** in `src/dsl/parser.rs` tests + ("after `…`", "expected table name", "found end of input", + "unknown type", "expected one of"). +- The **substring assertions** in `tests/it/parse_error_pedagogy.rs`, + which check for backticked literals and usage fragments + (e.g. `` `column` ``, `` `1` ``, "create table", "with pk"). This + test is `.contains()`-based, not snapshot-based, so a §2 gloss + that dropped the bare literal would fail it — which is precisely + the regression §2's rule prevents. + +The snapshot-based `tests/typing_surface/` matrix will re-baseline +on any §2 wording change (expected; reviewed via `cargo insta`), +but the two substring suites above must stay green without edits to +their assertions. + +## Out of scope + +1. **Advanced-SQL engine-error sanitisation** — ADR-0019 §OOS-2. +2. **Tab completion (I3) and syntax highlighting (I4)** as + features — they share the walker but are separate ADRs. +3. **Schema-aware "did you mean `Customers`?" spell-correction** — + ADR-0021's out-of-scope §2; belongs with I3. +4. **Multi-error reporting.** The walker reports the first error + and stops; unchanged. +5. **`messages`-style verbosity gating of the usage block.** Per + ADR-0021 §8 the usage block is always shown; parse errors are + exactly when pedagogical surface should be maximal. Unchanged. +6. **Auto-generating usage/help text from the grammar.** ADR-0024 + left help prose hand-curated; templates stay hand-written. + +## Consequences + +### Positive + +- H1a gains an explicit, enumerated definition of done instead of + an open-ended "systematic pass still pending". +- The matrix becomes a regression lock: future grammar changes + that degrade a near-miss message fail a snapshot. +- Literal-label glosses close the last terse-wording gap without a + blanket reword. +- The advanced-SQL surface reaches parity with the DSL surface for + the audience that has switched to raw SQL. + +### Costs + +- Wording iteration across many near-miss cases — but cheap, + catalog-driven, and snapshot-guarded. +- The §2 per-node label field is one more annotation a new command + may set (optional; default unchanged). +- Snapshot volume grows; acceptable given the existing ~160-entry + typing-surface matrix. + +### Neutral + +- No public API change. `parse_command*` signatures, the + `ParseError` shape, and the three-block render path are all + unchanged; this ADR adds wording, labels, and tests within them. + +## Implementation notes + +Order of operations (test-first throughout): + +1. Enumerate the per-command near-miss matrix (§1) as failing/asserting + tests in `tests/typing_surface/` + `tests/it/parse_error_pedagogy.rs`. + Capture current rendering as the starting baseline. +2. Triage: which entries read poorly? Only those get wording work. +3. Add the optional expectation-label mechanism (§2) and apply it + to the high-value keyword/punct positions surfaced in triage. +4. Advanced-SQL near-miss audit + fixes (§3), distinguishing parse + from engine errors as they arise. +5. Catalog validator + anchor-phrase checks stay green (§4). +6. Update `requirements.md` H1a with the matrix as the done-marker; + flip to `[x]` only when the matrix is complete and green. + +## See also + +- ADR-0021 — Parser-as-source-of-truth for H1a (mechanism + superseded; intent continued here). +- ADR-0020 — Tokenization layer (superseded by the scannerless + walker). +- ADR-0024 — Unified grammar tree (the architecture H1a is built + on). +- ADR-0022 — Ambient typing assistance (shares the expected-set + machinery). +- ADR-0019 — Friendly-error layer and i18n catalog (§OOS-2 is the + adjacent engine-error scope). +- ADR-0009 — DSL command-syntax conventions (usage surface form). +- `requirements.md` — H1a tracking entry. diff --git a/docs/adr/README.md b/docs/adr/README.md index 13bc423..b41111f 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -25,8 +25,8 @@ This directory contains the project's ADRs, recorded per - [ADR-0017 — Column type-change compatibility](0017-column-type-change-compatibility.md) - [ADR-0018 — Auto-fill contracts for `serial` and `shortid` columns](0018-auto-fill-contracts-for-serial-and-shortid.md) - [ADR-0019 — Friendly error layer (H1) and i18n message catalog](0019-friendly-error-layer-and-i18n.md) -- [ADR-0020 — Tokenization layer for the DSL parser](0020-tokenization-layer-for-the-dsl-parser.md) -- [ADR-0021 — Parser-as-source-of-truth for H1a (per-command usage in parse errors)](0021-parser-as-source-of-truth-for-h1a.md) +- [ADR-0020 — Tokenization layer for the DSL parser](0020-tokenization-layer-for-the-dsl-parser.md) — **Superseded by ADR-0024 (never implemented).** Specified a `chumsky`-over-tokens architecture (separate lexer, `define_keywords!`, `&[Token]` grammar). ADR-0024 adopted a scannerless hand-rolled walker and removed `chumsky` entirely; the lexer/keyword/token model here does not exist. Kept as institutional memory of the path not taken. +- [ADR-0021 — Parser-as-source-of-truth for H1a (per-command usage in parse errors)](0021-parser-as-source-of-truth-for-h1a.md) — **Mechanism superseded by ADR-0024; H1a scope continued in ADR-0042.** The *intent* (show the command's grammar at the point of error) shipped — `usage_ids` on each `CommandNode`, the `parse.usage.*` templates, and the `available_commands` fallback all exist — but via grammar nodes, not the `chumsky` `UsageEntry` registry / `parse.token.*` keys described here (which were never built). - [ADR-0022 — Ambient typing assistance: colour, hint panel, completion (I3 + I4)](0022-ambient-typing-assistance.md) — **Amendment 1 supersedes §12's simple-mode-only carve-out**: the unified mode-aware walker (ADR-0030/0031/0032) now speaks SQL, so advanced-mode ambient assistance is re-enabled. `ambient_hint_in_mode` + `hint_resolution_at_input_in_mode` + `expected_for_hint_snapshot` thread `Mode`; `render_hint_panel` calls ambient for all modes (no more advanced-mode `None`); the one-shot `:` sigil is stripped before the ambient walk. Fixes a live bug where advanced-mode SQL hinting/completion-preview were dead despite Phase 2 marking them green (validated at the engine layer, not the UI). Simple-mode gating, highlighting, and the §13 performance posture are unchanged; covered by an app-level render test plus ambient-layer regression locks; **Amendment 2 reverses the handoff-14 keywords-first candidate ordering** — schema identifiers (table/column/relationship names) now sort *before* keywords so a name the user would have to look up stays visible in the single-row, window-scrolled candidate line (keywords are learned over time; the `tok_identifier`/`tok_keyword` colour split marks the boundary); shipped with a `walk_repeated` fix that surfaces a list item's trailing optionals at a clean boundary (`order by Name ` → `asc`/`desc`, `select Name ` → `as`, `create table … Code(text) ` → `not`/`unique`/`default`/`check`; the `,` separator deliberately not surfaced); records a deferred two-line hint box for growing lists; **Amendment 3 makes the ambient-hint fallback rung schema-aware** — Amendment 1's bottom-rung `parse_command_in_mode` was schemaless while every earlier rung was not, so between-values insert hints pointed at `)` (type-blind close) instead of `,` and wrong-arity closed tuples read "submit with Enter" for an input the schema-aware parse rejects (issue #2); now uses `parse_command_with_schema_in_mode`, no extra walk, with the friendly arity diagnostic still winning at its higher rung; **Amendment 4 gives column types a dedicated highlight class** — both `Node::Ident.highlight_override` *and* the `Word.highlight_override` field were dead (driver destructured the former to `_`, `walk_word` hardcoded `Keyword`); now both wired through, with a new `HighlightClass::Type` + eighth `Theme` field `tok_type` (a pink/deep-magenta distinct from both keyword purple and identifier teal) so types no longer render identically to identifiers (issue #8); the three `IdentSource::Types` slots opt in via `Some(Type)` (advanced-mode single-word SQL aliases — `float`, `varchar`, … per ADR-0035 §3 — ride along for free), and the two-word `double precision` alias opts in via the new `Word::type_keyword` constructor so it matches its synonyms; **Amendment 5 lets the hint panel grow for long prose hints** — a fixed one-row panel clipped long field-value/usage hints past the first line (issue #12); `resolve_hint_lines` now pre-wraps prose and `render_right_column` sizes the panel to the line count (1 row default, up to `MAX_HINT_ROWS`=3, reclaimed when short) with a `clamp_wrapped` ellipsis backstop; the candidate list still scrolls horizontally on one row (Amendment 2's deferred two-line candidate box stays deferred); also shortens the 299-char `parse.usage.sql_create_table` synopsis to a terse one-liner (full grammar remains in `help.ddl.sql_create_table`); **Amendment 6 adds a curated SQL function-name list** (`src/dsl/sql_functions.rs`, `KNOWN_SQL_FUNCTIONS` — aggregates + common + broader scalars; `cast` deliberately excluded as its `CAST(x AS type)` syntax isn't a plain-call shape) as the single source of truth shared by two consumers at the `sql_expr_ident` slot (ADR-0031 §1): **issue #15** offers the functions as Tab candidates under a new `CandidateKind::Function` + ninth `Theme` colour `tok_function` (a blue distinct from keyword/identifier/type, parallel to Amendment 4's `tok_type`) so a learner discovers `sum`/`upper`/…; **issue #16** restores the typing-time column-typo flag the issue-#6 fix had dropped wholesale at this slot — `invalid_ident_at_cursor` now bails only when the partial prefix-matches a known function, else falls through to the schema-column check, so `select Agx` warns again at typing time while `select sum` does not (the issue-#6 lockdown tests + the submit-time `unknown_column` diagnostic path are untouched, and the no-validation-allowlist posture stands); see ADR-0031's status note for the grammar-side anchor - [ADR-0023 — Unified declarative grammar tree](0023-unified-grammar-tree.md) — direction (superseded for execution detail by ADR-0024) - [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) @@ -47,3 +47,4 @@ This directory contains the project's ADRs, recorded per - [ADR-0039 — EXPLAIN over advanced-mode SQL queries](0039-explain-over-advanced-sql.md) — **Accepted** (2026-05-27), **implemented 2026-05-30 (issue #7)**, **supersedes ADR-0030 §13 OOS-2**. Lets `explain` wrap the advanced SQL commands (`Select`/`SqlInsert`/`SqlUpdate`/`SqlDelete`, plus `with`/CTE which builds a `Select`) in addition to the DSL `ShowData`/`Update`/`Delete` it already covers (ADR-0028), running `EXPLAIN QUERY PLAN` over the validated SQL text through the existing ADR-0028 span-styled plan tree (advanced mode only; DSL `explain` unchanged in both modes). Implemented via a second `Advanced` `explain` CommandNode (`EXPLAIN_SQL`) registered under the shared `explain` entry word — reusing the established `insert`/`update`/`delete` shared-word dispatch (`decide`: SQL-first / DSL-fallback), so `explain show data …` and DSL-only `--all-rows` still reach the DSL node; rejected a `DynamicSubgrammar` mode-gate (its resolution cache key omits `mode`). `build_explain_sql` slices the inner SQL off the source (excludes `explain`) and reuses the existing SQL builders; `do_explain_plan` runs the carried text verbatim, no params. Advanced `explain update`/`delete` now route through SQL (identical plan, full SQL syntax); DSL-explain tests pinned to simple mode. Reframed OOS-2 as a *deferred* exclusion (per ADR-0000's out-of-scope discipline), not a rejection. OOS (deferred): EXPLAIN of DDL (no query plan exists) - [ADR-0040 — A per-command completion marker (✓/✗) replaces the `[ok]` summary line](0040-completion-marker-replaces-ok-summary.md) — **Accepted 2026-05-30 (issue #9)**, amends ADR-0014 / ADR-0028 / ADR-0019 output conventions, builds on ADR-0037's mode-tagged echo. An audit of the whole command surface found the `[ok] ` summary line duplicates the echo line above it (verb+subject) everywhere; its only unique contribution is the success-vs-error signal (and `explain select` even rendered `[ok] explain` with an empty subject post-ADR-0039). Decision: drop the `[ok]` line and the symmetric `"…" failed:` prefix; the echo line gains a trailing inline **✓** (green, success) / **✗** (red, failure) — `running:` becomes a pending state that resolves to ` ✓/✗` on completion (status set via the existing `rfind(Echo)` lookup). Content (row counts, structure, data, plan tree, teaching echo) unchanged. Scoped to the DSL/data/SQL family that has the redundant echo+`[ok]` pair; app-command `[ok]` lines (`rebuild`/`export`/`now editing`) are payload-bearing, have no echo to mark, and stay as-is. `ok.summary` retired; `dsl.failed` reduced to the rendered reason. Broad but mechanical snapshot churn. OOS: app-command `[ok]` lines, the `[WRN]` validity indicator, and the tag colours (issue #10) - [ADR-0041 — Copy the output panel to the system clipboard](0041-copy-output-to-clipboard.md) — **Accepted 2026-06-02 (issue #11)**, amends ADR-0003's app-command registry (adds **`copy`** / `copy all` / `copy last`). The friction it removes: filing a bug report meant terminal-selecting the output panel and fighting wrapping/borders. New **app-level command** (sigil-free, both modes): `copy` / `copy all` copy the whole panel; `copy last` copies from the most recent echo line to the end. **Mechanism — OSC 52 *and* native (`arboard`), always both**, because OSC 52 acceptance is undetectable (no terminal ack), so a true "fall back when unsupported" can't be built: emit the OSC 52 escape (no new dep — `base64`+`crossterm`; works over SSH; tmux-passthrough-wrapped via `$TMUX`), then a best-effort native write whose failure is ignored (headless host — OSC 52 carried it); the two carry identical content. **Format — plain text verbatim as rendered** (tags, `✓`/`✗`, box-drawing) joined by `\n`, without viewport padding/wrapping; a drift-lock test pins `OutputLine::plain_text` to `render_output_line`. `arboard` added **`--no-default-features`** (drops the `image` crate; X11-only on Linux — `wayland-data-control` deliberately omitted as it ~doubles the dep tree and OSC 52 covers native-Wayland). Security: write-only, scans clean for arboard's tree (cargo audit / osv-scanner / grype), 1Password-maintained, minimal surface. OOS: Markdown export, selection/range, a keybinding, OSC 52 read, `screen` passthrough +- [ADR-0042 — H1a parse-error pedagogy in the grammar-tree era](0042-h1a-parse-error-pedagogy-grammar-tree.md) — **Accepted 2026-06-03.** Continues **H1a** from ADR-0021 against the ADR-0024 grammar tree (ADR-0021's chumsky mechanism is dead). Records the **baseline already shipped** — per-command `usage:` block (38 `parse.usage.*` templates), available-commands fallback, structural "after `…`, expected …" wording, source-derived ident slot labels ("table name"/"column name"), curated `parse.custom.*` near-miss messages, and the ADR-0027/0033/0036 schema-aware `[ERR]` diagnostics — so H1a is *substantially* delivered at the intent level. Defines the remaining work as **(1)** a verified per-command **near-miss matrix** (`tests/typing_surface/` + `tests/it/parse_error_pedagogy.rs`) as the definition of done, test-first; **(2)** **friendlier literal expectation labels** — optional prose glosses on `Word`/`Punct`/`Flag` positions that *add* role context while always keeping the exact literal visible (e.g. "a filter clause: `where …` or `--all-rows`"); **(3)** **advanced-mode SQL** near-miss parity (RETURNING scope, CTE-arity positioning, `CROSS JOIN … ON`, INSERT…SELECT count) — **in scope**, kept distinct from ADR-0019 §OOS-2 which covers advanced-SQL *engine*-error sanitisation, a different layer. Catalog/anchor-phrase discipline (ADR-0019) preserved; no public API change. OOS: I3/I4, spell-correction, multi-error reporting, verbosity-gating the usage block