59 KiB
Active work — cross-machine resume ledger
Snapshot: 2026-07-28. 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.
No lane below is retained past its merge. The PTY terminate
diagnostic (#176) was the last one — retained because rule 4 removes a
merged lane only after its durable facts reach
docs/agent-handoff.md, and that absorption was unowned. This snapshot
owns it: #176's facts are now in the handoff (§1's arc bullet and §5's
two ops lessons about ticking observers and proving child exit), so its
lane is gone. The Lean 4, GPU-terminal-input, inline-math (#172), dired
(#169), and terminal config + copy mode lanes were removed the same way
— the last of these was #180's work, folded into #182 so two open PRs
would stop re-conflicting in this file.
Trust the canonical-base line below over any lane header: if a PR
number appears in git log --first-parent githubsucks/main, it has
landed regardless of what a lane says.
Two open PRs had no lane here at all before this snapshot — #174 and
#171. An open PR is exactly the volatile work this file exists to
record, so its absence is a ledger defect rather than a tidy omission:
#171 drifted 153 commits while invisible here, and its still-green
old CI run describes a tree nobody has looked at since. When a PR is
opened, give it a lane. #174 has since merged, so per rule 4 its lane
is gone again and its durable lesson is in docs/agent-handoff.md §5;
#171's lane is below.
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@0442d78(the M4 config-sink race fix #174, atop bottom-panel Stage 2B-1 #184, the Journey/GPU directory-target ratchet #183, Journey Stage 1a #182 and the previously recorded landed work). Protocol schema support isv6..=v21; the production server-firstHellostill advertises v20 — two different facts, and #184 landed only the first. The previous snapshot named7fd646d, and the recovery floor advances with it: the check below now requires0442d78or newer, so a tree at7fd646d— or at6bee09d— no longer passes. That is deliberate — the floor moves with the base, because a check that accepts an older commit than the declared base passes on a tree the rest of this file does not describe. Lanes below that name an older base have not been re-based; derive their integration surface fromgit diff <their base>..main. - 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 0442d78 — the base named above — or a
newer intentional main. Keep this threshold and the canonical-base line in
step: a recovery check that accepts an older commit than the base it
declares canonical will pass on a tree the rest of this file does not
describe.
If it does not, stop and repair the remote/fetch configuration.
The CRDT half of the test corpus is dark in CI — NEEDS A LANE
-
No branch, no framing yet. Found while gating #166, then measured properly during the vterm as-framed audit. Deliberately kept out of #166 so a CI change would not arrive after review approval.
-
Root cause:
.github/workflows/ci.ymlnever enables thecrdtfeature anywhere — zero hits across the workflow directory. Thetestjob runscargo test --all-targets --no-default-features --features luajit|lua54. Every#[cfg(feature = "crdt")]test is therefore not compiled in CI, not merely skipped. -
Measured,
--listunder CI's exact flags versus the same flags pluscrdt: 3,176 vs 3,449 — 273 tests dark. Re-measured at74301d1(2026-07-26; atfe8b8bait read 3,170 vs 3,443, the same 273 dark — #176 added six tests, none of themcrdt-gated). The number moves with every merge and must be re-measured, not quoted. #168 reported 3,024 vs 3,288 — 264 dark, 177 in the library — at1b6a084; #178 then added CRDT-only generated-buffer coverage, and other lanes landed CRDT tests in between. Per target:dark CI full target 185 1,848 2,033 the library itself ( src/lib.rs)21 15 36 m5_5_acceptance13 1 14 gpu_invocation_acceptance13 1 14 gpu_initial_target_acceptance8 0 8 m10_11_acceptance6 0 6 auto_pair_crdt_acceptance6 0 6 m10_2_perf4 5 9 vterm_stage3_acceptance4 0 4 m10_10_perf3 0 3 compile_mode_crdt_acceptance2 22 24 theme_faces_acceptance2 0 2 m11_5_semantic_acceptance1 14 15 terminal_copy_mode_acceptance1 9 10 vterm_stage1_acceptance1 7 8 statusline_segments_acceptance1 10 11 gpu_font_acceptance1 0 1 auto_indent_crdt_acceptance1 0 1 m10_11_perfThe rows sum to 273; the table is the whole census, not its head.
-
The single worst line is the library.
cargo test --lib --features crdtis a REQUIRED local gate inCLAUDE.md, and CI has never run it. 185 library tests — the whole CRDT half — are developer-machine-only, and that count grows with every merged branch that adds acrdt-gated unit test. -
Ten suites run zero or one test in CI, including
gpu_initial_target(#148's entire acceptance, 1/14),gpu_invocation(#141's, 1/14), anda37, the Vterm Stage 3 real-daemon/real-PTY/real-wgpu path that #135 built specifically because "a decoded-message fixture would prove none of the three fit together". -
⚠
a37will report green in the new job without running, unless the job buildspmacs-gpuAND setsPMACS_REQUIRE_GPU=1. Measured 2026-07-26 while gating #173.a37_real_daemon_real_pty_and_headless_gpu_ render_one_terminal_sessionderives its sibling binary path fromCARGO_BIN_EXE_pmacs, and on a missing binary iteprintln!s a skip and returnsok. A fresh worktree runningcargo test --features crdt --test vterm_stage3_acceptancereports 9/9 in 0.17 s having never run it; a real run takes ~4 s. OnlyPMACS_REQUIRE_GPU=1promotes that skip to a failure, andCLAUDE.mdapplies that flag tocargo test -p pmacs-gpu— a different package, so the required local gate does not cover a37 either. Thegpu-renderjob already sets the flag, which is what makes fix-shape part 2 sound; state it as a requirement of that job rather than inheriting it by luck, because acrdtleg added to the plaintestjob would run a37 vacuously. -
a37is also load-sensitive, which changes how to read the expected first-run failures. It passed atd152120and failed at that same commit twenty minutes later, with a second agent saturating the machine withrustcin between; it then failed identically ond152120,04c5ad1, and the #173 merge commit, which is how #173 established the failure was not its own. The signature islast_frame_textall spaces withrendered_nonuniform_framesnonzero — frames arrive, content does not.pmacs-gpu's own suite flaked the same way under the same load (201/202, then 202/202 on immediate rerun). So a red a37 on the first CI run is ambiguous by construction: before treating it as a real failure, run the same command on the merge base, and prefer serialized execution for this suite over retry-until-green. -
Sort deliberate from accidental before proposing a fix. Some of the 264 are perf suites that are
#[ignore]d by default and belong to their own jobs (m10_2_perf6,m10_11_perf1).m10_10_perfhas no#[ignore]and no CI job naming it, so it looks accidental. This classification is not finished and is the lane's first task. -
Fix shape, two parts (the flag combination is verified to work:
--no-default-features --features luajit,crdtlists 10 vterm Stage 1 tests versus 9 without):- a
crdtleg on thetestjob for the non-GPU suites and the library; - the GPU-requiring
crdtsuites onto the existinggpu-renderjob, which already has lavapipe andPMACS_REQUIRE_GPU=1—vterm_stage3_acceptance,gpu_invocation_acceptance,gpu_initial_target_acceptance,gpu_font_acceptance.
- a
-
Expect first-run failures, and budget for them. These would execute in CI for the first time ever: real PTY timing on CI runners, wgpu under lavapipe, and daemon-socket tests at unfamiliar concurrency. Start ubuntu-only and decide about macOS from evidence. A red first run is the lane working, not the lane failing.
-
Mitigating fact, verified rather than assumed: #166's three unit pins are not
crdt-gated and do run under CI's exact flags, including the controller-release pin whose only job is catching the plausible wrong fix. -
This lane also owns a
--lib --features crdtflake, observed and scoped without overclaiming its cause (inherited from #178's gating, where the terminal lane recorded it).cargo test --lib --features crdtfailed ~1 run in 5 onprocess::tests::setsid_escapee_is_not_reaped_and_teardown_reclaims_readers—active_reader_probereturningNoneatprocess.rs:3179("live runtime probe"). Pre-existing and unrelated to #178: that branch did not touchsrc/process.rsat all, and the test passed 10/10 standalone; the observed failures were during parallel full-suite runs. That localizes the trigger to suite load or interaction, but does not distinguish parallelism from another full-suite effect — no serial full-suite bite was run. The leading code-path explanation is the knowndrain_untiltrap: draining forStartedalso ticks, and a tick can reap the leader before the followingactive_reader_probe. That is an inference from the failure site and control flow, not yet a falsified root cause. Discriminating it belongs here. Two unnamed CRDT failures in #178's round-2 gating are a plausible match but remain unattributed — no test names were captured. -
A second standing obstacle for this lane:
cargo clippy --workspace --all-targets --features crdt -- -D warningsfails onmain— measured at74301d1: seven errors before the build aborts, four insrc/daemon.rs(useless_conversionat 3996, missing doc backticks at 4076,too_many_lines112/100 at 4083, an unneededmutat 4965) and three intests/vterm_stage3_acceptance.rs(too_many_linesat 637 and 793, a redundantcontinueat 843). Treat that as a lower bound, not an inventory: Clippy abandons the remaining targets once one fails, and a run on an older tree surfaced a further doc-backticks error intests/auto_indent_crdt_acceptance.rs:42that this run never reached. The standing gate list runs Clippy withoutcrdt, so these lints have never been enforced. Any CI job that compiles thecrdttargets has to fix them first or it will be red on arrival.
Bottom-panel lane (Arc 7) — 2B-1 MERGED; 2B-2 IN FLIGHT
Stage 1, the Stage 2 framing, Stage 2A, and Stage 2B-1 are all on
main. Framing revision 5's three-way split of 2B was explicitly
approved on 2026-07-27; revision 6 records PR #184's review correction.
2B-2 — the daemon panel projection and epoch machine — is under way
on branch bottom-panel-stage2b2, worktree ../pmacs-bp-stage2b2,
branched fresh from githubsucks/main @ 6bee09d. It is not stacked
on the 2B-1 branch, which is the rule for every slice in this arc. Note
that main has since advanced to 0442d78; the only difference is the
test-only #174, so the slice's integration surface is unchanged.
2B-2's boundaries, restated because they are easy to overrun: the
production Hello stays at v20 and panel_capable stays false. The
slice is dark/test-only capability exactly as 2B-1 was. Compatible v21
activation, the GPU band, and the negotiated capability flip are all
2B-3's, and 2B-3 may not simply change the unsolicited Hello to
21.
-
Stage 2B-1 MERGED as #184 (
main@6bee09d, 2026-07-28; all twelve checks green on the reviewed head5539b6e; two review rounds plus a gate-found follow-up). Branchgithubsucks/bottom-panel-stage2band worktree../pmacs-bp-stage2bare retained and carry nothing unmerged. Durable facts — the schema-support-versus-advertisement split, the sharedwire_gridboundary, authoritativeAbsent, and the two epochs — are indocs/agent-handoff.md§1 per rule 3, not here. -
What 2B-1 deliberately did not do, because 2B-2 and 2B-3 must not re-litigate it: no producer, no consumer, no capability change.
panel_capableis stillfalsefor every semantic session, so the journey grade is unchanged and every shipped v20 client remains attachable. -
2B-3 inherits a hard constraint from 2B-1's review: it owns a compatibility-preserving v21 activation mechanism and may not simply change the unsolicited
Helloto 21. The handshake is server-first, so that one-line change locks out every shipped v20 frontend before it can even send anAttachRequest. -
Two review rounds, and what each cost. Round 1:
PanelFrameneeded an explicitbuffer_id, the transport ratchet had to drive the real attach path rather than a detached codec assertion, and shared grid bounds needed one validator. Round 2: the server-firstHellomade the advertised v20↔v21 compatibility one-way;COHERENCE.mdand the handoff still named only v20 schema support; framing §9 named a nonexistent aggregate 2B suite instead of the three exact slice suites; and the panel plus copied-terminal "one byte over" fixtures were actually two bytes over. -
The full gate — not review — found two version-ladder omissions and one probe contradiction. The statusline and Vterm Stage 3 ladders still pinned v20 and rejected v21. Separately, Vterm Stage 3's headless probe exited its loop as soon as resize plus two nonuniform composites were observed, while its acceptance later required the PTY child's
VTERMROWoutput in the final frame; the v20-compatible handshake made that scheduling race deterministic, so the report sampled a blank frame. The probe now waits for the exact child-output observation its acceptance asserts. -
The probe fix then leaked its own fixture, and that is the reusable lesson. The generic runner hard-coded the producer fixture's
VTERMROWbreadcrumb, so the CAT input fixture could satisfy every assertion yet never satisfy the loop exit — it waited out the 20-second safety deadline and passed on the deadline. Producer probes now name their own required frame text while input probes finish on the latched echo, and the report exposescompletion_observedwhich both paths assert, so a deadline-driven pass cannot hide a stall again. -
Final verification on the reviewed head:
cargo fmt --check; strict workspace Clippy; library 1,849 passed + 3 ignored default and 2,034 passed + 4 ignored CRDT; bottom-panel Stage 1 / 2A / 2B-1 46 / 17 / 16; folding Stage 2 48; GPU font 11; statusline 8 CRDT; m11_5 semantic 2 CRDT; GPU initial target and invocation 15 / 15 CRDT; the handshake consumers m5_5 / m5_7 / mode-system wiring 36 / 7 / 1 CRDT; Vterm Stages 1 / 2 / 3 10 / 6 / 9 CRDT, including the required real daemon + real PTY + real wgpu probe; M4 121 passed + 3 ignored + 1 filtered; required GPU 202/202; the isolated-config one-invocation full workspace sweep; andgit diff --check.- Retained as classification rather than erased: the first
required-GPU pass was 201/202 on
a_fraction_draws_rule_pixels_between_its_operand_rows, a rendering test structurally outside a protocol-only diff, which passed immediately in single-threaded isolation and 202/202 on the mandatory complete rerun. Separately, library and Vterm attempts inside the restricted tool sandbox producedOperation not permittedfailures in socket-based attach tests; the authoritative outside-sandbox reruns passed.
- Retained as classification rather than erased: the first
required-GPU pass was 201/202 on
-
Ordering for the rest of the arc is fixed: 2B-2 branches from landed
main; 2B-3 branches only after 2B-2 lands; Stage 3 (the adopter default flip) last. Each slice starts fresh from landed main. -
Stage 2A MERGED as #177 (
main@0a3fcd1, 2026-07-26, all twelve checks green at8424172, three review rounds). Branchgithubsucks/bottom-panel-stage2aand worktree../pmacs-bp-stage2aare retained and carry nothing unmerged. Five commits: the classified census routing, the painter extraction + acceptance, the lane record, then the round-1, round-2 and round-3 review fixes. No protocol change; no behavior change for any frontend today — withpanel_capable = falsefor semantic sessions,primary_document_windowreturnsview.activein every existing configuration, so this is seam adoption that becomes load-bearing in 2B. -
Stage 2A verification on its merge result:
cargo fmt --checkclean; strict workspace Clippy clean; 1,832 default + 2,015 CRDT library tests;bottom_panel_stage2a_acceptance17; bottom-panel Stage 1 46; statusline segments 8 CRDT; m11_5 semantic 2 CRDT; GPU initial target 14 CRDT; terminal config 12 CRDT; vterm Stage 1/2 10 / 6; folding Stage 2 48; M4 121; required GPU 202;git diff --checkclean. -
Every routed producer is now pinned at a seam its production caller uses, and each pin was falsified by revert: #1 follow, #2 lazy CRDT upgrade, #3
CursorByte, #5 decorations, #7Viewport(aligns without focusing), #8Pointer(aligns and focuses), #9 the terminal-context gate, #12 statusline, #21 the publication filter, plus the focus-class negatives. #1/#3/#21 required extracting three named helpers, because their only production caller isdispatcher_loop, which no test can drive. -
Three lessons about the TESTS, not the code, all from review: (a) a structural test comparing the two authorities directly does not catch a misrouted consumer — only consumer-level assertions do; (b) a daemon-path test must
register_sessionor the event is dropped at the uninstalled-session check before reaching the code under test; (c) a discriminating fixture must make the two routings DISAGREE — comparing two non-terminal buffers, or two windows with no selection, yields the same answer either way and proves nothing. Round 2 found four of my own pins vacuous by exactly these shapes, and round 3 found two more problems of the same family: a pin placed at a HELPER while production called it from a producer (reverting only the producer's call site left every test green), and a socket-pair assertion whose blocking read made a regression HANG instead of fail. Both now assert at the producer, with read timeouts on every read. -
Review round 1 closed: 4 P1 + 2 P2, all real. The P1s were a stale-
Pointerfocus steal (the failed-alignment arm returned the window, so #8's activation focused it beforedispatch_pointerrejected the buffer), the missing A2A-2 two-context fan-out, a census suite that asserted the AUTHORITY rather than the CONSUMERS, and the missing main integration. Two of the new pins were themselves vacuous on the first attempt — the dispatcher test passed because an unregistered session is dropped atdaemon.rs:1962before reaching the aligner, and the painter test was a fixed-point check that survived deletingtext_view.render. Both now fail under their own bite. -
vterm_stage3_acceptance::a37is a pre-existing flake here, not a Stage 2A regression: measured 6/8 failures on the base commit and 7/8 on the branch in matched isolated samples. It needs a real daemon + real PTY + headless GPU and is documented load-sensitive. It also silently returnsokunlesspmacs-gpuhas been built, and iscrdt-gated so CI never runs it at all. -
Two suites are dark without
--features crdt:m11_5_semantic_acceptancereports 0 tests andgpu_initial_target_acceptancereports 1 in the default config. Both are semantic-census suites, so Stage 2A must be gated with the feature on or its most relevant coverage never executes. -
Stage 1 merged as #155 (
main@e745068, 2026-07-24, after two review rounds). No protocol change. Durable substrate facts live indocs/agent-handoff.md§1; the two round lessons are in §5. -
Landed-docs follow-up merged as #156 (
main@d152120, 2026-07-25). -
Stage 2 framing:
docs/bottom-panel-stage2-framing.mdrevision 6 is on branchgithubsucks/bottom-panel-stage2b(revision 5 is commit56301edthere), worktree../pmacs-bp-stage2b. Revisions 1–4 remain ongithubsucks/bottom-panel-stage2-framing(head4fbd47f, four framing commits, revision 4 at49757e5). Round 1 closed 2 blocking + 3 high; round 2 closed 1 blocking + 2 high + 1 medium and decided both open items; round 3 closed 1 blocking + 1 high + 1 medium. No open items remain. Revision 5 adds no decision; it records the approved 2B-1/2B-2/2B-3 implementation split. Revision 6 corrects the server-first compatibility contract, durable protocol claims, exact acceptance-suite names, andlimit + 1fixture. The parent framingdocs/bottom-panel-framing.md(rev 4) remains authoritative, including its acceptance criteria 37–55. -
Retained, carrying nothing unmerged: branch
bottom-paneland worktree../pmacs-bottom-panel. -
Stage 2 ships as four serial implementation slices, each landing before the next branches: 2A = classified §1.3 census routing +
paint_frameper-window painter extraction (with the active-window auto-scroll preparation), no protocol change; 2B-1 = reserved protocol schema v21, with production advertisement held at v20, (InstanceMessage::PanelFrameplusFrontendEvent::{FrontendCellGeometry, PanelResizeRows, PanelPointer}, gated both directions, each extended enum byte-pinned on its own previous final variant); 2B-2 = daemon panel projection and epoch machine; 2B-3 = compatible v21 activation, the GPU band, and the negotiatedpanel_capableflip. Stage 3 is the adopter default flip. -
Correction — this entry previously mis-stated the census contract. It is not "route every consumer through
primary_document_window". Q#BP14 classifies the 23 reads into four classes and routes only the Projection class that way; focus/input (#13–#15, #23), focus chrome and surface-routed (#16–#19), and focus/session (#20) keep their own authorities. Rerouting them would break remote-op validation and application,DispatchIdle, presence, focused search/menu/completion routing, and terminal bell ownership. The Stage 2 framing carries the full table. -
The GPU document bottom is three boundaries, not one.
text_area_bottom(pmacs-gpu/src/main.rs:8490) is todaystatus_band_top,geometry_capacity_bottom, anddocument_text_bottomat once. Once a band is installed they diverge: the status chrome must stay pixel-identical at the physical window bottom while document consumers move. A blanket rewrite of that helper moves both together and passes an "everything moved" assertion, so the Stage 2 criterion asserts both directions in one scenario. The census is 20 production sites (8 status-owned, 12 document-owned) + 1 definition + 8 test sites = 29 matches; the framing carries the per-site table. The three easiest to misclassify are document completion:6140, minibuffer candidates:7351, and edge scrolling:8561— each with its own visible symptom. -
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
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).
dired Stage 2 framing lane — PR #171 AT REVISION 9, AWAITING APPROVAL
-
Portable branch:
githubsucks/dired-stage2-framing; worktree../pmacs-dired-stage1. PR #171, basemain, integrated up to canonicalmain@7586905. Framing only —docs/dired-stage2-framing.md, 3,844 lines measured at this revision, no runtime code. This lane rides that PR — it has since rev 6, when #185 merged; do not open a standalone ledger PR. -
Status: PROPOSED, never approved. Revision 5 was reviewed and NOT approved (six findings, four P1); revision 6 answered them; revision 7 resolved a cross-lane conflict with PR #186; revision 8 was reviewed and NOT approved (six findings, four P1); revision 9 answers them. The framing is at revision 9 after seven review rounds; that is not the same as approval, and it must not be read as one.
-
Round 8 (rev 9) made every rev-8 correction singular and executable:
- Q#DR26 no longer retains the withdrawn execution-order claim; it owns only the homogeneous enum/vector shape and delegates sequence semantics to Q#DR29: bus arrival, not filesystem execution.
- 2a owns acceptance 23–38 and 50–55, including every suffixed item, so rev 8's new tests cannot fall between implementation PRs.
- The LSP gate is one manager-owned, generation-scoped
HashSet<(LspServerId, String)>. Bothabsorb_publish_diagnosticsand the now-server-awaremark_document_stalecheck the exact pair;did_openclears one pair, and server teardown clears all and only that server's pairs. It is reclaimed, not falsely called size-bounded. Acceptance covers both writers and both reclamation paths. - Buffer-name following now uses explicit
BufferNameOrigin::{Explicit, PathDerived}state. The acceptance collision is a file${cwd}/notesexplicitly namednotes, which defeats rev 8's path-equivalence heuristic.
-
Round 7 (rev 8) found five contract defects of ONE family, and the family is the transferable lesson: a guarantee assumed from a mechanism whose implementation was never read. Reply order assumed to be execution order; a refusal assumed to be inert;
forget_uriassumed to cover every writer. Three of the five were literally that. The decisions:- Reply order is NOT execution order.
AsyncRuntime::tick(src/async_runtime.rs:1003) iswhile let Ok(env) = self.main.try_recv()— a bus drain with no execution token, so a worker can finish, be descheduled before sending, and have a later reply arrive first. The ordering guarantee was withdrawn, not engineered: reconciliation is order-independent, andfs.lua:155-165already recommends serialization. Rev 9 makes the public contract exact: overlapping mutations dispatch serially, awaiting one before the next. A lock was rejected — it would serialize every fs mutation for a hazard absent from shipped in-tree callers. EditorCore::kill_bufferdamages editor state BEFORE it can refuse. It clearsround_trip_buffers, closes side windows and redirects ordinary windows, and only then canBufferRegistry::removereturnConcurrentEdit. So "refused" is not "nothing happened". Fixed by preflightingediting_in_progress— sound because phase 1 is pureEditorCoreand holds no Lua handle, so nothing can re-enter between check and removal.- Buffer names are set from
path.display()AS GIVEN while onlyfile_pathis normalized (src/editor_core.rs:917,src/lua_bindings/mod.rs:3112), so a relative open leaves a short name that a string-equality rule mistakes for user-chosen. Rev 8 moved to path-equivalence; rev 9 replaces that inference with explicit provenance because a custom name can equal the path. diag_storehas ZERO correlated writers, andDiagnosticStore.by_uriis keyed by URI alone with no server component (src/diag.rs:198) — so the store most needing protection is the one apending_routespurge cannot help. Rev 8 incorrectly inferred that the tombstone must share the store's URI-only key; rev 9 gates inLspManager, wheresidis still available.- A first pass of rev 8 checked only
handle_notificationand concludedpublishDiagnosticswas the only uncorrelated writer. Wrong lens boundary —pub fn mark_document_stale(src/lsp.rs:3108) takes noLspServerIdand creates URI keys across three stores for every server. Rev 9 changes that surface and its private Lua binding to(sid, uri)and pins it independently.
- Reply order is NOT execution order.
-
Both new hooks are
all-must-succeed, not short-circuit — a gap the review did not raise and the sweep found.src/hook.rsdefines threeHookKinds; registered short-circuit, oneresource.renamedsubscriber returning falsey would silently stop every later subscriber reconciling. -
CROSS-LANE SPLIT WITH PR #186 — settled, recorded verbatim so the two lanes cannot diverge again:
#186 owns the urgent pre-filesystem refusal for synchronous
apply_resource_op. #171 later owns full post-delete lifecycle reconciliation, including the async race where a buffer becomes modified after dired dispatch. #171's revision 7 adopts the refusal and stops saying LSP intentionally deletes modified files.PR #186 (
resource-op-delete-guard) owns the pre-filesystem refusal. Do not implement a competing dirty check from #171. Rev 7 adopts #186's Q#RD1 (refuse, do not prompt, do not save, do not back up) and its Q#RD6 shared query (scan every path-bound buffer, not first-match; normalize once; component-awarePath::starts_with); whichever lane lands first owns that query and the other adopts it. The two fit deliberately: #186's Q#RD5 keeps reconciliation exact-path so it does not promote the dangling-window and last-buffer defects tree-wide, and #171's Q#DR27 is the lane that makes widening safe. -
What rev 7 removed. Rev 6 said an LSP-authored delete removes the file and orphans the modified buffer as accepted residue. That is withdrawn — 20 matched lines across 13 passages were censused and reclassified: 4 ownership claims reassigned to #186, 4 policy claims deleted, 3 ground-truth statements kept but attributed, 2 async-race claims kept and narrowed.
-
What rev 7 kept, and it is now one path not two. The async race — a buffer modified between dired's pre-dispatch check and
remove_blocking's syscall — survives #186's refusal, because dired never goes throughapply_resource_op: it callspmacs.fs.remove, which dispatches a worker no in-applier guard can see. Rev 6 carried this as "two paths, LSP and dired"; after #186 the LSP path has no residue at all. -
A gap neither lane closes, named in rev 7 §11:
pmacs.fs.removeis public Lua API with no dirty check of its own. After both lanes land the guards sit one layer above it on each side — atapply_resource_op(#186) and in dired's policy layer (#171) — so a third caller inherits neither. Latent, not live: it has zero production callers today. -
LSP failure-handling, verified against the 3.18 spec because #186 got it wrong and this lane was checked for the same error. This lane never made the claim — it asserts nothing about
WorkspaceEditrecovery. For the record: onlytextOnlyTransactionaldegrades to abort for resource operations;transactionalcovers them; and the specification states no default for a client that does not advertisefailureHandling(pmacs advertises none). So "Abort applies by omission" does not follow. -
Round 5's single theme, worth carrying because it will recur: rev 5 changed the slice split and the ownership of a decision, and the prose did not follow. Four of the six findings were that same defect in different places — stale slice labels, an acceptance allocation that contradicted the code split, and a PR body still describing revision 1's two-slice plan. When a cut changes, sweep for the label, do not patch the instance. Rev 6's own sweep found four more the review had not cited, one of which was wrong in both halves (
x's report tagged 2a,remove_dir_alltagged 2b). -
Rev 4 was scouted at
c8ec8f3, which is dired Stage 1's own merge commit (#165) — so dired Stage 1 and find-file (#162) were always its base, not new arrivals. Eighteen PRs landed on top before the re-scout. Rev 5 is that re-scout, against6bee09dand then integrated forward. -
Seven of rev 4's own claims about pmacs were wrong, which is the lane's most transferable lesson: a framing can verify all its external facts and still be wrong about the codebase it is for. The load-bearing one: §5 named
drain_external_cancelledbut citedlsp.rs:1596. Those are two different functions 35 lines apart —drain_external_cancelled(:1561) is the unconditional server-scoped drain and is the right precedent;drain_cancelled_externals(:1596) is a per-tick token/timeout sweep. A rename flips no token, so following the line number yields aforget_uriwhose drain half is a silent no-op and the awaiting coroutine hangs forever — exactly the failure that step exists to prevent. -
The rename path-owner census is SIX, not five. The sixth is
lean.lua'sM.file_progress, a URI-keyed Lua module table living in no Rust store, soforget_uristructurally cannot reach it. That is the first case outside dired proving the hook, not the Rust method, is the mechanism that scales. The other five: buffer path, buffer name, the URI-keyed LSP stores plusDiagnosticView(URI set once at construction), dired's pathless handles, and a captured Lua local no transaction can reach. -
The journey ratchet is split across two files, and both are gates:
tests/journey_acceptance.rs(24 tests, "stages add rows, none removes them", seven rows asserting on dired) andgpu_invocation_acceptance.rs, where #183 actually put the GPU row. A scout that checks only the first will think #183 added nothing. -
open_directorynow commits underpmacs.window.commit_to, whose scope REFUSES anawait(#182). That directly constrains the serialize-and-await batch contract the framing proposes. -
The typed-edit chain does not reach dired — verified rather than assumed. Its lessons bind the framing's two new hooks instead.
-
Scope MOVED OUT of this lane, and rev 6 records the handoff. Rev 5 added Q#DR25 to adopt
set_generated_contentsat the head of 2b, because dired's listing is a generated buffer whose paint bypasses an intercept over a still writable rope. That turned out to be a class bug, not dired's — the same idiom is in listview, compile and search/grep, and no Lua caller anywhere setsread_onlybecause there is no Luaset_read_onlyto call — so it is owned by the generated-buffer immutability lane. Done: rev 6 withdraws Q#DR25, and §3.1 hands that lane what this one's re-scout found, including the trap thatdired_buffer_is_read_only_and_round_trips_inputpasses either way and is therefore not coverage of the adoption. Do not implement Q#DR25 from this document. -
Two substrate facts rev 6 added that outlive this lane. (1) Buffer removal is two phases and no existing Rust path composes them:
EditorCore::kill_buffer(src/editor_core.rs:4590) does windows and registry,after_buffer_removed(src/lua_bindings/mod.rs:1602) does keymaps/config/folds/on_removed, and only the Lua bindingpmacs.buffer.kill(mod.rs:5476-5491) runs both — whileapply_resource_op's delete usesremove_buffer_and_fire, which skips phase 1, so a window displaying the deleted buffer keeps a removed id. That is a third defect on that arm beside the missing dirty check and the first-match lookup. (2) The drain outcome must carry deletions as well as renames in oneVec<ResourceOp>so every kind is expressible, but its sequence is bus-arrival order, not filesystem execution order; Q#DR29 requires callers with interdependent mutations to serialize. -
Intended serial implementation once approved: 2a rename/delete reconciliation substrate with no dired UI, 2b marks and operations, 2c mkdir/copy/recursive-delete primitives, then Stage 3 wdired. The re-scout re-examined this cut and it holds unchanged.
-
Ownership warning: dired 2a overlaps
src/editor_core.rs,builtin/runtime/lsp.lua, and the URI-keyed LSP state with other coherence work. Do not run it concurrently with Journey Stage 1b without assigning those files to one lane first. -
Two live bugs on
mainthis lane confirmed but does NOT fix, both now owned elsewhere:apply_resource_op's delete arm destroys unsaved work (no dirty check anywhere on the path), andViewstill lacksrename_resource.
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 — STALE, AND ITS DISPOSITION IS UNDECIDED
Measured 2026-07-28, not inferred:
githubsucks/handoff-2026-07-20is atc11d7e7, 1 commit ahead ofmainand 320 behind. Its whole diff againstmainis four documentation files (docs/active-work.md,docs/agent-handoff.md,docs/roadmap-2026-07.md,docs/vterm-framing.md), every one of which has been rewritten repeatedly since by the landed-doc PRs #156/#168/#169/#172/#180. Rule 4 removes a lane on merge or abandonment, and this one looks abandoned in substance — but "looks abandoned" is not the same as a decision, and no PR was ever opened for it. This snapshot deliberately annotates rather than deletes: whoever confirms the branch carries nothing unique removes the section. The bullets below are its original claims, preserved as written and now unverified.
- 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
-
Terminal configuration + copy mode arc — BOTH STAGES MERGED, lane removed. Stage 1 #173 (
main@cf54270, one review round) and Stage 2 #178 (main@fe8b8ba, four review rounds, twelve checks green on head1b44c69— verified byhead_sha, not by the check summary), both 2026-07-26, both with no protocol change. Approved framing:docs/terminal-config-and-copy-mode-framing.mdrev 4, committed as the first commit of Stage 1's branch; its Q#TC6a carries a superseded-in-part box rather than a silent rewrite. Durable facts moved todocs/agent-handoff.md§1 (the arc bullet) and §4 (theset_generated_contentsinvariant) per rule 3 below, and toCOHERENCE.md§14. Stage 2 ships eight of nine criteria and the missing one is named — criterion 17 needs a real GPU frontend, so it waits on thea37footing; the handoff records what it must assert. Branchesgithubsucks/terminal-configandgithubsucks/terminal-copy-modewith worktrees../pmacs-terminal-configand../pmacs-terminal-copy-modeare retained. The gate-run flake found while gating #178 moved to the CIcrdt-coverage lane above, which owns its discrimination. -
Dired Stage 1 (the directory view) — MERGED as #165 (
main@c8ec8f3, 2026-07-25, after one review round). pmacs has a directory surface:C-x d/C-x C-j, one read-only buffer per directory named*dired:<canonical path>*, adiredmajor mode carryingRET/f,^,n/p,g,q,s. No wire change (v20). The Rust is two things — a per-entry-tolerantread_dir(Q#DR6), which had to be Rust becauseread_dir_blockingfails a whole listing on any of five per-entry conditions and a tolerant wrapper cannot be written in Lua at all, andnormalize_buffer_pathgoingpubaspmacs.path.canonicalize(Q#DR2's preferred end state, so no Lua mirror exists and Stage 2 owes no mirror removal). The frozen m8_1/m8_2/m8_3 counts are unchanged, which is the additivity gate. 15 claims bite-verified; one came back VACUOUS (acceptance 3c cannot pin descent routing — dired holds focus in its own panel, so dedication is the only discriminator) and is documented at the assertion rather than relabelled. Its branch (dired-stage1) and worktree (../pmacs-dired-stage1) are done; the abandoneddiredbranch (ffdd642,../pmacs-dired-arc) was superseded by a fresh cut and carries nothing unmerged. Stage 2 (marks and operations) and Stage 3 (wdired) each still need their own framing, and the frozen fixture shrinks after Stage 3. Durable substrate facts and both new ops lessons live indocs/agent-handoff.md§§1/5; the implementation notes aredocs/dired-framing.md§0, S1-1…S1-12. Two named forward items for Stage 2:apply_resource_op's rename rebind is exact-PathBuf-equality, first-match-only, looked up with the raw path while stored paths are normalized — so a directory rename strands every buffer under it, andpmacs.fs.renamehas zero production callers, so it can be fixed at the primitive; and Q#DR5's seam is the main-thread drainAsyncRuntime::tick, not_take_result, where rename settles as an undifferentiatedReplyKind::FsUnitand so must be keyed onJobKind::FsRename. -
GPU terminal input (the double terminal-layout sync) — MERGED as #166 (
main@b889873, 2026-07-25, one review round, all twelve checks green after a macOS PTY-timing rerun). The dispatcher applied both terminal-layout syncs to every attached frontend; a semantic session satisfies both conditions, so its PTY was resized twice per tick forever and the child took aSIGWINCHstorm that made a GPU terminal untypable while output still flowed.sync_terminal_layoutis now split into a frontend-kind-neutral half (panel reconcile + controller liveness) and a grid-only geometry half, with the loop body extracted tosync_terminal_layouts_for_tickso the exclusivity is structural. No protocol change (v20). Durable lessons are indocs/agent-handoff.md§5; the framing (docs/gpu-terminal-input-framing.mdrev 2) carries three falsified hypotheses, the two-pre-image bite matrix, and two named out-of-scope items (Q#GT5 interactive-shell echo on a raw PTY, which reproduces in-process and so is not the GUI/TUI asymmetry; and a geometry change appearing to clear the visible screen, which reproduces pre-fix). Branchgpu-terminal-inputand worktree../pmacs-gui-term-inputretained. Its landed-doc pair MERGED as #168 (main@1b6a084, 2026-07-26): #166 recorded as landed, the CIcrdt-coverage gap measured (264 tests dark workspace-wide, 177 in the library — a reading taken at1b6a084and kept here only as history. The CIcrdt-coverage lane above is the authority for the live figure; do not quote this one forward), the vterm audit corrected — "only 3 of 9 acceptances drive a real daemon" was optimistic; without the frontend binary the honest number is 2 — and the a37 findings folded into the coverage lane. -
Inline-math slice — MERGED as #158 (
main@5aa9044, 2026-07-25). Detect → parse → layout → draw for$…$, entirely insidepmacs-gpu, no protocol change. Verified by the user's manual pass on a real paper after the landing. What is worth carrying forward:- The v0 subset is 34 Greek symbols, sub/superscript, and
\frac. An unsupported command fails the whole span back to source, so on a real document most inline spans still show LaTeX. Widening the symbol map is the highest-value next increment — ahead of display math, which is also deferred. - A stale frontend binary is invisible from the source tree. The
slice lives only in
pmacs-gpu, so after it merged the feature was absent untilcargo build --release -p pmacs-gpuand a client restart; the daemon needs neither. Diagnose withstringson the binary (Latin Modern Math,MathBox) rather than by re-reading the checkout, which was already current. - Main was integrated three times in one day (
8c86d34,46a1b8f,b889873), merged not rebased to preserve review anchors. Two conflicts, both this ledger and nothing else. The dangerous case was the one that did NOT conflict: #166 auto-merged intopmacs-gpu/src/main.rs, the file this lane rewrites, because the two edits sat in different regions of it. Decide whether to integrate from the shared-file set, never from whether git complained. - Integration was proved by test-count reconciliation, not by a
green run: predict what the other side adds, then check the deltas.
GPU 199→202 matched
e547a90's 3; later lib 1,826→1,829 and CRDT 2,003→2,006 matched #166's 3, with GPU unchanged because #166 adds none. Suites 91→92 was #161's new binary. - Why the branch had no CI for a day: a conflicting PR builds no
merge ref, so no
pull_requestrun is created. The ledger previously recorded this cause as unidentified; it is not. Checkmergeableand confirm a run exists for the current head SHA. m4_5_basedpyrighthas no timeout and hangs forever, parking a--workspacesweep (observed 2h26m at 38 of 92 suites). It is intermittent, so an earlier clean sweep proves nothing. Sweep withcargo test --workspace --no-fail-fast -- --skip basedpyrightand judge progress by whether the suite count advances.- Named v0 approximations: the peer-caret half of acceptance 14 is
pinned at the mapping level, not pixels; a soft-wrapped spacer draws
its box whole at the first run's origin; the fit budget reads the
bundled code face even under a custom
set_fontfamily.
- The v0 subset is 34 Greek symbols, sub/superscript, and
-
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 adopterdisplayopt-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 2still reached plainsplit_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_acceptance30/30 → 25/5). Fixed in9110f9fbefore merge. - Gating fact found on the way: the workspace sweep must run with an
isolated
XDG_CONFIG_HOME, because the real userinit.luainstalls a local package and the losing race leaks a status message into painted-frame comparisons. There is also a latent pre-existingmainbug in the buffer CRDT undo path, unrelated to this arc. compile_mode_acceptanceis load-sensitive under default parallelism (~1 run in 3, a different test each time); verified pre-existing by swapping inmain'scompile.lua. It is 67/67 at--test-threads=1.
- Round 1: the Q#BP6 side-window split guard had no production
caller —
-
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.