Merge branch 'main' into website (m:n, logging, UI nav, demo overlays, vi-nav)
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).
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
# Session handoff — 2026-06-10 (61)
|
||||
|
||||
Sixty-first handover. Continues from handoff-60 (Gitea migration
|
||||
cleanup + V1 relationship visualization, ADR-0044). This session was
|
||||
a **list-trimming pass on "easy wins"**: it closed **X1**
|
||||
(comprehensive logging, full sweep) and both **T3 residuals** (the two
|
||||
ADR-0043 messaging-polish items). Four commits, all green, all
|
||||
user-confirmed.
|
||||
|
||||
## §1. State at handoff
|
||||
|
||||
**Branch:** `main`. **HEAD `5a33f2a`.** 4 commits this session
|
||||
(`a8ad0c6` → `5a33f2a`) on top of session-60's 5; push is the user's
|
||||
step.
|
||||
|
||||
**Tests: 2211 passing / 0 failing / 1 ignored** (lib 1588, it 431,
|
||||
typing_surface_matrix 192; the 1 ignored is the long-standing
|
||||
doc-test). **Clippy clean** (nursery, all targets). +4 over the
|
||||
handoff-60 baseline of 2207 (one test per residual at each of the
|
||||
enrichment + render layers, plus the two grammar/worker tests).
|
||||
|
||||
This session's commits:
|
||||
```
|
||||
5a33f2a fix(fk): compound-FK violation message names every column pair
|
||||
6985a43 fix(fk): inline FK referencing a compound PK points at the table-level form
|
||||
0a7612e feat: comprehensive logging across parser, app, persistence, runtime (X1)
|
||||
a8ad0c6 feat(db): comprehensive logging across worker + executors (X1)
|
||||
```
|
||||
|
||||
## §2. X1 — comprehensive logging (closed, `[x]`)
|
||||
|
||||
The full-sweep instrumentation pass the "log liberally" standard
|
||||
called for. **~75 → 135 `tracing` sites** under a documented level
|
||||
discipline now living in the **`src/logging.rs` module doc** (read it
|
||||
before adding logs — it is the durable convention).
|
||||
|
||||
**Levels:** `error` = unrecoverable; `warn` = recoverable / fallback
|
||||
taken; `info` = low-volume lifecycle (worker start/exit, project
|
||||
open); `debug` = the bulk, one line per *executed* command + its
|
||||
decision points (off by default, opt-in `RDBMS_PLAYGROUND_LOG=debug`);
|
||||
`trace` = hot paths only (per-keystroke parse, per-key input).
|
||||
|
||||
**Where logs go (was a point of confusion):** always a **file**
|
||||
(stdout/stderr would corrupt the TUI). Path precedence: `--log-file`
|
||||
> `RDBMS_PLAYGROUND_LOG_FILE` > default `~/.rdbms-playground/
|
||||
playground.log` (append mode). Level filter is the *separate*
|
||||
`RDBMS_PLAYGROUND_LOG` env var, default `info`.
|
||||
|
||||
**Coverage by commit:**
|
||||
- `a8ad0c6` **db.rs** (26→67): entry-`debug!` on all 34 `do_*`
|
||||
executors (DDL/DML/relationship/index/read), 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_with_copy`
|
||||
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
|
||||
start/exit `debug!`→`info!`.
|
||||
- `0a7612e` **rest**: `persistence/mod.rs` logs every yaml/CSV/history
|
||||
write (the silent-failure disk paths); `runtime.rs`
|
||||
`execute_command_typed` dispatch; `app.rs` submit /
|
||||
`dispatch_app_command` / ADR-0044 diagram-vs-prose render choice;
|
||||
`dsl/parser.rs` parse begin/outcome at **`trace`** (the
|
||||
`parse_command_inner` choke point — `completion.rs` re-parses
|
||||
per-keystroke, probing candidates in a loop, so `debug` would
|
||||
flood).
|
||||
|
||||
**Verification:** emission proven end-to-end through the *real* worker
|
||||
thread + real `logging::init` via two throwaway smoke tests (db path
|
||||
and persistence path), both since deleted. The DA-honest gap: a few
|
||||
internal read-only helpers (`do_find_rows_matching`,
|
||||
`do_read_relationships`, `do_list_names_for`) and the thin `*_request`
|
||||
wrappers are not *individually* instrumented — the wrappers delegate
|
||||
to logged executors (skipped to avoid double-logging), the helpers are
|
||||
low-value. Effective coverage is complete via logged entry points; it
|
||||
is not literally 44/44.
|
||||
|
||||
## §3. T3 residuals — both closed (ADR-0043)
|
||||
|
||||
Two messaging-only items carried since handoff-59 §4; FK
|
||||
correctness/enforcement was never affected.
|
||||
|
||||
**#1 — inline-FK arity wording (`6985a43`).** `col REFERENCES P(a,b)`
|
||||
referencing a compound PK gave the generic arity error. An inline
|
||||
column-level FK is single-column by construction, so it now points 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)`."* Mechanism: new **`inline: bool` on
|
||||
`SqlForeignKey`**, set by the single shared grammar builder
|
||||
`consume_fk_reference` (true for the inline path at `ddl.rs:1560`,
|
||||
false for table-level `1590` and `build_alter_fk`); threaded into
|
||||
`resolve_fk_parent_columns`, which tailors the arity-mismatch message
|
||||
when `inline && parent_key.len() > 1`. 6 construction sites total (2
|
||||
grammar + 1 ALTER delegate + 3 test literals) — hand-edited, **not**
|
||||
the scripted sweep handoff-59 §4 warned about. The bare inline form
|
||||
(`col REFERENCES P`, no parens) hits the same arity branch, so it is
|
||||
covered by the same code (tested via the explicit-parens form).
|
||||
|
||||
**#2 — compound-FK violation names every pair (`5a33f2a`).**
|
||||
`enrich_fk_violation` (`runtime.rs`) picked only `local_columns
|
||||
.first()` / `other_columns.next()`. It now gathers all pairs of the
|
||||
matched relationship and carries them **comma-joined in the existing
|
||||
single-column facts slots** (`column`, `parent_column`, `value`), so
|
||||
the headline reads *"no parent row in `Region` has `country, code` =
|
||||
`7, 8`."* No facts-model or catalog change — joined strings flow
|
||||
through the existing `{parent_column}`/`{value}` placeholders.
|
||||
Single-column behaviour is byte-identical (a one-element join is the
|
||||
element). **Known minor awkwardness:** the *verbose hint* interpolates
|
||||
`{parent_table}.{parent_column}` → `Region.country, code`, which reads
|
||||
a touch oddly; the headline is clean. A perfectly-formatted compound
|
||||
hint would need catalog work, out of scope for a messaging-polish
|
||||
residual — flagged, not fixed.
|
||||
|
||||
## §4. Remaining open landscape (unchanged except X1)
|
||||
|
||||
**Closed this session:** X1 → `[x]`; both T3 residuals (ADR-0043 fully
|
||||
wrapped — no residuals left).
|
||||
|
||||
**Still `[/]` / `[~]` / larger (design-first, own ADR):**
|
||||
- **V2 / S3** multi-result tabs — output-model redesign.
|
||||
- **V3** whole-DB ER export; **V4** scrollable journal + Markdown
|
||||
(also the home for diagram live-reflow, ADR-0044 OOS-1).
|
||||
- **A1** app-commands — blocked on `seed` (SD1) + `hint` (H2).
|
||||
- **H1a** parse-error syntax help (partial; ADR-0021).
|
||||
- **DOC1** reference docs.
|
||||
|
||||
**`[ ]` not started:** H2 `hint`, SD1 `seed`, C4 m:n convenience, B3
|
||||
query-timeout, I1 multi-line input, I1b readline shortcuts, I5
|
||||
cancellation, **TT5 CI** (now Gitea Actions / Woodpecker — a fresh
|
||||
decision tied to the migration + ADR-0001's reopened distribution
|
||||
question), TT4 PTY (spec-only), D1–D3 distribution, NFR-1…7.
|
||||
|
||||
**ADR-0044 OOS for later:** OOS-7 user-configurable relationship-
|
||||
display setting (always-prose / always-diagram / auto-by-width).
|
||||
|
||||
## §5. Next job — candidates (by readiness)
|
||||
|
||||
No forced next step. Recommended order:
|
||||
1. **TT5 CI** — test infra is solid (2211 green) and now there is real
|
||||
logging to surface failures; no pipeline yet. A fresh **Gitea
|
||||
Actions / Woodpecker** decision (earns a short ADR; ties into
|
||||
ADR-0001's reopened distribution question). Highest leverage:
|
||||
protects everything else.
|
||||
2. **SD1 `seed`** then **H2 `hint`** — the two unblockers for **A1**
|
||||
app-commands; both are net-new, self-contained features (each its
|
||||
own ADR).
|
||||
3. **C4 m:n convenience** — auto-generate a junction table; depends on
|
||||
relationships, which are now solid (ADR-0043/0044 done).
|
||||
4. **V2/S3 tabs** or **V4 journal** — larger output-model redesign;
|
||||
design-first, own ADR. V4 also unlocks diagram live-reflow.
|
||||
|
||||
## §6. How to take over
|
||||
|
||||
1. Read handoffs 59 → 60 → 61, then `CLAUDE.md` (Gitea/`tea` section),
|
||||
`docs/requirements.md` (X1 now `[x]`), `docs/adr/README.md`.
|
||||
2. **Before adding any logging:** read the level-discipline block in
|
||||
the `src/logging.rs` module doc (the X1 convention).
|
||||
3. **For FK/relationship work:** ADR-0043 (compound FKs) + ADR-0044
|
||||
(visualization) are both fully landed; `SqlForeignKey` now carries
|
||||
`inline`.
|
||||
4. Codebase on `main` at `5a33f2a`, clean, 9 commits unpushed (5 from
|
||||
session 60 + 4 this session).
|
||||
5. Process pins that paid off: **verify log emission end-to-end, not
|
||||
just that it compiles** (throwaway smoke tests through the real
|
||||
worker thread caught nothing broken but proved the stack);
|
||||
**hot-path logging belongs at `trace`, not `debug`** (the parser);
|
||||
**test-first on both residuals** (red → green at every layer);
|
||||
**hand-edit struct-field ripples, never script them** (handoff-59
|
||||
§4's scare avoided). Commits user-confirmed, append-only, no AI
|
||||
attribution.
|
||||
@@ -0,0 +1,185 @@
|
||||
# Session handoff — 2026-06-10 (62)
|
||||
|
||||
Sixty-second handover. Continues from handoff-61 (X1 logging full sweep
|
||||
+ T3 residuals). This session was a **list-trimming + one-feature run**:
|
||||
it closed **C4** (the `create m:n relationship` convenience command,
|
||||
**ADR-0045**) and, in passing, resolved **Gitea issue #19** (drop-PK
|
||||
guard). Handoff-61 itself was written mid-session, so the X1 / T3 work
|
||||
it describes is also part of this session's commit range.
|
||||
|
||||
## §1. State at handoff
|
||||
|
||||
**Branch:** `main`. **HEAD `8bd43cc`.** Push is the user's step.
|
||||
|
||||
**Tests: 2237 passing / 0 failing / 1 ignored** (the 1 ignored is the
|
||||
long-standing doc-test). **Clippy clean** (nursery, all targets). +30
|
||||
over the handoff-60 baseline of 2207.
|
||||
|
||||
**This session's commits** (8, on top of session-60's 5):
|
||||
```
|
||||
8bd43cc feat: create m:n relationship convenience command (C4, ADR-0045)
|
||||
e598008 docs: ADR-0045 m:n convenience command (C4); accepted
|
||||
e44d298 test+docs: lock drop-PK-refused on advanced surface; document no-PK advanced mode (#19)
|
||||
b803468 docs: session handoff 61 — X1 logging full sweep + T3 residuals closed
|
||||
5a33f2a fix(fk): compound-FK violation message names every column pair
|
||||
6985a43 fix(fk): inline FK referencing a compound PK points at the table-level form
|
||||
0a7612e feat: comprehensive logging across parser, app, persistence, runtime (X1)
|
||||
a8ad0c6 feat(db): comprehensive logging across worker + executors (X1)
|
||||
```
|
||||
|
||||
**Requirements closed this session:** **X1** `[x]` (logging), **T3**
|
||||
residuals (both ADR-0043 messaging items), **C4** `[x]` (m:n). Gitea
|
||||
**#19 closed**.
|
||||
|
||||
## §2. X1 — comprehensive logging (closed) — see handoff-61 §2
|
||||
|
||||
Full detail in handoff-61. In brief: ~75 → **137** `tracing` sites under
|
||||
a documented level discipline (read the **`src/logging.rs` module doc**
|
||||
before adding logs). Logs go to a **file** (`--log-file` >
|
||||
`RDBMS_PLAYGROUND_LOG_FILE` > `~/.rdbms-playground/playground.log`);
|
||||
level via the separate `RDBMS_PLAYGROUND_LOG` env (default `info`).
|
||||
`debug` = per-command detail (off by default), `trace` = hot paths
|
||||
(per-keystroke parse).
|
||||
|
||||
## §3. T3 residuals (both closed) — see handoff-61 §3
|
||||
|
||||
`6985a43` inline-FK arity wording (points at the table-level form;
|
||||
added `inline: bool` to `SqlForeignKey`). `5a33f2a` compound-FK
|
||||
violation names every column pair (comma-joined in the single-column
|
||||
facts slots; `enrich_fk_violation`). ADR-0043 now has no residuals.
|
||||
|
||||
## §4. Issue #19 — drop-PK guard (closed, `e44d298`)
|
||||
|
||||
A parallel check the user requested. **Finding: dropping a PK column is
|
||||
already refused in both modes** via the shared `do_drop_column` guard
|
||||
(*"cannot drop primary-key column …"*) — simple `drop column` and
|
||||
advanced `ALTER … DROP COLUMN` both route through it. Added end-to-end
|
||||
coverage (`tests/it/sql_alter_table.rs`: single + compound PK, refusal
|
||||
for the right reason). **Corrected a long-standing misconception:** the
|
||||
issue's premise ("we don't support creating a table with no PK") is true
|
||||
only in **simple** mode — advanced SQL `create table t (a int)` makes a
|
||||
real **PK-less** table (SQLite's implicit `rowid` keys it; only
|
||||
`WITHOUT ROWID` lacks one, which this app never creates). The simple-mode
|
||||
`with pk` requirement is **pedagogical** (ADR-0029), not an engine
|
||||
constraint. Documented in `docs/simple-mode-limitations.md`.
|
||||
|
||||
## §5. C4 — `create m:n relationship` (the feature, ADR-0045)
|
||||
|
||||
`create m:n relationship from <T1> to <T2> [as <name>]` generates a
|
||||
**junction table**: one FK column per parent PK column
|
||||
(`{table}_{pkcol}`, typed via `fk_target_type` — ADR-0011), a **compound
|
||||
PK** over all of them, and **two `CASCADE` 1:n relationships** — all in
|
||||
**one `do_create_table` call = one undo step** (no batch needed;
|
||||
`do_create_table` already takes `foreign_keys` + writes per-FK
|
||||
relationship metadata). Auto-named `{T1}_{T2}` (optional `as`), available
|
||||
in **both modes**, compound-parent PKs supported (ADR-0043).
|
||||
|
||||
**Forks (all user-confirmed):** compound-over-FKs PK (vs surrogate /
|
||||
none); `CASCADE` actions; auto-name + optional `as`; both modes; FK
|
||||
columns `{table}_{pkcol}`. **Refused:** self-referential m:n (`from T to
|
||||
T` — full stop, OOS); PK-less parent; internal `__rdbms_*` junction
|
||||
name; name collision.
|
||||
|
||||
**Where the code lives:**
|
||||
- Grammar: a **separate `CREATE_M2N` `CommandNode`** in
|
||||
`dsl/grammar/ddl.rs` (entry `create`, opener `Node::Literal("m")` —
|
||||
not a keyword, so it never shadows an identifier), registered Simple
|
||||
in `grammar/mod.rs` `REGISTRY`. `build_create_m2n` →
|
||||
`Command::CreateM2nRelationship { t1, t2, name }`.
|
||||
- Worker: `Request::CreateM2nRelationship`,
|
||||
`Database::create_m2n_relationship`, executor
|
||||
`do_create_m2n_relationship` (reads each PK, guards self-ref /
|
||||
PK-less, builds columns + compound PK + 2 `SqlForeignKey`s, calls
|
||||
`do_create_table`).
|
||||
- Runtime: `execute_command_typed` arm. Echo:
|
||||
`echo::render_create_m2n` (advanced-mode DSL→SQL teaching echo, ADR-
|
||||
0038 — the generated `CREATE TABLE … FOREIGN KEY …`, round-trips as
|
||||
valid SQL), wired in `build_schema_echo`.
|
||||
- Surfaces: completion `("m","m:n")` composite; `help.ddl.create_m2n` +
|
||||
`parse.usage.create_m2n` catalog (+ `keys.rs` declarations);
|
||||
highlighting is grammar-driven (automatic).
|
||||
|
||||
**Tests:** 14 integration (`tests/it/m2n.rs`), 7 typing-surface matrix
|
||||
(`tests/typing_surface/create_m2n.rs` — completion/hint/highlight/parse),
|
||||
plus echo / highlight / usage-disambiguator / internal-name units.
|
||||
|
||||
## §6. Framework fixes the C4 build + two `/runda` passes surfaced
|
||||
|
||||
C4's "separate node" design rested on an ADR premise that proved **only
|
||||
half true**: *"the walker already dispatches multiple nodes per entry
|
||||
word"* held in **advanced** mode but not **simple**. Three latent
|
||||
simple-mode assumptions ("≤1 DSL form per entry word") were generalized,
|
||||
**all behaviour-preserving for existing single-form commands**:
|
||||
|
||||
1. **Dispatch** (`walker/mod.rs` `decide`) committed `simple.first()`
|
||||
unconditionally → now tries simple candidates (so `create table` no
|
||||
longer shadows `create m:n`). Reduces to the old single-candidate
|
||||
commit when there is one.
|
||||
2. **Completion continuation-merge** (`walker/mod.rs`) was gated
|
||||
`if mode == Advanced` → now runs in simple mode too, **gated on
|
||||
`simple_count > 1`** so single-form entry words are untouched.
|
||||
3. **Usage disambiguator** (`grammar/mod.rs` `usage_key_for_input`)
|
||||
knew the `1:n` opener but not `m:n` → added an explicit branch.
|
||||
|
||||
Plus a **root-cause bug fix** (user-chosen scope): `do_create_table`
|
||||
now rejects internal `__rdbms_*` names. This closed both the C4 `as
|
||||
__rdbms_*` hole **and a pre-existing hole** — simple-mode DSL `create
|
||||
table __rdbms_*` was accepted at parse (the `TABLE_NAME_NEW` slot had no
|
||||
guard; only the advanced-SQL path rejected internal names). The shared
|
||||
executor is the single choke point; the SQL path still rejects earlier
|
||||
at parse.
|
||||
|
||||
**Process note:** the two `/runda` passes were worth it. The first
|
||||
(pre-build) corrected the inverted "no PK-less tables" assumption and
|
||||
confirmed the `do_create_table` reuse against code. The second
|
||||
(pre-commit) closed **five** test-coverage gaps — two of which
|
||||
(highlighting, persistence round-trip) had been **wrongly claimed
|
||||
verified** (the typing-surface `Assessment` has no highlight field;
|
||||
"transitively covered" was a hand-wave) — and found the two bugs above.
|
||||
Lesson re-confirmed: verify a claimed-tested surface actually has an
|
||||
assertion; "transitively covered" is a DA red flag.
|
||||
|
||||
## §7. Remaining open landscape
|
||||
|
||||
**Closed since handoff-60:** X1, both T3 residuals, C4, #19. ADR-0043 and
|
||||
ADR-0045 fully landed.
|
||||
|
||||
**Still open (by readiness, unchanged otherwise):**
|
||||
1. **TT5 CI** — test infra solid (2237 green); no pipeline. **Gitea
|
||||
Actions / Woodpecker** (a fresh decision tied to the migration +
|
||||
ADR-0001's reopened distribution question). **Friction:** the
|
||||
requirement is Linux/macOS/Windows on stable — self-hosted Gitea can
|
||||
do Linux easily, but mac/Windows runners need machines that may not
|
||||
exist; likely needs a Linux-first scope decision.
|
||||
2. **SD1 `seed`** then **H2 `hint`** — the two unblockers for **A1**
|
||||
app-commands; both net-new, own ADR (SD2 is the seed-generator design
|
||||
ADR). SD1 should now seed **m:n junctions** too (valid FK refs from
|
||||
parent rows) — C4 makes that concrete.
|
||||
3. **V2/S3 multi-result tabs** or **V4 journal** — larger output-model
|
||||
redesign, design-first, own ADR. V4 also unlocks diagram live-reflow.
|
||||
4. **C3a modify relationship** — small follow-up (drop+add covers it
|
||||
today; ADR pending).
|
||||
|
||||
**ADR-0045 OOS for later:** self-referential m:n (deliberate non-goal);
|
||||
per-relationship action overrides; extra junction payload columns;
|
||||
m:n-as-diagram echo. **Pre-existing, now-fixed:** the internal-name hole
|
||||
(§6) — no separate issue needed, it's closed.
|
||||
|
||||
## §8. How to take over
|
||||
|
||||
1. Read handoffs 60 → 61 → 62, then `CLAUDE.md`, `docs/requirements.md`
|
||||
(X1/C4 now `[x]`), `docs/adr/README.md`.
|
||||
2. **Before adding logging:** the level discipline in the
|
||||
`src/logging.rs` module doc.
|
||||
3. **For grammar/command work:** an entry word can now carry **multiple
|
||||
DSL forms** in simple mode (C4 generalized the dispatch + completion +
|
||||
usage paths). `create` is the first such entry word (table + m:n).
|
||||
4. **For relationship/FK work:** ADR-0013/0043/0044/0045 are all landed;
|
||||
`SqlForeignKey` carries `inline`; `do_create_table` now guards
|
||||
internal names.
|
||||
5. Codebase on `main` at `8bd43cc`, clean. Commits user-confirmed,
|
||||
append-only, no AI attribution. Process pins that paid off: **two
|
||||
`/runda` passes per feature** (design + pre-commit) — both found real
|
||||
bugs and gaps every time; **verify a claimed-tested surface has an
|
||||
actual assertion**; **escalate genuine forks** (every C4 design choice
|
||||
+ the internal-name fix scope was the user's).
|
||||
@@ -0,0 +1,159 @@
|
||||
# Session handoff — 2026-06-10 (63)
|
||||
|
||||
Sixty-third handover. Continues from handoff-62 (C4 m:n + #19). This
|
||||
was a **single-ADR, full-build session**: it designed and implemented
|
||||
**ADR-0046** end to end — the UI work for the three sidebar/input
|
||||
issues **#20 / #21 / #23**, all now **closed** on Gitea.
|
||||
|
||||
## §1. State at handoff
|
||||
|
||||
**Branch:** `main`. **HEAD `22bec61`** (plus an uncommitted docs
|
||||
finalization — ADR status flip + this handoff — see §7). Push is the
|
||||
user's step.
|
||||
|
||||
**Tests: 2263 passing / 0 failing / 1 ignored** (the 1 ignored is the
|
||||
long-standing doc-test). **Clippy clean** (nursery, all targets). +26
|
||||
over the handoff-62 baseline of 2237.
|
||||
|
||||
**This session's commits** (8 + the docs finalization):
|
||||
```
|
||||
22bec61 feat(ui): scroll the focused sidebar panel + refine the nav overlay (DC3 + DC2)
|
||||
c9da6ff feat(ui): Ctrl-O navigation mode — peek + expand the schema sidebar (DC1/DC2/DC4)
|
||||
94825d0 feat(ui): relationships sidebar panel + schema data (DB2/DB4)
|
||||
386627a feat(ui): width-derived sidebar visibility — hide at <=90 cols (DB1)
|
||||
41bae99 feat(ui): two-row input display on tall terminals (DA4)
|
||||
e0b9470 feat(ui): horizontal-scroll long input so the cursor stays visible (DA3)
|
||||
9f5f76b fix(ui): geometry-fixed hint-panel height kills the typing jump (DA1/DA2)
|
||||
93266b9 docs: ADR-0046 UI sidebar nav-mode + responsive input/hint
|
||||
```
|
||||
|
||||
**Issues closed:** **#20**, **#21**, **#23** (all via ADR-0046).
|
||||
**#22** (in-app overlay/keystroke-annotation layer for casts/lessons)
|
||||
remains **open** — its own future ADR; adjacent but out of scope here.
|
||||
|
||||
## §2. What shipped — ADR-0046 (read it; it's the source of truth)
|
||||
|
||||
Three coupled UI issues, treated as one decision because they share the
|
||||
terminal width/height budget. Phased A → B → C.
|
||||
|
||||
**Phase A — input & hint (#20, #23).**
|
||||
- **DA1/DA2 (#20):** the Hint panel height is now a pure function of
|
||||
terminal geometry (`hint_rows` → later `panel_heights`), **fixed
|
||||
between resizes** — it no longer resizes as you type, killing the
|
||||
jump. Compact (`<40` rows) = hint 2; comfortable = hint 2, or 3 only
|
||||
when the column is narrow (`inner < 54`). This **reverses issue #12's**
|
||||
shrink-to-content sizing (its two tests were replaced by an anti-jump
|
||||
invariant). Long hints ellipsize at the fixed budget.
|
||||
- **DA3 (#23):** long input **horizontally scrolls** to keep the cursor
|
||||
visible (`input_scroll_offset`, pure `input_scroll_offset()` helper),
|
||||
with muted `<` / `>` edge markers; resets on submit / history.
|
||||
Preserves ADR-0027's 6-col indicator reserve.
|
||||
- **DA4 (#23):** on a tall terminal (`>=40` rows) the input renders
|
||||
across **two visual rows** (soft-wrap of the single logical line;
|
||||
indicator stays on row 1). Distinct from deferred multi-line **I1**;
|
||||
`expand_runs_to_cells` is the substrate I1 should reuse.
|
||||
|
||||
**Phase B — the sidebar (#21).**
|
||||
- **DB1:** the left column is **width-optional** — `sidebar_visible() =
|
||||
width > 90`, so it's hidden at <=90 (the 90-col screencasts) and the
|
||||
right column takes the full width. (Resize a terminal below ~90 to see
|
||||
it; in a normal wide terminal it shows, by design.)
|
||||
- **DB2/DB4:** a **Relationships panel** stacks below Tables — each
|
||||
relationship is name + endpoints broken at the arrow
|
||||
(`Customers.id ->` / indented `Orders.customer_id`), ellipsized. The
|
||||
panel floors at 5 rows ("(none)") and grows to a 50%-of-column cap
|
||||
(`relationships_panel_height`). **Overrides S2** (relations were to be
|
||||
*nested* in the tables list; a sibling panel is the honest shape).
|
||||
|
||||
**Phase C — navigation mode (#21).**
|
||||
- **DC1/DC4:** **`Ctrl-O`** enters a navigation mode orthogonal to the
|
||||
input mode, cycling focus **Input → Tables → Relationships → Input**
|
||||
(`Esc` exits). It's routed in the main key handler *after* the modal
|
||||
gate, so it's inert behind a modal; in nav mode every non-nav key is
|
||||
inert (the input is occluded). `NavFocus` enum on `App`.
|
||||
- **DC2:** the focused panel is revealed (peek, even when width-hidden)
|
||||
and drawn as a **45-col expanded overlay**, clearing the sidebar strip
|
||||
**+ a one-column gutter** and leaving the base output/input/hint
|
||||
visible (unchanged) to the right. *(Two variants were eyeballed; this
|
||||
partial-clear-with-gutter was chosen over a full-area clear.)*
|
||||
- **DC3:** the focused panel **scrolls** — Up/Down by a line,
|
||||
PageUp/PageDown by its visible rows; per-panel offsets clamped to
|
||||
content at render time, mirroring the output-panel scroll.
|
||||
|
||||
**`Ctrl-B` was rejected** for nav mode (it's the tmux prefix →
|
||||
unreachable inside tmux); `Ctrl-O` is multiplexer-safe.
|
||||
|
||||
## §3. Two decisions that landed differently from the draft
|
||||
|
||||
Both recorded inline in the ADR (and called out in its Status):
|
||||
1. **Relationship data on `App`, not `SchemaCache`** (DB2). `SchemaCache`
|
||||
is walker/completion-facing and needs only relationship *names*
|
||||
(untouched); the full records are UI-only, so `App.relationships`
|
||||
mirrors `app.tables`, and it avoided editing ~23 `SchemaCache`
|
||||
literals. Delivered via `Database::read_all_relationships` (new worker
|
||||
request) + `AppEvent::RelationshipsRefreshed` from the runtime's
|
||||
schema refresh.
|
||||
2. **Nav overlay = partial clear + 1-col gutter** (DC2), not a full-area
|
||||
clear — truer to "underneath keeps its layout."
|
||||
|
||||
## §4. Process notes
|
||||
|
||||
- **The pre-build `/runda` pass earned its keep again.** It caught the
|
||||
`Ctrl-B`/tmux collision, a `SchemaCache` retype that would have broken
|
||||
completion, the 2-row-input/indicator placement, the missing nav-mode
|
||||
key disposition + modal gate, and **three unreferenced requirements**
|
||||
(S1 evolved, S2 overridden, S4 corrected — `requirements.md` updated).
|
||||
- **Snapshot discipline:** DB1's 90-col threshold collided with the
|
||||
test-suite's 80-col convention — many snapshots/tests were retuned
|
||||
(sidebar-dependent ones now render at 110; input tests at narrower
|
||||
widths so the now-wider input still overflows). One masked-intent
|
||||
integration check (matched "Customers" in output, not the panel) was
|
||||
corrected.
|
||||
- Each phase was committed green + clippy-clean, user-confirmed message,
|
||||
no AI attribution, append-only.
|
||||
|
||||
## §5. Requirements / S-items touched
|
||||
|
||||
`requirements.md` annotated: **S1** (three-region layout → left region
|
||||
width-optional), **S2** (*overridden* — relationships get a sibling
|
||||
panel, not nested), **S4** (*corrected* — the "keyboard-toggleable" hint
|
||||
claim was never implemented and is struck; the panel is always-on).
|
||||
|
||||
## §6. Remaining open landscape (unchanged from handoff-62, minus the closed items)
|
||||
|
||||
1. **TT5 CI** — test infra solid (2263 green); no pipeline. Gitea Actions
|
||||
/ Woodpecker decision + likely a Linux-first scope call.
|
||||
2. **SD1 `seed`** then **H2 `hint`** — the two unblockers for **A1**
|
||||
app-commands; both net-new, own ADR. SD1 should seed m:n junctions.
|
||||
3. **V2/S3 multi-result tabs** or **V4 journal** — larger output-model
|
||||
redesign, design-first, own ADR.
|
||||
4. **C3a modify relationship** — small follow-up (drop+add covers it).
|
||||
5. **#22 overlay/annotation layer** — own ADR; shares the cast + overlay
|
||||
space with DC2 (designed to coexist).
|
||||
6. **Tutorial/lesson system** — acknowledged in scope; needs its own ADR.
|
||||
|
||||
**ADR-0046 OOS (deferred):** true multi-line input (I1); readline
|
||||
shortcuts (I1b); cross-session sidebar persistence; a persistent
|
||||
show/hide toggle (Ctrl-O peek covers it); output as a third nav focus;
|
||||
relationship search/edit from the panel; a hint-area toggle.
|
||||
|
||||
## §7. How to take over
|
||||
|
||||
1. Read handoffs 61 → 62 → 63, then `CLAUDE.md`, `docs/requirements.md`,
|
||||
`docs/adr/README.md`, and **ADR-0046** (fully landed).
|
||||
2. **Pending:** an uncommitted docs finalization (ADR-0046 status →
|
||||
*implemented*; README index status; this handoff). Commit it as
|
||||
`docs: session handoff 63` (the user confirms commit messages).
|
||||
3. **For UI/layout work:** `src/ui.rs` now has `panel_heights`,
|
||||
`sidebar_visible`, `relationships_panel_height`, the nav overlay, and
|
||||
`&mut App` sidebar panels (they report scroll viewports). `App` gained
|
||||
`input_scroll_offset`, `nav_focus`, `relationships`, and the
|
||||
`tables_scroll` / `relationships_scroll` (+ `last_*_visible`) fields.
|
||||
4. **For relationship/schema-cache work:** relationship *names* are in
|
||||
`SchemaCache.relationships` (completion); full records are on
|
||||
`App.relationships` via `Database::read_all_relationships` +
|
||||
`RelationshipsRefreshed`.
|
||||
5. **Eyeball reminder honoured:** the user reviewed the nav overlay
|
||||
appearance and chose the partial-clear + 1-col-gutter variant.
|
||||
6. Run a `cargo sweep` at some point — `target/` has grown across this
|
||||
build-heavy session.
|
||||
@@ -0,0 +1,140 @@
|
||||
# Session handoff — 2026-06-11 (64)
|
||||
|
||||
Sixty-fourth handover. Continues from handoff-63 (ADR-0046 sidebar/nav).
|
||||
This session closed **two unrelated, website-screencast-enabling Gitea
|
||||
issues**: **#24** (vi-style load-picker navigation) and **#22**
|
||||
(in-app demonstration overlay layer — its own **ADR-0047**, built end
|
||||
to end across three phases + a restyle).
|
||||
|
||||
## §1. State at handoff
|
||||
|
||||
**Branch:** `main`. **HEAD `2d0f4b2`** plus an **uncommitted docs
|
||||
finalization** (ADR-0047 status → implemented, README index, this
|
||||
handoff — see §6). Push is the user's step.
|
||||
|
||||
**Tests: 2290 passing / 0 failing / 0 skipped / 1 ignored** (the 1
|
||||
ignored is the long-standing `friendly` doctest). **Clippy clean**
|
||||
(nursery, all targets). +27 over the handoff-63 baseline of 2263.
|
||||
|
||||
**This session's commits:**
|
||||
```
|
||||
2d0f4b2 feat(ui): flat filled rectangles for demo overlays (#22, ADR-0047 D4)
|
||||
241f60c feat(ui): demo-mode step-caption stealth buffer (#22, ADR-0047 D3/D4)
|
||||
2584e76 feat(ui): demo-mode keystroke badges (#22, ADR-0047 D2/D4/D5)
|
||||
f879d54 feat(cli): --demo demonstration mode flag + app plumbing (#22, ADR-0047 D1)
|
||||
e9eb1b1 docs: ADR-0047 — demonstration overlay layer for casts/teaching (#22)
|
||||
638b4c9 feat(app): vi-style j/k/g/G navigation in the load picker (#24)
|
||||
```
|
||||
|
||||
**Issues closed:** **#24** (vi nav) and **#22** (demo overlays) — close
|
||||
#22 once the docs finalization commit lands.
|
||||
|
||||
## §2. #24 — vi-style load-picker navigation (commit `638b4c9`)
|
||||
|
||||
Purely additive to the ADR-0015 load picker (`handle_load_picker_key`,
|
||||
`LoadPickerSubMode::List`): **`j`/`k`** mirror Down/Up (bounds-
|
||||
respecting, no wrap), **`g`/`G`** jump to first/last. Existing keys
|
||||
(`↑↓`/`Enter`/`Esc`/`b`) unchanged; the footer hint is **left as-is** at
|
||||
the user's request (the new keys are not advertised). No ADR (additive).
|
||||
Motivation: `autocast` (the website cast driver) can only send typeable
|
||||
characters — not arrow keys — so the projects demo needs `j`/`k` to
|
||||
drive the picker. Tests: `load_picker_jk_navigates_like_arrows`,
|
||||
`load_picker_g_jumps_to_first_and_last` (test-first).
|
||||
|
||||
## §3. #22 — ADR-0047 demonstration overlay layer (read the ADR)
|
||||
|
||||
An in-app **demonstration mode** (`--demo` flag / `RDBMS_PLAYGROUND_DEMO`
|
||||
env, **off by default, zero footprint when off**) that renders two
|
||||
transient overlays so `autocast` screencasts — and live teaching, and a
|
||||
future guided-lesson system — can show otherwise-invisible interactions.
|
||||
|
||||
- **Phase A (`f879d54`):** `--demo` + env → `App.demo_mode`, threaded
|
||||
through `run_loop` like `--no-undo`. `--help` line mentions **only the
|
||||
visible badges**; the `Ctrl+]` caption trigger is kept low-profile
|
||||
(user decision, D6).
|
||||
- **Phase B (`2584e76`):** **automatic keystroke badges**
|
||||
(`[TAB]`/`[ENTER]`/`[UP]`/…) over a fixed set of glyph-less keys —
|
||||
pure `demo_badge_label(&KeyEvent)`, set in `App::update` **before the
|
||||
modal gate** (so they fire over the load picker), expired by a **~1.5 s
|
||||
runtime timer**. The timer extends the event loop's time-boxed-`recv`
|
||||
via a new pure `nearest_deadline` helper; the rewrite tracks `Instant`
|
||||
deadlines and was **verified not to regress the ADR-0027 indicator
|
||||
debounce**. New `App.last_output_area: Rect` (set in
|
||||
`render_output_panel`) anchors the overlays.
|
||||
- **Phase C (`241f60c`):** the **stealth `Ctrl+]` caption buffer** —
|
||||
`Ctrl+]` (byte `0x1D` → `Char('5')+CONTROL`, verified vs crossterm
|
||||
0.29) toggles an invisible buffer; typed chars accumulate without
|
||||
touching input/output, `Backspace` edits, other keys inert, a second
|
||||
`Ctrl+]` commits (empty commit dismisses). In pure-sync `App::update`,
|
||||
intercepted **before the modal gate**; an ordinary keystroke clears a
|
||||
visible caption.
|
||||
- **Restyle (`2d0f4b2`):** the overlays render as **flat filled yellow
|
||||
rectangles** (no border glyphs, one-cell text margin) — user decision,
|
||||
deliberately unlike the bordered panels so they pop. Shared
|
||||
`fill_overlay_rect` (borderless `Block` fill + inset `Paragraph`).
|
||||
|
||||
**Placement:** both float at the output panel's inner bottom-right,
|
||||
drawn **last over modals**, badge **stacked directly above** the caption
|
||||
when both show; caption **wraps to ≤ 3 lines** then ellipsises; clamp/
|
||||
skip guard for tiny terminals.
|
||||
|
||||
**Process:** ADR-first (user chose), pre-build `/runda` (10 findings,
|
||||
folded in) + whole-implementation `/runda` (**PASS, no blockers**). Every
|
||||
fork user-confirmed via mockups/questions, incl. the two post-draft
|
||||
follow-ups: `Ctrl+]` trigger (over `Ctrl+!`, which `autocast` cannot
|
||||
send — not a single ASCII byte) and wrap-to-3-line captions.
|
||||
|
||||
## §4. Two things to know about the implementation
|
||||
|
||||
1. **Ownership split (intentional, mirrors `input`/`input_indicator`):**
|
||||
`demo_caption`/`demo_caption_capturing`/`demo_caption_buffer` are
|
||||
driven by `App::update` (input); `demo_badge` is **set** by
|
||||
`App::update` but its expiry is **timed by the runtime**
|
||||
(`demo_badge_seq` bumps so a repeated key restarts the timer).
|
||||
2. **`Ctrl-C` is inert while capturing** — by spec ("every other key is
|
||||
inert"); exit capture with `Ctrl+]`. User-acknowledged; flagged in
|
||||
the ADR. The only behaviour worth a second look if it ever annoys.
|
||||
|
||||
## §5. Honest coverage note
|
||||
|
||||
Everything *testable* is tested (label fn, full caption FSM incl.
|
||||
over-modal + demo-off, `nearest_deadline`, all rendering, CLI parse/env).
|
||||
The **only** untested wiring is inside `run_loop` (the badge-timer
|
||||
arm/clear and `app.demo_mode = demo_mode`) — `run_loop` is not
|
||||
unit-testable (terminal + DB + channels), exactly the posture the
|
||||
existing `IndicatorDebounce` already takes. A future Tier-4 PTY harness
|
||||
(ADR-0008 TT4, still unwired) would close it.
|
||||
|
||||
## §6. How to take over
|
||||
|
||||
1. Read handoffs 62 → 63 → 64, `CLAUDE.md`, `docs/requirements.md`,
|
||||
`docs/adr/README.md`, and **ADR-0047** (fully landed).
|
||||
2. **Pending:** the docs finalization commit (ADR-0047 status →
|
||||
implemented; README index; this handoff). Commit as
|
||||
`docs: session handoff 64 + ADR-0047 implemented (#22/#24)` (the user
|
||||
confirms commit messages). Then close **#22** on Gitea.
|
||||
3. **For demo-overlay work:** `App` has `demo_mode`, `demo_badge`,
|
||||
`demo_badge_seq`, `demo_caption`, `demo_caption_capturing`,
|
||||
`demo_caption_buffer`, `last_output_area`. Rendering:
|
||||
`render_demo_overlays` / `render_badge_box` / `render_caption_box` /
|
||||
`fill_overlay_rect` in `ui.rs`; colours `DEMO_OVERLAY_FG/BG` in
|
||||
`theme.rs`; key handling `handle_demo_caption_key` + the top-of-
|
||||
`handle_key` gate; timer in `runtime.rs` (`nearest_deadline`,
|
||||
`DEMO_BADGE_TTL`).
|
||||
|
||||
## §7. Remaining open landscape (from handoff-63, minus the closed items)
|
||||
|
||||
1. **Wire the overlays into the website casts** — `casts.mjs` on the
|
||||
`website` branch can now emit `^]`/text/`^]` for captions and rely on
|
||||
automatic badges. Website-branch follow-up (OOS for #22's app scope).
|
||||
2. **TT5 CI** — 2290 green, no pipeline yet.
|
||||
3. **SD1 `seed`** then **H2 `hint`** — the unblockers for **A1**
|
||||
app-commands; own ADRs.
|
||||
4. **V2/S3 multi-result tabs** / **V4 journal** — larger output-model
|
||||
redesign, own ADR.
|
||||
5. **C3a modify relationship** — small (drop+add covers it).
|
||||
6. **Tutorial/lesson system** — acknowledged in scope; needs its own
|
||||
ADR; ADR-0047's overlay primitive is what it will reuse.
|
||||
|
||||
Run a `cargo sweep` at some point — `target/` grew across this
|
||||
build-heavy session.
|
||||
Reference in New Issue
Block a user