Commit Graph

115 Commits

Author SHA1 Message Date
claude@clouddev1 fde50ce3bf fix(ui): mark sidebar focus with an accent colour, not bold (#25)
The focused sidebar panel border (ADR-0046 DC3) was bright `fg` plus
`Modifier::BOLD`. Bold box-drawing glyphs render as broken/gapped
line-art in the asciinema cast player and are fragile in some terminals.

`panel_border_style` now marks focus with a non-bold accent colour
(`theme.mode_simple`, blue); the unfocused border stays muted. Bold is
untouched on text spans (titles, key hints) — the constraint is
specifically that box-drawing borders carry no bold attribute.

Pure style change: the Tier-2 snapshots are text-only so none needed
re-accepting; the Tier-1 assertion was updated and a render-level test
now checks the rendered border cells carry the accent and no bold.

ADR-0046 Amendment 1.
2026-06-12 15:01:26 +00:00
claude@clouddev1 3d4a0fd45e fix(render): trim IEEE-754 noise from displayed decimal arithmetic (#32)
`decimal` is stored as exact TEXT, but SQLite has no native decimal type,
so arithmetic/aggregation implicitly coerces it to an IEEE-754 double.
The computed result carries no playground type, so `sum(price * qty)`
rendered the double's full noise — `298.59999999999997` for `298.60` — a
confusing, off-topic float lesson for a teaching tool.

Add `format_real_display`: round REAL values to 15 significant figures
(a double's reliable precision) then take the shortest round-tripping
form, collapsing `298.59999999999997` to `298.6`. Wired into `format_cell`
(result-set / `show data` cells) only — the sole surface where the noise
appears, since it arises from arithmetic.

Every other f64->string path keeps full precision for semantic, not
cosmetic, reasons: CSV persistence stays byte-exact for round-trip;
`render_value` is a canonical identity key for the uniqueness dry-runs
(dry_run_unique, check_uniqueness_collisions), where rounding would
report collisions the exact-valued engine wouldn't; FK-key matching and
EXPLAIN-SQL literals likewise stay exact.

ADR-0005 Amendment 1; +7 tests.
2026-06-12 14:42:22 +00:00
claude@clouddev1 7e4bc122be fix(completion): treat a bare in-scope table alias as an alias, not an unknown column (#31)
A bare table alias typed where a column is expected — `… GROUP BY o`,
with `o` aliasing `FROM Orders o` — was a blind spot: completion offered
nothing for `o`, and the hint panel called the in-scope alias an unknown
column (`no such column o on table Orders, ...`).

Completion now offers each FROM source's qualifier (alias-if-present-else
table-name) at a bare sql_expr_ident slot, folded into the column
candidate list; on an exact-qualifier partial the alias source steps
aside so the diagnostic can surface. The bare-reference diagnostic arm
emits a targeted `alias_used_as_column` / `table_used_as_column` hint
("`o` is a table alias — write `o.<column>` ...") after the
projection-alias check, so ORDER-BY alias refs still win and a genuine
unknown column still reports `unknown_column`.

Two guards keep the qualified-form advice correct: SQL only (role
`sql_expr_ident`, so the DSL `expr_column` path keeps `unknown_column`
since the DSL has no `table.column` syntax) and effective-qualifier
match (alias-if-present-else-table, so an aliased source referenced by
its shadowed real name falls through rather than being advised as
`name.<column>`). The diagnostic is a drop-in replacement for
`unknown_column` at the same span/Error severity, so verdict/overlay/hint
paths are unchanged.

ADR-0032 Amendment 3; +10 tests.
2026-06-12 14:03:00 +00:00
claude@clouddev1 30b2677bf3 docs: ADR-0048 Phase 2 implemented + handoff 66
- ADR-0048: status → Phase 1 + Phase 2 implemented; D2 amendment
  (quoted dates, no date-literal token) and the override × UNIQUE
  capacity-guard decision; phasing/Status blocks marked done.
- README index: 0048 entry updated (Phase 2 shipped, 2400 tests).
- requirements.md: SD2 → [x] (the override-hooks core + column-fill).
- handoff 66: this session's Phase 2 build + the two /runda passes.
2026-06-12 09:44:36 +00:00
claude@clouddev1 78c38e8b33 docs: ADR-0048 Phase 1 accepted/implemented + handoff 65
- ADR-0048 status -> Accepted; Phase 1 implemented (commits
  202e25a..fbd219b), with the pre-build and post-implementation /runda
  passes and the 2358-test green state recorded; index entry updated.
- requirements.md: SD1 [x] (whole-row seed + FK/junction, both modes,
  --seed reproducibility with no exceptions), SD2 [/] (core generators /
  determinism done; the set override clause + column-fill are Phase 2),
  A1 14/15 (only hint/H2 remains unregistered).
- Handoff 65: the full seed Phase-1 build, the two /runda passes, where
  the code lives, and Phase-2 / next steps.
2026-06-11 21:49:06 +00:00
claude@clouddev1 202e25a94f feat(seed): fake-data generation library + fake dependency (ADR-0048 P1.1)
The pure generation half of `seed` — no command wiring yet:
- src/seed/: ColumnSpec + Generator model and a seeded StdRng; the
  type-gated name-heuristic catalogue (D7) with documented
  false-positive guards; table-context name disambiguation (D11);
  identifier (D10) and enum-ish (D12) detection; per-type + bounded-date
  generators (D8); the hand-rolled product generator (D9); and PickFrom
  for IN-CHECK / enum lists.
- Adds the `fake` crate (v5, default features). Verified: single rand
  0.10.1 (no duplication), determinism via one seeded StdRng driving
  both fake and the hand-rolled generators, security-clean across
  osv/grype/trivy.
- ADR-0048 D3 updated to record the dependency verification.

32 Tier-1 tests (exact-value via fixed --seed); 1673 lib tests pass,
clippy all-targets clean.
2026-06-11 15:35:17 +00:00
claude@clouddev1 0af7f56821 docs: ADR-0048 — seed fake-data generation command (SD1/SD2, A1)
Dedicated `seed` command: realistic, name-aware fake data via the
`fake` crate + a type-gated heuristic catalogue, table-context name
disambiguation, a hand-rolled product generator, bounded dates, an
identifier-uniqueness rule, a quoted `set` override clause (value /
list / generator / range), `--seed` reproducibility, FK sampling
(empty-parent error, junction distinct combinations), CHECK
derive-or-friendly-fail, undo as one batch step, replay as a
data-write, capped auto-show, and an enum/CHECK advisory.

Design settled across an extended user fork dialogue, then hardened
by a /runda DA pass that found six blockers (undo, replay, set
quoting, CHECK handling, advisory phasing, auto-show flood) — all
folded in, genuine forks re-escalated and user-resolved.

Takes up SD2 ([~]->[ ]); SD1 implementation in progress.
2026-06-11 13:48:19 +00:00
claude@clouddev1 f0afec3812 docs: session handoff 64 + ADR-0047 implemented (#22/#24)
Flip ADR-0047 Status -> implemented (commits f879d54..2d0f4b2, phased
A->B->C + flat-rectangle restyle); update the README index entry to
match (implemented, flat black-on-yellow rectangles, final 2290-green
tally, website-cast follow-up noted). Add session handoff 64 covering
#24 (vi load-picker nav) and #22 (ADR-0047 demo overlay layer).
2026-06-11 09:59:51 +00:00
claude@clouddev1 2d0f4b2958 feat(ui): flat filled rectangles for demo overlays (#22, ADR-0047 D4)
Render the keystroke badge and step caption as a solid yellow rectangle
with no border glyphs and a one-cell text margin, instead of a
rounded-border box — deliberately unlike the app's bordered panels so
the demo overlays read as a distinct, eye-catching callout. Shared
fill_overlay_rect helper (borderless Block fill + inset Paragraph).
Snapshots regenerated; ADR-0047 D4 wording updated.
2026-06-11 08:40:07 +00:00
claude@clouddev1 f879d54721 feat(cli): --demo demonstration mode flag + app plumbing (#22, ADR-0047 D1)
Add `--demo` (and the RDBMS_PLAYGROUND_DEMO env fallback) to enter
demonstration mode, threaded onto App.demo_mode through run_loop —
mirrors the --no-undo plumbing. Off by default, zero footprint when
off. The --help line advertises only the visible keystroke badges;
the Ctrl+] caption trigger is kept low-profile (ADR-0047 D6 updated).

Phase A of ADR-0047; behaviour (badges/captions) lands in B and C.
2026-06-10 22:22:12 +00:00
claude@clouddev1 e9eb1b177e docs: ADR-0047 — demonstration overlay layer for casts/teaching (#22)
Accepted decision record for the in-app demo overlay: a --demo mode
that shows automatic keystroke badges ([TAB], [ENTER], …) and a
stealth Ctrl+]-delimited step-caption buffer, both as floating
black-on-yellow boxes at the output panel's bottom-right. All forks
user-confirmed; a /runda pass contributed 10 tightening findings.
Indexed in docs/adr/README.md.
2026-06-10 22:16:44 +00:00
claude@clouddev1 18303784a0 docs: session handoff 63 + ADR-0046 marked implemented (#20/#21/#23)
ADR-0046 status -> Accepted + implemented (8 commits 9f5f76b..22bec61);
README index updated; the two draft-divergent decisions recorded inline
(App.relationships not SchemaCache; nav overlay partial-clear + gutter).
Handoff 63 covers the full UI build across Phases A/B/C; issues
#20/#21/#23 closed on Gitea.
2026-06-10 21:30:00 +00:00
claude@clouddev1 22bec61d11 feat(ui): scroll the focused sidebar panel + refine the nav overlay (#21, ADR-0046 DC3 + DC2)
DC3 — navigation-mode scroll: the focused Tables / Relationships panel
scrolls (Up/Down by a line, PageUp/PageDown by its visible-row count).
Per-panel offsets are clamped to content at render time, and the
renderer reports each panel's visible rows for paging — mirroring the
output panel's scroll. render_items_panel / render_relationships_panel
take &mut App, count their rows, and store+clamp the offset before
building the borrowing lines.

DC2 refinement: the expand-on-focus overlay now clears only the sidebar
strip plus a one-column gutter, leaving the base output/input/hint
visible (unchanged) to the right rather than blanking the whole area —
truer to "underneath keeps its layout", with the gutter keeping the
cut-off edge clean (chosen after eyeballing both variants). ADR DC2 and
the overlay snapshot updated to match.

Tests: line/page scroll move only the focused panel and clamp; the
render clamps a past-the-end offset so the last row stays visible.
2026-06-10 21:27:13 +00:00
claude@clouddev1 94825d0f36 feat(ui): relationships sidebar panel + schema data (#21, ADR-0046 DB2/DB4)
The left column now stacks a Tables panel over a Relationships panel.
Each relationship renders as three narrow lines — its name, then the
endpoints broken at the arrow (Customers.id -> / indented
Orders.customer_id) — ellipsized past the inner width. The panel is
content-sized within [5 rows ("(none)" when empty), half the column];
the Tables panel keeps the rest (>=3 rows). Phase C adds focus+scroll
for content beyond the cap (clipped for now).

Data path: a new worker Request::ReadAllRelationships +
Database::read_all_relationships returns full RelationshipSchema
records; the runtime posts them via a RelationshipsRefreshed event
alongside the schema-cache refresh, and the App holds them in a new
`relationships` field.

ADR deviation (recorded in ADR-0046 DB2 + index): DB2 specified this
data on SchemaCache; it lives on the App instead — SchemaCache is
walker/completion-facing and needs only relationship names (untouched),
while the full records are UI-only, so App is the cleaner home and it
avoids editing ~23 SchemaCache literals. No behavioural difference.

Tests: panel-height bounds, the three-line render, the empty "(none)"
case, a snapshot, read_all_relationships end-to-end (real DB via the
m:n junction), and the event->field handler.
2026-06-10 18:44:27 +00:00
claude@clouddev1 93266b99c9 docs: ADR-0046 UI sidebar nav-mode + responsive input/hint (#20/#21/#23)
Accepted; implementation pending, phased A→B→C. Treats the three
coupled UI issues as one decision (shared width/height budget):

- #20 hint jumpiness: hint height becomes a function of terminal
  geometry, fixed between resizes, so it no longer shoves the
  input/output panels.
- #21 left column: kept but width-optional (hidden by default ≤90),
  with a new relationships sibling panel and a Ctrl-O navigation/focus
  mode (peek-reveal, expand-on-focus overlay, scroll).
- #23 long input: single-logical-line horizontal scroll plus a 2-row
  display when tall, preserving the ADR-0027 indicator reserve.

A pre-build /runda DA pass drove key corrections: Ctrl-B→Ctrl-O (Ctrl-B
is the tmux prefix), an additive SchemaCache.relationship_details field
(retyping would break completion), full nav-mode key disposition +
modal gate, and Tier-2 snapshot coverage. Reconciles requirements
S1 (evolved), S2 (overridden — separate relationships panel), and
S4 (corrected — the stale "keyboard-toggleable" hint claim is struck;
no toggle added).

Updates docs/adr/README.md index and docs/requirements.md S1/S2/S4.
2026-06-10 16:57:46 +00:00
claude@clouddev1 8bd43ccadf feat: create m:n relationship convenience command (C4, ADR-0045)
`create m:n relationship from <T1> to <T2> [as <name>]` generates a
junction table with one FK column per parent PK column ({table}_{pkcol},
typed via fk_target_type), a compound PK over them, and two CASCADE 1:n
relationships -- all in one do_create_table call = one undo step.
Auto-named {T1}_{T2} (optional `as`), both modes, compound-parent PKs
supported (ADR-0043). Self-referential m:n / PK-less parent / internal
junction name / name collision all refused.

Wired across every surface: grammar (separate CREATE_M2N node), worker
executor, runtime dispatch, completion ("m:n" composite), hints,
highlighting, help + usage catalog + disambiguator, and the advanced-mode
DSL->SQL teaching echo (render_create_m2n, round-trips as valid SQL).

Generalized/fixed framework assumptions the build + two /runda passes
surfaced (all behaviour-preserving for existing commands):
- simple-mode dispatch committed simple.first() unconditionally -> tries
  candidates, so `create table` no longer shadows `create m:n`.
- the completion continuation-merge was advanced-only -> runs in simple
  mode too when an entry word has >1 DSL form (gated simple_count>1).
- do_create_table now rejects internal `__rdbms_*` names (closes a
  pre-existing hole on the DSL create-table path too, not just m:n).
- usage disambiguator now recognizes the `m:n` opener.

Tests: 14 integration (tests/it/m2n.rs), 7 typing-surface matrix, echo /
highlight / usage / internal-name units. Closes C4.
2237 pass / 0 fail / 1 ignored. Clippy clean.
2026-06-10 14:26:33 +00:00
claude@clouddev1 e598008ecf docs: ADR-0045 m:n convenience command (C4); accepted
create m:n relationship from <T1> to <T2> [as <name>] generates a
junction table (compound PK over the two FK column sets, CASCADE FKs)
plus two 1:n relationships, in one do_create_table call = one undo
step. Forks user-confirmed; /runda DA pass verified the reuse against
code and the no-PK-tables-exist-in-advanced-mode fact (parent-PK guard
retained). Self-referential m:n refused; FK cols named {table}_{pkcol}.
2026-06-10 13:18:07 +00:00
claude@clouddev1 0a343036d8 feat: compound-FK bus routing + complete V1 relationship visualization (ADR-0044)
Completes requirement V1. A compound (multi-column) FK now routes a
bus connector — each paired endpoint's stub merges into a shared
vertical channel that splits to the other side — plus an explicit
"(a, b) ▶ P.(x, y)" pairing line; the bus generalises the single-column
jog (reproducing it exactly, so prior snapshots are unchanged).
Self-referential FKs render as two same-named boxes.

- output_render.rs: gutter_seg routes all endpoint pairs via a
  junction() bus; pairing line for compound FKs; compound, self-ref,
  and compound-from-data (build_diagram_table glue) tests + snapshots
- compound_fk.rs: worker test that show_relationship carries both
  paired column lists into the diagram payload
- db.rs: document do_show_one's now-app-superseded relationship prose
  branch (retained as a worker-API/text fallback; could back a future
  non-visual display option, cf. ADR-0044 OOS-7)

Second /runda pass over the implementation: confirmed ADR-compliance,
UTF-8/byte-range safety, and edge-case routing. The ADR §3 last-resort
helper line was considered and rejected (vertical fallback + ratatui
truncation cover all realistic cases). ADR-0044 marked implemented;
requirements.md V1 -> [x].

Full suite 2207 pass / 0 fail / 1 ignored; clippy nursery clean.
2026-06-10 10:17:09 +00:00
claude@clouddev1 bb02dfb752 docs: ADR-0044 relationship visualization (V1); accepted
Two-table connector diagrams (Style A) for relationships, resolving
ADR-0016 OOS-1 and the open half of requirements.md V1. Reach is
'relationship-relevant' (show relationship / show table / relationship
DDL echoes; incidental echoes keep prose). App-side rendering, width-
adaptive side-by-side vs vertical, compound-FK pair routing, bold box
title rows. Revised after a /runda DA pass corrected three inverted-
architecture claims (App-side rendering, untracked output width,
prose-in-worker show-relationship).

Index updated per ADR-0000.
2026-06-09 21:47:35 +00:00
claude@clouddev1 b17148b467 docs: scrub GitHub-specifics after Gitea migration; add tea issue conventions
- Cargo.toml: repository -> git.lazyeval.net/oli/rdbms-playground
- requirements.md: backlog now tracked as Gitea issues
- ADR-0001 Amendment 1: distribution channel reopened post-migration
  (Decision text preserved per supersede-don't-rewrite)
- CLAUDE.md: issue-tracking working method + Gitea/tea operational section
2026-06-09 20:07:40 +00:00
claude@clouddev1 4752ba29a0 feat: compound-PK foreign-key references — grammar + tests (ADR-0043)
Multi-column FK parsing on both surfaces: DSL from P.(a, b) to
C.(x, y) (parenthesized endpoint; single bare form unchanged) and
SQL FOREIGN KEY (a, b) REFERENCES P(x, y) incl. bare-reference
auto-expand. consume_fk_reference + the table-level/ALTER FK
parsers collect column lists; the from P. completion now offers
( (snapshots updated). 12 integration tests in
tests/it/compound_fk.rs cover parse (both surfaces), engine-enforced
FK, arity + partial-PK + per-pair-type-mismatch refusal,
--create-fk per-column, save->rebuild round-trip, undo (one step),
and single-column preservation. Mark T3 [x]; ADR-0043 implemented.
2026-06-09 18:44:37 +00:00
claude@clouddev1 b14f0199e9 refactor: relationship model to column lists for compound FK (ADR-0043)
Move the FK column fields String->Vec<String> through all six
layers (AddRelationship/SqlForeignKey AST, RelationshipSchema,
metadata, project.yaml, ReadForeignKey, RelationshipEnd). Metadata
stores comma-joined lists in the existing TEXT cells; project.yaml
endpoints now columns: [a, b] (house style). Executor logic is
multi-column ready: resolve_fk_parent_columns (full-PK F-A +
auto-expand F-D), per-pair type-compat, schema_to_ddl multi-column
emission, pragma FK read grouped by id, auto-name + --create-fk
per-column, multi-column teaching echo. Single-column behaviour
preserved (one-element vecs); all 2181 tests green. The grammar to
parse multi-column input lands next.
2026-06-09 18:25:40 +00:00
claude@clouddev1 b688592b4c docs: ADR-0043 implementation-readiness notes from /runda DA pass
DA pass found three change sites the first sketch missed
(teaching-echo renderers, --create-fk per-column creation, the
auto-name generator) and made explicit the rules the forks left
implicit: SQLite FK precondition (compound PK provides the unique
index), explicit parent cols must be the PK set (any order,
positional), arity/empty/inline-rejection wording, single-in-parens
accepted, --create-fk per-column typed to fk_target_type. Expanded
the test plan to cover enforcement, auto-expand, undo, round-trip.
Fixed a stale 'legacy yaml loads' test line (no back-compat).
2026-06-09 17:11:01 +00:00
claude@clouddev1 274e2b17b7 docs: ADR-0043 compound-PK foreign-key references (T3); accepted
Audit found single-column FK woven through ~15-20 sites; earns an
ADR. Decision: reference the parent's full compound PK, matched
positionally to an equal-length child list, per-pair type compat.
DSL `from P.(a,b) to C.(x,y)`; SQL `FOREIGN KEY (x,y) REFERENCES
P(a,b)` with bare-FK auto-expansion. Storage follows the existing
primary_key: [...] list convention (yaml columns: [a,b], uniform
JSON in unchanged metadata TEXT cols); back-compat not required,
so no migration. Also marks T3's verified scope.
2026-06-09 17:01:38 +00:00
claude@clouddev1 c305dc7282 docs: mark H1a done via the ADR-0042 systematic pass
requirements.md H1a → [x]: the per-command near-miss matrix (entry words,
missing clauses, committed multi-forms, both modes) plus the gap fixes
(G1 `1:n relationship`, G2 select projection gloss, G3 mode-aware usage
showing all valid forms, G4 `with` CTE template, CROSS JOIN ON teaching
message) close the systematic pass. The advanced-SQL items the survey
flagged (INSERT…SELECT count, RETURNING scope) were verified already
present. One low-priority residual is deferred by decision (submit-time
expression first-set at non-projection positions).

ADR-0042: record the pre-existing `SELECT *` arity caveat (INSERT…SELECT
with a star projection isn't expanded for pre-flight arity; engine
catches it at execution — adjacent to ADR-0019 §OOS-2).
2026-06-06 07:44:55 +00:00
claude@clouddev1 d6e229f0f5 feat: H1a CROSS JOIN ON teaching message; advanced-SQL gaps re-verified (ADR-0042)
Empirically re-checking ADR §3's advanced-SQL "gaps" reversed two of
three — the code survey that produced the list was wrong:
- INSERT…SELECT column-count: already handled (verdict=Error, "the
  column list names N column(s) but M value(s) are given";
  insert_select_arity_mismatch_fires).
- RETURNING scope: already handled (completion offers the table's
  columns; `returning <unknown>` → unknown_column diagnostic).

The one genuine residual is fixed: `select … cross join b on …`
rejected the ON with a bare "expected end of input". Add
parse.cross_join_no_on — "a CROSS JOIN has no ON clause — it pairs
every row; for a join condition use `JOIN … ON`, or filter with
`WHERE`" — rendered when the failing token is `on` and the most
recent consumed join is a CROSS join (a precise signature: every
other join requires `on`, so `on` is expected there, not a failure).
Render-only in format_walker_error; two misfire guards locked (plain
join still asks for ON; a stray `on` with no join does not fire).

ADR-0042 §3 corrected + Implementation-outcome records the advanced-SQL
re-check and the user-confirmed low-priority residual (submit-time
expression first-set at non-projection positions, where typing-time
completion already offers the right candidates).

Full suite green (lib 1578 / it 388 / typing_surface_matrix 192); clippy clean.
2026-06-05 19:02:11 +00:00
claude@clouddev1 1d4923b15b fix: H1a G3 advanced usage shows all valid forms; complete near-miss matrix (ADR-0042)
The /runda DA pass found G3 over-corrected: advanced-mode `create`/`drop`
showed SQL forms only, hiding the DSL fallback forms that are valid input
in advanced mode (verified: `create table Foo with pk`, `drop column …`
parse and dispatch). Per the user decision, the advanced usage block now
shows every form valid in the mode, SQL-primary first, then the DSL
fallback forms — a usage hint must never hide working input. Simple mode
unchanged (DSL forms only).

Matrix completion (closing the residual coverage tail):
- arg-less app commands (help/rebuild/new/load/undo/redo/export/import)
  audited + locked — all reject trailing junk with "expected end of
  input" + usage.
- committed multi-forms (add index/constraint/1:n relationship, drop
  index/constraint/relationship, show table, change column, create index,
  alter table add/drop) audited + locked in
  near_miss_matrix_committed_multiforms — each renders its own
  form-specific missing-keyword message + usage.

Also from the DA pass:
- G2 distinct+all detector empirically verified unique to projection
  start (no misfire at count( / union / union all / select distinct).
- stale `chumsky` comment removed (app.rs import handler).
- ADR-0042 Implementation-outcome section records G1–G4, the
  user-confirmed G3 decision, and the now-complete matrix coverage.

Full suite green (lib 1578 / it 387 / typing_surface_matrix 192); clippy clean.
2026-06-05 18:46:57 +00:00
claude@clouddev1 0e6f767848 docs: ADR-0042 — continue H1a parse-error pedagogy on the grammar tree
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)
2026-06-03 14:05:09 +00:00
claude@clouddev1 d0c8f9d5d2 feat: copy the output panel to the system clipboard (#11)
New app-level `copy` / `copy all` / `copy last` command (ADR-0041).
Delivery is OSC 52 *and* a best-effort native write (arboard), always
both — OSC 52 acceptance is undetectable, so a true fallback can't be
built. Payload is the panel's plain text exactly as rendered (tags,
✓/✗, box-drawing), drift-locked to render_output_line. arboard added
--no-default-features (X11-only; OSC 52 covers Wayland).

Amends ADR-0003's command registry; requirements V6.
2026-06-02 14:23:21 +00:00
claude@clouddev1 516848ff63 test: integration-test the mode persist-on-unload wiring (#14)
The post-/runda DA pass on 4cd574b found the persist-on-unload wiring
(quit + project switch calling Database::set_mode) had no integration
test — only the db-level set_mode behaviour was covered, not that the
runtime actually invokes it on unload.

Add runtime::switch_persists_the_outgoing_projects_mode, driving the
real handle_project_switch end-to-end and asserting the outgoing
project's project.yaml recorded the mode it was left in. Red-first
verified: with the set_mode call disabled it fails (None vs
Some(Advanced)). The quit unload site shares the same set_mode call;
Action::Quit emission is already covered in app tests.

Updates ADR-0015 Amendment 1 coverage note.
2026-06-02 08:06:48 +00:00
claude@clouddev1 4cd574b909 feat: persist & restore per-project input mode (#14)
The input mode always started in simple; a learner who quit in advanced
had to re-toggle every launch. Store the mode per-project in project.yaml
(project.mode:, optional, default simple) and restore it on every open.

Mode is live UI state, not schema: the worker stamps the current mode
into project.yaml on every write, so a later command rewrites the live
value rather than clobbering it — no db round-trip needed. The mode is
persisted on unload (quit + project switch) so the mode you leave a
project in is always what reopens; the `mode` command also persists
immediately. A switch saves the outgoing mode, then restores the
incoming project's stored mode.

New --mode simple|advanced CLI flag (precedence --mode > stored >
simple; combines with --resume). A teacher can ship a project that
opens in advanced mode and export it to students (the mode travels in
the zip).

ADR-0015 Amendment 1; ADR-0003 note; help banner; requirements L1b.
2026-06-02 06:47:34 +00:00
claude@clouddev1 ae57c6fc82 feat: colour output tags by status, not mode — readable error bodies (#10)
The output tag was tinted by submission mode for every line kind, so a
[system] line and an [error] line rendered with an identical leftmost
tag — distinguishable only by body colour. And flooding the whole error
body in red made long messages hard to read.

Colour the tag by message status instead (its OutputKind): [system] →
green, [error] → red; the echo tag keeps the mode tint (ADR-0037's
actual purpose — per-command success rides the ✓/✗ marker). Bodies go
neutral; the error body stays bold for weight (rustc-style: severity-
coloured label, readable bold message). Yields a status traffic-light
matching the ✓/✗ palette.

Narrows ADR-0037's mode side-channel to the echo line it was always for.
ADR-0037 Amendment 1; closes the tag-colour gap ADR-0040 flagged as OOS.
2026-05-31 22:02:12 +00:00
claude@clouddev1 6d8c9eea36 feat: curated SQL function list — Tab completion (#15) + typing-time typo hint (#16)
Add src/dsl/sql_functions.rs (KNOWN_SQL_FUNCTIONS) as the shared source
of truth at sql_expr_ident slots:

- #15: offer the functions as Tab candidates under a new
  CandidateKind::Function + ninth Theme colour tok_function (blue,
  distinct from keyword/identifier/type).
- #16: restore the column-typo flag the #6 fix had dropped wholesale —
  invalid_ident_at_cursor now bails only when the partial prefix-matches
  a known function, else falls through to the schema-column check.

A column named like a function (e.g. `count`) is deduped (column wins).
`cast` is excluded — CAST(x AS type) is not a plain-call shape.
The no-validation-allowlist posture stands: the list drives completion +
the typo hint only, never parse-time acceptance.

Docs: ADR-0022 Amendment 6, ADR-0031 status note, README index,
requirements I3/I4 + refreshed test baseline.
2026-05-31 11:49:10 +00:00
claude@clouddev1 8311de44a8 feat: replace the [ok] summary line with a ✓/✗ echo marker
An audit of the command surface found the `[ok] <verb> <subject>`
summary line duplicated the echo line above it everywhere; its only
unique signal was success-vs-error. Retire it: a command's echo line
now resolves from `running: <input>` to `<input> ✓` / `<input> ✗`
on completion, and the symmetric `"<verb> <subject>" failed:` prefix
is dropped (only the reason remains). Content lines (row counts,
structure, plan tree, teaching echo) are unchanged.

Echo lines carry an EchoStatus; executed commands push Pending and
resolve the oldest-pending echo on their result event (FIFO worker —
correct under interleaving). Parse-time and pre-flight rejections are
not executed and keep their running: + caret rendering. App-command
[ok] lines (rebuild/export/replay) are payload-bearing and untouched.
ADR-0040.
2026-05-30 21:38:48 +00:00
claude@clouddev1 f62cccec55 feat: support explain over advanced-mode SQL queries
explain now wraps the advanced SQL commands — select, with (CTE),
insert, update, delete — in addition to the DSL show data/update/
delete it already covered, rendering through the same plan tree
(ADR-0039, closing the ADR-0030 OOS-2 gap).

Implemented as a second Advanced `explain` CommandNode under the
shared entry word, reusing the established shared-word dispatch
(SQL-first, DSL-fallback) rather than new grammar machinery.
build_explain_sql slices the inner SQL off the source and reuses the
existing SQL builders; do_explain_plan runs EXPLAIN QUERY PLAN over
the carried text verbatim (never executes, so safe for destructive
verbs). Advanced explain update/delete now route through SQL with an
identical plan; DSL-explain tests pinned to simple mode. Help and
usage text now list the advanced explain forms.
2026-05-30 18:44:05 +00:00
claude@clouddev1 f7ca288fe1 fix: grow the hint panel for long prose hints
A long prose hint (insert field-value hints, the parse.usage.*
synopses) wrapped but was clipped by the fixed one-row Hint panel,
hiding the most useful tail. The candidate list already scrolled
horizontally, so only prose was affected.

Pre-wrap the prose body and size the Hint panel to the wrapped line
count: one row by default, growing to a 3-row cap and reclaiming the
space when short, with an ellipsis backstop on the last row. Also
shorten the 299-char create-table usage synopsis to a terse one-liner
(the full grammar remains under `help`). ADR-0022 Amendment 5.
2026-05-30 09:02:12 +00:00
claude@clouddev1 d20f765325 feat: give column data types a dedicated syntax-highlight colour
Both Node::Ident and Word carried a highlight_override field, and
both were dead — the walker driver discarded the Ident's and
walk_word hardcoded Keyword. So column types (int, serial, …)
rendered identically to table/column names.

Wire both overrides through, and add a dedicated HighlightClass::Type
with its own theme colour (tok_type), distinct from keyword-purple
and identifier-teal. The three type Ident slots opt in, so canonical
types and the advanced-mode single-word SQL aliases (float, varchar,
…) render as types; the two-word `double precision` alias opts in via
a new Word::type_keyword constructor. ADR-0022 Amendment 4.
2026-05-29 22:07:18 +00:00
claude@clouddev1 10e5197c19 feat: bring simple-mode insert arity diagnostics to parity with advanced
A wrong-count simple-mode insert now shows the friendly per-column arity
message at typing time (instead of a bare "expected `,`/`)`") and is
blocked from dispatch at submit — unifying simple and advanced mode onto
the one ADR-0027 model (structural parse + ERROR diagnostic), where they
had diverged.

Grammar: a simple-mode-only arity gate (dsl_insert_value_list) routes a
wrong-count DSL insert tuple to the type-blind fallback so it matches
structurally and the per-tuple arity diagnostic fires. The gate is gated
to simple mode, so advanced behaviour is unchanged. count_tuple_values
and the target-column selection (insert_target_columns) are now shared
by both grammars.

Diagnostic: dml_insert_arity_diagnostics is mode-aware — advanced Form B
expects all columns; simple Form B/C expects the user-fillable columns
(serial/shortid auto-fill). It counts the DSL Form A role and scans the
keyword-less Form C tuple. New catalog keys name the fillable/auto split
and the all-auto-table case.

Submit: a wrong-count DSL insert now parses Ok + carries the ERROR
diagnostic, so a unified Ok-arm pre-flight (dsl_insert_count_mismatch_notes)
blocks dispatch and teaches; the previous Err-arm note retires.
advanced_alternative_note's gate now reads the validity verdict so it
still fires for the parse-Ok-with-error shape.

Docs: ADR-0036 Amendment 2 (+ README index) and requirements.md H1a.
2026-05-29 20:45:21 +00:00
claude@clouddev1 fa5d0dc6da fix: insert VALUES between-values hint points at comma not close-paren
The ambient-hint fallback in ambient_hint_core_in_mode parsed
schemalessly, so the type-blind grammar closed an `insert … values
(…)` tuple after the first value and the "Next:" hint pointed at `)`.
With a schema available the walk knows the remaining columns and the
correct next token is `,`. Parse the fallback with the schema cache so
the expected-token prose matches the rest of the (already
schema-aware) hint ladder.

Also corrects wrong-arity closed tuples where the schemaless parse
accepted the input and the hint said "submit with Enter" for a command
the schema-aware parse rejects — the hint now surfaces the accurate
error. Three typing-surface snapshots updated to match.

Docs: ADR-0022 Amendment 3 (+ README index) records the schema-aware
fallback; requirements.md H1a cites the hint-accuracy improvement.
2026-05-29 10:22:57 +00:00
claude@clouddev1 c12ed1da9a fix: INSERT Form B value-count UX (ADR-0033 Amendment 5)
Three layered fixes for advanced/simple-mode positional INSERT
value-count mismatches (e.g. `insert into T values (...)` with the
wrong number of values for T's column count), plus ADR-0033
Amendment 5 recording the gate refinement.

Walker diagnostic (dml_insert_arity_diagnostics): the function's own
doc-comment recorded the no-column-list (Form B) case as deferred.
This commit closes that gap. Form B mismatches now emit a new
diagnostic.insert_arity_mismatch_form_b ERROR per offending tuple,
keyed off the target table's column count from the schema cache. The
[ERR] validity indicator (ADR-0027) lights up at typing time for the
reported scenario, no longer needing a submit.

Cross-mode pointer gate (advanced_alternative_note): refactored from
a hand-rolled Form B count check to a single input_verdict_in_mode(
input, schema, Mode::Advanced) call. The pointer fires only when the
verdict is None — the ADR-0027 sense of "valid". Any future static
check added to the verdict pipeline participates automatically; no
per-feature maintenance.

Teaching notes for the value-count cases users can hit before the
indicator turns red:
  - simple-mode submit: insert.form_b_extra_values_note covers under-,
    in-window, and over-supply against the Form B contract; suppressed
    when the cross-mode pointer fires (to avoid parallel advice).
  - advanced-mode dispatch pre-flight:
    insert.form_b_positional_count_mismatch_note catches a submitted
    mismatch with a teaching message before the engine produces its
    raw NOT-NULL / type error.

The advanced_mode.also_valid_sql pointer wording was reworked to
"trying to write SQL? switch with `mode advanced`, or prefix `:` to
run once". One insta snapshot regenerated.

ADR-0033 Amendment 5 records the gate change: Amendment 3's "would
parse in advanced mode" now reads as "valid in advanced mode" in the
explicit verdict-is-None sense, with the precise definition spelled
out so future readers can't drift back to the syntactic-only reading.
ADR-0000 index entry updated; docs/requirements.md H1a citation added
listing the three new pedagogical strings.

Tests added (8): four walker arity tests (under-supply, over-supply,
match, unknown-table); two app-level teaching-note tests for the
sibling cases (under-supply, over-supply beyond total); one pointer-
gate unit test pinning the bug-case suppression; one gate-precedence
test ensuring only one advice line per error. Existing
simple_mode_submit_of_sql_construct_appends_advanced_pointer updated
to use a known schema (the new validity gate requires it).

Full suite: 2031 passed, 0 failed, 0 unexpected skips. Clippy clean.
2026-05-28 16:38:33 +00:00
claude@clouddev1 63b2927f10 docs: session handoff 49 — ADR-0038 done, M4 + ADR statuses in sync 2026-05-28 12:24:07 +00:00
claude@clouddev1 5cb105b74b docs(adr): /runda DA cleanup — reflect Phases 1-3 done, pin Bucket C
Surfaces from a Devil's-Advocate audit of the DSL → SQL teaching echo
(ADR-0038) after Phases 1-3 landed: three doc-drift bugs introduced
by the earlier handoff-47 / ADR-promotion commits — requirements.md
M4 and both ADR-0038 README index entry + Status block still said
"Phase 2 / Phase 3 remain," but `275c726` and `e6ad1ae` shipped them.
Updated to reflect actual state: Buckets A + B complete plus the
category-3 prose; only the §4 styled-runs polish remains. ADR-0037's
README entry also touched to note all four shipping commits of its
consumer.

Plus a missing test slice the DA flagged: explicit no-echo coverage
for the Bucket C cases that flow through command_to_sql's catch-all
(show table, explain, replay, every Command::App variant). The
contract — ADR-0030 §10 / ADR-0038 §7 Bucket C — forbids echoes for
these; a future renderer arm added at the wrong place could silently
leak one. The new bucket_c_no_echo_commands_all_return_none pins
that.

Tests: 2015 passed / 0 failed / 1 ignored (pre-existing); clippy
clean. Nothing to escalate.
2026-05-28 10:06:16 +00:00
claude@clouddev1 558cfae151 docs(adr): promote ADR-0037 + ADR-0038 to Accepted (Phase 1 shipped) 2026-05-28 07:10:06 +00:00
claude@clouddev1 04c8e4295f feat: DSL→SQL teaching echo — channel + create-table slice (ADR-0037 + ADR-0038)
Walking skeleton validating the whole echo architecture end to end; the
Command→SQL renderer currently covers `create table`, with the rest of
Bucket A / B / category-3 to follow (ADR-0038 §8).

- Channel (ADR-0037): the three-way EffectiveMode (reusing the existing
  enum, not a new SubmissionMode — recorded in the ADR) rides on
  Action::ExecuteDsl to the runtime. `replay` bypasses the interactive
  spawn, so it never echoes (silent, for free).
- Echo (ADR-0038): built at the runtime's ExecuteDsl dispatch — the worker
  gets decomposed calls, not the Command, so ADR §4's "worker builds it"
  was corrected to the dispatch layer. Gated by echo_for (advanced
  effective mode + DSL-form). Carried on DslSucceeded; rendered by
  note_ok_summary as `Executing SQL: …` immediately beneath `[ok]`. New
  src/echo.rs renderer; echo.executing_sql i18n key.
- command_to_sql: `create table` → `CREATE TABLE T (id serial PRIMARY KEY)`
  (single inline / compound table-level PK), playground type vocabulary,
  round-trip-verified against the advanced walker (the §1 contract).

Tests: echo.rs (render, round-trip contract, mode gate, Sql*-not-echoed);
app.rs (submit carries the 3-way mode; echo renders beneath [ok]).
Suite 1970/0/1; clippy clean.
2026-05-27 22:09:54 +00:00
claude@clouddev1 338dc8a4cf feat: advanced ALTER COLUMN SET/DROP NOT NULL & DEFAULT, SET DATA TYPE (ADR-0035 Am2)
The standard-first ALTER COLUMN constraint gap-fill advanced mode lacked:

- ALTER COLUMN <c> SET DATA TYPE <ty> — ISO canonical synonym for the
  PostgreSQL TYPE shorthand (same AlterColumnType action + executor).
- SET NOT NULL / DROP NOT NULL — reuse the ADR-0029 do_add_constraint /
  do_drop_constraint executors (dry-run + internal-table guards free).
- SET DEFAULT <expr> / DROP DEFAULT — SET DEFAULT uses a dedicated
  raw-SQL executor (do_set_column_default); sql_expr yields no typed
  Value, so it can't go through do_add_constraint. DROP DEFAULT reuses
  do_drop_constraint.

Grammar: AT_ALTER_COLUMN gains a tail Choice (type / set / drop), reusing
SQL_TYPE and the CREATE TABLE DEFAULT_NODES; builder dispatch routes the
new column-attribute forms; runtime decomposes to the executors.

ADR-0035 Am2 corrected in-place: SET DEFAULT decomposes to
do_set_column_default, not do_add_constraint (Value-based) — found during
build.

Tests (test-first): 6 parse + 7 Tier-3 execution via run_replay. Suite
1962/0/1; clippy clean.
2026-05-27 21:03:14 +00:00
claude@clouddev1 9f15f386d5 docs(adr): design the DSL→SQL teaching echo (ADR-0038) + dependencies
Realises ADR-0030 §10 (the DSL→SQL teaching bridge) as a /runda'd design
set, before implementation:

- ADR-0037 (new): execution-time mode side-channel — SubmissionMode
  {Simple, Advanced, AdvancedOneShot} threaded Action→worker, output-only;
  redeems ADR-0033 Amendment 3's deferred follow-up. Replay stays silent.
- ADR-0038 (new): the teaching echo + full catalogue (Buckets A/B/C),
  the copy-paste round-trip contract, the three-category framework, and
  the Value→SQL-literal renderer. DDL + show-data centric (overlapping
  DML is SQL-first, so already SQL). Build-order deps recorded.
- ADR-0035 Amendment 2: standard-first dialect stance + ALTER COLUMN
  SET/DROP NOT NULL, SET/DROP DEFAULT, ISO SET DATA TYPE gap-fill.
- ADR-0033 Amendment 4: reclassifies the `update … --all-rows`
  non-fall-back as a bug; it now falls back to the DSL Update and echoes
  (keyed on adjacent `--`; spaced arithmetic preserved).
- ADR-0039 (new): EXPLAIN over advanced SQL — decision recorded, build
  deferred; supersedes ADR-0030 §13 OOS-2.
- ADR-0000: out-of-scope discipline (deferred vs rejected). README index
  updated for all of the above.

Reconcile CLAUDE.md: simple-mode column ops are implemented, not pending
(requirements.md C2/B2 already [x]).
2026-05-27 20:44:38 +00:00
claude@clouddev1 8906661f69 feat: ADR-0036 Phase 3b — live typed-slot hints + highlighting for INSERT VALUES
Give each positional INSERT VALUES position its column identity so a lone
literal gets the column-typed slot (live per-column hint + mismatch
highlight) and any expression falls through to sql_expr — completing the
typed-DML-values feature for the INSERT surface (single/multi-row, Form A
and Form B).

New zero-width Node::SetColumn(&TableColumn) primitive establishes the
active column for the value position that follows (sets current_column +
pending_value_column, like an Ident{writes_column} but without consuming
input); a DynamicSubgrammar emits SetColumn(col) + the shared SET_VALUE
per position. Column mapping mirrors do_sql_insert: Form A → listed
columns; Form B → all columns in declaration order (advanced-mode Form B
auto-fills nothing; an omitted shortid in Form A is auto-filled and has no
VALUES position).

Reconcile with the per-tuple arity diagnostic (ADR-0033 §8.1): a
fixed-length typed Seq would reject wrong-arity tuples and suppress that
post-walk diagnostic, so the tuple value list is an arity-gating lookahead
— a correct-arity tuple uses the typed Seq; a wrong-arity tuple keeps the
type-blind sql_expr repeat so §8.1 fires unchanged. Correct-arity tuples
get full live feedback, including a wrong-kind literal like 'text' into an
int column.

Records ADR-0036 Amendment 1 (Phase 3b detail + the arity reconciliation);
ADR-0036 is now fully implemented.

Tests: 1947 passing (+8), 0 failed, 0 skipped, 1 ignored; clippy clean.
2026-05-27 07:22:44 +00:00
claude@clouddev1 49ea03b0d5 feat: ADR-0036 Phase 3a — live typed-slot hints + highlighting for SQL SET values
Wire the DSL's column-typed value slots into the advanced-mode SQL
UPDATE/UPSERT `SET col = <rhs>` value position so a learner gets the same
per-column hint ("for `Email`: type a quoted string") and live numeric-
shape mismatch highlight the simple-mode DSL gives.

Discriminate literal-vs-expression with a boundary-aware lookahead
(shared::SET_VALUE), NOT the naive `Choice(typed-slot, sql_expr)` the ADR
originally sketched: the walker's Choice is first-match-wins with no
backtrack, so a typed slot would greedily match the leading `1` of `1 + 2`
and commit, regressing valid SQL (e.g. the existing `values (1, 1 + 2)`
test). The lookahead peeks the whole value position: a literal routes to
the typed slot only when it fills the position up to the next
`,`/`)`/`;`/`where`/`returning`/end; everything else falls through to the
full sql_expr grammar unchanged. The SET column ident gets
`writes_column: true` so `current_column` drives the slot + hint.

Scope: Phase 3a covers UPDATE's assignment list and INSERT's ON CONFLICT
DO UPDATE SET. Phase 3b (INSERT VALUES — needs a per-position grammar
restructure + multi-row) is deferred. Records ADR-0036 Amendment 1 with
the mechanism correction + the 3a/3b split.

Tests: 1939 passing (+5), 0 failed, 0 skipped, 1 ignored; clippy clean.
2026-05-26 22:48:46 +00:00
claude@clouddev1 8c3b13b313 feat: ADR-0036 Phase 2 — validate advanced-mode UPDATE SET literals + retain the value
Mirror Phase 1's capture-at-parse technique on the UPDATE SET assignment
list. build_sql_update calls the new capture_set_literals (data.rs), which
walks the matched tokens (no reparse, no grammar change) and classifies
each top-level `SET col = <rhs>` as a literal (Some, incl. signed numbers)
or an expression (None), using paren depth so a comma inside a function
call or a `where` inside a scalar subquery is not mistaken for a boundary,
and the trailing top-level WHERE is excluded.

Command::SqlUpdate gains set_literals; do_sql_update validates the literals
against their column types via the shared impl_value_for before the still
verbatim update; user_value_for_column reads them so a constraint error
names the offending value. WHERE stays unvalidated; execution and command
identity are unchanged.

Also corrects the stale data.rs header comment (DSL typed slots are wired,
not "deferred") and flips ADR-0036 + README to Phases 1–2 implemented.

Tests: 1934 passing (+4), 0 failed, 0 skipped, 1 ignored; clippy clean.
2026-05-26 22:20:12 +00:00
claude@clouddev1 dc9a4759ce docs: ADR-0036 revised to surgical "validate-and-retain"; +X4/X5 open questions
Narrow ADR-0036 from "bind literals via the DSL path" to "validate literal
values (shared validators) + retain them; execute verbatim, keep auto-fill
and command identity mode-specific" — after a concrete auto-fill difference
(non-PK serial) confirmed the modes aren't identical even for single-row
literals. Augments (no longer supersedes) ADR-0030 §4 / ADR-0033 §10;
Amendment 3 stands. README + forward-notes on 0030/0033 updated. Records
requirements.md X4 (serial auto-fill — possible bug) and X5 (framework
cohesion / share-mechanics-not-commands).
2026-05-26 21:58:19 +00:00