# 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 the ADR-0027 highlight / hint follow-up (precise WARNING
spans, the diagnostic overlay + hint wiring, the
`LIKE`-on-numeric WARNING, the debounce state machine) plus
two manual-testing bug fixes (optional trailing-flag
completion; the `--resume` temp-project pointer):
**1131 passing, 0 failing, 1 ignored** (`cargo test` — the one
ignored test is a long-standing `` ```ignore `` doc-test in
`src/friendly/mod.rs`). Clippy clean with the nursery lint
group enabled. (Earlier reference points: 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.)

---

## 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.
- [ ] **I4** Syntax highlighting for both the DSL and SQL.
- [ ] **I5** In-flight query/command cancellation (Ctrl-C in the
  output area or input field).

## Input modes (per ADR-0003)

- [ ] **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.
- [ ] **M2** Advanced mode accepts SQL plus the canonical
  app-level commands without any sigil.
- [ ] **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.
- [~] **M4** Execution-time mode side-channel — deferred, awaiting
  its own ADR (ADR-0033 Amendment 3). Every command should know, at
  execution time, which of three modes it ran under — `simple`,
  `advanced`, or `advanced-one-shot` (the `:` escape) — so execution
  can adjust *output* without changing command *identity* (e.g. a
  simple-mode `create table` echoing the generated SQL when run in
  advanced mode, silent in simple). Today only the *rendering*
  side-channel exists (`OutputLine.mode_at_submission`); the `Mode`
  enum is two-way, the one-shot distinction is collapsed at
  submission, and neither `Action::ExecuteDsl` nor the worker carries
  any mode. The work widens `Mode` to three-way and threads it
  through the `Action` → worker interface. Not required for Phase 3
  dispatch correctness; tracked here so it is not lost.

## 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 pending.)*
- [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`.
  `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 <name>
  [on delete <action>] [on update <action>]`. 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 <T1> to
  <T2>` 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 <T>`,
  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

- [ ] **Q1** SQL parsed via `sqlparser-rs`; supported subset is
  defined (specifics deferred to a future ADR).
  *(Progress: SQL handling in advanced mode is still a
  placeholder echo. The architecture is now decided — ADR-0030:
  SQL is authored as grammar within the unified grammar tree
  (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. Implementation is phased and
  pending.)*
- [ ] **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 <kind> [<name>]` family of commands for
  redisplaying schema info on demand. *(Progress: `show table
  <name>` and `show data <Table>` implemented;
  `show tables`, `show relationships`, etc. pending.)*

## Project lifecycle (per ADR-0004)

- [x] **P1** Auto-named temp project on startup under
  `<data-root>/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:
  `<YYYYMMDD>-[temp]-<word>-<word>-<word>` 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] <name>` for temp projects,
  `Project: <name>` 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/<table>.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<N>.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)

- [ ] **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. *(Designed + amended + planned in
  `docs/plans/20260524-adr-0006-undo-snapshots.md`; not yet
  implemented.)*
- [ ] **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). *(Designed + amended; not yet
  implemented — see U1's plan.)*
- [x] **U3** `history.log` records every submitted command in
  append-only form, tagged with its outcome (Iteration 2;
  broadened by ADR-0034). Format: `<ISO-8601 Z>|<status>|<source>`
  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 (`<ts>|<status>|
  <source>`), 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-<projectname>-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 <zip> [as <t>]`
  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 <table> [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.)*
- [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 (...)`"). 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
  <command>`-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 `<data-root>/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).
- [~] **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)

- [ ] **TT1** Tier 1: `cargo test` + `proptest` covering
  pure-logic modules (parser, dispatcher, type mapping, project
  I/O, snapshot ring buffer, replay log).
- [ ] **TT2** Tier 2: Ratatui `TestBackend` + `insta` snapshots
  for representative views.
- [ ] **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.

---

## 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.
