Sweep: input_verdict tests confirm the schema-existence check fires across the identifier-taking commands — unknown table on drop / show / add column, unknown column on drop column / update — and that known references stay clean. The Step B check is grammar-generic, so this is verification + coverage rather than new code. Docs: requirements.md S6 -> [x], baseline 1096; CLAUDE.md deferred list reconciled (C5a and S6 are done — removed); ADR-0026's as-built note updated (step 5 shipped via ADR-0027); ADR-0027 gains an As-built notes section recording the post-walk diagnostics realization, the pre-rendered message, the timeout-based debounce, coarse WARNING spans, and the deferred highlight/hint wiring.
11 KiB
RDBMS Playground — project notes for Claude
What this project is
A cross-platform TUI application that gives learners a sandbox for exploring relational database concepts: tables, columns, primary and foreign keys, relationships, indexes, queries, and query plans. The audience is students from beginners to those ready for raw SQL, and the design accommodates both ends of that spectrum.
The application is a teaching tool, not a database administration tool. Decisions about the type system, command surface, and backend choices are skewed toward pedagogy over breadth.
Authoritative decisions
All significant design decisions live in docs/adr/. Read
docs/adr/README.md for the index. Before proposing changes
that touch a decided area, read the relevant ADR. Decisions are
not re-litigated casually — if a decision needs to change, write a
new ADR that supersedes the old one.
Current decisions at a glance (each backed by an ADR):
- Stack: Rust + Ratatui + Crossterm;
sqlparser-rsreserved for SQL (advanced mode, not yet wired);chumskyfor the DSL;rusqlitefor the database (ADR-0001). - Backend: SQLite with
STRICTtables and FK enforcement on (ADR-0002). Database access through a dedicated worker thread with mpsc/oneshot request channels (ADR-0010). - Input: simple mode (DSL only) by default; advanced mode
(SQL + app-level commands) on toggle;
:one-shot escape from simple to advanced (ADR-0003). No other sigils. - Project format:
project.yaml+data/<table>.csv+history.log;playground.dbis a derived artifact (ADR-0004, amended by ADR-0015). Implemented through Iteration 4 + cleanup; export/import (Iter 5) and migration framework / --resume / persistent input history (Iter 6) pending. - Project storage runtime: every command persists through to
db + yaml + csv + history.log in one execution context, gated
by the combined db persistence logic; commit-db-last ordering
for crash-recoverable state; existence-only load + explicit
rebuildcommand; in-TUI list-with-browse load picker; lock file for single-instance enforcement; persistence failures are fatal (banner + quit) (ADR-0015). Empty tables produce no CSV. CSV reader hand-rolled to preserve NULL-vs-empty distinction. Temp projects are marked by a literal[temp]segment in their directory name (validate_user_name rejects brackets, so user-named projects can never collide). - Temp project cleanup: unmodified empty temps
(kind=Temp, empty schema, empty data dir) are auto-deleted
on switch and on quit by
safely_delete_temp_project, which stacks containment / symlink-rejection /[temp]-marker / contents-allowlist guards. Anything unexpected → refuse, never delete the wrong thing. - Types:
text,int,real,decimal,bool,date,datetime,blob,serial,shortid. Compound primary keys supported. No real UUIDs (ADR-0005). FK column type compatibility viaType::fk_target_type()—serial → int,shortid → text, others identity (ADR-0011). - Safety: auto-snapshot before destructive ops;
:undo; append-onlyhistory.logfor replay and scripting (ADR-0006). (Designed; not yet implemented.) - Sharing:
exportcommand produces a zip without the.db; no hosted publishing (ADR-0007). - Testing: four-tier strategy from
cargo testunits up to PTY-based end-to-end (ADR-0008). Tiers 1–3 are active; Tier 4 is wired only for the listed critical flows. - DSL syntax conventions: required clauses use keyword
grammar (
with pk,to tableoptional,from..to,set,where);--flags are reserved for opt-in choices; one sigil only (:); keywords case-insensitive, identifiers case-preserving (ADR-0009). - Internal metadata tables (ADR-0012, ADR-0013): the database
carries
__rdbms_playground_columnsfor user-facing column types and__rdbms_playground_relationshipsfor named FKs. These are the source of truth for round-tripping schema info. Internal tables follow the__rdbms_*naming convention and are filtered out oflist_tables. - FK relationships: declared via
add 1:n relationship [as <name>] from <P>.<col> to <C>.<col> [on delete <action>] [on update <action>] [--create-fk]. Implemented through the rebuild-table primitive — the same machinery covers B2's pending column drop/rename/type-change use cases (ADR-0013). - Data operations:
insert / update / delete / show datawith required WHERE plus--all-rowsopt-in for unfiltered ops; auto-show after writes shows just the affected rows; DELETE reports per-relationship cascade summaries (ADR-0014).
Repository layout
.
├── Cargo.toml # dependencies, lints (nursery)
├── CLAUDE.md # this file
├── docs/
│ ├── adr/ # all decision records (read 0000 first)
│ ├── handoff/ # session-handover notes
│ └── requirements.md # the Phase-1 checklist with progress
├── src/
│ ├── action.rs # Action enum (Quit / ExecuteDsl)
│ ├── app.rs # App state + pure update() + Tier-1 tests
│ ├── cli.rs # CLI args (--theme, --log-file)
│ ├── db.rs # rusqlite worker, all DDL/DML, metadata tables
│ ├── dsl/
│ │ ├── action.rs # ReferentialAction enum + parsing
│ │ ├── command.rs # Command AST + RelationshipSelector + RowFilter
│ │ ├── mod.rs # re-exports
│ │ ├── parser.rs # chumsky grammar (all DSL commands)
│ │ ├── shortid.rs # base58 generator + validator
│ │ ├── types.rs # user-facing Type enum + fk_target_type
│ │ └── value.rs # Value/Bound + per-type validation
│ ├── event.rs # AppEvent (input + DSL outcomes)
│ ├── lib.rs # module re-exports for tests
│ ├── logging.rs # tracing setup, file-backed
│ ├── main.rs # binary entry; thin
│ ├── mode.rs # Simple/Advanced mode enum
│ ├── runtime.rs # Tokio loop, terminal setup, dispatch
│ ├── snapshots/ # insta snapshots for Tier-2 tests
│ ├── theme.rs # light/dark themes
│ └── ui.rs # ratatui rendering
└── tests/
└── walking_skeleton.rs # Tier-3 integration tests
Key invariants in the code:
update()is pure-sync. It returnsVec<Action>for the runtime to enact. Side effects belong in the runtime, not the update function — that's what makes Tier 1/3 tests tractable.- Database access goes through the worker thread. Always.
No direct
rusqlite::Connectionuse outsidedb.rs. - Schema mutations update metadata in the same transaction. See ADR-0012 / ADR-0013. Adding a new DDL operation must keep the column- and relationship-metadata tables in sync.
- Renderer is pure render of
Appstate. It reports viewport metrics back vianote_output_viewportso subsequent scroll input is wrap-aware.
Working style for this project
- Documentation discipline. Significant decisions get an
ADR. In-flight discussion stays in conversation or issues
until it settles. The ADR-0000 index-upkeep rule applies:
every ADR change updates
docs/adr/README.mdin the same edit. - Testing. Per the user's global standards, tests are established before changes, bugs are reproduced with failing tests before being fixed, and "all green, no skips" is the only acceptable end state. Integration tests exercise full flows.
- No silent feature loss. Anything in an ADR is decided. If implementation reveals that a decision is wrong or impractical, raise it explicitly and update the ADR — do not quietly drift.
- Pedagogy wins ties. When a design choice trades clarity for raw capability, prefer clarity. Real RDBMS power-user features exist; this app is not the place to teach them.
- No engine name in user-facing strings. The choice of database engine is an implementation detail per ADR-0002 (User-facing posture). Error messages, success notes, help text, and any other user-visible string refer to "the database" or "the engine" in the abstract — never the specific product (SQLite, STRICT, rusqlite, PRAGMA). ADR-internal prose and code comments may name it where technically necessary for precision.
- Confirm commits. Per the user's global rules, every
git commitis preceded by an explicit message proposal and user approval. No AI attribution in commit messages.
Things deliberately deferred
These are explicitly tracked (mostly in requirements.md) but
not yet implemented:
- Project storage (track 2): largely implemented through
Iteration 4 + cleanup pass + safety hardening (Iterations
1–4 of ADR-0015). Pending pieces:
export/import(Iter 5),--resume+ persistent input history hydration + migration framework scaffold (Iter 6). - SQL handling in advanced mode (Q1):
sqlparser-rsparser- a defined SQL subset (Q4 — its own ADR).
- Column drops/renames/type changes (B2 / C2 partial): the rebuild-table primitive (ADR-0013) is in place; the grammar and dispatch are pending.
- Indexes:
add index/drop indexdone (ADR-0025).EXPLAIN QUERY PLAN(QA1 / QA2) is designed in ADR-0028 — theexplainprefix command + span-styled plan tree — but not yet implemented. - Modify relationship (C3a): drop+add covers the use case today.
- m:n convenience (C4): auto-generates a junction table with appropriate FKs — depends on relationships being solid (they are).
- Friendly error layer (H1): partial — FK errors are enriched both ways; full SQL→English translation pending.
- Strong syntax-help in parse errors (H1a): point users at missing keywords/clauses rather than the unexpected character.
- Snapshot/replay/undo (U-series): designed in ADR-0006; not yet implemented.
- Tutorial/lesson system: acknowledged as in scope for design; needs its own ADR.
- Session log + Markdown export (V4): the bigger UX project — scrollable session journal, smart structure rendering, save-as-markdown.
- Readline shortcuts (I1b): Ctrl-A/Ctrl-E, Ctrl-W/Ctrl-K/ Ctrl-U.
- Multi-line input (I1): Enter inserts newline, Ctrl-Enter submits.
- Tab completion (I3), syntax highlighting (I4).
- ER diagram export (V3).
- CI (TT5): test infrastructure exists; CI workflow not yet configured.
Handoff notes
When taking over a session, read in order:
docs/handoff/— most recent file gives session context.CLAUDE.md(this file).docs/requirements.md— current progress on each item.docs/adr/README.mdand any ADR you'll touch.