02234e6c45
ADR-0028 (query plans / `explain`) is fully implemented; the handoff-16 design trio (ADR-0026 / 0027 / 0028) is now closed. - handoff-21: session summary, the two deliberate deviations from handoff-20's plan, test coverage, open clusters. - requirements.md: QA1 / QA2 ticked. - CLAUDE.md: the `EXPLAIN QUERY PLAN` deferred-items line updated to "implemented per ADR-0028".
223 lines
11 KiB
Markdown
223 lines
11 KiB
Markdown
# 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-rs` reserved
|
||
for SQL (advanced mode, not yet wired); `chumsky` for the DSL;
|
||
`rusqlite` for the database (ADR-0001).
|
||
- **Backend:** SQLite with `STRICT` tables 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.db` is 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
|
||
`rebuild` command; 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 via `Type::fk_target_type()` — `serial → int`,
|
||
`shortid → text`, others identity (ADR-0011).
|
||
- **Safety:** auto-snapshot before destructive ops; `:undo`;
|
||
append-only `history.log` for replay and scripting (ADR-0006).
|
||
*(Designed; not yet implemented.)*
|
||
- **Sharing:** `export` command produces a zip without the `.db`;
|
||
no hosted publishing (ADR-0007).
|
||
- **Testing:** four-tier strategy from `cargo test` units 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 table` optional, `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_columns` for user-facing column
|
||
types and `__rdbms_playground_relationships` for named FKs.
|
||
These are the source of truth for round-tripping schema info.
|
||
Internal tables follow the `__rdbms_*` naming convention and
|
||
are filtered out of `list_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 data`
|
||
with required WHERE plus `--all-rows` opt-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 returns `Vec<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::Connection` use outside `db.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 `App` state.** It reports
|
||
viewport metrics back via `note_output_viewport` so 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.md` in 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 commit` is 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-rs` parser
|
||
+ 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 index` done (ADR-0025).
|
||
`EXPLAIN QUERY PLAN` (QA1 / QA2) implemented per ADR-0028 —
|
||
the `explain` prefix command + span-styled plan tree.
|
||
- **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:
|
||
|
||
1. `docs/handoff/` — most recent file gives session context.
|
||
2. `CLAUDE.md` (this file).
|
||
3. `docs/requirements.md` — current progress on each item.
|
||
4. `docs/adr/README.md` and any ADR you'll touch.
|