# Active work — cross-machine resume ledger **Snapshot: 2026-07-24.** This file records volatile work that has not landed on `main`. Read it after `docs/agent-handoff.md`. Remove completed entries when their PR merges; do not let this become a second permanent backlog. ## Repository authority - Canonical development URL: `https://github.com/levineuwirth/pmacs.git`. This ledger uses the normalized local alias `githubsucks` so its refs and recovery commands are identical on every machine. Remote names are otherwise 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). - 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 verifying URLs and normalizing `githubsucks` rather than trusting `origin/main`. - The shared desktop checkout contained unrelated uncommitted work. The branches below were prepared in isolated worktrees; never clean or overwrite the shared checkout to recover them. Start on another machine by inspecting its remotes: ```sh git remote -v git remote get-url githubsucks ``` If the second command says the alias is absent, add it; if it prints a different URL, stop and resolve that collision rather than overwriting an unknown remote: ```sh git remote add githubsucks https://github.com/levineuwirth/pmacs.git ``` Then recover current refs: ```sh git fetch githubsucks --prune git log -1 --oneline githubsucks/main git worktree list git status --short --branch ``` The `git log` command must expose `0dd16a5` 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 - 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. ## Folding lane (Arc 6) — Stages 1 and 2 MERGED; Stage 3 (GPU) is next Both shipped stages are on `main`; nothing in this arc is in flight. Stage 3 has **no branch and no framing yet**. - Stage 1 (headless fold engine) merged as **#142**, Stage 2 (grid/daemon collapse) as **#149** — both under "Closed since the last snapshot". - Retained, carrying nothing unmerged: branches `folding` / `folding-tui` and worktrees `../pmacs-folding` / `../pmacs-folding-tui`. The framings `docs/folding-framing.md` (rev 5) and `docs/folding-stage2-framing.md` (rev 4) are the approved artifacts Stage 3 re-scouts against. - **Stage 3 (GPU) obligations, already named by the framings** — the starting point for its own framing doc: GPU collapse at TUI parity; caret/hit-test fold-awareness; the `BufferSnapshot` **fold-mirror clear** (parent R2-4 — without it, empty-after-revert diff suppression leaves stale folds on the GPU, the same trap class as #120); CRDT-origin and GPU-optimistic interactive unfold (parent R2-3); and flipping `FrontendView.fold_projection` to `true` for semantic frontends, which Stage 2 deliberately left `false` (Q#FD21). ## Parked lane: kill-ring browser + persistence - Portable branch: `githubsucks/kill-ring-browser` - Parked framing head: `503c489` - State: framing only, revision 2; no implementation and no PR. - Status: explicitly parked by the user on 2026-07-20. - Its original scout was based on `0efb5cd`. The preserved framing marks this ground truth stale and requires a complete re-scout against the then-current `githubsucks/main` before implementation. - Compile-mode has merged since the original scout, so old “compile-mode in flight” keybinding/touch-set assumptions are not authoritative. Recovery worktree, only when the user un-parks it: ```sh git worktree add --track \ -b kill-ring-browser \ ../pmacs-kill-ring-browser \ githubsucks/kill-ring-browser ``` ## Documentation lane - Portable branch: `githubsucks/handoff-2026-07-20` - Carries synchronized `AGENTS.md` / `CLAUDE.md`, this ledger, the durable handoff refresh, and the keybinding reference correction. - It changes no runtime code. - Review and merge this documentation branch separately; it must not be folded into a feature framing branch. - Now also absorbs both landed arcs: Vterm Stage 1 (#126) and the config registry (#127). Canonical `main` is merged into it up to `2e37c04`, so its diff against `main` is documentation only. ## Closed since the last snapshot - **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 `SessionBootstrapRequest` after `AttachRequest`, plus an appended `InstanceMessage::InitialTargetResult` pre-window readiness barrier; v6–v19 wire encodings are unchanged. Root owns launcher tilde/cwd resolution and exact raw-byte path transport; the daemon resolves/dedups/loads the target and runs load/switch hooks inside one dispatcher transaction, then publishes CRDT-upgraded targets to existing grid replicas (gated on `upgraded_to_crdt`, independent of the load/create outcome, so a dedup onto a hidden not-yet-backed buffer still reaches pre-attached replicas — round 2 finding). Semantic replicas receive a publication only when displaying that buffer, so a second target launch cannot switch an existing GPU window. Round 2 also closed a failure-containment gap: every dispatcher-side bootstrap failure now shuts down the socket (a dropped write-half clone does not close a shared FD), and the dispatcher drops any event from a session that was never installed, rather than reaching absent render/size state. Integrated cleanly with Folding Stage 2 (#149): fold projection at attach is selected from the same negotiated `semantic_render` bit the target bootstrap uses. Its lane, worktree (`../pmacs-gpu-initial-target`), and branch (`gpu-initial-target`) are done; the `-framing` branch is kept. Durable substrate facts and both review-round lessons live in `docs/agent-handoff.md` §§1/5 and `docs/gpu-initial-target-framing.md` rev 3. - **Folding Stage 2 (grid/daemon collapse) — MERGED as #149** (`main` @ `6ed4fe9`, 2026-07-24, after **five** review rounds). The grid TUI now renders collapses. Spine (Q#FD12): `src/fold_view.rs`'s `VisibleLineMap`, derived from the fold store plus a window's line offsets and **never stored**, threaded as `Option<&'a VisibleLineMap>` on a lifetime-bearing `Viewport<'a>` that stays `Copy`. No wire schema or protocol change; the GPU path is Stage 3. 48 acceptance tests on the real `paint_frame` grid, every behavioral claim bite-verified. Durable design points, each a trap Stage 3 inherits: - the map's unit is a **merged hidden component** (overlapping *or adjacent* intervals unioned, keeping the earliest visible head), not a fold — folds may cross, and a later fold's own head can be hidden; - instances are **per rendered window** and **per command/event operation**, never per frame; a command's map follows the operation's **target** window, since a wheel event names a pane without activating it; - fold projection is **per-frontend** (`FrontendView.fold_projection`) — shared `EditorCore` motion would otherwise make a simultaneous unfolded GPU session's cursor skip lines it still displays; - a hidden cursor normalizes by **position**, not row, and `set_view_top` clamps in the setter rather than being repaired at render time; - the interactive-Lua unfold keys on the **post-intercept** edit site — a managed buffer intercept may legally relocate the op. Process notes worth keeping: `main` moved under the arc, and the merge was textually clean but **not semantically clean** (#146 added `Viewport` literals the new `folds` field invalidated) — a clean `git merge-tree` does not mean the merged tree compiles. CI was red at review on the macOS/luajit `outline_5_level_100_entry_renders_within_100ms` budget flake and went green on rerun. - **Documentation ledger refresh — MERGED as #147** (`main` @ `0a479ae`, 2026-07-24). The #142 housekeeping, expanded after review found the ledger stale through four merges rather than one. Its own macOS/luajit red was the vterm `VTERM_ALT_READY` PTY timeout; green on rerun. - **Web grammars HTML + CSS — MERGED as #146** (`main` @ `47581f4`, 2026-07-23). `.html/.htm/.xhtml` and `.css` highlight off the official `tree-sitter-html` 0.23 / `tree-sitter-css` 0.25 crate query constants (no in-repo overlay), and HTML's `INJECTIONS_QUERY` lights up `