34 KiB
Active work — cross-machine resume ledger
Snapshot: 2026-07-25. 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 aliasgithubsucksso its refs and recovery commands are identical on every machine. Remote names are otherwise machine-local:originmay 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@8c86d34(the dired framing #164 atop find-file #162, COHERENCE.md #163, Lean 4 Stage 1 #160, the minimap blank-slab fix #159, bottom-panel Stage 1 #155, the inline-math re-scout #154, the vterm PTY-flake fix #153, and the GPU initial-target doc refresh #152; protocol v20). - On the transfer source,
origin/mainnamed a release mirror atd3fa632and lagged badly. On the current destination,originnames the canonical URL. This difference is why all recovery begins by verifying URLs and normalizinggithubsucksrather than trustingorigin/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:
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:
git remote add githubsucks https://github.com/levineuwirth/pmacs.git
Then recover current refs:
git fetch githubsucks --prune
git log -1 --oneline githubsucks/main
git worktree list
git status --short --branch
The git log command must expose 8c86d34 or a newer intentional main.
If it does not, stop and repair the remote/fetch configuration.
Lean 4 lane (Arc 8) — Stage 1 IN REVIEW (PR #160)
- Portable branch:
githubsucks/lean4-stage1, worked in the shared checkout (no sibling worktree), based ongithubsucks/main@e745068. - Approved framing:
docs/lean4-mode-framing.mdrevision 4, committed as the branch's first commit (a382965) after three review rounds. Seven stages, 19 decisions (Q#LN1–19), 64 acceptance criteria. North star: match or exceed VS Code's Lean support. - Stage 1 implemented; no wire change (protocol stays v20), no LSP, no
frontend change. Four commits: framing, grammar, theme captures,
editing surface + acceptance.
Cargo.toml+src/syntax.rs:arborium-lean2.18 and oneBUILTIN_LANGUAGESentry namedlean4(Q#LN2 — the name becomes thedidOpenlanguage_id), claiming.leanonly.src/highlight.rs: four capture entries —constructor,character,keyword.conditional,warning.builtin/runtime/{comment,pair,syntax}.lua:--comments, the⟨⟩ ⦃⦄ ⟮⟯pair set, thelean→lean4modeline alias.tests/lean4_stage1_acceptance.rsplus unit tests insyntax.rs/highlight.rs: 12 criteria, 17 tests.
- Q#LN1's open obligation is discharged.
tree-sitter-lean4is unusable (depends ontree-sitter ^0.25directly against our 0.26, exports noLANGUAGEconst despite its README, packages no queries);arborium-leanridestree-sitter-language 0.1with a pre-generated ABI-15 parser.cargo tree -dshows no duplicate core. The parse smoke pins the failure mode that matters:→/∀/≥must produce(arrow)/(forall)/(comparison), since a mismatched-core build degrades silently on exactly those characters rather than failing loudly. - Q#LN4 is a deliberate retro-paint of seven language entries, not
four:
tree_sitter_javascript::HIGHLIGHT_QUERYis concatenated base-first into javascriptreact/typescript/typescriptreact. Its shape is "every capitalized identifier" (#match? "^[A-Z]") plus every Lua table brace — not "constructors". Pinned in both directions per #146. - Implementation findings not in the framing:
warninghad to move from bold red to bold bright red:numberis plainfg(1), sosorryand an adjacent numeric literal were the same colour. Found by writing the test.Some(1)is not@constructor— in call position a narrower@functionpattern wins. Only bare or pattern-position capitalized identifiers reach it. Pinned so the blast-radius claim stays honest.- Lean node kinds nest:
module > declaration > def|theorem. pmacs.parse.injection_aliasesis a documented write-only Lua proxy (canonical map is Rust-side), so fence tests must drive_parse_nowand inspect layer languages, never read the table back.
- Review round 1 addressed. The finding: acc12's server-list assertion
could not fail for the regression it named — the shared
editor()helper wipespmacs.lsp.configbefore any buffer opens, so#pmacs.lsp.list() == 0holds for every language regardless of what Stage 1 ships. It now asserts against a pristineEditorStatethatpmacs.lsp.config.lean4is nil, with a non-vacuity check that the same lookup findsrust; bite-verified by adding alean4config tolsp.luaand watching it fail. Also fixed a stale column in ahighlight.rscomment. - Verification on this branch:
cargo fmt --checkclean; strict workspace Clippy clean; 1,826 default + 2,003 CRDT library tests; lean4 Stage 1 9/9; comment toggle 14; auto-pair 45; injection 4; M4 121; required GPU 152; isolated-config workspace sweep 3,150 across 90 suites;git diff --checkclean. The sweep needs an isolatedXDG_CONFIG_HOMEfor the reason recorded in the bottom-panel lane below. - Stage 2 is multi-root LSP server affinity — pure substrate, no Lean
content, and it changes
ensure_server, which every LSP language shares. It is sequenced next because Lean is the language that makes its absence a correctness failure rather than an inconvenience. Two corrections the framing already carries for it:rootis computed atlsp.lua:537, after the reuse loop, so the fix must hoist it; andproject_root_fornever returns nil for a file with a path, so the affinity key must be the root only when a root was actually detected, or markerless scratch files fragment into one server per directory for every language.
Dired lane — Stage 0 MERGED; Stage 1 IN REVIEW (PR #165)
- Approved framing:
docs/dired-framing.mdrevision 6 — rev 5 is the approved text (merged as its own docs PR #164), rev 6 adds §0's Stage 1 implementation notes (S1-1…S1-9). Stages 2 (marks and operations) and 3 (wdired) each get their own detailed framing after the prior stage lands. - Stage 0 (
C-x C-ffind-file) MERGED as #162 (main@2af1ab3, 2026-07-25, one review round, 12/12 CI green). Durable facts moved todocs/agent-handoff.md§1 per rule 3 below. - Stage 1 branch:
githubsucks/dired-stage1, worktree../pmacs-dired-stage1, based ongithubsucks/main@8c86d34(the framing merge #164). A fresh cut, not a rebase: the olderdiredbranch (ffdd642, worktree../pmacs-dired-arc) was based on the superseded0827dd1and carried only the framing content #164 already put onmain, so merging it would have reconciled two histories of one document. It is left untouched and carries nothing unmerged. - Stage 1 implemented; no wire change (protocol stays v20). What
landed on the branch:
builtin/runtime/dired.lua: one buffer per directory named*dired:<canonical path>*with the handle-table ownership check; read-only intercept +set_round_trip_input; thediredmajor mode and its mode-scoped keymap (RET/f,^,n/p,g,q,s); basename cursor re-seating across every wholesale repaint;display_filefor file visits and same-window reuse for directory descent;C-x d/C-x C-j; thedired.kill-when-openingsetting. Loaded afterwindow.lua.src/fs.rs:ReadDirTolerance,FsDirEntryError,FsDirListing, and one walk that either fails on a per-entry condition or records it (Q#DR6).src/async_runtime.rscarries the listing inReplyKind::ReadDir/JobResult::ReadDir;src/lua_bindings/mod.rskeys the Lua result shape onerrors.is_some(), so the bare array the frozen M8.2 fixture consumes withipairsis untouched;builtin/runtime/fs.luavalidates read-op opts and rejects unknown keys (a typo'dtolerantused to degrade silently to fatal).src/editor_core.rs+src/lua_bindings/mod.rs:normalize_buffer_pathispuband exposed aspmacs.path.canonicalize— Q#DR2's preferred end state, so no Lua mirror exists and Stage 2 owes no mirror removal. This makes B2 ("tolerantread_diris the only Rust change") false by one small binding, deliberately.tests/dired_acceptance.rs: 22 tests over framing items 1–16, dispatch-driven; item 17 is the m8_1/m8_2/m8_3 additivity gate.
- The framing claim the substrate falsified (S1-2): R2-3 expected a
dedicated dired panel to carry its dedication across a descent.
display_buffernever replaces the buffer in a slot dedicated to another one — it discards every side-specific parameter and falls back to the document window (Q#BP3 2.iii), and the exact-window arm errors. Dired does not unpin the user's panel; both arms are pinned. - The vacuity the bites found (S1-3): acceptance 3c cannot pin the
descent routing. Dired holds focus in its own panel, so a raw
switch_bufferlands in the same window and every 3c assertion holds either way. Dedication is the only discriminator, so the dedicated-panel test is the real pin — and the vacuity is documented at the assertion rather than relabelled. - The pre-existing test dired's first mode-scoped binding broke
(S1-4):
describe_key_identifies_every_default_bindingasserted every binding in the stack resolves throughdescribe.keycontext-free, which held only while the modes table was empty. It now sets the effective context per binding and explicitly clears the mode for global ones, because a leaked mode legitimately shadows a global chord of the same name (dired'sRETshadowsedit.newline-and-indent). - Durable substrate facts, independent of this arc:
pmacs.buffer.kill(notremove) redirects windows off a doomed buffer before removal, sokill-when-openingkills after the replacement is displayed.- Interactive origin does not survive an await: work resumed in
tick_asyncsees noInteractiveCommandOrigin, sopmacs.window.*acts for the ambient active frontend (S1-9). - Kinds are lstat-based in both
read_dirandstat, so nothing in an entry says whether a symlink points at a directory;RETprobes by trying to list it (S1-8). - A path-backed buffer's name is its full path, not its basename — worth knowing before writing any name assertion.
C-x dtakes no completion source on purpose (S1-5): with one, RET on an empty field opens whatever sorts first, and RET-on-where-you-are is the gesture the binding exists for. The field is prefilled instead.
- Bite verification: 15 claims, each mutated in place and required to
fail the test that names it.
dired.luais new, soscripts/bite's file swap does not apply; every mutation was applied and reverted withgit checkout --. One came back VACUOUS and is recorded above. - Review round 1 addressed (framing rev 7, S1-10…S1-12). Three
behavioral fixes, each bite-verified:
dired.revert's re-seat is guarded on the active buffer (an ambientmove_to_lineafter an await moved an unrelated buffer's cursor — the buffer-level instance of S1-9);fmt_sizekeeps the column width past ten digits, because_layoutis a contract Stage 3 is planned against; and the symlink descent dropped its probe, sinceopen_directory's changed-nothing-on-failure invariant is the probe (it was listing the target directory twice). Plus a consecutive-readdir-error cap, because nothing cancels a dired listing — it carries no supersede key, so cancellation was never the backstop the tolerant loop implicitly relied on. Naming/comment findings taken as-is.- Durable process lesson, hit twice now: a mutation-bite helper restores
with
git checkout --, which reverts to HEAD — so a fix must be committed before it is bitten. Round 1's fixes were briefly wiped by exactly that.
- Durable process lesson, hit twice now: a mutation-bite helper restores
with
- Verification on this branch:
cargo fmt --checkclean; strict workspace Clippy clean; 1,829 default + 2,006 CRDT library tests; dired acceptance 25 default + 25 CRDT; m8_1 10 / m8_2 15 / m8_3 32 unchanged; M4 121; required GPU 155; isolated-XDG_CONFIG_HOMEworkspace sweep 3,189 passed across 92 suites, zero failures;git diff --checkclean. The sweep needs the isolated config for the reason recorded in the bottom-panel lane below. - Coherence (framing §0.5, required since #163): serves
COHERENCE.md§20 Priority 1, which names this work explicitly; journey step 7's file half goes from no surface to a surface; adds no interaction island — keys are a mode-scoped keymap, and wdired will be a mode swap; adoptspmacs.configfordired.kill-when-opening; inherits §9's worker-attribution gap for itsread_dirjobs without worsening it. The audited claims this changes are updated inCOHERENCE.mditself, per its §25. - Boundary with the Journey Stage 1 arc (
COHERENCE.md§20 arc-cut 1): CLI directory-argument handling (pmacs .exits 1) belongs there, not here — Stage 1 does not fix it. The two meet atresolve_target_buffer; dired supplies the buffer a directory should resolve to, andpmacs .should route into it rather than growing a second directory surface.
Bottom-panel lane (window placement + side windows) — Stage 1 IN REVIEW
- Portable branch:
githubsucks/bottom-panel, worktree../pmacs-bottom-panel, based ongithubsucks/main@ddaa80d. - Approved framing:
docs/bottom-panel-framing.mdrevision 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_actionandorigin_document),Side, a depth-boundedQuitAction,MIN_WINDOW_OUTER_ROWS = 2,Layout::compute(area, fixed), thesubtree_min_rows/interactive_min_rowsrecursions,boundary_below, and the three newFrontendViewfields (panel_capable,frame_geometry,panel_hidden).
- implementation-owned
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-frontendJumpEntrys, and the sharedresolve_target_bufferseam that the #148 initial-target bootstrap now routes through as well.src/editor.rs: the reconciliation transaction, geometry declaration, the side-windowdispatch_idle_forgate, the divider paint, and the divider drag.src/lua_bindings/window_panel.rs: the wholepmacs.windowpanel surface plus the shared adopter-placement helpers;builtin/runtime/window.luaownswindow.panel-height/window.min-heightand the resize commands.- Adopters:
listview.open,compile.run,pmacs.terminal.openall takedisplay = "current" | "panel"(Stage 1 default"current"); LSP/compile visits route throughdisplay_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 soC-x 2/C-x 3, still went through plainsplit_active. Splitting a focused panel made the root wrapper's final child a split rather thanLeaf(side), which bothLayout::compute's fixed pass anddocument_subtreekey 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 nodisplayand duplicated a panel-placed*compilation*into the document window; andpaint_mode_line_graphemeshad lost its doc block to an insertion. Five bite-verified fixes (three viascripts/bite, two by manual revert since their tests sharesrc/daemon.rswith the production code). - Two Stage-2 hazard pins now exist in
src/daemon.rs, closing the gap the review named: a fresh attach whileLOCALis focused in a panel inheritsLOCAL's document buffer, and an initial-target bootstrap whoseafter-loadhook 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_bottomis the instantaneous geometric readoutscroll_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.rsnow hasrearm_follow_on_growth, reached by one shareddeclare_view_sizehelper 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 byassert!(!top_before.is_empty())); acc33's contrast case asserted nothing;start_runletalready_in_paneloverride an explicitdisplay = "current", which is the documented opt-out from the Stage 3 flip (now gated on omission); andwindow_dragwas a daemon-global slot that a peer's mode-line press could clear. - Durable test lessons from this round, both the same class:
- A geometric readout is not a state predicate.
at_bottomsays "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). - 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.
- A geometric readout is not a state predicate.
- 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-scopedselected_windowvalidator made it fallible, andacting_frontendcan name a frontend with no registered view (a baredispatch_keyfrom an unattached peer does exactly that). The runtime calls that function on ordinary edits fromkillring,syntax,autosave,pair,indentandcommentwithoutpcall, so the raise never surfaced as an error — it silently dropped the operation.kill_ring_acceptancewent 30/30 → 25/5 (frontend_detached_drops_per_frontend_state: "B has kill state"). The no-arg arm is back on ambientactive_buffer_id()and documented as deliberately infallible; the explicit-window arm keeps its Q#BP11 validation. New acc19c pins it through the real path (abuffer.after-editsubscriber during a viewless peer'sdispatch_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 --checkclean; strict workspace Clippy clean; 1,817 default + 1,994 CRDT library tests;bottom_panel_stage1_acceptance46/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 --checkclean.- Run the sweep with an isolated
XDG_CONFIG_HOME. The real~/.config/pmacs/init.luaon this desktop callspmacs.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 breaksfolding_stage2_acceptance::unfolded_frame_is_identical_to_the_pre_folding_baseline, which compares whole painted frames. Standalone it is 48/48. This generalizes the knowncompile_mode_acceptancereal-config trap: any suite that paints the status area inherits it. - A latent pre-existing
mainbug surfaced while gating and is NOT this branch's:buffer::tests::proptests::rope_matches_crdt_projection_after_arbitrary_editsfails onmain@352bf0bwithops = [Insert(0,"a"), Insert(0,"aaa"), Replace(0,1,"a"), Undo]— undo of a textually-nullReplacereturns a no-op edit result still carryingcrdt_op = Some, violating the suite's own shape invariant.src/buffer.rsis 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_offsetis 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, plusat_bottomfor the follow re-arm. compile_mode_acceptanceneeds--test-threads=1locally; 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 ingithubsucks/main'sbuiltin/runtime/compile.luaand reproducing the same rate. Thepmacs-gpubin tests have historically gone red under a loaded sweep (wgpu device contention). Rerun isolated before treating either as a regression.
- Run the sweep with an isolated
- 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-tuiand worktrees../pmacs-folding/../pmacs-folding-tui. The framingsdocs/folding-framing.md(rev 5) anddocs/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
BufferSnapshotfold-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 flippingFrontendView.fold_projectiontotruefor semantic frontends, which Stage 2 deliberately leftfalse(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-currentgithubsucks/mainbefore 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:
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
mainis merged into it up to2e37c04, so its diff againstmainis 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 …] FILEopens a target before the GPU window appears. Protocol bumped 19 → 20: a semantic-sessionSessionBootstrapRequestafterAttachRequest, plus an appendedInstanceMessage::InitialTargetResultpre-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 onupgraded_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 negotiatedsemantic_renderbit the target bootstrap uses. Its lane, worktree (../pmacs-gpu-initial-target), and branch (gpu-initial-target) are done; the-framingbranch is kept. Durable substrate facts and both review-round lessons live indocs/agent-handoff.md§§1/5 anddocs/gpu-initial-target-framing.mdrev 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'sVisibleLineMap, derived from the fold store plus a window's line offsets and never stored, threaded asOption<&'a VisibleLineMap>on a lifetime-bearingViewport<'a>that staysCopy. No wire schema or protocol change; the GPU path is Stage 3. 48 acceptance tests on the realpaint_framegrid, 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) — sharedEditorCoremotion 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_topclamps 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:
mainmoved under the arc, and the merge was textually clean but not semantically clean (#146 addedViewportliterals the newfoldsfield invalidated) — a cleangit merge-treedoes not mean the merged tree compiles. CI was red at review on the macOS/luajitoutline_5_level_100_entry_renders_within_100msbudget 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 vtermVTERM_ALT_READYPTY timeout; green on rerun. -
Web grammars HTML + CSS — MERGED as #146 (
main@47581f4, 2026-07-23)..html/.htm/.xhtmland.csshighlight off the officialtree-sitter-html0.23 /tree-sitter-css0.25 crate query constants (no in-repo overlay), and HTML'sINJECTIONS_QUERYlights up<script>→ js and<style>→ css. Durable lesson recorded indocs/web-grammars-html-css-framing.md: thehighlight.rscapture table is global, so adding a capture name retro-paints every other language — check the reverse direction and pin it. -
LaTeX Stage 1 — MERGED as #144, with its parent inline-math framing committed as #145 (
main@f09b0a1, 2026-07-23)..tex/.latex/.sty/.clshighlight viacodebook-tree-sitter-latex0.6 plus the first in-repo query overlay (builtin/queries/latex/highlights.scm,include_str!) — the reusable pattern for grammars whose crate ships no usable queries. The crates.iotree-sitter-latexis provably broken (noscanner.c). The math parser and Tiers 3–4 are deferred to the inline-math arc. -
Folding Stage 1 (headless fold engine) — MERGED as #142 (
main@c49a8c7, 2026-07-23, after three review rounds; round 3 clean). The instance-side fold store + translating/droppingView, the structural source (derived head line, closer-aware tail), the Lua data API + interactiveC-c @commands, the command-path pre-edit unfold, and authoritative-emptyFoldStateproduction landed with no protocol bump. Thefoldingbranch and worktree (../pmacs-folding) are retained but carry nothing unmerged; thefolding-framing.mdframing is preserved. CI red at merge was an unrelated environmental perf flake (outline_5_level_100_entry_renders_within_100ms, macOS/luajit only), green on rerun. Stage 2 has since merged as #149 (above); durable substrate seams live indocs/agent-handoff.md§1. -
Vterm Stage 3 (protocol v19 + GPU terminal) — MERGED as #135 (
main@cac4961, 2026-07-22, after two review rounds). Arc 5's terminal stage is complete (compile mode #113, Stage 1 #126, Stage 2 #130, Stage 3 #135). Its lane, worktree (../pmacs-vterm-gpu), and branch are done; durable substrate facts live indocs/agent-handoff.mdanddocs/vterm-framing.md. -
Branches deleted 2026-07-22 (authorized):
vterm-stage3-framing(Revision 8 framing; its content is carried onvterm-gpu, verified as a superset before deletion — the branch was NOT an ancestor ofvterm-gpubecause the framing was copied rather than merged, so it needed a forced local delete) andtab-width-parity(a clean ancestor ofmainvia #137). Both removed as worktree + local ref +githubsucksref; theorigintracking refs were pruned. The-framingbranches for each are deliberately kept. -
Tab-width rendering parity — MERGED as #137 (
main@2625ec7, 2026-07-22). One fixed 8-columnTAB_STOP_COLUMNSinpmacs-protocolnow drives core/TUI columns, GPU code projection, and minimap width; source bytes and protocol ranges are unchanged. Its lane, worktree, andtab-width-paritybranch (local +githubsucks) are deleted; thetab-width-parity-framingbranch is kept. This closes the long-standing "tab width is a rendering-parity bug, NOT a config gap" deferral recorded indocs/agent-handoff.md§5. -
Locals-query processing — MERGED as #134 (with handoff #136), and modeline detection handoff #133. Both landed between this lane's base and its canonical-main integration.
-
Config registry — MERGED as #127 (
main@2e37c04). Its lane (config-registry, worktree../pmacs-config-registry) is done; the branch is kept but carries nothing unmerged. Durable substrate facts moved todocs/agent-handoff.md§1 per rule 3 below. -
Both this and Vterm Stage 1 ran as concurrent lanes in sibling worktrees off
main, with the shared files (src/editor.rs,src/lua_bindings/mod.rs,src/lib.rs) assigned to one lane each in advance. The rebase of the second lane onto the first had zero conflicts — worth repeating for future parallel work, along with its precondition: agree the file split before either lane starts, and keep each lane's footprint in the other's files to a single line.
Update protocol
Whenever a listed lane changes materially:
- update its public branch and head/state here;
- record new verification and remove superseded caveats;
- keep durable architecture in
docs/agent-handoff.md, not here; - remove the lane after merge or abandonment;
- verify every recovery command from a clean worktree before calling the transfer complete.