Projects cast review fixes:
- Pace the confirms: `save as` name and `new` now type, pause, then Enter as
separate steps, so the viewer can read them before they execute.
- Insert `show tables` at each phase (before save / after `new` / after load),
since the schema sidebar is hidden at 90 cols (ADR-0046) — the sequence now
reads "books -> no tables -> books" so the round-trip is followable.
STYLE.md: new "Cast pacing & clarity" guidelines (beat-before-submit; surface
state where the sidebar would). Handoff item 2: chase these up across the
existing casts.
Expand next-work item 2: review whether neutral step-caption banners (the demo
overlay) would improve the existing casts — narrating phases or calling out the
relationship diagram / teaching echo — cast-by-cast, with the no-naming-keys
constraint.
Captures everything since website-1: the ADR-namespace move, all Reference +
Guides + the new SQL queries page, the cast pipeline + 7 casts (incl. the new
projects cast via #24 vi-nav), --demo on all casts (#22), and the main merge
(m:n/ADR-0045, UI sidebar+responsive input/ADR-0046, demo overlays/ADR-0047,
logging, FK fixes).
Flags the next-session work: document the merge's new features (m:n command,
--demo flag, ADR-0046 UI) which are not yet in the docs; the no-advertising
constraint (vi keys / Ctrl+] secret); cast tooling limits (no arrow keys);
the capture-harness recipe; Phase B; and open STYLE decisions.
- New projects cast: create → save as library → new (fresh) → load → navigate
the picker to the saved project (j, now possible via #24 vi-nav) → Enter
loads it, the table is restored. Runs under an isolated --data-dir so the
picker lists only this cast's projects.
- Turn on the demonstration overlay (--demo, #22 / ADR-0047) for ALL casts,
for a consistent viewer experience: special keys show a badge — e.g.
[ENTER], and [TAB] at the assistive-editor's completion moment, finally
making that keystroke visible. Plain j/k navigation stays unbadged, so the
picker navigation is not surfaced.
- Generator: per-cast `dataDir` (isolated data root) + default-on `--demo`
(opt out with demo:false). All 7 casts regenerated.
Convert projects.md → .mdx and embed. Build clean (26 pages). Visual playback
of all casts pending a tunnel check.
Brings a large batch of app work onto the website branch so the docs (and
casts) can reflect it:
- #24 vi-style j/k/g/G navigation in the load picker (ADR-0047 era) — unblocks
a scriptable projects cast (autocast can send j/k; not arrows)
- #22 demonstration overlay layer (ADR-0047): `--demo` mode, keystroke badges,
and step-caption info banners — usable from casts to highlight key moments
- C4 m:n convenience command (ADR-0045): `add m:n relationship … via <junction>`
- ADR-0046 UI: width-derived schema sidebar + Ctrl-O nav mode, responsive
two-row input + horizontal scroll, geometry-fixed hint panel
- X1 comprehensive logging sweep across worker/parser/app/persistence/runtime
- FK fixes: compound-FK violation message names every column pair; inline FK
referencing a compound PK points at the table-level form
Merged clean — no conflicts (the docs/website/ ADR namespace split kept the new
main ADRs 0045–0047 from colliding). Tests on the merged tree: 2290 passed,
0 failed (1 ignored doctest, inherited from main).
Render the keystroke badge and step caption as a solid yellow rectangle
with no border glyphs and a one-cell text margin, instead of a
rounded-border box — deliberately unlike the app's bordered panels so
the demo overlays read as a distinct, eye-catching callout. Shared
fill_overlay_rect helper (borderless Block fill + inset Paragraph).
Snapshots regenerated; ADR-0047 D4 wording updated.
Ctrl+] (decodes to Char('5')+CONTROL) toggles an invisible capture
buffer: typed characters accumulate without touching the input/output,
Backspace edits, every other key is inert, and a second Ctrl+] commits
the text to a caption box (empty commit dismisses). Handled at the top
of handle_key — before the badge and modal gates — so captions can be
authored over the load picker (the #24 cast); an ordinary keystroke
clears a visible caption. The caption renders as a floating
black-on-yellow box at the output panel's bottom-right, wrapped to <=3
lines (then ellipsised), with the keystroke badge stacked directly
above it when both are present.
Tier 1: capture/commit, invisible accumulation, backspace, inert keys
(incl. no badge), empty-commit dismiss, next-key clear, over-modal,
demo-off inert. Tier 2: caption / stacked / wrapped snapshots. Phase C
of ADR-0047 — feature complete.
In --demo mode, an otherwise-invisible key (Tab, Enter, arrows,
Ctrl-O, …) raises a transient [LABEL] badge — a floating
black-on-yellow box inset at the output panel's bottom-right. Set in
App::update before the modal gate (so it shows over the load picker,
the #24 cast); pure demo_badge_label maps the key set. The runtime
expires it on a ~1.5s timer via a new nearest_deadline helper that
extends the existing time-boxed-recv arm condition without disturbing
the ADR-0027 indicator debounce. New App.last_output_area lets the
top-level draw anchor the overlay; overlay colours centralised in
theme.rs.
Tier 1 (label fn, badge set/seq, over-modal), Tier 2 (dark/light
snapshots, black-on-yellow style, too-small clamp), runtime unit
(nearest_deadline). Phase B of ADR-0047; captions land in C.
Add `--demo` (and the RDBMS_PLAYGROUND_DEMO env fallback) to enter
demonstration mode, threaded onto App.demo_mode through run_loop —
mirrors the --no-undo plumbing. Off by default, zero footprint when
off. The --help line advertises only the visible keystroke badges;
the Ctrl+] caption trigger is kept low-profile (ADR-0047 D6 updated).
Phase A of ADR-0047; behaviour (badges/captions) lands in B and C.
Accepted decision record for the in-app demo overlay: a --demo mode
that shows automatic keystroke badges ([TAB], [ENTER], …) and a
stealth Ctrl+]-delimited step-caption buffer, both as floating
black-on-yellow boxes at the output panel's bottom-right. All forks
user-confirmed; a /runda pass contributed 10 tightening findings.
Indexed in docs/adr/README.md.
Add j (down), k (up), g (first) and G (last) to the load picker's
list sub-mode, alongside the existing arrow keys. Typeable keys keep
the picker drivable by autocast in the website's documentation casts,
which cannot emit arrow keys. Footer hint left unchanged.
ADR-0046 status -> Accepted + implemented (8 commits 9f5f76b..22bec61);
README index updated; the two draft-divergent decisions recorded inline
(App.relationships not SchemaCache; nav overlay partial-clear + gutter).
Handoff 63 covers the full UI build across Phases A/B/C; issues
#20/#21/#23 closed on Gitea.
DC3 — navigation-mode scroll: the focused Tables / Relationships panel
scrolls (Up/Down by a line, PageUp/PageDown by its visible-row count).
Per-panel offsets are clamped to content at render time, and the
renderer reports each panel's visible rows for paging — mirroring the
output panel's scroll. render_items_panel / render_relationships_panel
take &mut App, count their rows, and store+clamp the offset before
building the borrowing lines.
DC2 refinement: the expand-on-focus overlay now clears only the sidebar
strip plus a one-column gutter, leaving the base output/input/hint
visible (unchanged) to the right rather than blanking the whole area —
truer to "underneath keeps its layout", with the gutter keeping the
cut-off edge clean (chosen after eyeballing both variants). ADR DC2 and
the overlay snapshot updated to match.
Tests: line/page scroll move only the focused panel and clamp; the
render clamps a past-the-end offset so the last row stays visible.
Ctrl-O enters a navigation mode orthogonal to the input mode, cycling
focus Input -> Tables -> Relationships -> Input (Esc exits). While a
sidebar panel is focused the sidebar is revealed (a peek, even when
width-hidden) and drawn as an expanded 45-column overlay over a cleared
main area, so the schema is browsable without the cramped 26-column
unfocused width. The focused panel gets an accent border.
Routing lives in the main key handler after the modal gate, so Ctrl-O
and nav keys are inert while a modal is open; in nav mode every
non-navigation key (printable/Enter/Tab/Backspace/...) is inert because
the input is occluded. Scroll keys (Up/Down, PageUp/PageDown) are
reserved for DC3 (next).
New App state: NavFocus { Input, SidebarTables, SidebarRelationships }.
Tests: the focus cycle, Esc exit, input-keys-inert, overlay reveal +
expansion, the accent-border style, and an overlay snapshot.
The left column now stacks a Tables panel over a Relationships panel.
Each relationship renders as three narrow lines — its name, then the
endpoints broken at the arrow (Customers.id -> / indented
Orders.customer_id) — ellipsized past the inner width. The panel is
content-sized within [5 rows ("(none)" when empty), half the column];
the Tables panel keeps the rest (>=3 rows). Phase C adds focus+scroll
for content beyond the cap (clipped for now).
Data path: a new worker Request::ReadAllRelationships +
Database::read_all_relationships returns full RelationshipSchema
records; the runtime posts them via a RelationshipsRefreshed event
alongside the schema-cache refresh, and the App holds them in a new
`relationships` field.
ADR deviation (recorded in ADR-0046 DB2 + index): DB2 specified this
data on SchemaCache; it lives on the App instead — SchemaCache is
walker/completion-facing and needs only relationship names (untouched),
while the full records are UI-only, so App is the cleaner home and it
avoids editing ~23 SchemaCache literals. No behavioural difference.
Tests: panel-height bounds, the three-line render, the empty "(none)"
case, a snapshot, read_all_relationships end-to-end (real DB via the
m:n junction), and the event->field handler.
The schema sidebar (the left Tables column) is now shown only when the
terminal is wider than 90 columns; at or below that it is hidden and
the output/input panels span the full width. This reclaims horizontal
space on narrow terminals — notably the 90-column screencasts, where
the sidebar added little and cost the output panel its width.
Visibility is a pure function of terminal width (sidebar_visible);
the Ctrl-O peek-reveal lands in Phase C. render() splits the layout
conditionally — full-width right column when the sidebar is hidden.
Snapshots/tests that rendered at 80 wide now reflect the hidden
sidebar; those whose intent IS the sidebar (populated_with_table, the
items-panel and drop-table integration checks) render at 110 so the
Tables list is actually exercised — one masked-intent integration
check (matched "Customers" in the output, not the panel) is corrected
the same way. New tests cover the width gate and the show/hide
boundary.
On a comfortable terminal (height >= 40) the input panel shows two
rows: the single logical command soft-wraps across them — the first
row stops 6 columns short for the ADR-0027 validity indicator, the
second uses the full width — so a medium command is fully visible
without horizontal scrolling. A line longer than both rows still
scrolls (DA3-style, one column each side reserved for < / > markers)
to keep the cursor visible.
hint_rows generalises to panel_heights(area) -> (input_rows, hint_rows):
compact (<40) stays input 1 / hint 2; comfortable becomes input 2,
degrading hint-then-input on tiny terminals to protect the output
Min(5). render_input_panel splits into render_input_one_row (the
existing DA3 path, unchanged) and render_input_two_rows, with a new
expand_runs_to_cells helper placing styled cells across the rows.
Tests: panel_heights geometry, two-row wrap, overflow-scroll, the
indicator-stays-on-the-first-row case, and a two-row layout snapshot.
Compact one-row snapshots are byte-identical (that path is untouched).
A command longer than the input field used to clip silently at the
right edge, hiding the cursor and the command tail. Now the single
logical input line scrolls horizontally to keep the cursor in view,
with muted `<` / `>` markers at the reserved edge columns signalling
hidden content on either side.
The offset is a pure function of (line length, cursor column, field
width, previous offset) — input_scroll_offset — so the view only moves
when the cursor would leave the window, and one column is held on each
side for the markers so a marker never hides the cursor. The stored
App::input_scroll_offset resets when the buffer is replaced wholesale
(submit, history recall). The ADR-0027 6-column indicator reserve is
preserved.
Tests: pure-offset cases, tail-visible + head-visible render checks,
and the reset-on-submit/history check. One layout snapshot now shows a
long command's tail instead of its clipped head.
The hint panel's height was recomputed every frame from the wrapped
hint content (1–3 rows), so it resized as the user typed and shoved
the input/output panels — the flicker visible in the screencasts.
Make the height a pure function of terminal geometry (new hint_rows),
fixed between resizes: 2 content rows on compact (<40-row) terminals,
3 only on comfortable terminals narrow enough (<54 inner cols) to wrap
the longest catalog hint past two lines, degrading toward 1 on tiny
terminals to protect the output Min(5). resolve_hint_lines clamps to
that fixed budget (long hints ellipsize; short ones leave rows blank).
This reverses issue #12's shrink-to-content "reclaim"; its two tests
are replaced by an anti-jump invariant plus geometry-helper and
third-row tests. Two layout snapshots regenerated.
Accepted; implementation pending, phased A→B→C. Treats the three
coupled UI issues as one decision (shared width/height budget):
- #20 hint jumpiness: hint height becomes a function of terminal
geometry, fixed between resizes, so it no longer shoves the
input/output panels.
- #21 left column: kept but width-optional (hidden by default ≤90),
with a new relationships sibling panel and a Ctrl-O navigation/focus
mode (peek-reveal, expand-on-focus overlay, scroll).
- #23 long input: single-logical-line horizontal scroll plus a 2-row
display when tall, preserving the ADR-0027 indicator reserve.
A pre-build /runda DA pass drove key corrections: Ctrl-B→Ctrl-O (Ctrl-B
is the tmux prefix), an additive SchemaCache.relationship_details field
(retyping would break completion), full nav-mode key disposition +
modal gate, and Tier-2 snapshot coverage. Reconciles requirements
S1 (evolved), S2 (overridden — separate relationships panel), and
S4 (corrected — the stale "keyboard-toggleable" hint claim is struck;
no toggle added).
Updates docs/adr/README.md index and docs/requirements.md S1/S2/S4.
Three more casts on "doing" pages:
- first-project reuses the quickstart cast (the create→insert→show tour)
- modes (new): a simple command, then `mode advanced` where the same command
also prints "Executing SQL: …" (the teaching echo — "learn the SQL underneath")
- undo-redo (new): insert two rows, `undo` (Y-confirm modal) backs one out,
`redo` restores it
Also fix the cast endings (review feedback): scripts ended by typing a `quit`
command, which — once the trim drops the shell exit — left a dangling "quit" in
frame with no payoff. End every cast with Ctrl-C instead (the app's quit key,
KeyCode::Char('c')+CTRL): it types nothing, so the cast ends cleanly on the
last content frame. Generator gains a `CtrlC` key; all six casts regenerated.
Convert the three pages to .mdx and embed. Build clean (26 pages); 6 casts.
New dedicated Reference page for the advanced-mode SQL query features
under-covered by "Querying & inspecting": DISTINCT, GROUP BY/HAVING, set
operations (UNION/INTERSECT/EXCEPT), subqueries (IN + correlated NOT EXISTS),
CTEs (WITH), and expressions (CASE/CAST/functions) — each with a worked example
on the library schema and real captured output. Adds an explicit "supported
subset" boundary note (views, triggers, transactions, window functions,
multi-statement batches are not available) rather than linking to general SQL,
which would advertise unsupported features. Grounded in ADR-0030 §3/§13 and the
SQL grammar tests.
Cross-link added from Querying & inspecting. Build clean (26 pages);
box-drawing output verified; forbidden terms clean.
"A cast wherever the app does something": broad on Getting-started /
Using-the-playground / Guides + the landing; selective on Reference (motion
beats the still, static output kept regardless); skip pure-lookup/conceptual.
Casts are selective (a representative slice, not every command); autoplay only
the landing; all re-record via `pnpm casts`.
`create m:n relationship from <T1> to <T2> [as <name>]` generates a
junction table with one FK column per parent PK column ({table}_{pkcol},
typed via fk_target_type), a compound PK over them, and two CASCADE 1:n
relationships -- all in one do_create_table call = one undo step.
Auto-named {T1}_{T2} (optional `as`), both modes, compound-parent PKs
supported (ADR-0043). Self-referential m:n / PK-less parent / internal
junction name / name collision all refused.
Wired across every surface: grammar (separate CREATE_M2N node), worker
executor, runtime dispatch, completion ("m:n" composite), hints,
highlighting, help + usage catalog + disambiguator, and the advanced-mode
DSL->SQL teaching echo (render_create_m2n, round-trips as valid SQL).
Generalized/fixed framework assumptions the build + two /runda passes
surfaced (all behaviour-preserving for existing commands):
- simple-mode dispatch committed simple.first() unconditionally -> tries
candidates, so `create table` no longer shadows `create m:n`.
- the completion continuation-merge was advanced-only -> runs in simple
mode too when an entry word has >1 DSL form (gated simple_count>1).
- do_create_table now rejects internal `__rdbms_*` names (closes a
pre-existing hole on the DSL create-table path too, not just m:n).
- usage disambiguator now recognizes the `m:n` opener.
Tests: 14 integration (tests/it/m2n.rs), 7 typing-surface matrix, echo /
highlight / usage / internal-name units. Closes C4.
2237 pass / 0 fail / 1 ignored. Clippy clean.
Fourth cast: build a minimal two-table schema with rows, switch to advanced
mode (`mode advanced`), and run a join pairing each book with its author —
shows the mode switch + SQL + multi-table result, motion that complements the
guide's static examples. Convert the guide to .mdx and embed above the intro.
Recorded via `pnpm casts`; build clean (25 pages).
Third earmarked cast: declare a 1:n relationship, then `show relationship`
draws the two-table connector diagram — showcases the V1 relationship
visualization with motion a still block can't. Convert the relationships
reference page to .mdx and embed it above the syntax (the static diagrams
below remain the exact reference).
Recorded via `pnpm casts`; build clean (25 pages).
Address cast review feedback:
- Trim every cast to the in-app region (generate.mjs): the recording now
starts with the app already running and ends on the last in-app frame —
drops the `$ rdbms-playground` launch and the return-to-shell frame (the
latter was the stray cursor-under-$ artifact). Opt out per cast with
`keepShell: true` for demos that document the CLI launch.
- Landing quickstart cast: autoPlay + loop, with a 2.5s hold on the final
frame so it pauses before restarting.
- Cap the demo at max-width 46rem and centre it, so the player (fit:'width')
no longer scales its font up to the full splash column.
Casts re-recorded via `pnpm casts`. Build clean (25 pages).
Tab-keypress visibility deferred to an in-app overlay primitive (filed as
issue #22 — also serves the planned guided-lesson system); the cast notes
Tab in its caption for now.
create m:n relationship from <T1> to <T2> [as <name>] generates a
junction table (compound PK over the two FK column sets, CASCADE FKs)
plus two 1:n relationships, in one do_create_table call = one undo
step. Forks user-confirmed; /runda DA pass verified the reuse against
code and the no-PK-tables-exist-in-advanced-mode fact (parent-PK guard
retained). Self-referential m:n refused; FK cols named {table}_{pkcol}.
Dropping a PK column was already refused in both modes via the shared
do_drop_column guard; this adds end-to-end coverage on the advanced
ALTER surface (single-column + compound PK, asserting refusal for the
right reason) and documents the asymmetry that advanced-mode SQL can
create a PK-less table (SQLite's implicit rowid keys it) while simple
mode forbids it. See issue #19 comment for the full assessment.
Astro 6.4.5 deprecated markdown.remarkPlugins/rehypePlugins/remarkRehype in
favour of the new unified() API. The warning came from @astrojs/starlight
0.39.3's own integration code, not our config; Starlight 0.40.0 adopts the new
API, so it's gone.
- astro 6.4.4 -> 6.4.5; @astrojs/starlight 0.39.3 -> 0.40.0
(brings astro-expressive-code 0.43.1, @astrojs/markdown-remark 7.2.0)
- Starlight 0.40.0 adds an optional @astrojs/markdown-satteri peer (an opt-in
high-performance markdown engine); it's an optional peer so pnpm doesn't
install it, and we have no need for it — we stay on the default
markdown-remark/unified pipeline
Verified: deprecation gone; pnpm build clean (25 pages); rendering signals
unchanged vs baseline (highlighting, > prompt + copy-button :has() CSS, asides,
embedded casts); pnpm audit clean.
The previous commit captured only the .md→.mdx rename — a botched `git add`
(a stale .md pathspec aborted the whole add) dropped the actual content. This
adds it:
- casts.mjs: the assistive-editor cast definition (Tab completion → the [ERR]
validity indicator catching a misspelled table → friendly error → corrected
command). Behavior verified by a throwaway spike before scripting.
- public/casts/assistive-editor.cast (generated via `pnpm casts`)
- embed the cast under the intro on the assistive-editor page
Verified: pnpm build clean (25 pages); cast bundled, served, and referenced.
Visual playback check pending (verify via dev server/tunnel).
Add a second demo, earmarked as prime cast material: Tab completes a table
name, then the [ERR] validity indicator catches a misspelled table before
submit, the friendly error confirms it, and the corrected command runs.
Behavior verified by a throwaway spike before scripting.
- casts.mjs: assistive-editor cast definition
- public/casts/assistive-editor.cast (generated)
- convert the-assistive-editor.md -> .mdx and embed the cast under the intro
Verified: pnpm build clean (25 pages); cast bundled, served, and referenced
on the page. Visual playback check pending (verify via dev server/tunnel).
Settle the cast toolchain (STYLE.md #9) and build the demo pipeline end to
end. Driver: autocast, chosen by spike — its !Interactive feeds keys to the
running TUI and captures the redraw, the right model for a full-screen
crossterm app. asciinema-automation was rejected (assumes shell echo/\n Enter;
produced a garbled cast against the TUI).
- add asciinema-player; Cast.astro (player island) + Demo.astro (the WASM-swap
seam, ADR-website-001 §3)
- casts-src/: human-readable command-lists (casts.mjs) + generate.mjs, exposed
as `pnpm casts`; expands steps to autocast YAML and records to public/casts/.
Command-lists are the durable source; .cast files are regenerable (final
re-record sweep due once the app is locked).
- quickstart.cast (create -> add columns -> insert -> show data) embedded on
the landing page above the feature cards.
Verified: pnpm build clean (25 pages); player + cast bundled and served;
landing HTML references the cast. Visual playback check pending (no headless
browser here — verify via dev server over the tunnel).
Build the library: polish and extend from a 2-table draft into the full
guided build — all four tables, the authors→books 1:n and the books↔members
m:n through the loans bridge (bridge-table concept taught in context), the
isbn unique constraint, and captured show-relationships / show-data output.
Remove the draft marker; it is now publication-quality. Uses the same sample
rows as the Reference pages so output matches across the site.
Querying with joins (new): joins built up from two tables → the three-table
bridge join → a filtered join → a group-by aggregate, all in advanced mode
with real captured output.
Verified: pnpm build clean (25 pages); no forbidden terms; internal links
resolve. Advanced-mode `mode advanced`/`mode simple` and the unique
constraint checked against source.
ADR-0043 residual: a compound-FK violation's friendly error named only the
first child->parent column pair (the ADR-0019 facts model is single-column).
enrich_fk_violation now gathers all pairs of the matched relationship and
carries them comma-joined in the existing single-column facts slots, so the
headline reads e.g. "no parent row in `Region` has `country, code` = `7, 8`."
instead of naming just `country`.
Single-column behaviour is unchanged (a one-element join is the element
itself). No facts-model or catalog change -- the joined strings flow through
the existing `{parent_column}` / `{value}` placeholders.
Tests: enrichment facts (compound names every pair, single-column
regression) + translate rendering (headline names both columns). 2211 pass
/ 0 fail / 1 ignored. Clippy clean.
Expand columns, relationships, indexes, constraints, inserting-and-editing-
data, and querying-and-inspecting from syntax-only stubs into full pages,
each with worked examples on the library schema and real captured app output
(structure boxes, relationship diagrams, data tables, show-lists, query
plans, cascade summaries). All output captured verbatim from the app — never
hand-drawn. Both simple- and advanced-mode forms shown where both apply;
advanced syntax verified against tests/source.
STYLE.md: record the output-block convention (plain unlabelled fence,
captured from a throwaway harness, not hand-drawn).
Verified: pnpm build clean (24 pages); no forbidden terms; internal links
and heading anchors resolve.
ADR-0043 D4 residual: an inline column-level FK (`<col> REFERENCES P(a,b)`)
is single-column by construction, so referencing a parent's compound PK
gave the generic arity error ("1 foreign-key column(s) on the child side,
but `P`'s key has 2..."). It now points the user at the table-level form:
"an inline column reference can only name one column ... Use the table-level
form instead: FOREIGN KEY (<columns>) REFERENCES P (a, b)".
- Adds `inline: bool` to SqlForeignKey, set by the grammar's single shared
builder consume_fk_reference (true for the inline path, false for the
table-level and ALTER paths).
- resolve_fk_parent_columns takes `inline` and tailors the arity-mismatch
message when an inline FK meets a compound key.
Tests: parse-layer (inline=true / table-level=false) + end-to-end worker
refusal wording. 2209 pass / 0 fail / 1 ignored. Clippy clean.
Completes the X1 full sweep started in a8ad0c6 (db.rs). Closes X1 -> [x].
- persistence/mod.rs: debug! on every yaml/CSV/history write -- the
silent-failure-prone disk paths (write_schema, write_table_data incl.
the empty->delete branch, append_history/_failure).
- runtime.rs: debug! on execute_command_typed dispatch (one per executed
command, complements the db.rs executor logs).
- app.rs: debug! on submit (route + submission mode), dispatch_app_command,
and the ADR-0044 diagram-vs-prose render-mode choice.
- dsl/parser.rs: trace! on parse begin/outcome at the parse_command_inner
choke point -- trace, not debug, because the live overlay/completion
re-parse per keystroke (hot path).
- logging.rs: documented level discipline (error/warn/info/debug/trace) so
the convention survives across sessions.
Levels verified end-to-end through the real worker thread + logging::init.
~75 -> 135 tracing sites total. Tests: 2207 pass / 0 fail / 1 ignored.
Clippy clean.
Instrument db.rs to the CLAUDE.md "log liberally" bar (X1). 26 -> 67
tracing sites:
- Entry-level debug! on all 34 do_* executors (DDL, DML, relationship,
index, read paths), matching the existing do_sql_delete/do_run_select
style -- so the route through delegating executors (e.g. add_column ->
add_constrained_column_via_rebuild) is visible in the log sequence.
- Decision-point logs: rebuild_table primitive (begin/commit; FK-check
failure and foreign_keys re-enable failure as warn), do_insert autofill
summary, do_delete cascade summary, do_create_table FK resolution.
- Worker lifecycle (start/exit) raised debug! -> info! so it shows at the
default level.
Levels per the X1 discipline: debug for per-command detail (off by
default, opt-in via RDBMS_PLAYGROUND_LOG=debug), info for lifecycle, warn
for fallbacks. Loops log summary counts, never per-row.
Tests: 2207 pass / 0 fail / 1 ignored (unchanged). Clippy clean.
Brings main's relationship-visualization feature (ADR-0044 in the main
namespace) and Gitea-migration cleanup onto the website branch, so the
docs can be written against the new diagram output:
- show relationship <name> / show table <T> render two-table connector
diagrams (child-FK-left, parent-right, n…1 cardinality)
- compound-FK bus routing + pairing line
- ~2000 lines across src/{app,db,event,output_render,runtime,ui}.rs,
new insta snapshots, tests/it/{show_list,compound_fk}.rs
Merged clean — no conflicts. The prior commit moved the website ADR out
of docs/adr/ into its own namespace, so main's ADR-0044
(relationship-visualization) lands with no collision.
Tests on the merged tree: 2207 passed, 0 failed, 0 skipped
(1 ignored doctest, inherited from main).
The website subproject drew ADR numbers from the same global integer pool
as main, so every merge risked a collision — this already happened twice
(drafted 0042, bumped to 0044, both landing on numbers main had taken; main
has now used 0044 for relationship visualization). Give the website its own
ADR namespace so the two never compete again.
- docs/adr/0044-public-website-...md
-> docs/website/adr/20260604-adr-website-001.md (id: ADR-website-001)
- docs/plans/20260604-adr-0044-website.md
-> docs/website/plans/20260604-website-implementation-plan.md
- new docs/website/adr/README.md index (dated <date>-adr-website-<NNN> seq)
- docs/adr/README.md: drop the 0044 entry, add a namespace pointer in the
intro (keeps the list tail == merge-base, so main's 0044 merges cleanly)
- ADR-0000: record the subproject-ADR-namespace convention
- update references in STYLE.md, astro.config.mjs, the plan body
Handoff files left untouched as point-in-time history.
First handoff for the website work, on a branch-scoped name sequence
(…-website-handoff-N.md) to avoid colliding with main's handoff-NN files.
Captures stack/layout, the five-section structure, binding conventions
(no DSL / no engine name / fence + prompt + copy rules), the canonical
library schema, a verified-syntax cheat-sheet, the dev-server IPv4 gotcha,
next-work priorities (fill the 6 Reference stubs, iterate guides, Phase B
landing, deferred casts), and process pins.
Restructure the docs into five top-level sections, splitting the
application you drive from the database language you build with.
- New "Using the playground" section: command-line options; the assistive
editor (completion, highlighting, [ERR]/[WRN] indicator, hints, in-line
editing); the output pane (scrolling); projects (save/load/new/rebuild);
undo/redo & history; export & import; clipboard; getting help. Grounded in
the in-app help/usage and ADR-0003/0022/0027.
- Reference: seed the remaining topic pages (Columns, Relationships,
Indexes, Constraints, Inserting & editing data, Querying & inspecting)
with real syntax synopses; worked examples to follow.
- Surface the assistive editor on the landing page and in Getting started;
restore cross-links now that targets exist.
Plan + STYLE updated to the five-section structure. 24 pages, build green,
links resolve, content clean; planned features carry "planned" callouts.
Completes requirement V1. A compound (multi-column) FK now routes a
bus connector — each paired endpoint's stub merges into a shared
vertical channel that splits to the other side — plus an explicit
"(a, b) ▶ P.(x, y)" pairing line; the bus generalises the single-column
jog (reproducing it exactly, so prior snapshots are unchanged).
Self-referential FKs render as two same-named boxes.
- output_render.rs: gutter_seg routes all endpoint pairs via a
junction() bus; pairing line for compound FKs; compound, self-ref,
and compound-from-data (build_diagram_table glue) tests + snapshots
- compound_fk.rs: worker test that show_relationship carries both
paired column lists into the diagram payload
- db.rs: document do_show_one's now-app-superseded relationship prose
branch (retained as a worker-API/text fallback; could back a future
non-visual display option, cf. ADR-0044 OOS-7)
Second /runda pass over the implementation: confirmed ADR-compliance,
UTF-8/byte-range safety, and edge-case routing. The ADR §3 last-resort
helper line was considered and rejected (vertical fallback + ratatui
truncation cover all realistic cases). ADR-0044 marked implemented;
requirements.md V1 -> [x].
Full suite 2207 pass / 0 fail / 1 ignored; clippy nursery clean.
show table <T> and add/drop relationship echoes now render the focal
structure box plus a Relationships section of compact stacked
connector diagrams (child-left/parent-right, n…1, actions); incidental
DDL echoes keep the prose References:/Referenced by: form. Selected by
command in handle_dsl_success via the "relationship-relevant" reach.
- output_render.rs: render_structure refactored into section helpers
(box/prose/index/constraint), byte-identical output; new
render_structure_with_diagrams + compact-box rendering
- app.rs: handle_dsl_success routes ShowTable/Add/DropRelationship to
the diagram path, others to prose
- fixes: eager widths[1] index on compact (1-col) boxes; body-cell
padding under title-widening (name wider than columns)
Tests: unit + snapshot + integration; add-relationship echo test
updated to the diagram form. Full suite 2203 pass / 0 fail / 1 ignored;
clippy clean. V1 still [/] (compound routing + self-ref remain).
Add a custom Shiki grammar for the simple-mode command language
(src/grammars/rdbms.mjs), registered with Expressive Code. Two language ids
share it: rdbms (real commands) and rdbms-syntax (abstract templates).
Simple-mode blocks now highlight; advanced examples keep sql.
Separation + copy ergonomics via CSS (global.css): a decorative, copy-safe
"> " prompt on rdbms command lines (not in the copy buffer), and the copy
button hidden on multi-command rdbms blocks and on rdbms-syntax templates
(the app input is single-line, so a multi-command paste is not runnable);
single-command, sql, and sh blocks keep copy.
Content: convert 22 simple-mode fences to rdbms; lead the simplest examples
(first project, Tables reference) with bare "with pk" (the beginner default
that creates a ready-made id key), pointing to the named form. Record the
fence + prompt conventions in STYLE.md.
The merge from main added user-facing surface the pre-merge inventory had
listed as planned. Mark them documented-as-shipped: show tables /
relationships / indexes + show relationship/index <name> (V5/V5a),
help [<command>] + help types (H3), compound-primary-key foreign-key
references (T3, ADR-0043), and friendlier parse-error messaging (H1a).
Refresh the test count to 2193 and note requirements.md now uses a [/]
partial marker (trust the code, not the marker).
The first wired slice of relationship visualization (V1). `show
relationship <name>` now renders the relationship as two full
structure boxes joined by a width-jogging connector (child-left /
parent-right, n…1 cardinality, on delete/update actions), styled
App-side, with a vertical-stack fallback for narrow terminals.
- db.rs: RelationshipDiagramData + show_relationship worker path
(structured data: the relationship + both endpoint TableDescriptions)
- runtime.rs: named relationships route to the structured outcome
(boxed); other show <kind> forms stay prose
- app.rs/event.rs/ui.rs: DslShowRelationshipSucceeded rendered App-side;
new diagram OutputStyleClass variants; App::last_output_width from ui.rs
- output_render.rs: styled Seg layout engine (boxes, connector routing,
side-by-side + vertical), composing the ADR-0016 box primitives
Tests: 4 unit + 4 integration; full suite 2201 pass / 0 fail / 1 ignored;
clippy nursery clean. requirements.md V1 stays [/] (show table diagrams,
compound routing, DDL-echo wiring remain).
Two-table connector diagrams (Style A) for relationships, resolving
ADR-0016 OOS-1 and the open half of requirements.md V1. Reach is
'relationship-relevant' (show relationship / show table / relationship
DDL echoes; incidental echoes keep prose). App-side rendering, width-
adaptive side-by-side vs vertical, compound-FK pair routing, bold box
title rows. Revised after a /runda DA pass corrected three inverted-
architecture claims (App-side rendering, untracked output width,
prose-in-worker show-relationship).
Index updated per ADR-0000.
Astro/Vite's default localhost bind resolves to IPv6 ::1 on this host,
which silently breaks `ssh -L 4321:127.0.0.1:4321` tunnels (they target
IPv4). Pin server.host to 127.0.0.1 so dev/preview is reachable over an
IPv4 loopback forward. Loopback-only — no network exposure.
Codifies the fix for the ADR-0042 cross-branch collision (resolved this
merge by renumbering the website ADR to 0044): ADR numbers are assigned
when a branch merges to main, not at creation. On a branch, draft under
a placeholder (ADR-XXXX title / draft-<slug>.md filename); main's
docs/adr/README.md is the single source of truth for the next free
number.
- ADR-0000: new "Numbering discipline" section.
- CLAUDE.md: pointer to it from the documentation-discipline note.
Brings website up to date with main (18 commits): H1a parse-error
pedagogy, V5/H3/V5a show+help commands, ADR-0043 compound-PK FK,
handoffs 58-59, and the GitHub->Gitea doc scrub (Cargo.toml repository,
CLAUDE.md, ADR-0001 amendment, requirements).
Conflict: docs/adr/README.md. main and website had each created an
ADR-0042 (main: H1a parse-error pedagogy; website: public website &
docs site). Renumbered the website ADR to 0044 (next free after main's
0042/0043) and updated all references (ADR file, plan file, STYLE.md,
astro.config.mjs, README index). Website build verified green.
Multi-column FK parsing on both surfaces: DSL from P.(a, b) to
C.(x, y) (parenthesized endpoint; single bare form unchanged) and
SQL FOREIGN KEY (a, b) REFERENCES P(x, y) incl. bare-reference
auto-expand. consume_fk_reference + the table-level/ALTER FK
parsers collect column lists; the from P. completion now offers
( (snapshots updated). 12 integration tests in
tests/it/compound_fk.rs cover parse (both surfaces), engine-enforced
FK, arity + partial-PK + per-pair-type-mismatch refusal,
--create-fk per-column, save->rebuild round-trip, undo (one step),
and single-column preservation. Mark T3 [x]; ADR-0043 implemented.
DA pass found three change sites the first sketch missed
(teaching-echo renderers, --create-fk per-column creation, the
auto-name generator) and made explicit the rules the forks left
implicit: SQLite FK precondition (compound PK provides the unique
index), explicit parent cols must be the PK set (any order,
positional), arity/empty/inline-rejection wording, single-in-parens
accepted, --create-fk per-column typed to fk_target_type. Expanded
the test plan to cover enforcement, auto-expand, undo, round-trip.
Fixed a stale 'legacy yaml loads' test line (no back-compat).
Audit found single-column FK woven through ~15-20 sites; earns an
ADR. Decision: reference the parent's full compound PK, matched
positionally to an equal-length child list, per-pair type compat.
DSL `from P.(a,b) to C.(x,y)`; SQL `FOREIGN KEY (x,y) REFERENCES
P(a,b)` with bare-FK auto-expansion. Storage follows the existing
primary_key: [...] list convention (yaml columns: [a,b], uniform
JSON in unchanged metadata TEXT cols); back-compat not required,
so no migration. Also marks T3's verified scope.
Fold the singular per-item forms into Command::ShowList { kind,
name: Option<String> } (name: Some = one item). Two grammar
branches reuse the relationship/index completion sources; worker
do_show_one renders a labelled detail block or a friendly
"No ... named X." line, reusing the V5 render path. Help +
parse-usage entries, two ADR-0042 near-miss rows, 5 integration
tests. Mark V5a [x] — V5's [<name>] clause now complete.
HELP node takes an optional single-word topic (BarePath);
AppCommand::Help { topic }. note_help_topic renders the help
block(s) of every command sharing that entry word (so `help
create` covers both create forms), plus `help types` and a
friendly "no help for X" pointer for unknown topics. Full help
gains a detail-hint footer. Catalogued help.detail_hint /
help.unknown_topic; parse-error matrix updated (help now takes a
topic, so the near-miss is the multi-word case). 9 integration
tests in tests/it/help_command.rs. Mark H3 [x].
Add the list-all show family as one Command::ShowList { kind }
variant. A read-only worker show_list formats count-headed lists
(reusing do_list_tables / read_all_relationships /
read_table_indexes, so it never drifts from the items panel);
internal __rdbms_* tables excluded. Help + parse-usage entries
added; 10 integration tests in tests/it/show_list.rs.
Mark V5 [x]. Split the singular show relationship/index <name>
detail forms (the [<name>] half) into a new tracked V5a [ ] item
rather than leaving them as an untracked footnote.
Audit of all 35 [ ] items vs source found ~46% mis-marked. Add a
[/] partial marker; promote 7 shipped-but-open items to [x]
(S1/S4/S5/I1a/I3/I4/C1) and 9 substantially-built ones to [/]
with gap notes (S3/A1/V1/V2/V5/T3/H3/DOC1/X1). Fix CLAUDE.md's
false "Tier 4 is wired" claim — no PTY tests exist.
requirements.md H1a → [x]: the per-command near-miss matrix (entry words,
missing clauses, committed multi-forms, both modes) plus the gap fixes
(G1 `1:n relationship`, G2 select projection gloss, G3 mode-aware usage
showing all valid forms, G4 `with` CTE template, CROSS JOIN ON teaching
message) close the systematic pass. The advanced-SQL items the survey
flagged (INSERT…SELECT count, RETURNING scope) were verified already
present. One low-priority residual is deferred by decision (submit-time
expression first-set at non-projection positions).
ADR-0042: record the pre-existing `SELECT *` arity caveat (INSERT…SELECT
with a star projection isn't expanded for pre-flight arity; engine
catches it at execution — adjacent to ADR-0019 §OOS-2).
Phase D foundation. Configures the pragmatic four-section sidebar
(Getting started / Guides / Reference / Concepts) and replaces the
template example pages with grounded content built on the shared
"library" example database (authors/books/members/loans):
- Getting started: installation, first project, simple vs advanced,
the example library.
- Reference: Types (all ten + serial/shortid + advanced aliases),
Tables (create/drop, compound PK, advanced CREATE TABLE).
- Concepts: projects & storage (readable files, derived database,
autosave, temp projects).
- Guides: Build the library (draft, to be refined for teaching).
Command syntax grounded in en-US.yaml usage/help, command.rs, and
types.rs (verified against tests). Records the settled doc decisions
in STYLE.md. Build green (10 pages, Pagefind); content clean of
"DSL"/engine-name.
Empirically re-checking ADR §3's advanced-SQL "gaps" reversed two of
three — the code survey that produced the list was wrong:
- INSERT…SELECT column-count: already handled (verdict=Error, "the
column list names N column(s) but M value(s) are given";
insert_select_arity_mismatch_fires).
- RETURNING scope: already handled (completion offers the table's
columns; `returning <unknown>` → unknown_column diagnostic).
The one genuine residual is fixed: `select … cross join b on …`
rejected the ON with a bare "expected end of input". Add
parse.cross_join_no_on — "a CROSS JOIN has no ON clause — it pairs
every row; for a join condition use `JOIN … ON`, or filter with
`WHERE`" — rendered when the failing token is `on` and the most
recent consumed join is a CROSS join (a precise signature: every
other join requires `on`, so `on` is expected there, not a failure).
Render-only in format_walker_error; two misfire guards locked (plain
join still asks for ON; a stray `on` with no join does not fire).
ADR-0042 §3 corrected + Implementation-outcome records the advanced-SQL
re-check and the user-confirmed low-priority residual (submit-time
expression first-set at non-projection positions, where typing-time
completion already offers the right candidates).
Full suite green (lib 1578 / it 388 / typing_surface_matrix 192); clippy clean.
The /runda DA pass found G3 over-corrected: advanced-mode `create`/`drop`
showed SQL forms only, hiding the DSL fallback forms that are valid input
in advanced mode (verified: `create table Foo with pk`, `drop column …`
parse and dispatch). Per the user decision, the advanced usage block now
shows every form valid in the mode, SQL-primary first, then the DSL
fallback forms — a usage hint must never hide working input. Simple mode
unchanged (DSL forms only).
Matrix completion (closing the residual coverage tail):
- arg-less app commands (help/rebuild/new/load/undo/redo/export/import)
audited + locked — all reject trailing junk with "expected end of
input" + usage.
- committed multi-forms (add index/constraint/1:n relationship, drop
index/constraint/relationship, show table, change column, create index,
alter table add/drop) audited + locked in
near_miss_matrix_committed_multiforms — each renders its own
form-specific missing-keyword message + usage.
Also from the DA pass:
- G2 distinct+all detector empirically verified unique to projection
start (no misfire at count( / union / union all / select distinct).
- stale `chumsky` comment removed (app.rs import handler).
- ADR-0042 Implementation-outcome section records G1–G4, the
user-confirmed G3 decision, and the now-complete matrix coverage.
Full suite green (lib 1578 / it 387 / typing_surface_matrix 192); clippy clean.
Phase A of docs/plans/20260604-adr-0042-website.md. Scaffolds the site
under website/ from the Starlight template; adds Tailwind v4 (via
@tailwindcss/vite) bridged to Starlight with @astrojs/starlight-tailwind
(src/styles/global.css + customCss). Production build is green: static
output, Pagefind search index, sharp image optimization.
Template placeholders (title, example pages, sidebar) are left for
Phase B/D. Reconciles the ADR/plan/index wording from "Astro 5" to
"Astro 6" to match the scaffolded toolchain.
Close the three remaining ADR-0042 triage gaps, each test-first, and
lock the advanced-mode near-miss matrix.
G2 — bare `select` dumped the 14-item expression first-set. Collapse
it to "a projection: `*`, a column, or an expression" in the error
message only (parser::format_walker_error), detected by the joint
`distinct`+`all` quantifier signature unique to a projection start.
Render-only: completion/hints still expand the full set (typing-surface
matrix unchanged).
G3 — the usage block was mode-blind: advanced `create table` showed the
DSL `create table … with pk …` template. usage_key(s)_for_input gain
mode-aware `_in_mode` variants selecting candidates by CommandCategory;
render_usage_block and the typing-time ambient usage thread the
submission mode. Advanced `create` now shows both SQL forms. A fallback
covers shared SQL nodes (insert/update/delete) that declare no
usage_ids of their own — without it they regressed to the
available-commands fallback (caught by the new advanced matrix).
G4 — `with` borrowed `select`'s usage template; give it its own
parse.usage.with CTE template.
Tests: new near_miss_matrix_advanced_mode (12 SQL-surface cases incl.
the available-commands regression guard) + per-gap tests; removed the
temporary baseline_dump. Full suite green (lib 1578 / it 386 /
typing_surface_matrix 192); clippy clean.
Planning artifacts for the first public website, recorded before any
code is written.
- ADR-0042: the decisions — Astro 5 + Starlight + Tailwind v4 (over
SvelteKit); asciinema .cast demos reusable in docs (scripted-input
driver, not history.log replay); in-page WASM playground deferred
behind a stable demo seam, with the portable-core vs native-edge
boundary recorded for a future ADR; portable static hosting (Vercel
target); monorepo (website/); website is the canonical docs home;
full-feature-set docs with "planned" callouts; user-facing copy uses
no engine name and no "DSL"; install via prebuilt binaries + package
managers.
- docs/plans/20260604-adr-0042-website.md: implementation plan with the
grounded documentation inventory and phases A–E.
- website/STYLE.md: living documentation style guide + open-decisions log.
- docs/adr/README.md: index updated for ADR-0042 (numerical order).
Start the ADR-0042 §1 near-miss matrix: a table-driven
near_miss_matrix_simple_mode locking 26 simple-mode parse-error
renderings (every DSL entry word's bare/missing-clause cases plus the
mode arg-errors and the "this is SQL" rail), and an #[ignore]
baseline_dump capturing the full rendering for ongoing triage.
G1 fix: the bare `1` cardinality literal that opens `add 1:n
relationship …` rendered cryptically in expected-sets. Render it as
`1:n relationship` in error wording only (format_expectation) —
completion/hints still read the raw Expectation::Literal("1"), so the
candidate surface is unchanged. Updated the one anchor test.
Full suite green (lib 1578 / it 382 / typing_surface_matrix 192).
ADR-0020/0021 specified a chumsky-based H1a; ADR-0024 replaced chumsky
with the scannerless walker, leaving both obsolete. Mark them superseded
(kept as institutional memory) and add ADR-0042, which restates H1a
against the architecture as built.
ADR-0042 records that H1a is substantially shipped already — per-command
usage block, available-commands fallback, source-derived ident slot
labels, curated parse.custom.* near-miss messages, and schema-aware
[ERR] diagnostics — and defines the remaining work: a verified
per-command near-miss matrix (the definition of done), friendlier
literal expectation labels that add role context while keeping the
exact literal visible, and advanced-mode SQL parse parity (RETURNING
scope, CROSS JOIN ON, INSERT…SELECT count), kept distinct from
ADR-0019 §OOS-2 engine-error sanitisation.
- docs/adr/0020,0021: superseded notes + README entries
- docs/adr/0042: new ADR
- docs/adr/README.md: index upkeep (ADR-0000 rule)
Captures the post-handoff-56 arc: H1 (ADR-0019) verified already shipped
(not "partial"); build-hygiene fixes (incremental off, line-tables debug,
cargo-sweep) cutting target/ from 38 GB to ~1.6 GB; and consolidating 25
integration crates into one `it` binary (disk win, not the predicted
speed win). Flags the new test layout (add tests under tests/it/ + a mod
line) and points the next session at H1a (ADR-0021) as the queued job.
Each top-level tests/*.rs was its own crate → its own binary, each
statically linking the bundled engine + every dep. 26 of them, so an
edit to the lib relinked all 26. Moved the 25 standalone files into
tests/it/ under one tests/it/main.rs (the pattern typing_surface
already uses); cargo auto-detects it as the `it` target. End state: 2
integration-test binaries instead of 26.
Result: target/debug/deps 1.5 GB → 629 MB (-58%). Build time barely
moved (clean 22.9s→22.4s, lib-edit relink 13.3s→12.4s) — wall-clock is
dominated by compiling, not linking, so this is a disk win, not a speed
win (see docs/plans/20260602-test-consolidation.md). Tests unchanged at
2151/0/1; clippy clean; no fixups needed. typing_surface_matrix stays
its own already-consolidated binary.
Tradeoff: the 25 files now share one crate (a compile error fails the
whole `it` binary; module-scoped namespaces, no clashes) — negligible
for a solo project.
target/ had reached 38 GB, dominated by a 16 GB incremental cache
(~28 compilation units × every historical config, never evicted) and
~25 separate integration-test binaries each statically embedding the
bundled engine + full debug info.
[profile.dev] (inherited by the test profile):
- incremental = false: the cache earns little in a full-suite workflow
and never self-evicts; off, target/ stays bounded.
- debug = "line-tables-only": keeps file:line in panics/backtraces (we
debug via tracing logs) at a fraction of debug=2's size.
Net: a clean full build dropped from 38 GB to ~1.6 GB. Document the
rationale plus the cargo-sweep workflow (the GC cargo lacks) under a
new CLAUDE.md "Build hygiene" section; also drop the now-shipped H1
from CLAUDE's deferred list (H1a remains).
Verification found H1 (ADR-0019) fully implemented and tested: the
friendly::translate_error chokepoint is wired on the live failure path
(runtime + app + DbError::friendly_message), covers all five error
categories (UNIQUE, FOREIGN KEY both sides, NOT NULL, CHECK,
type-mismatch) with operation×kind×verbosity wording, the messages
verbosity command, and §6 row-pinpointing via runtime-resolved facts —
backed by 44 friendly unit tests + 12 full-stack friendly_enrichment
integration tests. The "partial / FK-only" notes were stale.
Mark requirements H1 done; fix the obsolete "diagnostic_table is
always None" comment in translate.rs (pinpointing landed in 431645a).
Remaining ADR-0019 scope (§9 i18n sweep, §OOS-2 advanced-SQL
sanitization, §OOS-3 messages persistence) stays deferred.
serde_yml (RUSTSEC-2025-0068) and its libyml backend
(RUSTSEC-2025-0067) are archived, unsound, and unmaintained with no
patched version. Swap to serde_norway, the maintained serde_yaml fork
on unsafe-libyaml-norway — a drop-in for our from_str / to_string /
Value usage across persistence, undo, and the catalog parser.
Clears both advisories (cargo audit / osv-scanner / grype all clean;
serde_yml + libyml gone from the tree). No behaviour change; full
suite 2151/0/1.
New app-level `copy` / `copy all` / `copy last` command (ADR-0041).
Delivery is OSC 52 *and* a best-effort native write (arboard), always
both — OSC 52 acceptance is undetectable, so a true fallback can't be
built. Payload is the panel's plain text exactly as rendered (tags,
✓/✗, box-drawing), drift-locked to render_output_line. arboard added
--no-default-features (X11-only; OSC 52 covers Wayland).
Amends ADR-0003's command registry; requirements V6.
#10 (output [error]/[system] tag-colour collision) and #14 (per-project
input-mode persist & restore) closed. Two ADR amendments authored.
Records the two /runda saves on #14 (persist semantics re-decided by the
user; missing integration test added red-first) and a detailed pickup
brief for #11 (clipboard — new dependency, security review + command
surface to escalate).
The post-/runda DA pass on 4cd574b found the persist-on-unload wiring
(quit + project switch calling Database::set_mode) had no integration
test — only the db-level set_mode behaviour was covered, not that the
runtime actually invokes it on unload.
Add runtime::switch_persists_the_outgoing_projects_mode, driving the
real handle_project_switch end-to-end and asserting the outgoing
project's project.yaml recorded the mode it was left in. Red-first
verified: with the set_mode call disabled it fails (None vs
Some(Advanced)). The quit unload site shares the same set_mode call;
Action::Quit emission is already covered in app tests.
Updates ADR-0015 Amendment 1 coverage note.
The input mode always started in simple; a learner who quit in advanced
had to re-toggle every launch. Store the mode per-project in project.yaml
(project.mode:, optional, default simple) and restore it on every open.
Mode is live UI state, not schema: the worker stamps the current mode
into project.yaml on every write, so a later command rewrites the live
value rather than clobbering it — no db round-trip needed. The mode is
persisted on unload (quit + project switch) so the mode you leave a
project in is always what reopens; the `mode` command also persists
immediately. A switch saves the outgoing mode, then restores the
incoming project's stored mode.
New --mode simple|advanced CLI flag (precedence --mode > stored >
simple; combines with --resume). A teacher can ship a project that
opens in advanced mode and export it to students (the mode travels in
the zip).
ADR-0015 Amendment 1; ADR-0003 note; help banner; requirements L1b.
The output tag was tinted by submission mode for every line kind, so a
[system] line and an [error] line rendered with an identical leftmost
tag — distinguishable only by body colour. And flooding the whole error
body in red made long messages hard to read.
Colour the tag by message status instead (its OutputKind): [system] →
green, [error] → red; the echo tag keeps the mode tint (ADR-0037's
actual purpose — per-command success rides the ✓/✗ marker). Bodies go
neutral; the error body stays bold for weight (rustc-style: severity-
coloured label, readable bold message). Yields a status traffic-light
matching the ✓/✗ palette.
Narrows ADR-0037's mode side-channel to the echo line it was always for.
ADR-0037 Amendment 1; closes the tag-colour gap ADR-0040 flagged as OOS.
Add src/dsl/sql_functions.rs (KNOWN_SQL_FUNCTIONS) as the shared source
of truth at sql_expr_ident slots:
- #15: offer the functions as Tab candidates under a new
CandidateKind::Function + ninth Theme colour tok_function (blue,
distinct from keyword/identifier/type).
- #16: restore the column-typo flag the #6 fix had dropped wholesale —
invalid_ident_at_cursor now bails only when the partial prefix-matches
a known function, else falls through to the schema-column check.
A column named like a function (e.g. `count`) is deduped (column wins).
`cast` is excluded — CAST(x AS type) is not a plain-call shape.
The no-validation-allowlist posture stands: the list drives completion +
the typo hint only, never parse-time acceptance.
Docs: ADR-0022 Amendment 6, ADR-0031 status note, README index,
requirements I3/I4 + refreshed test baseline.
An audit of the command surface found the `[ok] <verb> <subject>`
summary line duplicated the echo line above it everywhere; its only
unique signal was success-vs-error. Retire it: a command's echo line
now resolves from `running: <input>` to `<input> ✓` / `<input> ✗`
on completion, and the symmetric `"<verb> <subject>" failed:` prefix
is dropped (only the reason remains). Content lines (row counts,
structure, plan tree, teaching echo) are unchanged.
Echo lines carry an EchoStatus; executed commands push Pending and
resolve the oldest-pending echo on their result event (FIFO worker —
correct under interleaving). Parse-time and pre-flight rejections are
not executed and keep their running: + caret rendering. App-command
[ok] lines (rebuild/export/replay) are payload-bearing and untouched.
ADR-0040.
explain now wraps the advanced SQL commands — select, with (CTE),
insert, update, delete — in addition to the DSL show data/update/
delete it already covered, rendering through the same plan tree
(ADR-0039, closing the ADR-0030 OOS-2 gap).
Implemented as a second Advanced `explain` CommandNode under the
shared entry word, reusing the established shared-word dispatch
(SQL-first, DSL-fallback) rather than new grammar machinery.
build_explain_sql slices the inner SQL off the source and reuses the
existing SQL builders; do_explain_plan runs EXPLAIN QUERY PLAN over
the carried text verbatim (never executes, so safe for destructive
verbs). Advanced explain update/delete now route through SQL with an
identical plan; DSL-explain tests pinned to simple mode. Help and
usage text now list the advanced explain forms.
A long prose hint (insert field-value hints, the parse.usage.*
synopses) wrapped but was clipped by the fixed one-row Hint panel,
hiding the most useful tail. The candidate list already scrolled
horizontally, so only prose was affected.
Pre-wrap the prose body and size the Hint panel to the wrapped line
count: one row by default, growing to a 3-row cap and reclaiming the
space when short, with an ellipsis backstop on the last row. Also
shorten the 299-char create-table usage synopsis to a terse one-liner
(the full grammar remains under `help`). ADR-0022 Amendment 5.
The undo/redo confirmation dialog capped at 60 columns and wrapped
even a short insert on wide terminals, showed a lowercase "snapshot
taken", a raw ISO-8601 timestamp, and lowercase yes/no labels.
Grow the dialog to fit its longest line (bounded 34–100), capitalise
Snapshot/Yes/No, and render the snapshot timestamp in local time,
human-formatted (24 May 2026, 11:00) via a new chrono dependency
(clock feature only; English month names). Yes/No capitalisation also
applies to the rebuild-confirm dialog.
Both Node::Ident and Word carried a highlight_override field, and
both were dead — the walker driver discarded the Ident's and
walk_word hardcoded Keyword. So column types (int, serial, …)
rendered identically to table/column names.
Wire both overrides through, and add a dedicated HighlightClass::Type
with its own theme colour (tok_type), distinct from keyword-purple
and identifier-teal. The three type Ident slots opt in, so canonical
types and the advanced-mode single-word SQL aliases (float, varchar,
…) render as types; the two-word `double precision` alias opts in via
a new Word::type_keyword constructor. ADR-0022 Amendment 4.
A wrong-count simple-mode insert now shows the friendly per-column arity
message at typing time (instead of a bare "expected `,`/`)`") and is
blocked from dispatch at submit — unifying simple and advanced mode onto
the one ADR-0027 model (structural parse + ERROR diagnostic), where they
had diverged.
Grammar: a simple-mode-only arity gate (dsl_insert_value_list) routes a
wrong-count DSL insert tuple to the type-blind fallback so it matches
structurally and the per-tuple arity diagnostic fires. The gate is gated
to simple mode, so advanced behaviour is unchanged. count_tuple_values
and the target-column selection (insert_target_columns) are now shared
by both grammars.
Diagnostic: dml_insert_arity_diagnostics is mode-aware — advanced Form B
expects all columns; simple Form B/C expects the user-fillable columns
(serial/shortid auto-fill). It counts the DSL Form A role and scans the
keyword-less Form C tuple. New catalog keys name the fillable/auto split
and the all-auto-table case.
Submit: a wrong-count DSL insert now parses Ok + carries the ERROR
diagnostic, so a unified Ok-arm pre-flight (dsl_insert_count_mismatch_notes)
blocks dispatch and teaches; the previous Err-arm note retires.
advanced_alternative_note's gate now reads the validity verdict so it
still fires for the parse-Ok-with-error shape.
Docs: ADR-0036 Amendment 2 (+ README index) and requirements.md H1a.
The candidates_at_cursor "input parses complete" gate — which
suppresses re-suggesting a keyword the user just finished typing —
used the schemaless parse while the rest of the completion path is
schema-aware. A schemaless parse can call a type-/arity-wrong tuple
complete.
The divergence is benign today (the drop only removes a re-offered
keyword equal to the partial, and schema divergence is confined to
value type/arity, which never coincides), but parse with the schema
so the gate matches the rest of the surface and stays correct if the
grammar evolves. Strictly safe: can only ever retain a candidate the
schemaless gate would have dropped, never the reverse.
The ambient-hint fallback in ambient_hint_core_in_mode parsed
schemalessly, so the type-blind grammar closed an `insert … values
(…)` tuple after the first value and the "Next:" hint pointed at `)`.
With a schema available the walk knows the remaining columns and the
correct next token is `,`. Parse the fallback with the schema cache so
the expected-token prose matches the rest of the (already
schema-aware) hint ladder.
Also corrects wrong-arity closed tuples where the schemaless parse
accepted the input and the hint said "submit with Enter" for a command
the schema-aware parse rejects — the hint now surfaces the accurate
error. Three typing-surface snapshots updated to match.
Docs: ADR-0022 Amendment 3 (+ README index) records the schema-aware
fallback; requirements.md H1a cites the hint-accuracy improvement.