merge: integrate main @ 6c9e765, and absorb three landed lanes

Merges `main` (PRs #186 and #171) into the 2B-2 branch — a clean
auto-merge, no conflict — and carries the ledger absorption those two
merges left owed.

`main` currently describes three lanes wrongly. The #186 lane still
says "OPEN, PROPOSED, DO NOT MERGE" and the #171 lane still says
"AT REVISION 9, AWAITING APPROVAL", because each PR merged carrying its
own pre-merge lane text; #187's lane says "OPEN" for the same reason
this commit exists. Rule 4 forbids relabelling a landed lane and allows
removing it only once its durable facts reach `docs/agent-handoff.md`,
so no earlier commit could legally fix any of them. This one does both
halves together.

Handoff §1 gains three arc bullets and an advanced anchor (`7586905` ->
`6c9e765`):

  * bottom-panel Stage 2B-2, recording what the review round taught
    rather than what the feature does — a durable transition
    implemented as a per-frame effect is a bug shape and four of five
    findings were that one shape; a producer should be checked against
    the other frontend kind's equivalent; a panel may legitimately be
    wider than a PTY; and mutation testing cannot reach behaviour never
    modelled, which is why 16/16 passed while five real defects stood;
  * dired Stage 2 (#171) as an approved framing with no implementation,
    carrying the order-independence decision and the reason a token was
    rejected rather than built, the already-destructive refused kill,
    path-derived buffer names, and the uncorrelated URI-store writers;
  * the resource-op delete guard (#186) likewise, carrying the
    four-phase ordering, why `find_buffer_for_path` is the wrong
    lookup, that pmacs advertises no `workspace.workspaceEdit`
    capability at all, and that `pmacs.fs.remove` is guarded by
    neither lane.

The ledger then drops both framing lanes (335 lines) and rewrites the
bottom-panel lane to the remaining 2B-3 plan rather than deleting it,
since 2B-3 and Stage 3 are still ahead. Two stale claims elsewhere in
the file are corrected as a consequence: the intro's "#171's lane is
below", and the recovery block's instruction not to start 2B-3 until
#187 lands.

#188 deliberately gets no lane here. Its lane lives on its own branch
and arrives with it — writing one here for work that lands elsewhere is
what makes this file re-conflict on every merge.

No runtime code. No protocol change.
This commit is contained in:
Levi Neuwirth 2026-07-28 22:33:12 -04:00
commit 7c1563c2c2
4 changed files with 5636 additions and 83 deletions

View File

@ -5,11 +5,26 @@ 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
**No lane below is retained past its merge.** This snapshot removes the
resource-op delete guard (#186) and dired Stage 2 framing (#171) lanes
the moment their PRs merged, because the same commit put their
load-bearing decisions into `docs/agent-handoff.md` §1 — rule 4's
precondition, satisfied deliberately rather than deferred. The
bottom-panel lane is not removed: 2B-2 landing leaves 2B-3 and Stage 3
ahead of it, so the lane is rewritten to the remaining plan.
**One open PR has no lane here: #188**, the generated-buffer
immutability framing. Its lane lives on its own branch and arrives with
it, which is deliberate — with several PRs open, a lane written here for
work that lands elsewhere re-conflicts on every merge.
The PTY terminate
diagnostic (#176) was the last lane retained past its merge — 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
`docs/agent-handoff.md`, and that absorption was unowned. The
2026-07-28 snapshot
owned 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
@ -20,14 +35,15 @@ would stop re-conflicting in this file.
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.
**Two open PRs had no lane here at all before the 2026-07-28
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 described a tree nobody had looked at since.
**When a PR is opened, give it a lane.** All three have since merged —
#174, #171 and #186 — so per rule 4 their lanes are gone again and
their durable facts are in `docs/agent-handoff.md` (§5 for #174's
lesson, §1 for the two framings).
## Repository authority
@ -229,18 +245,16 @@ If it does not, stop and repair the remote/fetch configuration.
never been enforced. Any CI job that compiles the `crdt` targets has to
fix them first or it will be red on arrival.
## Bottom-panel lane (Arc 7) — 2B-1 MERGED; 2B-2 PR #187 OPEN
## Bottom-panel lane (Arc 7) — 2B-2 MERGED; 2B-3 IS NEXT
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 open as
[PR #187](https://github.com/levineuwirth/pmacs/pull/187)** on branch
`bottom-panel-stage2b2`, worktree `../pmacs-bp-stage2b2`. It branched
fresh from landed 2B-1 rather than stacking on its feature branch, and
has since integrated landed main through `7586905` (#174, #185, and
#189). The exact pre-round-2 reviewed head was merge checkpoint
`0dba358`; the round-2 code-and-test checkpoint is `bfaaf2b`.
Stage 1, the Stage 2 framing, Stage 2A, Stage 2B-1, and **Stage 2B-2 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 —
landed as [PR #187](https://github.com/levineuwirth/pmacs/pull/187)**,
one review round of five findings on top of the implementation, 12/12
green, 22/22 mutations biting. Its durable lessons are in
`docs/agent-handoff.md` §1; what remains below is the 2B-3 plan.
**2B-2's boundaries, restated because they are easy to overrun:** the
production `Hello` stays at v20 and `panel_capable` stays `false`. The
@ -249,7 +263,7 @@ 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.
- **What PR #187 ships, dark by construction:** the semantic daemon's
- **What PR #187 shipped, dark by construction:** the semantic daemon's
`FrontendCellGeometry` epoch machine; one reconciled panel grid
derivation; `PanelFrame::{Present, Absent}` projection on both document
and terminal semantic paths; stable presentation epochs; resize and
@ -293,9 +307,8 @@ activation, the GPU band, and the negotiated capability flip are all
git rev-parse HEAD
```
Require the remote branch to contain `bfaaf2b` or newer and confirm
PR #187's exact-head checks before resuming. Do not start 2B-3 until
#187 lands.
#187 has landed, so `githubsucks/main` already contains this work and
the branch is retained only for provenance. Start 2B-3 from `main`.
- **Stage 2B-1 MERGED as #184** (`main` @ `6bee09d`, 2026-07-28; all
twelve checks green on the reviewed head `5539b6e`; two review rounds
plus a gate-found follow-up). Branch
@ -508,58 +521,6 @@ has **no branch and no framing yet**.
`FrontendView.fold_projection` to `true` for semantic frontends, which
Stage 2 deliberately left `false` (Q#FD21).
## dired Stage 2 framing lane — PR #171 OPEN, STALE, DO NOT MERGE AS-IS
- Portable branch: `githubsucks/dired-stage2-framing` (head `ab42a79`,
four framing commits); worktree `../pmacs-dired-stage1`. **PR #171**,
base `main`. Framing only — `docs/dired-stage2-framing.md`, 1,570
lines, no runtime code.
- **Measured 2026-07-28: 4 commits ahead of `main`, 153 behind**, merge
base `c8ec8f3`. GitHub reports it mergeable, and its old CI run is
green — **both facts are about a tree nobody has looked at in 153
commits**, and the document still says PROPOSED.
- **The commit history embodies three review rounds. That is not the
same as approval**, and GitHub records no formal review or comment on
it. Do not read the round count as a green light.
- **Its dependencies moved materially underneath it**, which is the real
reason not to merge. Note that dired Stage 1 (#165) and find-file
(#162) are its *base*, not new arrivals — the merge base `c8ec8f3`
**is** #165's merge commit. Eighteen PRs landed after it, and at least
three change ground the framing stands on:
- **#178 gave generated buffers a write invariant**
(`Buffer::set_generated_contents`). Dired's listing is a generated
buffer, and dired is named in the handoff as one of the writer
mechanisms that has **not** adopted it. Stage 2's marks and
operations write that buffer constantly.
- **#182 (Journey Stage 1a) made `resolve_target_buffer` the single
directory-open path**, with dired demoted to a *replaceable slot*
(`pmacs.path.directory_handler`) rather than a hook subscriber. Any
Stage 2 claim about how a directory reaches dired is now describing
a mechanism that no longer exists in that form.
- **#179/#181 landed the typed-edit consumer chain**, which is the
fan-out a rename transaction has to survive.
Re-scout against `6bee09d`, publish a new revision, and get explicit
framing approval before any implementation. **The re-scout is under
way** on the existing branch, so PR #171 keeps its three-round
history; the product is a revision 5, not a new document. (`main` has
since advanced to `0442d78`, but the only difference is the test-only
#174, so no re-scout conclusion turns on it.)
- **The rename problem the framing must still answer**, restated because
it is the hard part: a rename is a transaction across **five** path
owners — the buffer path, the buffer name, the URI-keyed LSP stores
plus `DiagnosticView` (whose URI is set once at construction), dired's
pathless handles, and a captured Lua local that no transaction can
reach.
- 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.
- **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.
## Parked lane: kill-ring browser + persistence
- Portable branch: `githubsucks/kill-ring-browser`

View File

@ -1,8 +1,13 @@
# Agent handoff — cross-machine continuity
**Last updated: 2026-07-28, after the docs-only coherence listview
correction (#189) and landed-state refresh (#185) merged; the canonical
landed base is `7586905`. The runtime anchor beneath them is the M4
**Last updated: 2026-07-28, as bottom-panel Stage 2B-2 (#187) — the
daemon panel projection and the epoch machine — lands on `6c9e765`,
which is the dired Stage 2 framing (#171) atop the resource-op delete
guard framing (#186). Those two are framing-only: both are approved
documents with no runtime code, and neither has begun implementation.
Beneath them, the docs-only coherence listview
correction (#189) and landed-state refresh (#185); the canonical
landed base beneath those is `7586905`. The runtime anchor beneath them is the M4
config-sink race fix (#174) and bottom-panel Stage 2B-1 (#184). #174 is
test-only. #184 is the substantive one — the reserved protocol-v21
bottom-panel wire family, dark by construction, with the production
@ -52,7 +57,10 @@ commands, read `docs/active-work.md` immediately after this file.
## 1. Where the project stands (2026-07-28)
- `main` @ `7586905` (the docs-only coherence listview correction #189,
- `main` @ `6c9e765` (the dired Stage 2 framing #171 and the resource-op
delete guard framing #186 — both framing-only, no runtime code, no
implementation started — atop
the docs-only coherence listview correction #189,
atop the docs-only landed-state refresh #185, the M4 config-sink race
fix #174 — test-only — atop bottom-panel Stage 2B-1 #184, the
Journey/GPU directory-target ratchet #183, Journey Stage 1a #182,
@ -603,6 +611,97 @@ commands, read `docs/active-work.md` immediately after this file.
statusline and Vterm Stage 3 ladders still pinned v20 and rejected
v21, in both structural and real-headless-probe form. Grep for the
outgoing version across `tests/` before calling a bump complete.
- **Bottom panel Stage 2B-2 (daemon projection + epoch machine) LANDED
#187** (one review round of five findings on top of the
implementation; 12/12 green; 22/22 mutations bite). What it
establishes, beyond the feature:
- **A durable transition implemented as a per-frame effect is a bug
shape, not a style.** Four of the five review findings were one
defect: a renderer-side conditional with a durable twin that
disagreed with it. Wire-area exhaustion hid the rendered frame but
left panel state live; a stale `panel_epoch` could address a
reopened same-buffer panel; `NoMessage` cleared statusline segments
it should have retained. The fix that generalizes is
`presentable_panel_grid`**one** private derivation behind both
the renderer and `reconcile_panel_layout_core`, because two
derivations of one predicate is *how* they drift. When touching
panel rendering, ask of every conditional: is there a durable twin,
and does it agree?
- **Ask what the other frontend kind's equivalent does.** The
semantic panel terminal missed the pre-child-drain resize its
document sibling had; the repro is a 4×20 panel reporting 3×120.
The new `sync_semantic_panel_terminal_layout` is disjoint from that
sibling *by construction* — one resolves through
`primary_document_window`, the other through `side_window_for` — so
the SIGWINCH storm the original extraction prevented cannot recur
through the new path.
- **A panel may legitimately be wider than a PTY.** `>512` columns
are legal on the wire (2B-1's `WireGridLimits` parameter), but
`snapshot_for_view` refused the band's content rect and the band
went `Absent` with `panel_hidden` false. Clamp the *terminal
projection*, not the band: the child takes the columns a PTY can
have and the remainder is band background.
- **Mutation testing cannot reach behaviour never modelled.** All 16
original mutations passed while five real defects stood; one of
them even fired, removing an `Absent` the test asserted. Mutations
prove assertions bite the code that exists. They say nothing about
the assertion never written.
- **One durability hole is accepted deliberately:** epoch exhaustion
stays a per-frame `Absent`, flagged rather than hidden, because
making it durable needs a new "presentation permanently
unavailable" reason in `FrontendView` for a state requiring 2^64
presentations in one session.
- **dired Stage 2 framing LANDED (document only) — #171**
(`docs/dired-stage2-framing.md`, revision 9; seven review rounds).
**Approved as a framing; no runtime code and no implementation
started.** Load-bearing decisions a reader must not re-derive:
- **Reconciliation is order-independent, deliberately.** Reply order
is *not* execution order — `AsyncRuntime::tick` drains the bus with
`try_recv`, so a worker can finish first and be descheduled before
sending. An ordering token was rejected rather than built: no
static rule rescues the hazard anyway (rename `dir`→`newdir` racing
delete `dir/child.txt` needs opposite orders depending on which ran
first), and no production path can produce it. The trigger to
revisit is named: the first production caller that fire-and-forgets
two interdependent mutations.
- **A refused mid-edit kill is already destructive.**
`kill_buffer` clears round-trip state and moves windows *before*
`BufferRegistry::remove` can return `ConcurrentEdit`, so
`editing_in_progress` must be preflighted rather than relied on to
refuse cleanly.
- **Path-backed buffer names are not full paths.**
`get_or_load_buffer` takes the name from the path *as given* and
normalizes only the stored path, so a relative open is named
`foo.rs`. Rename provenance is path-*equivalence*, not equality.
- **URI-keyed stores have uncorrelated writers.**
`textDocument/publishDiagnostics` is absorbed unconditionally, and
`mark_document_stale` takes no `LspServerId` at all while creating
URI keys across three stores. A route purge keyed on request
responses cannot cover either.
- **Resource-op delete guard framing LANDED (document only) — #186**
(`docs/resource-op-delete-guard-framing.md`, revision 5; five review
rounds). **Approved as a framing; no runtime code.** It owns the
urgent **pre-filesystem refusal** for synchronous `apply_resource_op`
— which on `main` today destroys unsaved work — while #171 owns full
post-delete lifecycle reconciliation. Carried facts:
- The sequence is `stat/no-op -> enumerate and validate -> mutate
filesystem -> reconcile`. Validation inspects without removing, so
a failed deletion leaves buffers intact and `on_removed` still
observes the path already gone.
- **`EditorCore::find_buffer_for_path` is the wrong lookup** — it
delegates to first-match-only `find_by_path`, and
`pmacs.buffer.from_file` creates path-bound buffers with no dedup,
so a clean first match can hide a modified second.
- **LSP does not promise batch atomicity, and pmacs advertises no
`workspace.workspaceEdit` capability at all** — no
`documentChanges`, no `resourceOperations`, no `failureHandling`.
Recovery is the client's declared choice, and there is no spec
default for a client that declares none. Say only that observed
pmacs behaviour *resembles* abort-style application.
- **`pmacs.fs.remove` has no dirty check of its own** and neither
lane adds one. After both land the guards sit one layer above it.
Latent — zero production callers — but "both lanes guard deletion"
reads as the primitive being guarded, and it is not.
- **GPU initial target LANDED — #148**
(`docs/gpu-initial-target-framing.md` rev 3; merge `0dd16a5`; two review
rounds). `pmacs --gpu [--socket NAME|PATH] FILE` transports exact Unix path

3844
docs/dired-stage2-framing.md Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff