Add sibling publish.yaml jobs (scoop-bucket, homebrew-tap) that render a
manifest from the release .sha256 sidecars and idempotently push it to the
org-level lazyeval/scoop-bucket and lazyeval/homebrew-tap repos, using the
scoped lazyeval-ci bot token (LAZYEVAL_PKG_TOKEN).
Render logic lives in dependency-free bash (the CI image has no jq/ruby):
scripts/render-scoop-manifest.sh and scripts/render-homebrew-formula.sh.
scripts/test-package-renders.sh exercises both: it validates the Scoop JSON
with node and asserts fields on both manifests, and additionally runs
`ruby -c` on the formula where ruby is present (dev box), skipping it
gracefully otherwise.
A new ci.yaml `manifests` job runs that test on every push so a render
regression surfaces immediately, not at the next manual publish dispatch.
The CI image has no ruby, so in CI the gate covers the Scoop JSON (node) and
field assertions for both manifests; the formula's Ruby syntax is checked
dev-side only (the static heredoc's variable parts cannot introduce syntax
errors).
- Scoop: x64 (gnu) + arm64 (gnullvm); #/-rename fragment so the bin shim is
version-stable; checkver, no autoupdate (the pipeline is the updater).
- Homebrew: on_macos/on_linux x arch bare-binary formula; no Windows.
Docs: ADR-0056 Amendment 2 (+ README index, requirements D3).
Unverified pending real use: scoop/brew install, the HEAD:main branch
assumption, macOS Gatekeeper-via-brew on the ad-hoc-signed binary.
A workflow_dispatch publish.yaml (mirrors release-macos.yaml) with a `tag`
input, run by hand once the automated release builds exist. Publishing
stays manual and keeps the registry token off every tag push: it's
irreversible (yank-only), the split release (tag Linux/Windows +
dispatched macOS) makes a human the 'all assets up' gate, and crates.io
has no Gitea-Actions trusted-publishing path. The crates.io job is
idempotent (crates.io API pre-check + cargo publish as backstop) and
independent (no inter-job needs), so future Scoop/Homebrew/winget jobs can
be added alongside without interfering or breaking re-runs. Token via the
CARGO_REGISTRY_TOKEN secret. ADR-0056 Amendment 1 + README index also
record 0.2.0 published + binstall verified.
The ADR-0054 version guard piped `nix develop -c cargo metadata` to node,
but the flake devShell prints a banner to stdout — corrupting the JSON
pipe, so the guard aborted under `set -e` and the v0.2.0 release failed
there (before building anything). Replace it with a toolchain-free
`grep -m1 '^version = ' Cargo.toml` (the anchor excludes dependency
`version =` keys). No real version mismatch occurred — the tagged commit
has version 0.2.0.
Distribution prep on the road to public availability (plan steps 2–3a).
- Cargo.toml: publish-ready (drop publish=false; homepage/keywords/
categories/exclude) + [package.metadata.binstall] with per-target
overrides (linux-gnu->musl, windows-msvc->gnu/gnullvm). dry-run clean.
- scripts/install.ps1: Windows `irm | iex` one-liner — written but
untested here (no PowerShell; validate on Windows). README Windows block.
- README.md (new); LICENSE-MIT + LICENSE-APACHE (dual, (c) Lazy
Evaluation Ltd); CONTRIBUTING.md (inbound=outbound dual-license note).
- ADR-0055 Amendment 1 (install.ps1), ADR-0056 (crates.io/binstall),
README index + plan updates.
The actual `cargo publish` remains a gated maintainer step (token,
irreversible) at a new tagged release; real cargo-binstall validation
pending.
scripts/install.sh — POSIX sh, shellcheck-clean: detects uname OS/arch ->
target triple (Linux uses the static musl build; Windows rejected with a
Scoop/winget pointer), resolves the latest release (or RDBMS_VERSION),
downloads the asset + its .sha256 and verifies it, installs to
~/.local/bin with a PATH hint. RDBMS_OS/RDBMS_ARCH + --print-target are
testing seams. Verified end-to-end against the live public v0.1.0 (all
mappings, pinned + latest, checksum incl. tamper-rejection, install+run).
ADR-0055 + README index; plan-doc step 2 done + decisions recorded
(crates.io=yes, releases public, tracking via doc+ADR).
Cargo.toml version is the single source of truth, surfaced by a
--version/-V CLI flag and an in-app `version` command (both via
cli::version_text -> cli.version_line). release.yaml gains a guard that
fails the release unless the v* tag equals v<CARGO_PKG_VERSION>, keeping
--version, the release name, and the asset in lockstep. New app command
wired across grammar/REGISTRY/dispatch/usage/help/hint-corpus/keys; 6
test-first tests. Also fixes a stale "macOS deferred" comment in
release.yaml. ADR-0054 + README index + plan-doc step 1.
The contextual hint overlay (ADR-0053) opens on F1, but F1 is an escape
sequence the autocast recorder can't emit — so casts (and presenter /
teacher sessions) couldn't trigger the most teaching-relevant overlay.
In demo mode only, Ctrl-G now aliases F1: it runs the same hint logic and
badges AS [F1], so a recording is visually identical to a real F1 press.
Ctrl-G is the only fit — Ctrl+digit (e.g. Ctrl-1) isn't encodable in a
legacy terminal (arrives as a bare `1`), and the kitty protocol that would
encode it needs escape sequences autocast can't send (and the app doesn't
enable keyboard-enhancement flags). Demo-gated, so the shipped keymap
stays F1-only; outside demo mode Ctrl-G is inert.
- app.rs: hint_key guard gains the demo-gated Ctrl-G disjunct;
demo_badge_label maps Ctrl-G -> [F1]; 3 Tier-1 tests + badge assertion.
- ADR-0047 Amendment 1 + README index; also removed two stray
</content> / </invoke> lines accidentally committed in the ADR file.
docs: drop three more stale "deferred" entries from CLAUDE.md — readline
shortcuts (I1b, ADR-0049), tab completion (I3), and syntax highlighting
(I4) are all implemented; only multi-line input (I1) remains open.
Completes H2:
- comprehensiveness coverage tests: every REGISTRY command form has a
hint_id resolving to a hint.cmd.* block, and every runtime error class
resolves to a hint.err.* block (enforces ADR-0053 D6)
- ADR-0051 keybinding strip advertises F1 in the editing (leads) and
default states; +shortcut.hint label; 12 full-panel snapshots
re-accepted (status-bar line only)
- flip ADR-0053 -> implemented, requirements H2 + A1 -> [x], README
2498 pass / 1 ignored, clippy clean.
Phase C scope decision: Diagnostic carries no class key, so the F1
diagnostic route would need a class field threaded through every
diagnostic site (broad change) for marginal value — tier-2 already
surfaces diagnostics and many duplicate the runtime error classes. Defer
the route + the ~33 diagnostic.* tier-3 blocks to issue #38. v1 ships
command-form hints + 9 runtime error-class hints (comprehensive for
those). Updates ADR-0053 D2/D6/Status/OOS + README.
The first exemplar (`add 1:n relationship`) showed per-node keying is
too coarse for multi-form commands, so revise the mechanism to per-form.
- CommandNode `hint_id: Option<&str>` -> `hint_ids: &[&str]` (mirrors
usage_ids); hint_key_for_input_in_mode reuses a factored-out
pick_form_key (shared digit/m:n/suffix form disambiguation with
usage_key_for_input_in_mode)
- wire INSERT + ADD (all four forms) with hint_ids
- author the three approved exemplars: hint.cmd.insert,
hint.cmd.add_relationship, hint.err.foreign_key.child_side
(what/example/concept) + keys.rs registration
- revise ADR-0053 D3 to per-form; record clause-concept hints as a
deferred extension (issue #37); update README + plan
- +5 tests; 2488 pass / 1 ignored, clippy clean
Settles the `hint` slot ADR-0003 left pending; closes the last open
piece of A1. Two surfaces (F1 → live-input hint; `hint` command →
last-error expansion), no topic arg, and a new tier-3 teaching corpus
keyed on a new CommandNode `hint_id` so advanced-SQL forms get distinct
mode-correct content. Comprehensive content for v1, authored
exemplars-first. Refines ADR-0003; references ADR-0019/0021/0022/0049/
0051. Files #36 for the parallel help-side gap.
ADR-0052 moved success journaling out of the worker to the dispatch
layer, leaving the `source` that handlers threaded purely for the
worker's old history.log write dead. Remove it:
- drop `_source` from finalize_persistence and do_rebuild_from_text
- inline + delete the three read-only *_request wrappers
- drop the now-unused `source` param from the ~30 forwarding worker
handlers (leaf + composite), compiler-guided
- remove the `source` field from the DescribeTable/QueryData/RunSelect
requests and their DatabaseHandle methods (call sites updated)
The only worker `source` left is the snapshot/undo label
(snapshot_then / stage_pre_mutation / begin_batch). Purely mechanical,
no behaviour change. 2471 pass / 0 fail / 1 ignored, clippy clean.
Record the submission mode per history entry so advanced commands are
reusable in simple mode, and fix the bug where a ':'-one-shot command
lost its ':' across sessions (ADR-0052, closing #30).
Format: the history.log status token gains an optional ':adv' suffix
(ok / ok:adv / err / err:adv); 'source' stays last and canonical, so
replay is unaffected. The in-memory ring (still Vec<String>) stores
advanced entries ': '-prefixed; recall strips the ':' in advanced mode
and keeps it in simple; hydration reconstructs the prefix from the tag.
Journaling moved from the worker to the dispatch layer (spawn_dsl_-
dispatch / run_replay / app-command sites), where the mode is in scope
with no worker plumbing; finalize_persistence writes only yaml/csv
(commit-db-last still atomic for state). The journal write is now
best-effort (command already committed), consistent with the failure
path. App commands journal simple, so they recall bare. Journaling is
now uniform (every successful command, per ADR-0034) — closing a gap
where show tables/relationships/explain didn't journal.
Amends ADR-0034 (status tag + journaling location), ADR-0015 §6
(history.log out of the worker tx), ADR-0040 (journal-write best-effort).
15 worker-level journaling tests retired, re-covered at the new layer
(history.rs format, app.rs recall matrix, iteration6 cross-session
regression, replay). 2471 pass / 0 fail / 0 skip, clippy clean.
Per ADR-0051 (closing issue #27): the bottom status line is now a
keystrokes-only, state-selected strip. A pure status_bar_bindings()
picks the binding set by priority (first match wins):
sidebar focus → Ctrl-O next pane · ↑↓/PgUp/PgDn scroll · Esc input
completion live → Tab/Shift-Tab cycle · Esc cancel · Enter run
history nav → ↑↓ browse · Esc clear · Enter run
editing → Esc clear · Ctrl-A/E home/end · Ctrl-W del word · Enter run
default (empty) → Ctrl-O sidebar · Tab complete · ↑ history · Enter run
The editing state surfaces the #29 readline keys (ADR-0049's deferred
advertisement). Typed-command words (mode advanced/simple, the ':'
one-shot) and Ctrl-C quit leave the strip; simple mode's empty-input
hint gains a '`mode advanced` for SQL' pointer (advanced mode shows
none — a switcher knows the way back, and help covers it).
Mechanism: status_bar_bindings + a thin renderer (unit-testable);
App::is_browsing_history() exposes the private history_cursor; the mode
pointer lives in resolve_hint_lines. Catalog: 12 new shortcut labels +
panel.hint_mode_advanced (en-US.yaml + keys.rs, validator 1:1), 5 dead
strip strings removed.
Forks user-chosen: editing state shows #29 keys; quit omitted; no
width-drop machinery (longest strip ~65 cols fits; a width-budget test
keeps it lean). Modal-aware strip is OOS (pre-existing). Tests: 9
Tier-1 unit (per-state key sets, width budget, mode pointer), 1 Tier-3
rewritten, 15 full-panel snapshots re-accepted (reviewed). 2467 pass /
0 fail / 0 skip, clippy clean.
Per ADR-0050 (closing issue #28): the confirmation echo after an
incidental structural edit — create table, add/drop/rename/change
column, add/drop index — now renders the structure only (header +
column box + indexes + constraints) and no longer appends the
References:/Referenced by: relationship block.
Rationale: a confirmation reports the change just made, not the
table's relationships, which the user didn't touch. Relationship info
is still one `show table <T>` away, and the relationship-subject
surfaces (show table, add/drop relationship) keep their ADR-0044
diagrams unchanged.
Scope is all incidental DDL (user-confirmed). Mechanism: drop the
relationship-block call from render_structure (all its callers are
incidental DDL); the handle_dsl_success diagram-vs-structure routing
is unchanged. The orphaned relationship_prose_lines + cols_disp
helpers are deleted (the prose format survives in ADR-0016 §5 + git
history for a future OOS-7 always-prose setting).
ADR-0050 supersedes ADR-0044 §1's incidental-DDL prose clause and the
relationship-block half of ADR-0016 §5 (both annotated). Tests: prose-
presence unit test + snapshot removed; new unit test locks structure-
only with inbound+outbound relationships present; the misnamed add-
column integration test inverted + renamed. 2458 pass / 0 fail / 0
skip, clippy clean.
Implement the deferred I1b readline shortcuts in the command input
field (ADR-0049, closing issue #29):
Esc clear a partly-typed command (only when no completion memo)
Ctrl-A cursor to line start (Home alias)
Ctrl-E cursor to line end (End alias)
Ctrl-W delete the previous word (readline-style, UTF-8 safe)
Ctrl-K kill to end of line
Ctrl-U kill to start of line
Esc precedence is preserved: a live Tab-completion memo still wins
(Esc undoes the completion first, ADR-0022); Esc clears only when no
memo is alive. While a sidebar panel is focused (Ctrl-O), Esc exits
navigation mode upstream and never clears the input draft. Cursor-only
keys leave history navigation intact like Home/End; buffer-mutating
keys end it like Backspace.
New helpers clear_input / delete_prev_word / kill_to_end /
kill_to_start in src/app.rs. 22 new Tier-1 tests (2458 pass / 0 fail
/ 0 skip, clippy clean). ADR-0049 amends ADR-0046's OOS list;
requirements.md I1b marked done.
At `seed <table> ▮` the hint showed only the `set`/`--seed` chips and
never mentioned the optional row count — a bare positional number with no
candidate, on an already-complete command, so neither the candidate
ladder nor the resolver surfaced it. (A prior IntroProse attempt was
reverted: pending_hint_mode is cleared by the trailing optionals.)
Carry a skipped Optional's IntroProse hint: walk_optional stashes the
inner's key into a new WalkContext.surviving_intro_hint (key + position)
before the empty match clears pending_hint_mode; the snapshot keeps it
only when the skip position is the cursor (so it never leaks past a
later-consumed `set …` clause, nor once the count is given); the
resolver returns it ahead of the empty-expected short-circuit. The seed
count is wrapped Hinted{IntroProse("hint.seed_count")}; the prose names
the count (default 20), the `.column` column-fill form, and `set` /
`--seed`. Tab still cycles the keywords.
Only IntroProse is carried; ProseOnly/ForceProse and the CREATE-TABLE
element (a required Repeated) are untouched. No AmbientHint/renderer
change. Fires in both modes.
ADR-0022 Amendment 7; +3 tests.
Two additive D7 catalogue rules, surfaced while writing the website seed
docs. No change to the type fallback, executor, or grammar.
#33 — year-like int columns. `published`/`birth_year` were just `int`, so
they fell to the unbounded int path and produced nonsense (`9419`). Add an
int-gated year rule (after the quantity rule, so `year_count` stays a
count): `year`/`*_year`/`published`/`founded` -> a bounded 1950-2025 year
(new `YearRecent`), or the dob-style birth window 1945-2007 for
`birth`/`born`/`dob` (new `YearBirth`). Plain int; not added to the D9
named-generator vocabulary.
#34 — conventional choice sets. A few enum-ish names have a near-canonical
small set that reads far better than lorem text. Add a type-gated PickFrom
lookup (reusing the existing generator): priority/prio, severity,
rating/stars. `status` is deliberately excluded (values too
domain-specific) and keeps the D12 advisory; a user IN-CHECK still wins.
`priority` leaves ENUM_TOKENS.
ADR-0048 Amendment 1; +8 tests (incl. a column-fill integration test that
also closes a pre-existing gap on that path).
The focused sidebar panel border (ADR-0046 DC3) was bright `fg` plus
`Modifier::BOLD`. Bold box-drawing glyphs render as broken/gapped
line-art in the asciinema cast player and are fragile in some terminals.
`panel_border_style` now marks focus with a non-bold accent colour
(`theme.mode_simple`, blue); the unfocused border stays muted. Bold is
untouched on text spans (titles, key hints) — the constraint is
specifically that box-drawing borders carry no bold attribute.
Pure style change: the Tier-2 snapshots are text-only so none needed
re-accepting; the Tier-1 assertion was updated and a render-level test
now checks the rendered border cells carry the accent and no bold.
ADR-0046 Amendment 1.
`decimal` is stored as exact TEXT, but SQLite has no native decimal type,
so arithmetic/aggregation implicitly coerces it to an IEEE-754 double.
The computed result carries no playground type, so `sum(price * qty)`
rendered the double's full noise — `298.59999999999997` for `298.60` — a
confusing, off-topic float lesson for a teaching tool.
Add `format_real_display`: round REAL values to 15 significant figures
(a double's reliable precision) then take the shortest round-tripping
form, collapsing `298.59999999999997` to `298.6`. Wired into `format_cell`
(result-set / `show data` cells) only — the sole surface where the noise
appears, since it arises from arithmetic.
Every other f64->string path keeps full precision for semantic, not
cosmetic, reasons: CSV persistence stays byte-exact for round-trip;
`render_value` is a canonical identity key for the uniqueness dry-runs
(dry_run_unique, check_uniqueness_collisions), where rounding would
report collisions the exact-valued engine wouldn't; FK-key matching and
EXPLAIN-SQL literals likewise stay exact.
ADR-0005 Amendment 1; +7 tests.
A bare table alias typed where a column is expected — `… GROUP BY o`,
with `o` aliasing `FROM Orders o` — was a blind spot: completion offered
nothing for `o`, and the hint panel called the in-scope alias an unknown
column (`no such column o on table Orders, ...`).
Completion now offers each FROM source's qualifier (alias-if-present-else
table-name) at a bare sql_expr_ident slot, folded into the column
candidate list; on an exact-qualifier partial the alias source steps
aside so the diagnostic can surface. The bare-reference diagnostic arm
emits a targeted `alias_used_as_column` / `table_used_as_column` hint
("`o` is a table alias — write `o.<column>` ...") after the
projection-alias check, so ORDER-BY alias refs still win and a genuine
unknown column still reports `unknown_column`.
Two guards keep the qualified-form advice correct: SQL only (role
`sql_expr_ident`, so the DSL `expr_column` path keeps `unknown_column`
since the DSL has no `table.column` syntax) and effective-qualifier
match (alias-if-present-else-table, so an aliased source referenced by
its shadowed real name falls through rather than being advised as
`name.<column>`). The diagnostic is a drop-in replacement for
`unknown_column` at the same span/Error severity, so verdict/overlay/hint
paths are unchanged.
ADR-0032 Amendment 3; +10 tests.
- ADR-0048 status -> Accepted; Phase 1 implemented (commits
202e25a..fbd219b), with the pre-build and post-implementation /runda
passes and the 2358-test green state recorded; index entry updated.
- requirements.md: SD1 [x] (whole-row seed + FK/junction, both modes,
--seed reproducibility with no exceptions), SD2 [/] (core generators /
determinism done; the set override clause + column-fill are Phase 2),
A1 14/15 (only hint/H2 remains unregistered).
- Handoff 65: the full seed Phase-1 build, the two /runda passes, where
the code lives, and Phase-2 / next steps.
Dedicated `seed` command: realistic, name-aware fake data via the
`fake` crate + a type-gated heuristic catalogue, table-context name
disambiguation, a hand-rolled product generator, bounded dates, an
identifier-uniqueness rule, a quoted `set` override clause (value /
list / generator / range), `--seed` reproducibility, FK sampling
(empty-parent error, junction distinct combinations), CHECK
derive-or-friendly-fail, undo as one batch step, replay as a
data-write, capped auto-show, and an enum/CHECK advisory.
Design settled across an extended user fork dialogue, then hardened
by a /runda DA pass that found six blockers (undo, replay, set
quoting, CHECK handling, advisory phasing, auto-show flood) — all
folded in, genuine forks re-escalated and user-resolved.
Takes up SD2 ([~]->[ ]); SD1 implementation in progress.
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).
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.
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.
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.
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.
`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.
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}.
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.
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.
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.
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.
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.
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.
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).
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)
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.
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.