# RDBMS Playground — Requirements (Phase 1) This document is the **consolidated Phase 1 requirements checklist** for RDBMS Playground. It captures everything the project has committed to so far, derived from the design conversation and the ADRs in `docs/adr/`. **Purpose.** Phase 5 verification at every milestone measures delivered work against this checklist. An item not on this list was not promised; an item silently dropped without confirmation is a process failure. **Scope.** The list is intentionally coarse — each item is a unit of "satisfied / not satisfied" judgement. When an item is taken up for implementation, it is decomposed further in a backlog (initially in this repo, later in GitHub issues once the repo is pushed). ## Status legend - `[ ]` — open, not yet implemented - `[x]` — satisfied (implemented + tested) - `[~]` — deferred, awaiting an ADR or further design before any implementation - `[-]` — explicitly out of scope (rationale at the bottom) ## Test baseline After ADR-0022 Amendment 6 (the curated SQL function-name list — issues #15 tab-completion + #16 typing-time typo hint): **1538 lib unit tests passing, 0 failing, 1 ignored** (`cargo test --lib`; the full `cargo test` across every binary is 2107 passing, 0 failing, 1 ignored — the one ignored is a long-standing `` ```ignore `` doc-test in `src/friendly/mod.rs`). Clippy clean with the nursery lint group enabled. (Earlier reference points — lib counts: 1131 after the ADR-0027 highlight / hint follow-up + the optional-trailing-flag / `--resume` manual-testing fixes; 1100 after ADR-0027's initial ship; 1079 after ADR-0026 (complex WHERE expressions); 1039 after ADR-0025 (indexes); 1006 after ADR-0024 + the handoff-14 cleanup; 449 after B2/C2. Note the intervening issue fixes #8/#13/#12/#7/#9 landed tests without a baseline bump; this is the first refresh since ADR-0027.) --- ## Distribution and install - [ ] **D1** Cross-platform binaries: Linux, macOS, Windows on x86_64 and aarch64. - [ ] **D2** Single static binary, no runtime dependencies. - [ ] **D3** Released via prebuilt binaries plus Homebrew, Scoop, `winget`, and `cargo binstall`. ## TUI shell - [ ] **S1** Three-region layout: items list (left), output panel (right), input field (bottom). - [x] **S2** Items list shows tables and per-table indexes; designed to extend to additional element kinds (relations, views, etc.) without restructuring. *(ADR-0025: the items panel renders a nested list — each table with its index names indented beneath it. The nested model is the extension point for future element kinds.)* - [ ] **S3** Output panel renders a visualization of the currently selected item and supports multiple tabs. - [ ] **S4** Hint area below the input field; keyboard-toggleable 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. - [x] **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. *(ADR-0027: `Severity` / `Diagnostic` on `WalkResult`; `input_verdict` combines the parse outcome, schema-existence ERRORs — unknown table / column — and the ADR-0026 §7 expression WARNINGs — type mismatch, `= NULL`. The runtime debounces the indicator's display ~1 s; the rightmost six columns of the input row are reserved unconditionally. New `warning` theme colour. A follow-up pass completed §2's highlight + hint wiring — diagnostics overlaid on the input field and surfaced in the hint panel, with precise per-literal WARNING spans — and Amendment 1 adds a `LIKE`-on-numeric-column WARNING.)* ## Input field - [ ] **I1** Multi-line entry that auto-expands; Ctrl-Enter (or equivalent) submits, plain Enter inserts a newline. - [ ] **I1a** In-line cursor editing in the input field: Left / Right arrows move the cursor by character (UTF-8 boundaries honoured), Home / End jump to the extremes, Delete removes the character at the cursor, Backspace removes the character before. Insertion happens at the cursor position. *(Implemented; multi-line editing per I1 still pending.)* - [ ] **I1b** Readline-style cursor shortcuts: Ctrl-A / Ctrl-E as aliases for Home / End for users on keyboards without those keys (and for ergonomics in command-driven workflows). Likely followed by Ctrl-W (delete previous word), Ctrl-K (delete to end), Ctrl-U (delete to start). Pending. - [x] **I2** Persistent navigable input history (project-scoped). *(Implemented across Iterations 2 + 6: per-command append to `history.log` (Iter 2); on project open, the in-memory navigable history is hydrated from the tail of `history.log` up to the same in-memory cap (Iter 6). Global rolling history is out of scope per OOS-6 / N4.)* - [ ] **I3** Tab completion for app commands, DSL keywords, table names, column names, and SQL keywords. *(Refinement 2026-05-30, issue #15: SQL expression slots (`sql_expr_ident`) now also offer a curated set of SQL function names — `KNOWN_SQL_FUNCTIONS` in `src/dsl/sql_functions.rs`, surfaced as a new `CandidateKind::Function` — ADR-0022 Amendment 6. The broad tab-completion goal stays open.)* - [ ] **I4** Syntax highlighting for both the DSL and SQL. *(Refinement 2026-05-29, issue #8: column data types now carry a dedicated `HighlightClass::Type` / `tok_type` colour, distinct from identifiers and clause keywords — ADR-0022 Amendment 4; a further refinement 2026-05-30, issue #15: SQL function-name candidates carry a dedicated `tok_function` colour (the ninth `Theme` token colour, ADR-0022 Amendment 6). The broad highlighting goal stays open.)* - [ ] **I5** In-flight query/command cancellation (Ctrl-C in the output area or input field). ## Input modes (per ADR-0003) - [x] **M1** Simple mode is the default. It accepts DSL data commands and the canonical app-level commands; raw SQL is rejected with a friendly hint. - [x] **M2** Advanced mode accepts SQL plus the canonical app-level commands without any sigil. - [x] **M3** Prefixing a single line with `:` in simple mode is a one-shot advanced escape (with the prompt label updated). The `mode simple` / `mode advanced` command switches modes persistently. - [x] **M4** Execution-time mode side-channel — **implemented** via ADR-0037 (the channel) and its motivating consumer ADR-0038 (the DSL → SQL teaching echo). Every command knows, at execution time, which of three modes it ran under — the three-way `EffectiveMode` { `Simple`, `AdvancedPersistent`, `AdvancedOneShot` } resolves at submit time and threads through `Action::ExecuteDsl` → runtime; the persistent `Mode` enum stays two-way (the transient one-shot `:` lives on the channel where it belongs, not in persistent state). The runtime gates the ADR-0030 §10 teaching echo on it: a DSL-form command run in advanced/one-shot mode renders the equivalent advanced-mode SQL beneath the command's echo line (the `[ok]` summary it once sat under was retired by ADR-0040, issue #9 — the echo line now carries a ✓/✗ completion marker instead); simple-mode and SQL-entered submissions stay silent. Echo coverage: **ADR-0038 is feature-complete** — every catalogue row in §7 round-trips per line through the advanced walker (the §1 copy-paste contract; §6 category 2 holds it per line), every §6 category-3 line surfaces, and the §4 de-emphasised styled-runs rendering polish (ADR-0028) is wired. Shipped across four feature commits: **Phase 1** Bucket A — single-statement DDL + `show data` + `--all-rows` fall-throughs (`90479cb`); **Phase 2** Bucket B — resolved-name + multi-line echoes (`add index` auto- and user-named, positional `drop index`, `add`/`drop relationship` in both selector forms, `drop column --cascade`, `add relationship --create-fk`) (`275c726`); **Phase 3** category-3 prose — `shortid` generation and type-conversion transforms via the pre-existing `client_side.auto_fill_*` / `client_side.transformed*` notes, plus the new `change column --dont-convert` caveat (`e6ad1ae`); **Phase 4 styled-runs polish** — `OutputKind::TeachingEcho` custom rendering branch (dimmed `Executing SQL:` prefix + the SQL re-lexed via `input_render::lex_to_runs_in_mode(Advanced)` for token highlighting, same as the input echo), `OutputStyleClass::Hint` for every cat-3 prose line (caveat + the existing illuminating notes — broader scope, visually consistent) (`2aab457`). ## App-level commands (per ADR-0003) - [ ] **A1** All canonical app-level commands implemented and available in both modes: `save`, `save as`, `load`, `new`, `rebuild`, `export`, `import`, `seed`, `replay`, `undo`, `redo`, `mode`, `help`, `hint`, `quit`. *(Progress: `quit`/`q`, `mode simple|advanced`, `help`, `save`, `save as`, `load`, `new`, `rebuild`, `export`, `import`, `replay` all implemented (Iterations 4 + 5; `replay` via ADR-0024 Phase E — see U4). `seed` in the seeding iteration; `undo` / `redo` in the U-series; `hint` with H2.)* ## DSL data commands - [ ] **C1** Table operations: create / drop / rename. *(Progress: create + drop done; **rename done on the advanced surface** — `ALTER TABLE … RENAME TO`, ADR-0035 §6 / 4h. A simple-mode rename-table verb is deliberately not provided — table rename is advanced-mode only.)* - [x] **C2** Column operations: add / drop / rename / change type. `drop column` and `rename column` use SQLite native ALTER TABLE (3.35+ / 3.25+); `change column` routes through the rebuild-table primitive since ALTER doesn't support type changes. PK and relationship-involved columns are refused with friendly messages (drop the relationship first); SQLite STRICT enforces type compatibility on the data copy during a type change. - [x] **C3** Schema constraints: primary key (single and compound), foreign key with `ON DELETE` / `ON UPDATE` referential actions, indexes, `NOT NULL`, `UNIQUE`, `CHECK`, `DEFAULT`. *(PK including compound done at create-table time; FK with `ON DELETE` / `ON UPDATE` actions done (ADR-0013) — declared via `add 1:n relationship`; symmetric outbound + inbound view in the structure renderer; type compatibility validated at declaration via `Type::fk_target_type()`. Indexes done (ADR-0025) — `add index` / `drop index`, rebuild-preserving, persisted in `project.yaml`; UNIQUE indexes added on the advanced-mode SQL surface (`CREATE UNIQUE INDEX`, ADR-0035 §4d / ADR-0025 Amendment 1; simple-mode `add unique index` deferred). `NOT NULL` / `UNIQUE` / `CHECK` / `DEFAULT` done (ADR-0029) — a constraint suffix on `create table` / `add column`, plus `add constraint` / `drop constraint` on existing columns; populated-column additions are guarded by a pre-flight dry-run that refuses with a table of offending rows.)* - [~] **C3a** Modify relationship: `modify relationship [on delete ] [on update ]`. Users can achieve the same via drop + add today; one-step modify is a small follow-up using the existing rebuild-table machinery. ADR pending. - [ ] **C4** Convenience: `create m:n relationship from to ` produces an auto-named junction table the user can rename; pulls primary keys and FK definitions automatically. - [x] **C5** Data operations: insert / update / delete via DSL. *(ADR-0014. INSERT short and long forms, UPDATE/DELETE with required WHERE plus `--all-rows` opt-in, `show data `, per-column-type value-literal validation, FK enforcement with metadata-driven error enrichment, auto-show after writes. Bulk insert, complex WHERE expressions, and SELECT in advanced mode are explicitly tracked separately — see C5a below.)* - [x] **C5a** Complex WHERE expressions (AND/OR, comparison operators, LIKE, IS NULL, IN, BETWEEN) for UPDATE/DELETE/ show-data filtering; `show data` also gains `where` and `limit`. *(ADR-0026 steps 1–4: the stratified expression grammar reached through a new `Subgrammar` node, the recursive `Expr` AST + `build_expr`, wiring into update / delete / show data, and `Expr` → parameterised SQL with an implicit primary-key `ORDER BY` for `limit`. Type-mismatched WHERE comparisons are permissive — they run rather than being rejected (§7). The §7 advisory **flagging** of type mismatches / `= NULL` is the seam with ADR-0027's diagnostics-severity model and is tracked there — see ADR-0026 "As-built notes".)* ## SQL handling - [x] **Q1** SQL parsed via `sqlparser-rs`; supported subset is defined (specifics deferred to a future ADR). *(Progress: the advanced-mode SQL surface is authored as grammar within the unified grammar tree (ADR-0030 / ADR-0024) and parsed by the existing walker — **not** a separate batch parser — so SQL gets the same completion / highlighting / hints as the DSL (ADR-0001's `sqlparser-rs` reservation is superseded). Implemented so far: full `SELECT` (ADR-0032), `INSERT` / `UPDATE` / `DELETE` (ADR-0033), and `CREATE TABLE` (ADR-0035, 2026-05-25 — executed structurally: columns + types + `NOT NULL`/`UNIQUE`/`PRIMARY KEY` + `IF NOT EXISTS` (4a), then per-column `DEFAULT`/`CHECK` (raw `sql_expr` text) and composite `UNIQUE(a,b)` (4a.2), then table-level/multi-column `CHECK` (4a.3 — round-trips via the new `__rdbms_playground_table_checks` metadata table, since the engine reports no CHECK constraints), then foreign keys (4b — inline `REFERENCES` + table-level `FOREIGN KEY` → ADR-0013 named relationships in the create transaction; self-references and bare `REFERENCES ` supported), then `DROP TABLE [IF EXISTS]` (4c — reuses `do_drop_table`; `IF EXISTS` is a no-op-with-note), then `CREATE [UNIQUE] INDEX` / `DROP INDEX [IF EXISTS]` (4d — reuse `do_add_index`/`do_drop_index`; `CREATE UNIQUE INDEX` admitted in advanced mode via the `IndexSchema.unique` flag, ADR-0025 Amendment 1), then `ALTER TABLE` add/drop/rename column (4e — `alter` is advanced-only, runtime-decomposed to the existing column executors; ADD COLUMN reaches CREATE-TABLE constraint parity; drop/rename refuse a table-CHECK- referenced column), then `ALTER TABLE … ALTER COLUMN TYPE` (4f — runtime-decomposed to `change_column_type` with `ForceConversion`, the §7 advanced policy: lossy converts with a note, incompatible + static refusals (`↔ blob`, non-`int → serial`) refuse, `int → serial` allowed; the internal-`__rdbms_*` guard folded into `do_change_column_type`), then `ALTER TABLE` add/drop constraint + add FK (4g — `ADD [CONSTRAINT ] (CHECK | UNIQUE | FOREIGN KEY)` + `DROP CONSTRAINT `; ADD = CHECK + composite UNIQUE + FK (PRIMARY KEY + named UNIQUE refused); table-CHECK/UNIQUE rebuild with a dry-run guard, FK reuses `add_relationship`; named table-CHECKs round-trip via a rebuild-only `name` column on `__rdbms_playground_table_checks` + a `project.yaml` `check_constraints` `{expr, name}` extension; the internal-table guard completed across `do_add_constraint`/`do_add_relationship`)). then `ALTER TABLE … RENAME TO` (4h — the one genuinely new low-level op, `do_rename_table`: native rename + one-transaction reconciliation of the CSV file and every metadata row naming the table, incl. **rewriting CHECK text that qualifies a column with the old table name** so a fresh rebuild round-trips; refuses same-name / existing-target / `__rdbms_*` / non-existent; auto-named indexes + relationships kept stale per §6 scope; one undo step), then the **4i verification sweep** (shared-entry- word completion merge + simple/advanced completion colour; `describe` of table-level constraints; self-ref FK pre-submit indicator; CREATE-TABLE help/usage refresh). **ADR-0035 Phase 4 (4a–4i) is complete.**)* - [x] **Q2** Non-standard syntax rejected with a clear message pointing at the supported subset. *(Design done — ADR-0030 §8: out-of-subset statements are refused with an engine-neutral message naming the construct. Implementation pending.)* - [x] **Q3** User-facing simplified types map transparently to SQLite STRICT types in generated DDL. *(All ten types implemented and tested.)* - [x] **Q4** Supported SQL subset specification — **ADR-0030**. Advanced mode is a standard-SQL surface, engine-neutral; the supported surface — `SELECT` (full query surface), `INSERT` / `UPDATE` / `DELETE`, `CREATE` / `DROP` / `ALTER TABLE`, `CREATE` / `DROP INDEX` — is authored as grammar in the unified tree. DDL routes through the typed `Command` executor (metadata + the playground type vocabulary preserved); DML and `SELECT` execute as validated SQL. Q1's implementation is now unblocked. ## Database backend (per ADR-0002) - [x] **B1** SQLite via `rusqlite`; all tables created `STRICT`; `PRAGMA foreign_keys = ON` per connection. *(Database accessed through a dedicated worker thread per ADR-0010.)* - [x] **B2** Schema evolution uses the rebuild-table technique internally where SQLite `ALTER TABLE` cannot — currently the change-column-type code path. Add-column, drop-column, and rename-column take the simpler ALTER TABLE route since modern SQLite supports them natively; metadata sync into `__rdbms_playground_columns` and `__rdbms_playground_relationships` happens in the same transaction either way. - [ ] **B3** Query timeout and cancellation supported (no cartesian-join-of-doom can hang the app). *(Progress: the worker-thread architecture is in place; the cancellation/timeout protocol on top of it is pending.)* ## Type system (per ADR-0005) - [x] **T1** All ten user-facing types implemented: `text`, `int`, `real`, `decimal`, `bool`, `date`, `datetime`, `blob`, `serial`, `shortid`. *(Mapping to SQLite STRICT covered by ADR-0005; FK target type rule by ADR-0011.)* - [x] **T2** `shortid` generation: base58, 10–12 characters, omits ambiguous characters; generated client-side at insert. *(Implemented per ADR-0014; auto-fills omitted shortid columns and validates user-supplied values against the same alphabet and length range.)* - [ ] **T3** Compound primary keys handled end-to-end (DSL, storage, display, FK reference). *(Progress: DSL grammar (`with pk a(int),b(int)`), storage, and table-info description are all present; the FK iteration references single-column PKs only — compound-key FK references remain pending.)* ## Visualizations - [ ] **V1** Single-element views render in the output pane: a selected table as its structure (columns, types, keys, constraints); a selected relationship as two tables joined by a line. *(Progress: a basic structure view (column rows with SQLite type names) is rendered after each successful DDL; pretty rendering, selection nav, and relationship line-art pending — see V4 for the broader direction.)* - [ ] **V2** SQL query results render as a dynamic table view in the output pane, with multiple result tabs supported. *(Progress: a basic aligned-column data view is rendered for `show data` and after every write (ADR-0014). Pretty box-drawing tables with truncation/scroll handling, plus multi-tab support, remain in V4 territory.)* - [~] **V3** Full ER-diagram export (whole-database graph, viewed outside the TUI) — low priority; design and ADR pending. - [~] **V4** Output panel as a *scrollable per-session log* with inline rich rendering. Direction agreed in conversation: the output area is a chronological journal of operations and selections (e.g. a "selected table X" entry with the rendered structure underneath); structure renderings choose between a compact ASCII-table form and a vertical line-per-column form based on dimensions; the log is exportable to Markdown so learners can keep a record of their session. Design and ADR pending before any implementation. *(Partial: PageUp / PageDown scrolling of the existing line buffer is in, with new output snapping the view to the most recent. The full V4 scope — smart structure rendering, log styling, Markdown export, scroll indicator — remains pending.)* - [ ] **V5** `show []` family of commands for redisplaying schema info on demand. *(Progress: `show table ` and `show data ` implemented; `show tables`, `show relationships`, etc. pending.)* - [x] **V6** Copy the output panel to the system clipboard (issue #11, ADR-0041). `copy` / `copy all` copy the whole panel; `copy last` copies the most recent command's output. Delivery is OSC 52 (SSH-friendly, no native dep) *plus* a best-effort native write (`arboard`), always both; the payload is the panel's plain text exactly as rendered. Removes the terminal-select-and-fight-wrapping friction of filing bug reports. (Complements V4's planned Markdown export — a different "get the session out" path.) ## Project lifecycle (per ADR-0004) - [x] **P1** Auto-named temp project on startup under `/projects/`. OS-standard data root via `directories` crate; `--data-dir` overrides (Iteration 1). - [x] **P2** `save` / `save as` elevate / copy + switch (Iteration 4b). `save` on a named project reports "already auto-saved". - [x] **P3** Auto-save: per-command write-through to YAML + CSV + `history.log` inside the SQLite tx with commit-db-last ordering (Iteration 2). No dirty state. - [x] **P4** `load` opens an in-TUI picker, sorted newest first, with `[TEMP]` markers and a `b`-to-browse path-entry sub-mode (Iteration 4b). - [x] **P5** Existence-only load + explicit `rebuild` command with confirmation modal (Iterations 3 + 4a). - [x] **P-NAME-1** Temp project directory naming pattern: `-[temp]---` from a 161-word built-in list (Iterations 1 + 4b). Bracketed `[temp]` marker is unambiguous against user-named projects because `validate_user_name` rejects brackets. - [x] **P-NAME-2** Display-name prettifier strips `YYYYMMDD-` AND `[temp]-`; splits kebab / snake / camel; title-cases each word. - [x] **P-NAME-3** Status bar shows `Project: [TEMP] ` for temp projects, `Project: ` for named. - [x] **P-CLEAN-1** Unmodified empty temp projects are auto-deleted on switch and quit, gated by `safely_delete_temp_project`'s stacked guards (containment, symlink rejection, `[temp]` marker, contents allowlist). ## Project file format (per ADR-0004) - [x] **F1** `project.yaml` with `version: 1` field carries schema (ordered tables + columns), relationships, and `created_at`. `data/
.csv` carries table data (UTF-8, header row, RFC 4180; NULL distinct from empty string) (Iteration 2). Empty tables produce no CSV. - [x] **F2** `.gitignore` template (`/playground.db`, `/.rdbms-playground.lock`, `/project.yaml.v*.bak`) created in each new project (Iteration 1). Per ADR-0007 amendment 1, `history.log` is NOT in the template — user decides whether to commit it. - [x] **F3** Migration framework scaffold (Iteration 6). `MigratorRegistry` + `migrate_to_latest` + `ensure_project_yaml_migrated` are wired into every project open; no migrators registered in v1 (the production registry is empty). The framework is exercised by tests that inject a fake v1→v2 migrator: registry plumbing, `.v.bak` backup, version-bump sanity check, and newer-than-supported / malformed-version errors are all covered. The first real migrator (when v2 ships) is a one-file change. ## Undo and replay (per ADR-0006) - [x] **U1** Auto-snapshot before **every** data/schema mutation (DSL + SQL) into a persisted ring buffer (size N=50, tunable), per ADR-0006 Amendment 1 (single-step undo, superseding the original destructive-only model). Snapshot is a hybrid whole-project copy (database via online backup API + `project.yaml` / `data/*.csv` as files); staged before the mutation's transaction, finalised after the db commit (preserves ADR-0015 §6). A batch command (`replay` / future batch ops) records one boundary snapshot; `import` takes none. A `--no-undo` CLI flag disables snapshotting. *(Implemented 2026-05-24, plan `docs/plans/20260524-adr-0006-undo-snapshots.md`: `src/undo.rs` ring + worker dispatch hook in `src/db.rs`; snapshots gated on a user command `source` so internal ops like open-time rebuild are not recorded; snapshot-bookkeeping failures are non-fatal, restore failures surface.)* - [x] **U2** `undo` restores the most recent snapshot (database + text, directly); `redo` re-applies (redo stack discarded on new work); both prompt for confirmation naming the command being undone / re-applied (`Y` confirms). *(Implemented 2026-05-24: `undo` / `redo` app commands, `Modal::UndoConfirm`, runtime prepare→confirm→restore→refresh; `--no-undo` reports undo is off, empty stacks report "nothing to undo/redo". UX polish 2026-05-29, issue #13: the confirm dialog grows to fit its summary on one row, capitalises `Snapshot` / `Yes` / `No`, and renders the snapshot timestamp in local time, human-formatted (`24 May 2026, 11:00`) via the new `chrono` dependency.)* - [x] **U3** `history.log` records every submitted command in append-only form, tagged with its outcome (Iteration 2; broadened by ADR-0034). Format: `||` per ADR-0015 §5 / ADR-0034 §1 — `status` is `ok` for a successful command and `err` for one that failed to parse or execute. Hydration (cross-session recall) reads all records; replay reads `ok` only. - [x] **U4** `replay` runs commands from a `history.log` or `.commands` file. *(Implemented via ADR-0024 Phase E: `runtime::run_replay` parses each non-blank, non-`#`-comment line in advanced mode and dispatches it through the normal pipeline; stops at the first genuine error, no rollback. ADR-0034 §3: replay reads journal records (`|| `), running `ok` records and skipping non-`ok`, while still accepting bare-command scripts. ADR-0034 Amendment 1: replay re-applies only schema/data write commands and **skips** every app-lifecycle command + nested `replay` — all skips continue (a nested `replay` is now skipped, not refused), with a `[skip]` warning on `import` / nested-`replay`. Covered by `tests/replay_command.rs`.)* ## Sharing and export (per ADR-0007) - [x] **E1** `export` produces a zip excluding `playground.db` AND `history.log` (per ADR-0007 amendment 1); default filename `YYYYMMDD--export-NN.zip` with a non-clobbering two-digit sequence under the active data root (Iteration 5). The zip preserves the project's directory name as a single top-level folder. `import [as ]` is the inverse: derive target name from the zip's top folder, auto-suffix `-NN` on collision (ADR-0015 §11 amendment), rebuild from text on open. - [ ] **E2** User documentation includes sharing recipes for git, email, and direct file transfer. ## Sample data / seeding - [ ] **SD1** `seed
[count]` generates plausible fake data; junction tables are seeded with valid foreign-key references drawn from existing parent rows. - [~] **SD2** Detailed seeding rules (per-type generators, locale, determinism, override hooks) — design and ADR pending. ## Query analysis - [x] **QA1** `EXPLAIN QUERY PLAN` is run on demand for queries; output is rendered as an annotated tree highlighting full scans, index use, and join order. *(Implemented per ADR-0028: the `explain` prefix over `show data` / `update` / `delete`, with a span-styled plan tree. `EXPLAIN QUERY PLAN` never executes, so explaining a destructive `update` / `delete` is safe. Extended 2026-05-30, issue #7 / ADR-0039: `explain` now also wraps advanced-mode SQL — `select` / `with` / `insert` / `update` / `delete` — via a second `Advanced` `explain` CommandNode on the shared entry word, reusing the same plan-tree renderer.)* - [x] **QA2** Plan rendering specifics — tree layout, annotation taxonomy, colour scheme. Implemented per ADR-0028 (§3–§6): a box-drawing tree, the substring-pattern taxonomy, and the `OutputLine` styled-runs mechanism. ## Hints, help, errors - [ ] **H1** Friendly error-rewriting layer translates SQLite error messages into learner-friendly equivalents. *(Progress: foreign-key constraint failures are enriched with both inbound and outbound relationship listings (so RESTRICT errors point at the children that still reference this table); full SQL → English translation pending.)* - [ ] **H1a** Strong syntax-help in parse errors. When the user types something near-correct (e.g. `insert into T ('Oli')` — forgotten `values`; or `update T set x=1` — missing WHERE), the error should *name the missing keyword or clause* rather than just point at the unexpected character. This is a separate effort from H1 (which targets database errors); it targets parser errors. Pending — multiple targeted fixes shipping piecemeal so far (e.g. `values` becoming optional in INSERT removes one such case; ADR-0024's typed value slots give per-column-type rejection wording; `insert into T (col)` with no `values` clause now flags "looks like Form A — add `values (...)`"; **issue #1 / ADR-0033 Amendment 5, 2026-05-28** added two pedagogical lines that teach the INSERT Form B positional-`VALUES` contract — a simple-mode submit-time teaching note covering under-supply, over-supply, and extra values (`insert.form_b_extra_values_note`) and an advanced-mode dispatch-time pre-flight note for the same value-count class (`insert.form_b_positional_count_mismatch_note`), plus a walker-level `diagnostic.insert_arity_mismatch_form_b` ERROR that lights the `[ERR]` validity indicator at typing time; **issue #2 / ADR-0022 Amendment 3, 2026-05-29** made the ambient-hint fallback rung schema-aware so the "Next:" prose names the schema-correct next token (`,` between values, `)` after the last) instead of the type-blind close-paren, and so a wrong-arity closed tuple surfaces the real parse error rather than a misleading "submit with Enter"; **issue #17 / ADR-0036 Amendment 2, 2026-05-29** then brought the §8.1 arity diagnostic to **simple mode** at parity with advanced — a wrong-count DSL insert (Form A/B/C) now fires the friendly *"N value(s) for `col`…"* message at typing time, counted against the user-fillable columns, with `serial`/`shortid` auto-fill named; new keys `diagnostic.insert_arity_mismatch_form_b_simple` / `diagnostic.insert_arity_mismatch_all_auto`). A systematic pass is still pending. - [ ] **H2** `hint` provides contextual help for the current input or the most recent error. - [ ] **H3** `help` provides general reference and per-command help. *(Progress: the `help` command lists currently-supported commands + DSL grammar reference + types. As of ADR-0024 §help_id it is assembled by iterating the command REGISTRY and translating each `CommandNode.help_id`, so a new command appears automatically. A general reference and `help `-style detailed per-command help are still the missing pieces.)* ## CLI - [x] **L1** Load a project via a positional CLI argument (Iteration 1). Plus `--data-dir` to override the data root and `--help` / `-h` for the usage banner. - [x] **L1a** `--resume` CLI flag opens the most recently used project (path tracked in `/last_project`). Iteration 6: errors cleanly with a stderr banner above the shell prompt if no previous project is recorded or the recorded path is gone — no silent fallback; mutually exclusive with a positional path argument (ADR-0015 §7). `last_project` is rewritten on every successful project open (startup, load, new, save as, import). - [x] **L1b** Per-project input-mode restore (issue #14, ADR-0015 Amendment 1). The input mode is stored in `project.yaml` (`project.mode:`), restored on every open, and persisted on a `mode` change and on unload (quit / project switch) — so the mode you leave a project in is what reopens. A teacher can ship a project that opens in advanced mode; a learner's last-used mode is restored per project. New `--mode simple|advanced` CLI flag (precedence `--mode` > stored > `simple`; combines with `--resume`). Independent of the `history.log` input-history hydration piece of Iteration 6. - [~] **L2** Submit a command alongside project load — deferred, not v1. ## Tutorials and lessons - [~] **TU1** Tutorial / lesson system — design and ADR pending before any implementation. Out of v1 unless an ADR is written. ## Documentation - [ ] **DOC1** User- and student-facing reference documentation under `docs/`: the DSL command surface, the type system, and the boundaries of simple mode. `docs/simple-mode-limitations.md` is the first piece — it doubles as student explanation and as detailed reference. Distinct from in-app `help` (`H3`), the interactive tutorial system (`TU1`), and the sharing recipes under `E2`. ## Testing (per ADR-0008) - [x] **TT1** Tier 1: `cargo test` + `proptest` covering pure-logic modules (parser, dispatcher, type mapping, project I/O, snapshot ring buffer, replay log). - [x] **TT2** Tier 2: Ratatui `TestBackend` + `insta` snapshots for representative views. - [x] **TT3** Tier 3: synthetic event-loop integration tests covering the user-facing flows in this checklist. - [~] **TT4** Tier 4: PTY-based end-to-end for the four critical flows named in ADR-0008 (cold launch → DDL → quit; save → reopen; export → import → rebuild; undo after DROP). - [ ] **TT5** CI runs all tiers on Linux, macOS, and Windows on stable Rust. ## Cross-cutting - [ ] **X1** Comprehensive logging via the project's logging infrastructure per `CLAUDE.md` (decision points, parameter values, fallback paths). - [~] **X2** Language: English-only for v1; multi-language is an open question to revisit later. - [~] **X3** Accessibility: TUI screen-reader support is best-effort and not a v1 commitment; revisit if user need emerges. - [x] **X4** Auto-fill semantics differ between simple and advanced mode — **resolved 2026-05-27** (raised 2026-05-26). Was: simple-mode `do_insert` auto-fills an omitted **non-PK `serial`** column with `MAX(col)+1`, but the advanced-mode SQL insert auto-filled **only `shortid`**, leaving an omitted non-PK serial **silently NULL** — violating ADR-0018 §1's "auto-generated on every path" contract (the column is `INTEGER UNIQUE`, not `NOT NULL`, so SQLite permits the NULL). Confirmed by characterization, escalated, and fixed (decision: advanced mode matches simple mode): the advanced-mode auto-fill reconstruction (`db.rs`, renamed `plan_shortid_autofill` → **`plan_autogen_autofill`**) now also fills an omitted non-PK serial with `MAX+1` per row (single- and multi-row), mirroring `do_insert` and the existing shortid fill. PK serial is excluded (rowid alias); Form B (no column list) still supplies every column. Covered by `tests/sql_insert.rs::sql_insert_autofills_omitted_nonpk_serial`. Honours ADR-0018 §1/§5; no ADR amendment needed (the contract already said "every path"). ADR-0036 was correct that it did not touch this. - [~] **X5** Framework cohesion / restructuring — **strategic, revisit later** (raised 2026-05-26). The grammar/execution framework (lexer → walker `Node`s → unified grammar tree → typed `Command`s → executors; ADR-0023/0024 and everything layered on since) grew organically from the original grammar outline and now reuses + mixes elements across many levels without a *cohesive written specification* of what the framework comprises, which elements are meant to be reusable, and where the boundaries sit for the recurring "reuse vs create new" decision. A concrete symptom: commands are coupled tightly enough to their execution that reusing an *execution behaviour* tempts reusing the whole *command* (see the ADR-0036 discussion — the temptation to emit `Command::Insert` from the advanced path just to reuse `do_insert`). Desired end state (user-stated): **unique commands for every unique case, with a clean, documented structure for reusable mechanics** (so execution helpers are shared as library functions, not by collapsing command identity). Consider a dedicated specification + restructuring run (its own ADR) to map the framework and set the reuse-boundary rules, easing future maintenance and extension. Not scheduled. --- ## Non-functional requirements NFRs are quality bars rather than discrete features. Where a target is measurable, it is stated numerically; where it is necessarily qualitative, the criterion is named and the bar is "reviewer judgement against the criterion." - [ ] **NFR-1 Performance — startup.** Cold launch to first rendered frame under 500ms on commodity hardware (developer laptop, mid-range desktop). Measured in CI on the Linux runner as a regression gate. - [ ] **NFR-2 Performance — input latency.** Keystroke-to-render latency under 16ms during normal editing; long-running queries must execute off the UI thread so the interface remains responsive (typing, scrolling, mode switching) while a query is running. - [ ] **NFR-3 Performance — resource footprint.** Idle memory under 50MB on the smallest target platform; no busy-loops; CPU near zero when waiting for input. - [ ] **NFR-4 Visual quality — distinctive design.** Colour palette and typography are deliberate and consistent across views; layout uses Unicode box-drawing and symbols where they add clarity; rendering avoids the generic flat-default look that ships with most TUI frameworks. Criterion: a reviewer can identify the app from a screenshot of any view. - [ ] **NFR-5 Visual quality — colour use.** Colour conveys information rather than decoration: mode indication, query result types (numeric vs text vs null), error severity, syntax highlighting categories. Foreground/background combinations meet WCAG-AA contrast (4.5:1 for normal text) even though we have not committed to broader accessibility. - [ ] **NFR-6 Cross-platform parity.** Behaviour and visual quality are equivalent across Linux, macOS, and Windows on crossterm-supported terminals. Platform-specific divergence (e.g. font fallbacks) is documented, not silently tolerated. - [ ] **NFR-7 Light and dark background support.** The colour scheme remains legible and visually coherent on both light and dark terminal backgrounds. The mechanism (auto-detect via terminal query, explicit user setting, or both) is an implementation choice, but the outcome is non-negotiable: no dark-on-dark or light-on-light readability failures on either background. --- ## Explicitly out of scope - [-] **N1** Hosted publishing platform — per ADR-0007. Sharing is local-artifact based. - [-] **N2** Real UUID column type — per ADR-0005. The `shortid` type covers the pedagogical need at TUI-friendly width. - [-] **N3** Cross-emulator visual regression coverage — per ADR-0008. Crossterm abstracts terminals adequately; we revisit only if a real regression surfaces. - [~] **N4** Global rolling input history (cross-session, cross-project). Mentioned in I2's wording; deferred per ADR-0015 §12 — project-scoped history (via `history.log`) is the v1 surface. Revisit if real demand emerges. --- ## Maintenance This document is updated whenever: - A new requirement is committed to (added as a new item with the next free ID in its section). - A deferred item is taken up (status moves from `[~]` to `[ ]`). - An item is satisfied (status moves to `[x]`, with a reference to the commit, PR, or test that demonstrates it). - An item moves out of scope (status moves to `[-]` with a rationale and a link to the decision). IDs are stable: once assigned, they are not reused. Removing a requirement leaves a "withdrawn" entry referencing the decision.