# 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.
