Handoff doc + CLAUDE.md refresh for next session
- New docs/handoff/20260507-handoff-1.md captures session state, what's implemented, what's pending (ranked recommendations for next moves), sharp edges, and a smoke-test sequence. - CLAUDE.md updated to reflect current reality: ADRs 0008- 0014 added to the decisions-at-a-glance list, the "repository layout (planned)" placeholder replaced with the actual layout, key invariants spelled out, deferred list rebuilt from current requirements.md.
This commit is contained in:
@@ -22,60 +22,175 @@ new ADR that supersedes the old one.
|
|||||||
|
|
||||||
Current decisions at a glance (each backed by an ADR):
|
Current decisions at a glance (each backed by an ADR):
|
||||||
|
|
||||||
- **Stack:** Rust + Ratatui + Crossterm; `sqlparser-rs` for SQL,
|
- **Stack:** Rust + Ratatui + Crossterm; `sqlparser-rs` reserved
|
||||||
|
for SQL (advanced mode, not yet wired); `chumsky` for the DSL;
|
||||||
`rusqlite` for the database (ADR-0001).
|
`rusqlite` for the database (ADR-0001).
|
||||||
- **Backend:** SQLite with `STRICT` tables and FK enforcement on
|
- **Backend:** SQLite with `STRICT` tables and FK enforcement on
|
||||||
(ADR-0002).
|
(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
|
- **Input:** simple mode (DSL only) by default; advanced mode
|
||||||
(SQL + app-level commands) on toggle; `:` one-shot escape from
|
(SQL + app-level commands) on toggle; `:` one-shot escape from
|
||||||
simple to advanced (ADR-0003). No other sigils.
|
simple to advanced (ADR-0003). No other sigils.
|
||||||
- **Project format:** `project.yaml` + `data/<table>.csv` +
|
- **Project format:** `project.yaml` + `data/<table>.csv` +
|
||||||
`history.log`; `playground.db` is a derived artifact (ADR-0004).
|
`history.log`; `playground.db` is a derived artifact (ADR-0004).
|
||||||
|
*(Format defined; track 2 implementation pending.)*
|
||||||
- **Types:** `text`, `int`, `real`, `decimal`, `bool`, `date`,
|
- **Types:** `text`, `int`, `real`, `decimal`, `bool`, `date`,
|
||||||
`datetime`, `blob`, `serial`, `shortid`. Compound primary keys
|
`datetime`, `blob`, `serial`, `shortid`. Compound primary keys
|
||||||
supported. No real UUIDs (ADR-0005).
|
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`;
|
- **Safety:** auto-snapshot before destructive ops; `:undo`;
|
||||||
append-only `history.log` for replay and scripting (ADR-0006).
|
append-only `history.log` for replay and scripting (ADR-0006).
|
||||||
|
*(Designed; not yet implemented.)*
|
||||||
- **Sharing:** `export` command produces a zip without the `.db`;
|
- **Sharing:** `export` command produces a zip without the `.db`;
|
||||||
no hosted publishing (ADR-0007).
|
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 (planned)
|
## Repository layout
|
||||||
|
|
||||||
The repository is in early planning. Once code lands, layout
|
```
|
||||||
conventions will be added here. For now, `docs/adr/` is the
|
.
|
||||||
canonical source of decided ground.
|
├── 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
|
## Working style for this project
|
||||||
|
|
||||||
- **Documentation discipline.** Significant decisions get an ADR.
|
- **Documentation discipline.** Significant decisions get an
|
||||||
In-flight discussion stays in conversation or issues until it
|
ADR. In-flight discussion stays in conversation or issues
|
||||||
settles.
|
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
|
- **Testing.** Per the user's global standards, tests are
|
||||||
established before changes, bugs are reproduced with failing
|
established before changes, bugs are reproduced with failing
|
||||||
tests before being fixed, and "all green, no skips" is the only
|
tests before being fixed, and "all green, no skips" is the
|
||||||
acceptable end state. Integration tests exercise full flows
|
only acceptable end state. Integration tests exercise full
|
||||||
(e.g. project save → reload → rebuild produces the same
|
flows.
|
||||||
observable database), not just units in isolation.
|
|
||||||
- **No silent feature loss.** Anything in an ADR is decided. If
|
- **No silent feature loss.** Anything in an ADR is decided. If
|
||||||
implementation reveals that a decision is wrong or impractical,
|
implementation reveals that a decision is wrong or
|
||||||
raise it explicitly and update the ADR — do not quietly drift.
|
impractical, raise it explicitly and update the ADR — do not
|
||||||
- **Pedagogy wins ties.** When a design choice trades clarity for
|
quietly drift.
|
||||||
raw capability, prefer clarity. Real RDBMS power-user features
|
- **Pedagogy wins ties.** When a design choice trades clarity
|
||||||
exist; this app is not the place to teach them.
|
for raw capability, prefer clarity. Real RDBMS power-user
|
||||||
|
features exist; this app is not the place to teach them.
|
||||||
|
- **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
|
## Things deliberately deferred
|
||||||
|
|
||||||
These have been discussed but not yet decided. Do not implement
|
These are explicitly tracked (mostly in `requirements.md`) but
|
||||||
ahead of an ADR for any of them:
|
not yet implemented:
|
||||||
|
|
||||||
- Tutorial / lesson system architecture (acknowledged as in scope
|
- **Project storage** (track 2 / P-series, F-series): file-
|
||||||
for design; needs its own ADR before code).
|
backed projects, save/load/new/export/import, persistent
|
||||||
- Query plan rendering specifics (the *what* is decided — annotated
|
history. Format is fully designed in ADR-0004; the
|
||||||
tree view of `EXPLAIN QUERY PLAN`; the *how* is not).
|
metadata-table round-trip lands here.
|
||||||
- Friendly error message rewriting layer (decided to exist; design
|
- **Complex WHERE expressions** (C5a): AND/OR/comparison/LIKE
|
||||||
not done).
|
in UPDATE/DELETE/show-data filters. The bridge from DSL
|
||||||
- Sample data generation rules, including FK-aware generation for
|
fluency to real SQL.
|
||||||
junction tables.
|
- **SQL handling in advanced mode** (Q1): `sqlparser-rs` parser
|
||||||
- Visual ER diagram export.
|
+ a defined SQL subset (Q4 — its own ADR).
|
||||||
- DSL grammar specifics (compound key syntax, m:n relationship
|
- **Column drops/renames/type changes** (B2 / C2 partial): the
|
||||||
command, etc.).
|
rebuild-table primitive (ADR-0013) is in place; the grammar
|
||||||
|
and dispatch are pending.
|
||||||
|
- **Indexes** (C3 partial): `add index`, `drop index`, then
|
||||||
|
`EXPLAIN QUERY PLAN` rendering for QA1.
|
||||||
|
- **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.
|
||||||
|
|||||||
@@ -0,0 +1,334 @@
|
|||||||
|
# Session handoff — 2026-05-07 (1)
|
||||||
|
|
||||||
|
This is the first handover note from the project. The session
|
||||||
|
took the project from an empty repository through five
|
||||||
|
feature-bearing iterations to a working DSL playground for
|
||||||
|
relational database concepts. The next session should be able
|
||||||
|
to pick up cleanly from `CLAUDE.md` + this file + the linked
|
||||||
|
ADRs.
|
||||||
|
|
||||||
|
## State at handoff
|
||||||
|
|
||||||
|
**Branch:** `main`. Working tree clean. Six commits on top of an
|
||||||
|
empty starting tree:
|
||||||
|
|
||||||
|
```
|
||||||
|
305e508 INSERT/UPDATE/DELETE + value model + auto-show, with polish
|
||||||
|
1650682 Foreign-key relationships, rebuild-table, polish round
|
||||||
|
c1e5292 DSL parser, async DB worker, types, history, metadata, polish
|
||||||
|
25a0f12 TUI walking skeleton (Phase 4)
|
||||||
|
aebfc7d Add Phase 1 requirements checklist with NFRs
|
||||||
|
3a0c03d Initial planning docs: CLAUDE.md and ADRs 0000-0008
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tests:** 200 passing (183 lib + 17 integration), 0 skipped.
|
||||||
|
**Clippy:** clean with `nursery` lints enabled.
|
||||||
|
**Release build:** ~5MB single binary.
|
||||||
|
|
||||||
|
The user's terminal is a real TTY; the TUI runs cleanly there
|
||||||
|
but cannot be exercised from a non-TTY environment. Use the
|
||||||
|
`cargo test` tiers for confident automated checks (Tier 4 PTY
|
||||||
|
tests are configured per ADR-0008 but not yet wired with
|
||||||
|
`expectrl`).
|
||||||
|
|
||||||
|
## What's implemented
|
||||||
|
|
||||||
|
**TUI shell:**
|
||||||
|
- Three-region layout: items list (left), output + input + hint
|
||||||
|
(right), bottom status bar with mode-aware shortcuts.
|
||||||
|
- Light/dark themes with `--theme` CLI flag and `COLORFGBG`
|
||||||
|
auto-detect.
|
||||||
|
- Simple/Advanced input modes; `:` one-shot escape with prompt
|
||||||
|
reaction (border colour + label flip); auto-inserted space
|
||||||
|
after a leading `:` in simple mode.
|
||||||
|
- In-line cursor editing (Left/Right/Home/End/Delete/Backspace,
|
||||||
|
UTF-8 boundary aware).
|
||||||
|
- In-memory command history (Up/Down with draft preservation,
|
||||||
|
consecutive-duplicate dedup).
|
||||||
|
- PageUp/PageDown output scrolling, wrap-aware (renderer
|
||||||
|
reports both visible-row count and total wrapped-row count
|
||||||
|
to App).
|
||||||
|
|
||||||
|
**Database (in-memory only this iteration; track 2 brings file
|
||||||
|
backing):**
|
||||||
|
- SQLite via `rusqlite` 0.39 with `STRICT` tables and
|
||||||
|
`PRAGMA foreign_keys = ON`.
|
||||||
|
- Dedicated worker thread (ADR-0010): `mpsc::Sender<Request>`
|
||||||
|
in / per-request `oneshot` reply out. App holds a
|
||||||
|
`Database` handle (cheap to clone).
|
||||||
|
- Internal metadata tables (ADR-0012, ADR-0013):
|
||||||
|
- `__rdbms_playground_columns(table_name, column_name,
|
||||||
|
user_type)` for round-tripping user-facing column types.
|
||||||
|
- `__rdbms_playground_relationships(name, parent_table,
|
||||||
|
parent_column, child_table, child_column, on_delete,
|
||||||
|
on_update)` for named FKs.
|
||||||
|
- Internal-table convention: prefix `__rdbms_*`; filtered out
|
||||||
|
of `list_tables`. Future internal tables follow this rule.
|
||||||
|
- DDL: `create_table`, `add_column`, `drop_table`,
|
||||||
|
`add_relationship`, `drop_relationship`, `query_data`,
|
||||||
|
`insert`, `update`, `delete`.
|
||||||
|
- `rebuild_table` primitive following SQLite's
|
||||||
|
ALTER-via-rebuild recipe (ADR-0013): `PRAGMA foreign_keys =
|
||||||
|
OFF` outside tx, copy-by-name, `foreign_key_check` before
|
||||||
|
commit, atomic metadata updates. Reusable for B2's pending
|
||||||
|
column-drop/rename/type-change work.
|
||||||
|
- FK error enrichment lists both outbound (INSERT/UPDATE
|
||||||
|
relevance) and inbound (DELETE/UPDATE on parent relevance)
|
||||||
|
relationships from the metadata.
|
||||||
|
|
||||||
|
**DSL grammar (chumsky):**
|
||||||
|
- `create table <Name> with pk [<name>:<type>[,<name>:<type>...]]`
|
||||||
|
— single PK or compound; `with pk` alone defaults to
|
||||||
|
`id:serial`; bare `create table X` errors with friendly
|
||||||
|
hint.
|
||||||
|
- `add column [to table] <Name>: <ColName> (<type>)` — `to
|
||||||
|
table` optional (just verbose).
|
||||||
|
- `drop table <Name>` — refuses if other tables still
|
||||||
|
reference this one (lists the offending relationships).
|
||||||
|
- `add 1:n relationship [as <name>] from <P>.<col> to
|
||||||
|
<C>.<col> [on delete <action>] [on update <action>]
|
||||||
|
[--create-fk]` — auto-name format
|
||||||
|
`<Parent>_<pcol>_to_<Child>_<ccol>`. `[as <name>]` requires
|
||||||
|
the `as` keyword to be unambiguous.
|
||||||
|
- `drop relationship <name>` or `drop relationship from
|
||||||
|
<P>.<col> to <C>.<col>`.
|
||||||
|
- `show table <Name>` — re-renders structure.
|
||||||
|
- `show data <Name>` — renders data view.
|
||||||
|
- `insert into <T> [(cols)] values (vals)` and
|
||||||
|
`insert into <T> (vals)` (short form omitting `values`,
|
||||||
|
disambiguated by literal-vs-identifier content).
|
||||||
|
- `update <T> set <c>=<v>[,<c>=<v>...] where <c>=<v> | --all-rows`.
|
||||||
|
- `delete from <T> where <c>=<v> | --all-rows`.
|
||||||
|
|
||||||
|
App-level commands (always available in both modes per
|
||||||
|
ADR-0003): `quit`/`q`, `mode simple|advanced`. The rest of the
|
||||||
|
canonical list (`save`, `load`, `export`, etc.) lands with the
|
||||||
|
features they belong to.
|
||||||
|
|
||||||
|
**Type system (ADR-0005):**
|
||||||
|
All ten user-facing types implemented:
|
||||||
|
`text` / `int` / `real` / `decimal` / `bool` / `date` /
|
||||||
|
`datetime` / `blob` / `serial` / `shortid`.
|
||||||
|
- `serial` → `INTEGER PRIMARY KEY`, auto-filled by SQLite.
|
||||||
|
- `shortid` → `TEXT`, auto-generated by us (10-char base58,
|
||||||
|
no ambiguous glyphs); explicit values validated against the
|
||||||
|
same alphabet/length range.
|
||||||
|
- `decimal` / `date` / `datetime` stored as TEXT; per-type
|
||||||
|
format validation at INSERT/UPDATE time with friendly
|
||||||
|
errors naming the column.
|
||||||
|
- `Type::fk_target_type()` (ADR-0011): `serial → int`,
|
||||||
|
`shortid → text`, others identity. Used at FK declaration
|
||||||
|
to validate the FK column type.
|
||||||
|
|
||||||
|
**Auto-show after writes:** INSERT/UPDATE/DELETE return typed
|
||||||
|
result structs (`InsertResult`, `UpdateResult`, `DeleteResult`).
|
||||||
|
INSERT auto-shows the just-inserted row (via
|
||||||
|
`last_insert_rowid`). UPDATE captures matching rowids up-front
|
||||||
|
so the post-update display still finds the rows even if WHERE
|
||||||
|
column changed. DELETE reports per-relationship cascade effects
|
||||||
|
via row-count diffing of inbound child tables. UPDATE-side
|
||||||
|
cascades are not detected (would need value diffing).
|
||||||
|
|
||||||
|
## ADR index (read these before touching the related areas)
|
||||||
|
|
||||||
|
```
|
||||||
|
0000 Record architecture decisions (process)
|
||||||
|
0001 Language and TUI framework (Rust + Ratatui)
|
||||||
|
0002 Database engine (SQLite STRICT)
|
||||||
|
0003 Input modes and command dispatch
|
||||||
|
0004 Project file format (deferred — track 2)
|
||||||
|
0005 Column type vocabulary (ten types)
|
||||||
|
0006 Undo snapshots and replay log (deferred)
|
||||||
|
0007 Sharing and export (deferred — depends on track 2)
|
||||||
|
0008 Testing approach (four tiers)
|
||||||
|
0009 DSL command syntax conventions
|
||||||
|
0010 Database access via worker thread
|
||||||
|
0011 FK column type compatibility
|
||||||
|
0012 Internal metadata for user-facing column types
|
||||||
|
0013 Relationships, naming, and rebuild-table strategy
|
||||||
|
0014 Data operations, value literals, and auto-show
|
||||||
|
```
|
||||||
|
|
||||||
|
## What's pending — proposed next moves (in order of my recommendation)
|
||||||
|
|
||||||
|
Several natural directions; ranking is my read at handoff.
|
||||||
|
|
||||||
|
### 1. Project storage — track 2 (highest user impact)
|
||||||
|
|
||||||
|
The largest UX friction left: every quit loses everything. The
|
||||||
|
ADR-0004 design is fully in place — `project.yaml` + `data/<table>.csv`
|
||||||
|
+ `history.log`, with `playground.db` as a derived artifact.
|
||||||
|
|
||||||
|
What's needed:
|
||||||
|
|
||||||
|
- File-backed projects: `Database::open(path)` already accepts
|
||||||
|
any path (currently always `:memory:` from runtime).
|
||||||
|
- Auto-temp project on startup, stored in
|
||||||
|
`~/.rdbms-playground/projects/temp-<name>` (P1).
|
||||||
|
- `save` / `save as` (P2).
|
||||||
|
- Auto-save on every change (P3).
|
||||||
|
- `load` picker (P4).
|
||||||
|
- `playground.db` rebuild from `project.yaml` + `data/`
|
||||||
|
(P5) — confirmation when `.db` exists.
|
||||||
|
- YAML schema serialiser/deserialiser. The metadata tables
|
||||||
|
(ADR-0012, ADR-0013) are already the source of truth.
|
||||||
|
- CSV per-table data round-trip.
|
||||||
|
- Persistent command history (the in-memory I2 becomes
|
||||||
|
durable).
|
||||||
|
- `.gitignore` template (F2) and the format-version
|
||||||
|
migration framework (F3).
|
||||||
|
- The `export`/`import` app-level commands (E1, ADR-0007).
|
||||||
|
|
||||||
|
Estimated scope: substantial — 800–1500 lines + tests +
|
||||||
|
ADR(s). Probably wants its own design ADR before coding.
|
||||||
|
|
||||||
|
### 2. Complex WHERE expressions (C5a)
|
||||||
|
|
||||||
|
The user explicitly flagged this as the bridge between DSL
|
||||||
|
fluency and real SQL. AND/OR/comparison operators (`<`, `>`,
|
||||||
|
`!=`)/`LIKE`/`IS NULL` in UPDATE/DELETE/show-data filters.
|
||||||
|
Builds on the existing parser. Requires its own ADR for the
|
||||||
|
expression grammar.
|
||||||
|
|
||||||
|
Estimated scope: moderate — 400–600 lines + tests + ADR.
|
||||||
|
|
||||||
|
### 3. Indexes (C3 partial) + EXPLAIN QUERY PLAN rendering (QA1)
|
||||||
|
|
||||||
|
Indexes are conceptually simple but unlock the most powerful
|
||||||
|
teaching demo we haven't done yet: showing how `EXPLAIN QUERY
|
||||||
|
PLAN` output changes when an index is added. ADR-0008 plus the
|
||||||
|
SQLite EXPLAIN docs are the relevant references.
|
||||||
|
|
||||||
|
Estimated scope: moderate — 300–500 lines + tests + ADR.
|
||||||
|
|
||||||
|
### 4. B2 column drops/renames/type changes
|
||||||
|
|
||||||
|
The `rebuild_table` primitive already exists (ADR-0013). What's
|
||||||
|
needed is grammar (`drop column from T: c`, `rename column on
|
||||||
|
T: old to new`, `change column on T: c (new_type)`) and the
|
||||||
|
executor wiring.
|
||||||
|
|
||||||
|
Estimated scope: moderate — 400 lines + tests.
|
||||||
|
|
||||||
|
### 5. Friendly error layer (H1)
|
||||||
|
|
||||||
|
Promote `DbError::friendly_message()` from passthrough to a
|
||||||
|
real translator: rewrite SQLite's terse messages to learner-
|
||||||
|
friendly ones, identify exact violating columns/rows, suggest
|
||||||
|
fixes ("did you mean…").
|
||||||
|
|
||||||
|
Pairs nicely with H1a (parse-error syntax-help that points at
|
||||||
|
missing keywords/clauses).
|
||||||
|
|
||||||
|
Estimated scope: moderate — 300–500 lines + tests + ADR.
|
||||||
|
|
||||||
|
### 6. Session log + Markdown export (V4 — the bigger UX project)
|
||||||
|
|
||||||
|
The output panel as a scrollable per-session journal with
|
||||||
|
inline rich rendering (smart table-vs-vertical decision based
|
||||||
|
on dimensions); Markdown export of the log. Largest single
|
||||||
|
piece of UX work outstanding.
|
||||||
|
|
||||||
|
Estimated scope: large — 800+ lines + tests + ADR.
|
||||||
|
|
||||||
|
### 7. m:n convenience command (C4)
|
||||||
|
|
||||||
|
Auto-generates a junction table from `create m:n relationship
|
||||||
|
from <T1> to <T2>`; pulls primary keys; assigns a compound PK
|
||||||
|
on the junction. Depends on relationships being solid (they
|
||||||
|
are).
|
||||||
|
|
||||||
|
Estimated scope: small/moderate once the design is settled —
|
||||||
|
200–400 lines + tests + ADR.
|
||||||
|
|
||||||
|
### 8. Smaller polish
|
||||||
|
|
||||||
|
- Readline shortcuts I1b (Ctrl-A/Ctrl-E and friends).
|
||||||
|
- Multi-line input I1 (Enter inserts newline, Ctrl-Enter
|
||||||
|
submits).
|
||||||
|
- Tab completion I3.
|
||||||
|
- Syntax highlighting I4.
|
||||||
|
- CI (TT5) — the test infrastructure exists; the workflow
|
||||||
|
file does not.
|
||||||
|
|
||||||
|
## Sharp edges and subtleties
|
||||||
|
|
||||||
|
Things that took thought during this session and might trip up
|
||||||
|
a new agent:
|
||||||
|
|
||||||
|
- **`update()` is pure-sync; the runtime is async.** The Elm
|
||||||
|
pattern (ADR-0001 / ADR-0010) is a hard rule. Don't make
|
||||||
|
`update()` async or do DB calls inside it; emit `Action`s
|
||||||
|
and let the runtime dispatch.
|
||||||
|
- **Schema mutations always update metadata.** Both
|
||||||
|
`__rdbms_playground_columns` and `__rdbms_playground_relationships`
|
||||||
|
are in transactions with the DDL they belong to. A new DDL
|
||||||
|
operation that touches columns or relationships and forgets
|
||||||
|
to update metadata will produce subtly wrong `describe_table`
|
||||||
|
output later.
|
||||||
|
- **`rebuild_table` is the load-bearing primitive for any FK
|
||||||
|
change.** SQLite cannot ALTER FK constraints in place. The
|
||||||
|
primitive runs `PRAGMA foreign_keys = OFF` *outside* a
|
||||||
|
transaction (the pragma is a no-op inside one), then
|
||||||
|
performs the swap.
|
||||||
|
- **Wrap-aware scroll math.** The renderer reports both
|
||||||
|
visible-row count and total wrapped-row count to App via
|
||||||
|
`note_output_viewport`. Writing scroll-related code without
|
||||||
|
understanding this will produce off-by-many bugs (the user
|
||||||
|
hit one twice in this session).
|
||||||
|
- **Command::target_table vs display_subject.** They differ
|
||||||
|
for relationship commands: `target_table()` is the parent
|
||||||
|
(drives the post-action description); `display_subject()`
|
||||||
|
prints the `from .. to ..` form for the `[ok]` line.
|
||||||
|
- **Ratatui's `Paragraph::line_count` is unstable.** We
|
||||||
|
approximate via character-count division
|
||||||
|
(`approximate_wrapped_rows_from_output` in `ui.rs`). Off-by-
|
||||||
|
one at boundaries is acceptable for scroll capping.
|
||||||
|
- **`set_default` is not implemented.** ADR-0014 commits to
|
||||||
|
the four other actions (`no action`, `restrict`, `set null`,
|
||||||
|
`cascade`). When DEFAULT constraints land (C3 partial),
|
||||||
|
`set default` joins the family.
|
||||||
|
- **The user has expressed strong preferences several times.**
|
||||||
|
Reread the relevant ADRs (especially 0009 on syntax
|
||||||
|
conventions; 0011 on type compatibility; 0013 on
|
||||||
|
relationship naming) before making any DSL grammar change.
|
||||||
|
|
||||||
|
## How to take over
|
||||||
|
|
||||||
|
1. Read this file.
|
||||||
|
2. Read `CLAUDE.md` for the working-style rules and current
|
||||||
|
layout.
|
||||||
|
3. Read `docs/requirements.md` for granular progress.
|
||||||
|
4. Skim `docs/adr/README.md`; read any ADR you'll touch.
|
||||||
|
5. Run `cargo test` to confirm the 200-test green baseline.
|
||||||
|
6. `cargo run --release` to see the app in action — try the
|
||||||
|
smoke test below.
|
||||||
|
|
||||||
|
### End-to-end smoke test
|
||||||
|
|
||||||
|
Verifies the playground works from create-table through
|
||||||
|
relationships through writes through cascade:
|
||||||
|
|
||||||
|
```
|
||||||
|
create table Customers with pk id:serial
|
||||||
|
add column Customers: Name (text)
|
||||||
|
create table Orders with pk id:serial
|
||||||
|
add column Orders: CustId (int)
|
||||||
|
add 1:n relationship from Customers.id to Orders.CustId on delete cascade
|
||||||
|
insert into Customers ('Alice')
|
||||||
|
insert into Customers ('Bob')
|
||||||
|
insert into Orders (CustId) values (1)
|
||||||
|
show data Customers
|
||||||
|
show data Orders
|
||||||
|
delete from Customers where id=1
|
||||||
|
show data Orders -- empty (cascaded)
|
||||||
|
quit
|
||||||
|
```
|
||||||
|
|
||||||
|
If anything in that sequence fails, something is wrong. The
|
||||||
|
sequence exercises auto-PK, auto-shortid (no, wait — Customers
|
||||||
|
is serial, not shortid; for shortid replace `pk id:serial`
|
||||||
|
with `pk id:shortid`), FK compatibility (Customers.id is
|
||||||
|
serial → Orders.CustId must be int), the rebuild-table dance
|
||||||
|
when adding the relationship, the insert short form, and
|
||||||
|
cascade delete.
|
||||||
Reference in New Issue
Block a user