From 7f7ad1934cc36ebff2b611dae1cd8415483b7715 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Fri, 24 Jul 2026 20:33:32 -0400 Subject: [PATCH] docs: record bottom-panel Stage 1 (#155) landing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handoff §1 gains the arc entry: the window-parameter substrate, the two production `Layout::compute` callers, the recursive minima, hiding as a durable transition, per-window input gating, the per-frontend jump origins, and the shared initial-target load seam. §5 gains four durable lessons, three of them the same class: - a guard with no production caller passes every direct-call test; - a geometric readout (`at_bottom`) is not a state predicate; - a PTY does not translate LF to CRLF, so text equality over clipped output is vacuous; - widening an ambient resolver into a scoped one can make a total function partial — which is what took CI red on all four Test jobs. The roadmap position, the arc's named deferrals, and DAP's unblocking are recorded too. active-work closes the lane, refreshes the canonical base to `e745068`, and keeps Stage 2's named obligations plus the two gating facts found on the way (the sweep needs an isolated XDG_CONFIG_HOME; compile_mode acceptance is load-sensitive, verified pre-existing). Co-Authored-By: Claude Opus 5 (1M context) --- docs/active-work.md | 208 ++++++++++++------------------------------ docs/agent-handoff.md | 134 +++++++++++++++++++++++++-- 2 files changed, 185 insertions(+), 157 deletions(-) diff --git a/docs/active-work.md b/docs/active-work.md index fb835a6..cb0d839 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -14,10 +14,10 @@ backlog. machine-local: `origin` may name this canonical URL, a release mirror, or something else, and therefore has no authority by name alone. - Canonical base at this snapshot: - `githubsucks/main` @ `0dd16a5` (GPU initial-target #148 atop folding Stage 2 - landed-doc refresh #150, folding Stage 2 #149, the ledger refresh #147, web - grammars HTML+CSS #146, and the LaTeX Stage 1 #144 / inline-math framing - #145 pair; protocol v20). + `githubsucks/main` @ `e745068` (bottom-panel Stage 1 #155 atop GPU + initial-target #148, folding Stage 2 landed-doc refresh #150, folding + Stage 2 #149, the ledger refresh #147, web grammars HTML+CSS #146, and + the LaTeX Stage 1 #144 / inline-math framing #145 pair; protocol v20). - On the transfer source, `origin/main` named a release mirror at `d3fa632` and lagged badly. On the current destination, `origin` names the canonical URL. This difference is why all recovery begins by @@ -51,156 +51,34 @@ git worktree list git status --short --branch ``` -The `git log` command must expose `0dd16a5` or a newer intentional main. +The `git log` command must expose `e745068` or a newer intentional main. If it does not, stop and repair the remote/fetch configuration. -## Bottom-panel lane (window placement + side windows) — Stage 1 IN REVIEW +## Bottom-panel lane (Arc 7) — Stage 1 MERGED; Stage 2 (GPU band) is next -- Portable branch: `githubsucks/bottom-panel`, worktree - `../pmacs-bottom-panel`, based on `githubsucks/main` @ `ddaa80d`. -- Approved framing: `docs/bottom-panel-framing.md` revision 4, committed - as the branch's first commit (`c27f75a`). -- **Stage 1 implemented; no wire change (protocol stays v20).** What - landed on the branch: - - `src/window.rs`: `WindowParams` (`side` / `fixed_rows` / `dedicated` - + implementation-owned `quit_action` and `origin_document`), `Side`, - a depth-bounded `QuitAction`, `MIN_WINDOW_OUTER_ROWS = 2`, - `Layout::compute(area, fixed)`, the `subtree_min_rows` / - `interactive_min_rows` recursions, `boundary_below`, and the three - new `FrontendView` fields (`panel_capable`, `frame_geometry`, - `panel_hidden`). - - `src/editor_core.rs`: `primary_document_window`, the non-side target - rule, `display_buffer` + the Q#BP3 placement policy, `quit_window`, - `reconcile_panel_layout_core`, `resize_boundary`, per-frontend - `JumpEntry`s, and the shared `resolve_target_buffer` seam that the - #148 initial-target bootstrap now routes through as well. - - `src/editor.rs`: the reconciliation transaction, geometry - declaration, the side-window `dispatch_idle_for` gate, the divider - paint, and the divider drag. - - `src/lua_bindings/window_panel.rs`: the whole `pmacs.window` panel - surface plus the shared adopter-placement helpers; - `builtin/runtime/window.lua` owns `window.panel-height` / - `window.min-height` and the resize commands. - - Adopters: `listview.open`, `compile.run`, `pmacs.terminal.open` all - take `display = "current" | "panel"` (Stage 1 default `"current"`); - LSP/compile visits route through `display_file`. -- **Review round 1 addressed.** The load-bearing finding: the Q#BP6 - side-window split guard (`try_split_active`) had **no production - caller** — `pmacs.window.split_horizontal` / `split_vertical`, and so - `C-x 2` / `C-x 3`, still went through plain `split_active`. Splitting a - focused panel made the root wrapper's final child a split rather than - `Leaf(side)`, which both `Layout::compute`'s fixed pass and - `document_subtree` key on. It survived the first round because the - acceptance test called the core method **directly**; it now goes - through the real Lua binding. This is the folding-arc round-2 lesson - repeating exactly: *after wiring a guard into a production hook, pin it - through the real path — a direct-call test misses the wiring.* - Also fixed: the armed divider drag was not scoped to its arming - frontend (it could cancel and swallow a peer's mouse events); a - recompile carries no `display` and duplicated a panel-placed - `*compilation*` into the document window; and - `paint_mode_line_graphemes` had lost its doc block to an insertion. - Five bite-verified fixes (three via `scripts/bite`, two by manual - revert since their tests share `src/daemon.rs` with the production - code). -- Two Stage-2 hazard pins now exist in `src/daemon.rs`, closing the gap - the review named: a fresh attach while `LOCAL` is focused in a panel - inherits `LOCAL`'s **document** buffer, and an initial-target bootstrap - whose `after-load` hook creates and selects a panel still reasserts - into a document window. -- **Review round 2 addressed.** The load-bearing finding: **Q#BP7 item 1 - — "growth reaching the live tail re-arms follow" — was never - implemented.** `at_bottom` is the instantaneous geometric readout - `scroll_offset == 0`, which a still-anchored view satisfies whenever it - is momentarily tall enough to reach the tail, so the round-1 assertion - could not see the gap: the next rows the child printed pushed the - anchored view back into history. `src/terminal/view.rs` now has - `rearm_follow_on_growth`, reached by one shared `declare_view_size` - helper from every size-declaring path (`snapshot_for_view`, - `record_view_size`, `view_status_for_size`) so grid and semantic - declarations cannot disagree. - Also fixed: the PTY fixtures emitted LF-only output, which staircases - until every row clips to blanks — so the anchor assertions compared - `""` with `""` and could not fail (now CRLF, each guarded by - `assert!(!top_before.is_empty())`); acc33's contrast case asserted - nothing; `start_run` let `already_in_panel` override an **explicit** - `display = "current"`, which is the documented opt-out from the Stage 3 - flip (now gated on omission); and `window_drag` was a daemon-global - slot that a peer's mode-line press could clear. -- Durable test lessons from this round, both the same class: - 1. **A geometric readout is not a state predicate.** `at_bottom` says - "the viewport currently reaches the tail", not "this view follows - the tail". Pinning follow requires feeding MORE output and asserting - the view moved (acc32b uses a filesystem gate between two bursts). - 2. **A PTY in the default mode does not translate LF to CRLF.** An - `echo`-driven fixture staircases rightward and clips to blanks past - the viewport width, so any text equality over it is vacuously true. - Emit `\r\n`, and guard text comparisons with a non-empty assertion - the way the daemon pin guards on `!panel_hidden`. -- **Round-2 self-review caught a regression the round-2 commit - introduced**, in the change it labelled "minor": routing - `pmacs.window.buffer()`'s **no-argument** arm through the fid-scoped - `selected_window` validator made it **fallible**, and - `acting_frontend` can name a frontend with **no registered view** (a - bare `dispatch_key` from an unattached peer does exactly that). The - runtime calls that function on ordinary edits from `killring`, - `syntax`, `autosave`, `pair`, `indent` and `comment` **without - `pcall`**, so the raise never surfaced as an error — it silently - dropped the operation. `kill_ring_acceptance` went 30/30 → 25/5 - (`frontend_detached_drops_per_frontend_state`: "B has kill state"). - The no-arg arm is back on ambient `active_buffer_id()` and documented - as deliberately infallible; the explicit-window arm keeps its Q#BP11 - validation. New **acc19c** pins it through the real path (a - `buffer.after-edit` subscriber during a viewless peer's `dispatch_key`) - and bites against the regressing commit. - Generalizes: **a "uniformity" cleanup that changes a function's - fallibility is not minor** — check every caller's error discipline - first, and remember that an ambient resolver's fallback IS its - contract. -- Verification on this branch: `cargo fmt --check` clean; strict - workspace Clippy clean; 1,817 default + 1,994 CRDT library tests; - `bottom_panel_stage1_acceptance` 46/46; kill ring 30 default + 30 CRDT; - vterm Stage 1 9 default + 10 CRDT; M4 121; required GPU 152; - compile 67; vterm Stage 2 4 / Stage 3 5 (7 CRDT); folding Stage 2 48; - statusline 7; listview 6; - **isolated-config workspace sweep 3,130 passed across 89 suites, zero - failures**; `git diff --check` clean. - - **Run the sweep with an isolated `XDG_CONFIG_HOME`.** The real - `~/.config/pmacs/init.lua` on this desktop calls - `pmacs.packages.install_local(...)`, so every editor the sweep builds - races on one shared install root; a losing race sets a status message - that leaks into the mode line and breaks - `folding_stage2_acceptance::unfolded_frame_is_identical_to_the_pre_folding_baseline`, - which compares whole painted frames. Standalone it is 48/48. This - generalizes the known `compile_mode_acceptance` real-config trap: - any suite that paints the status area inherits it. - - **A latent pre-existing `main` bug surfaced while gating and is NOT - this branch's**: `buffer::tests::proptests::rope_matches_crdt_projection_after_arbitrary_edits` - fails on `main` @ `352bf0b` with `ops = [Insert(0,"a"), - Insert(0,"aaa"), Replace(0,1,"a"), Undo]` — undo of a textually-null - `Replace` returns a no-op edit result still carrying `crdt_op = - Some`, violating the suite's own shape invariant. `src/buffer.rs` is - byte-identical here, and the seed was deliberately **not** committed - (it would make an unrelated failure deterministically red on this - PR). Needs its own lane. - - Durable test lesson from this round: `TerminalViewStatus.scroll_offset` - is documented as the retained rows between **this viewport** and the - live tail, so it necessarily tracks the viewport height. Asserting it - constant across a panel height change is either vacuous or wrong — - the invariant Q#BP7 actually states is that the **anchor** is frozen, - which the acceptance now pins by comparing the first visible row's - text, plus `at_bottom` for the follow re-arm. - - `compile_mode_acceptance` needs `--test-threads=1` locally; it is - 67/67 there. Under default parallelism it fails roughly 1 run in 3, - with a *different* test each time (acc14/acc25a, then acc24) — - **verified pre-existing** by swapping in `githubsucks/main`'s - `builtin/runtime/compile.lua` and reproducing the same rate. The - `pmacs-gpu` bin tests have historically gone red under a loaded sweep - (wgpu device contention). Rerun isolated before treating either as a - regression. -- Stage 2 (the GPU panel band, next available protocol version) has its - own re-framing obligation before implementation; Stage 3 is the default - placement flip. +Stage 1 is on `main`; nothing in this arc is in flight. Stage 2 has **no +branch and no framing yet** — the approved parent framing +`docs/bottom-panel-framing.md` (rev 4) is what it re-scouts against. + +- Stage 1 merged as **#155** (`main` @ `e745068`, 2026-07-24, after two + review rounds). No protocol change. Durable substrate facts live in + `docs/agent-handoff.md` §1; the two round lessons are in §5. +- Retained, carrying nothing unmerged: branch `bottom-panel` and worktree + `../pmacs-bottom-panel`. +- **Stage 2 obligations, already named by the framing** — the starting + point for its own framing doc: `InstanceMessage::PanelFrame` plus + `FrontendEvent::{FrontendCellGeometry, PanelResizeRows, PanelPointer}` + at the next available protocol version, gated in both directions and + each extended enum byte-pinned on its own previous final variant; + extracting `paint_frame`'s per-window body *together with* the + active-window auto-scroll preparation; routing every consumer in the + framing's §1.3 census of 23 transitive active-context reads through + `primary_document_window`; the focus-chrome surface matrix (Q#BP14b); + and Q#BP17's fold-projection parameter plus the stale invariant comment + at `src/window.rs`. Stage 3 is the adopter default flip. +- **Folding Stage 3 and this arc's Stage 2 both touch the semantic + projection.** Whichever is framed second re-scouts the other's landed + state. ## Folding lane (Arc 6) — Stages 1 and 2 MERGED; Stage 3 (GPU) is next @@ -258,6 +136,34 @@ git worktree add --track \ ## Closed since the last snapshot +- **Bottom panel Stage 1 — MERGED as #155** (`main` @ `e745068`, + 2026-07-24, after two review rounds). Window placement, window + parameters, TUI side windows, the divider, and the adopter `display` + opt-in, with no protocol change. Both rounds found the same class of + defect and are worth keeping: + - **Round 1**: the Q#BP6 side-window split guard had *no production + caller* — `C-x 2` still reached plain `split_active` — and survived + because the acceptance test called the core method directly. + - **Round 2**: Q#BP7's terminal growth re-arm had *never been + implemented*, and the assertion meant to pin it (`at_bottom`) is a + geometric readout that a still-anchored view satisfies; the anchor + assertions beside it compared `""` with `""` because the PTY fixture + emitted LF-only output. + - **Post-round-2 self-review**, caught by CI going red on all four Test + jobs: resolving `pmacs.window.buffer()`'s no-argument arm through the + acting frontend made a total function partial, and six runtime modules + silently dropped operations (`kill_ring_acceptance` 30/30 → 25/5). + Fixed in `9110f9f` before merge. + - Gating fact found on the way: **the workspace sweep must run with an + isolated `XDG_CONFIG_HOME`**, because the real user `init.lua` + installs a local package and the losing race leaks a status message + into painted-frame comparisons. There is also a latent pre-existing + `main` bug in the buffer CRDT undo path, unrelated to this arc. + - `compile_mode_acceptance` is load-sensitive under default + parallelism (~1 run in 3, a different test each time); verified + pre-existing by swapping in `main`'s `compile.lua`. It is 67/67 at + `--test-threads=1`. + - **GPU initial target — MERGED as #148** (`main` @ `0dd16a5`, 2026-07-24, after two review rounds). `pmacs --gpu [--socket …] FILE` opens a target before the GPU window appears. Protocol bumped 19 → 20: a semantic-session diff --git a/docs/agent-handoff.md b/docs/agent-handoff.md index fbdf4df..af0ac46 100644 --- a/docs/agent-handoff.md +++ b/docs/agent-handoff.md @@ -1,7 +1,8 @@ # Agent handoff — cross-machine continuity -**Last updated: 2026-07-24, after GPU initial-target (#148, protocol v20) -landed, following folding Stage 2 (#149) and its landed-doc refresh (#150), +**Last updated: 2026-07-24, after bottom-panel Stage 1 (#155) landed, +following GPU initial-target (#148, protocol v20), +folding Stage 2 (#149) and its landed-doc refresh (#150), web grammars HTML+CSS (#146), the LaTeX Stage 1 / inline-math framing pair (#144/#145), folding Stage 1 (#142), one-command GPU invocation (#141), the documentation refresh (#140), Vterm Stage 3 (#135), tab-width rendering @@ -21,12 +22,81 @@ commands, read `docs/active-work.md` immediately after this file. ## 1. Where the project stands (2026-07-24) -- `main` @ `0dd16a5` (GPU initial-target #148 atop folding Stage 2 landed-doc - refresh #150, folding Stage 2 #149, ledger refresh #147, web grammars #146, +- `main` @ `e745068` (bottom-panel Stage 1 #155 atop GPU initial-target #148, + folding Stage 2 landed-doc refresh #150, folding Stage 2 #149, ledger + refresh #147, web grammars #146, LaTeX Stage 1 #144 / inline-math framing #145, and folding Stage 1 #142), protocol **v20** (`SUPPORTED=[6..=20]`; v16 = `ThemeFacts`, v17 = `FontFacts`, v18 = `StatuslineSegments`, v19 = terminal frames/events, v20 = the GPU initial-target semantic bootstrap family). +- **Bottom panel Stage 1 (window placement + TUI side windows) LANDED — + #155** (`docs/bottom-panel-framing.md` rev 4; merge `e745068`; two review + rounds). **No protocol change (still v20).** Arc 7's substrate: pmacs now + has Emacs's `display-buffer` + window parameters, and a buffer can be + displayed in a fixed-height window pinned to the bottom of the frame that + feature code targets **by policy** instead of by stealing the selected + window. + - `src/window.rs`: `WindowParams { side, fixed_rows, dedicated }` plus + implementation-owned `quit_action` / `origin_document` (Lua reads them, + `set_params` refuses them); `MIN_WINDOW_OUTER_ROWS = 2`; + `Layout::compute(area, fixed)` subtracts fixed children before dividing + the remainder by weight, preserving last-flexible-takes-the-remainder, so + a tree with no fixed leaves computes byte-identically to before. + - **`Layout::compute` has TWO production callers**, and both must feed the + same shared `panel_fixed_rows` map: `window_placements` and + `src/overlay_paint.rs`'s peer-presence pass, which derives its own + text-area `Rect` and never routes through the first. Leaving the second + on unfixed geometry paints every peer cursor at the row it would occupy + with no panel open. + - **The minimum is recursive** (`subtree_min_rows`: horizontal splits sum, + vertical splits max). "Two rows at the root" does not give each nested + leaf two rows. `interactive_min_rows` is the same recursion over the + user's `window.min-height` preference, and applies to drag/keyboard + resize ONLY — the layout pass and frame-resize reconciliation use the + structural floor, so changing a preference can never invalidate an + existing layout. + - **Hiding a panel is a durable state transition, not a per-frame effect** + (`EditorState::reconcile_panel_layout`): it moves focus out and releases + the terminal controller, because the terminal resize path merely returns + on zero content without releasing. It runs after attach/resize/display/ + close and defensively before input dispatch, terminal sync, and paint. + - `FrontendView` gains `panel_capable`, `frame_geometry` + (`None` = **unknown**, never the GPU attach request's permanent 24×80 + placeholder), and derived `panel_hidden` — each spelled explicitly at + every construction site, preserving folding's non-`Default` discipline. + - `EditorCore::primary_document_window` is the Q#BP14 projection seam; + `display_buffer` is Phase 1 of the display transaction (exact target → + side affinity → ordinary reuse, with option-valued height/dedication); + the Lua layer owns Phase 2 (activate → hook → reconcile → revalidate → + final-focus matrix). + - **Optimistic input is gated per WINDOW, not per buffer**: + `dispatch_idle_for` returns `false` whenever the acting frontend's active + window is a side window. Marking the panel's BUFFER round-trip would be + wrong — `round_trip_buffers` is global by `BufferId`, so it would disable + optimistic apply for another frontend editing that buffer as its document. + - Jump entries are per frontend and carry their origin `WindowId`; a stale + **side** origin is SKIPPED, because degrading it to an active-window + switch is exactly the duplicate-panel corruption the arc removes. + - `pmacs.window.display / display_file / quit / panel / params / + set_params / resize / display_target`, plus `builtin/runtime/window.lua` + (`window.panel-height`, `window.min-height`, `C-x ^` / `C-x C-^`). + Adopters take `display = "current" | "panel"`; **Stage 1 default is + `"current"`** and Stage 3 flips it. + - The divider is the upper subtree's existing mode-line row — no row added + or consumed, `ui.divider` restyles every exposed segment of one boundary, + and drag state is `HashMap` so frontends cannot steal each + other's gestures. + - `open_initial_target` now shares one `resolve_target_buffer` + + exact-window install seam with `display_file`, and reasserts into a + document window after hooks (a startup hook can now create a panel). + - Final gates: 1,817 default + 1,994 CRDT library tests; the new + `bottom_panel_stage1_acceptance` 46; kill ring 30; compile 67; M4 121; + required GPU 152; initial-target 14 CRDT; all three vterm suites; folding + Stage 2 48. All 12 CI checks green at merge. + - **Stage 2 (the GPU panel band) needs its own re-framing** before + implementation and takes the next available protocol version; the + framing's §1.3 census of 23 transitive active-context reads is its map. + Stage 3 is the adopter default flip. - **GPU initial target LANDED — #148** (`docs/gpu-initial-target-framing.md` rev 3; merge `0dd16a5`; two review rounds). `pmacs --gpu [--socket NAME|PATH] FILE` transports exact Unix path @@ -524,8 +594,14 @@ commands, read `docs/active-work.md` immediately after this file. (GPU) is next**, unframed. - **Web grammars HTML+CSS LANDED — #146**, and **LaTeX Stage 1 — #144** with its inline-math parent framing **#145**. - - Remaining ranked arcs: 6 folding Stage 3, 7 DAP, 8 GPU splits, plus - the `.ipynb` arc (its JSON-grammar prerequisite shipped in #123). + - **Arc 7 (bottom panel) Stage 1 LANDED — #155** — window placement, + window parameters, TUI side windows, the divider, and the adopter + `display` opt-in. **Stage 2 (the GPU band) is next and needs its own + re-framing**; Stage 3 is the default flip. DAP was parked awaiting + exactly this arc's Stage 1 and can now re-baseline its touch census. + - Remaining ranked arcs: 6 folding Stage 3, 7 bottom-panel Stages 2–3, + DAP, 8 GPU splits, plus the `.ipynb` arc (its JSON-grammar + prerequisite shipped in #123). ## 2. How we work (the part that must not drift) @@ -756,6 +832,44 @@ final variant — its own round-trip cannot detect a discriminant shift. before any `FrontendEvent` touches render/size/editor state. Generalizes: when a new failure path can leave a handle installed without its owning session, dropping a handle is not the same as tearing down the connection. +- **A guard with no production caller passes every direct-call test.** + #155 round 1: `EditorCore::try_split_active` implemented the side-window + split refusal, but `pmacs.window.split_horizontal` / `split_vertical` — and + therefore `C-x 2` / `C-x 3` — still called plain `split_active`. The + acceptance test called the core method directly, so reverting the guard + entirely would have left every test green. Same shape as folding #142 + round 2. Assert through the outermost user-reachable seam + (`try_exec(&s, "pmacs.window.split_horizontal()")`), then falsify by + revert. When the test shares a file with the code it pins, `scripts/bite` + cannot swap it — break the production line by hand and `git checkout --`. +- **A geometric readout is not a state predicate.** + `TerminalViewStatus::at_bottom` is defined as `scroll_offset == 0` — "the + viewport currently reaches the tail", not "this view follows the tail". A + still-anchored view satisfies it whenever it happens to be tall enough, so + asserting it could not detect that Q#BP7's growth re-arm had never been + implemented (#155 round 2): the next rows the child printed pushed the + anchored view back into history. Pinning *following* requires advancing the + world — feed more child output through a filesystem gate — and asserting the + view came along. Related: `scroll_offset` is viewport-relative, so + "unchanged across a height change" is vacuous or wrong; the invariant is + the frozen ANCHOR. +- **A PTY in the default mode does not translate LF to CRLF.** An + `echo`-driven test fixture staircases rightward, and past the viewport + width every row clips to blanks — so `assert_eq!(top_before, top_after)` + compares `"" == ""` and passes for any regression (#155 round 2). Emit + `printf '...\r\n'`, and guard text comparisons with + `assert!(!observed.is_empty())` the same way the panel daemon pin guards on + `!panel_hidden`. +- **Widening an ambient resolver into a scoped one can make a total function + partial.** #155 round 2 resolved both arms of `pmacs.window.buffer()` + through the acting frontend "for uniformity". `acting_frontend` follows the + interactive origin, which can name a frontend with no registered view (a + bare `dispatch_key` from an unattached peer), so the no-argument arm began + raising instead of answering. No runtime caller `pcall`s it, so killring, + syntax, autosave, pair, indent and comment silently dropped operations — + `kill_ring_acceptance` went 30/30 to 25/5 on every CI platform. The ambient + resolver's fallback is what makes it *total*; keep it, and document that as + deliberate. Uniformity is not free when the paths have different totality. - **An upgrade decision must be tracked independently of the outcome that triggered it.** #148 published a target's fresh `BufferSnapshot` to existing grid replicas only when the buffer was `newly_loaded || @@ -836,6 +950,14 @@ setup (reader → editable → kernel execution) now has its JSON grammar prerequisite, but remains a real arc, not a one-shot. GPU: auto-reconnect after daemon restart, splits/multi-buffer, gutter riders (whitespace guides, folding, git markers). +Bottom panel (SHIPPED Stage 1, #155; full list in its framing "Deferred"): +left/right/top side windows, multiple slots per side, rehoming a leaf across +the tree, the whole `no_other_window` parameter, manual panel hide/show and +`window.toggle-panel`, `display-buffer-alist`-style user rules, panel +persistence (blocked on settings persistence), `OSC 22` pointer shape in the +TUI, per-panel statusline segments on the wire, proportional-font panels, +`window-configuration` registers, atomic windows, panel-local keymaps, and +horizontal (`C-x {`/`}`) resize. Themes (full list in theme-faces framing rev 9 "Deferred (named)"): popup/menu/dropdown bg + selected-row faces, `ui.background` / `ui.caret`, `ui.modeline.inactive`, minimap chrome, peer-cursor