996 lines
57 KiB
Markdown
996 lines
57 KiB
Markdown
# Active work — cross-machine resume ledger
|
||
|
||
**Snapshot: 2026-07-30.** 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.
|
||
|
||
**This snapshot is an absorption pass.** Eight PRs merged on 2026-07-29
|
||
and 2026-07-30 (#188, #190, #191, #194, #195, #196, #197, #198) and the
|
||
ledger had drifted to 1,854 lines carrying six lanes whose work was
|
||
already on `main`. Those six are removed and their load-bearing
|
||
decisions are in `docs/agent-handoff.md` §1 — rule 4's precondition,
|
||
satisfied rather than deferred. The file is now 609 lines.
|
||
|
||
**A lane is removed when its ARC is done, not when a PR merges.** Two
|
||
lanes survive their sub-stage merges and are rewritten to the remaining
|
||
plan rather than deleted: generated-buffer immutability (Stage 1 merged,
|
||
Stage 2 not started) and bottom-panel (Stage 2 complete, Stage 3 ahead).
|
||
The bottom-panel block said so in its own text — *"this lane is not
|
||
removed at 2B-3's merge"* — and a wholesale removal keyed on "the PR
|
||
merged" would have discarded live planning. Read each block before
|
||
cutting it.
|
||
|
||
**#188's lane arrived with #188**, which is the point: with several PRs
|
||
open, a lane written on `main` for work that lands elsewhere
|
||
re-conflicts on every merge. Written on its own branch it costs one
|
||
conflict, at the merge that would have happened anyway.
|
||
|
||
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. 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
|
||
— 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 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
|
||
|
||
- Canonical development URL:
|
||
`https://github.com/levineuwirth/pmacs.git`. This ledger uses the
|
||
normalized local alias `githubsucks` so its refs and recovery commands
|
||
are identical on every machine. Remote names are otherwise
|
||
machine-local: `origin` may name this canonical URL, a release mirror,
|
||
or something else, and therefore has no authority by name alone.
|
||
- Canonical base at this snapshot:
|
||
`githubsucks/main` @ `fbcf235` (the reap-ledger diagnostic #202, atop
|
||
the test ambient-root isolation framing #201, the reap-ledger framing
|
||
#200, the ledger absorption #199, the M5.5 protocol-version pin #198,
|
||
the docs-only coherence listview correction #189, the docs-only
|
||
landed-state refresh #185, the M4 config-sink race fix #174,
|
||
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 is
|
||
`v6..=v21`; the production server-first `Hello` still advertises
|
||
v20** — two different facts, and #184 landed only the first. The
|
||
previous snapshot named `7586905`, and **the
|
||
recovery floor advances with it**: the check below now requires
|
||
`fbcf235` or newer, so a tree at `7586905` 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 from `git diff <their base>..main`.**
|
||
- On the transfer source, `origin/main` named a release mirror at
|
||
`d3fa632` and lagged badly. On the current destination, `origin` names
|
||
the canonical URL. This difference is why all recovery begins by
|
||
verifying URLs and normalizing `githubsucks` rather than trusting
|
||
`origin/main`.
|
||
- The shared desktop checkout contained unrelated uncommitted work. The
|
||
branches below were prepared in isolated worktrees; never clean or
|
||
overwrite the shared checkout to recover them.
|
||
|
||
Start on another machine by inspecting its remotes:
|
||
|
||
```sh
|
||
git remote -v
|
||
git remote get-url githubsucks
|
||
```
|
||
|
||
If the second command says the alias is absent, add it; if it prints a
|
||
different URL, stop and resolve that collision rather than overwriting an
|
||
unknown remote:
|
||
|
||
```sh
|
||
git remote add githubsucks https://github.com/levineuwirth/pmacs.git
|
||
```
|
||
|
||
Then recover current refs:
|
||
|
||
```sh
|
||
git fetch githubsucks --prune
|
||
git log -1 --oneline githubsucks/main
|
||
git worktree list
|
||
git status --short --branch
|
||
```
|
||
|
||
The `git log` command must expose `fbcf235` — 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.yml` never enables the `crdt` feature
|
||
anywhere — zero hits across the workflow directory. The `test` job runs
|
||
`cargo test --all-targets --no-default-features --features luajit|lua54`.
|
||
Every `#[cfg(feature = "crdt")]` test is therefore **not compiled** in CI,
|
||
not merely skipped.
|
||
- **Measured, `--list` under CI's exact flags versus the same flags plus
|
||
`crdt`: 3,176 vs 3,449 — 273 tests dark.** Re-measured at `74301d1`
|
||
(2026-07-26; at `fe8b8ba` it read 3,170 vs 3,443, the same 273 dark —
|
||
#176 added six tests, none of them `crdt`-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 — at `1b6a084`; #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_acceptance` |
|
||
| 13 | 1 | 14 | `gpu_invocation_acceptance` |
|
||
| 13 | 1 | 14 | `gpu_initial_target_acceptance` |
|
||
| 8 | 0 | 8 | `m10_11_acceptance` |
|
||
| 6 | 0 | 6 | `auto_pair_crdt_acceptance` |
|
||
| 6 | 0 | 6 | `m10_2_perf` |
|
||
| 4 | 5 | 9 | `vterm_stage3_acceptance` |
|
||
| 4 | 0 | 4 | `m10_10_perf` |
|
||
| 3 | 0 | 3 | `compile_mode_crdt_acceptance` |
|
||
| 2 | 22 | 24 | `theme_faces_acceptance` |
|
||
| 2 | 0 | 2 | `m11_5_semantic_acceptance` |
|
||
| 1 | 14 | 15 | `terminal_copy_mode_acceptance` |
|
||
| 1 | 9 | 10 | `vterm_stage1_acceptance` |
|
||
| 1 | 7 | 8 | `statusline_segments_acceptance` |
|
||
| 1 | 10 | 11 | `gpu_font_acceptance` |
|
||
| 1 | 0 | 1 | `auto_indent_crdt_acceptance` |
|
||
| 1 | 0 | 1 | `m10_11_perf` |
|
||
|
||
The rows sum to 273; the table is the whole census, not its head.
|
||
|
||
- **The single worst line is the library.** `cargo test --lib --features crdt`
|
||
is a REQUIRED local gate in `CLAUDE.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 a `crdt`-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), and
|
||
`a37`, 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".
|
||
- **⚠ `a37` will report green in the new job without running, unless the
|
||
job builds `pmacs-gpu` AND sets `PMACS_REQUIRE_GPU=1`.** Measured
|
||
2026-07-26 while gating #173. `a37_real_daemon_real_pty_and_headless_gpu_
|
||
render_one_terminal_session` derives its sibling binary path from
|
||
`CARGO_BIN_EXE_pmacs`, and on a missing binary it `eprintln!`s a skip and
|
||
**returns `ok`**. A fresh worktree running
|
||
`cargo test --features crdt --test vterm_stage3_acceptance` reports **9/9
|
||
in 0.17 s having never run it**; a real run takes ~4 s. Only
|
||
`PMACS_REQUIRE_GPU=1` promotes that skip to a failure, and `CLAUDE.md`
|
||
applies that flag to `cargo test -p pmacs-gpu` — a **different package**,
|
||
so the required local gate does not cover a37 either. The `gpu-render`
|
||
job 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 a `crdt` leg added to the plain `test` job would run a37
|
||
vacuously.
|
||
- **`a37` is also load-sensitive, which changes how to read the expected
|
||
first-run failures.** It passed at `d152120` and failed at that *same
|
||
commit* twenty minutes later, with a second agent saturating the machine
|
||
with `rustc` in between; it then failed identically on `d152120`,
|
||
`04c5ad1`, and the #173 merge commit, which is how #173 established the
|
||
failure was not its own. The signature is `last_frame_text` all spaces
|
||
with `rendered_nonuniform_frames` nonzero — 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_perf` 6, `m10_11_perf` 1). `m10_10_perf` has **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,crdt` lists 10 vterm Stage 1 tests
|
||
versus 9 without):
|
||
1. a `crdt` leg on the `test` job for the non-GPU suites and the library;
|
||
2. the GPU-requiring `crdt` suites onto the existing `gpu-render` job, which
|
||
already has lavapipe and `PMACS_REQUIRE_GPU=1` —
|
||
`vterm_stage3_acceptance`, `gpu_invocation_acceptance`,
|
||
`gpu_initial_target_acceptance`, `gpu_font_acceptance`.
|
||
- **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 crdt` flake, observed and
|
||
scoped without overclaiming its cause** (inherited from #178's gating,
|
||
where the terminal lane recorded it). `cargo test --lib --features
|
||
crdt` failed ~1 run in 5 on
|
||
`process::tests::setsid_escapee_is_not_reaped_and_teardown_reclaims_readers`
|
||
— `active_reader_probe` returning `None` at `process.rs:3179` ("live
|
||
runtime probe"). **Pre-existing and unrelated to #178:** that branch
|
||
did not touch `src/process.rs` at 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 known `drain_until`
|
||
trap: draining for `Started` also ticks, and a tick can reap the leader
|
||
before the following `active_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 warnings` **fails on `main`** —
|
||
measured at `74301d1`: seven errors before the build aborts, four in
|
||
`src/daemon.rs` (`useless_conversion` at 3996, missing doc backticks at
|
||
4076, `too_many_lines` 112/100 at 4083, an unneeded `mut` at 4965) and
|
||
three in `tests/vterm_stage3_acceptance.rs` (`too_many_lines` at 637
|
||
and 793, a redundant `continue` at 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 in `tests/auto_indent_crdt_acceptance.rs:42` that this run never
|
||
reached. The
|
||
standing gate list runs Clippy without `crdt`, so these lints have
|
||
never been enforced. Any CI job that compiles the `crdt` targets has to
|
||
fix them first or it will be red on arrival.
|
||
|
||
## Journey lane (P1) — 1a, 1b-1, 1b-2 MERGED; 1b-3 PR #205 OPEN
|
||
|
||
**Rewritten, not removed.** Rule 4 removes a lane when its ARC is done;
|
||
the journey arc is not — 1b-3 is the last stage of the 1b split and is
|
||
still open. Stages 1a (#182/#183), 1b-1 (#203) and **1b-2 (#204)** are
|
||
all on `main` and their durable facts are in `docs/agent-handoff.md` §1,
|
||
which is rule 4's precondition satisfied rather than deferred — so their
|
||
per-stage blocks are gone from here rather than left to rot as
|
||
"PR OPEN".
|
||
|
||
**#203's merge obligation is DISCHARGED** on this branch: `COHERENCE.md`
|
||
§2's step-9 row reads **Works**, §2's keybinding-inversion paragraph
|
||
records all three examples answered (the quote itself deliberately
|
||
unchanged), §20 Priority 1 and its arc list say "landed", and the
|
||
handoff bullet says LANDED. **#204's is discharged too**: §2's step-6
|
||
row names it as landed and stays **Partial** for a reason that landing
|
||
did not touch — a server that starts and then *crashes* is still
|
||
unsurfaced. Both rode this branch rather than standalone docs PRs,
|
||
which would have re-conflicted on every merge.
|
||
|
||
### Stage 1b-3 — IMPLEMENTED, PR #205 open
|
||
|
||
- **Branch `journey-stage1b3-welcome`**, worktree `../pmacs-journey-1b3`,
|
||
based on `githubsucks/main` @ `1f290d5`, **integrated with `main` @
|
||
`5376af1`** (#204). **Implemented; PR #205 open.**
|
||
`docs/journey-stage1b3-welcome-framing.md` revision 4, three
|
||
review rounds closed (round 1: four findings; round 2: two acceptance
|
||
holes plus a doc correction; round 3: a visibility mismatch and an
|
||
unobservable assertion; all accepted). The last of the 1b split
|
||
(1b-1 landed #203, 1b-2 landed #204 at `5376af1`).
|
||
- **What it is.** Journey step 4 / `COHERENCE.md` §18: a fresh `pmacs`
|
||
greets the user with an empty buffer, an empty status line, and no
|
||
indication that `M-x` exists. The stage renders a three-line welcome
|
||
into `*scratch*` and adds a minimal `M-x help`.
|
||
- **`EditorState::new()` is NOT the no-argument entry point**, and
|
||
revision 1 rested every criterion on the assumption that it was.
|
||
`EditorState::open` calls it *before* resolving the target
|
||
(`src/editor.rs:944`), the daemon constructs one too, user config runs
|
||
*inside* it, and desktop restore happens much later
|
||
(`restore_desktop_if_armed`, `:3637`, inside `run()`'s `RunLocal`
|
||
arm). The stage now adds a **launch-finalization seam** called right
|
||
after desktop restore, with `had_file` — already threaded to that
|
||
point for the same kind of question — as the no-target signal.
|
||
**Round 2 closed the wiring hole rather than disclaiming it.**
|
||
Revision 2 called `run()`'s call to the seam an untestable residual —
|
||
but deleting that one line left every proposed pin green while
|
||
shipping no welcome, because the pins called the seam by hand. The
|
||
terminal-free prefix of `run()` (everything before `Frontend::new()`)
|
||
is now extracted into `prepare_startup`, which `run()` delegates to
|
||
and the pins drive. It is **`pub`**, not `pub(crate)`: the journey
|
||
suite is a separate integration crate and cannot reach crate-private
|
||
items, and `run`/`new`/`open`/`install_state_dirs`/
|
||
`restore_desktop_if_armed` are already public, so this completes that
|
||
surface rather than widening it for a test. The pin must assert
|
||
desktop restore was **unarmed** (an ambient `init.lua` calling
|
||
`desktop_mode(true)` would otherwise change the result) and must
|
||
assert buffer content only, since `install_state_dirs` resolves real
|
||
XDG/`PMACS_STATE_HOME` roots that tests cannot override
|
||
(`set_var` is unsafe and forbidden).
|
||
- **The step-2 pin is NOT amended.** Revision 1 analysed a status-line
|
||
welcome and then chose `*scratch*`, but kept the amendment — an
|
||
internal contradiction. The status stays empty, so the pin stays true,
|
||
and "no error text" has no defined predicate over an unstructured
|
||
status string anyway.
|
||
- **`C-h` is NOT free, and the reason is load-bearing.** It is bound to
|
||
`buffer.delete-word-backward` because non-kitty terminals cannot
|
||
disambiguate Ctrl+Backspace from Ctrl+H — both produce byte 0x08
|
||
(`builtin/keymaps/default.lua:78-86`). Rebinding it to a help prefix
|
||
would break Ctrl+Backspace on every legacy terminal, so §2's step-4
|
||
row calling it an oversight is wrong: it is a deliberate trade. The
|
||
help-prefix decision is deferred to §20 Priority 4's discovery arc
|
||
with the constraint recorded.
|
||
- **`*help*` already exists** (`builtin/commands/default.lua:1226`,
|
||
`show_help_text`, reused buffer, buffer-local `q`), used by
|
||
`editor.describe-command` / `-setting`, over `src/help.rs`'s
|
||
renderers and link resolution. Two gaps recorded rather than
|
||
inherited silently: it writes with `buf:delete`/`buf:insert` instead
|
||
of `set_generated_contents`, and it is **found by name**, so a
|
||
foreign `*help*` would be cleared.
|
||
- **Deliberately NOT `set_generated_contents` for the welcome** — that
|
||
lifts read-only, discards history and marks the buffer generated, all
|
||
wrong for a buffer step 5 requires the user to type into immediately.
|
||
The one place not adopting that invariant is correct, stated so a
|
||
later audit does not "fix" it.
|
||
- **Step 4 stays Partial, but there IS a §25 obligation** — revision 1
|
||
claimed there was none. The scorecard's row 18 and §18's ground truth
|
||
both read **Missing**, and both become false on merge: they move to
|
||
**Partial**. A stage can be too small to flip its journey step while
|
||
still falsifying a "missing entirely" grade.
|
||
- **The welcome renders from a structured entry list**, not from scraped
|
||
prose: `M-x help` mixes a chord with a command name and `C-c c` is two
|
||
chords whose boundary prose does not mark, so the binding checks run
|
||
`pmacs.keymap.lookup` over the same list that renders the text.
|
||
- **`pmacs.command.invoke` is NOT the M-x path** — it is the
|
||
programmatic API. M-x is `editor.execute-command`, a minibuffer with
|
||
the `commands` completion source that calls `invoke_interactive` on
|
||
accept. The `M-x help` pin dispatches the chord, enters the name and
|
||
accepts — and asserts **`pmacs.minibuffer.selected() == "help"`
|
||
BEFORE RET**, because a selected candidate shadows typed text (dired
|
||
refused a completion source for exactly this reason) and `accept()`
|
||
does `session.take()`, so nothing about the accepted value survives
|
||
afterwards.
|
||
- **Integrated with `main` @ `5376af1`** (#204). The journey suite's
|
||
conflict was additive on both sides — step 4 (this lane) and step 6
|
||
(#204) — and both are kept: **44 pins, covering steps 2, 3, 4, 5, 6
|
||
and 9**.
|
||
- **§18 and the scorecard move Missing → Partial ON THIS PR**, per §25;
|
||
§2's step-4 row stays Partial because `C-h` and the tutorial remain.
|
||
No deferred flip is owed at merge — unlike 1b-1's.
|
||
- **Bites, all directed.** Deleting the production `run()` wiring fails
|
||
the two greeting pins — **the mutation revision 2's design would have
|
||
survived entirely**. Removing `mark_clean` fails the editable/clean
|
||
pin; the `had_file` guard fails the directory pin (not the file pin,
|
||
because a file buffer is active by then while the dired listing is
|
||
async); the emptiness guard fails the existing-content pin; the
|
||
active-buffer requirement fails the backgrounded pin; and advertising
|
||
an unbound key fails the binding pin.
|
||
|
||
```sh
|
||
git fetch githubsucks
|
||
git worktree add ../pmacs-journey-1b3 \
|
||
-b journey-stage1b3-welcome \
|
||
githubsucks/journey-stage1b3-welcome
|
||
```
|
||
|
||
## Discovery Stage 1 (P4) — FRAMING APPROVED (rev 5); implementation next
|
||
|
||
- **Branch `discovery-stage1-commands`**, worktree `../pmacs-p4-discovery`,
|
||
based on `githubsucks/main` @ `54a092e`. **Framing only; no code, no
|
||
PR yet.** `docs/discovery-stage1-command-family-framing.md` revision 5,
|
||
three review rounds closed (round 1: two blocking, two major; round 2:
|
||
two blocking, two major; round 3: two factual corrections; all
|
||
accepted), and **Q#D2 / Q#D3 decided by the user**.
|
||
- **What it is.** `COHERENCE.md` §20 Priority 4 — "almost pure wiring,
|
||
the best payoff-per-effort in this document". **Eleven commands under
|
||
one `help.*` prefix**: nine new (describe-key/mode/hook/buffer,
|
||
where-is, list-commands, list-keybindings, list-settings, apropos)
|
||
plus `editor.describe-command` / `editor.describe-setting` renamed,
|
||
with the old names retained as **forwarders** so nothing documented
|
||
breaks. Typing `help` at M-x surfaces the whole family, which is the
|
||
discoverability win the arc exists for (Q#D2).
|
||
- **`apropos` matches by SUBSTRING, not fuzzy** (Q#D3). `fuzzy_score`
|
||
is subsequence-based and descriptions are long sentences, so fuzzy
|
||
would match nearly every command. Pinned by a
|
||
subsequence-that-is-not-a-substring finding nothing.
|
||
- **It adds no Rust.** `pmacs.describe.*`, `pmacs.keymap.list()`,
|
||
`pmacs.command.list()` and `pmacs.config.list()` already return
|
||
everything needed, and `parse_completion_source` accepts a **Lua
|
||
`Function`** (`CompletionSource::Custom`), so even the prompts need no
|
||
new Rust.
|
||
- **Completion is ASSISTANCE, not validation.**
|
||
`resolve_accepted_value` returns the **literal typed text** whenever
|
||
no candidate is selected, so a non-matching typo still reaches
|
||
`on_accept` and the existing error path — and a fuzzy near-miss can
|
||
silently describe a *different* setting, a new failure mode.
|
||
Closed-set acceptance ("refuse a non-candidate") is Rust work and is
|
||
deferred. The custom source needs a **mapper**: `config.list()`
|
||
yields descriptor *tables* while `Custom` consumes a sequence of
|
||
strings. **It does not control display order** —
|
||
`recompute_candidates` runs `filter_and_sort` (fuzzy score, lexical
|
||
tiebreak); sorting the pool matters only because `.take(
|
||
CANDIDATE_LIMIT)` runs *before* the sort, so pool order decides which
|
||
candidates survive truncation.
|
||
- **`invoke_interactive` is NOT the M-x path** — the error #205
|
||
corrected, repeated one PR later. The path is dispatch `M-x` →
|
||
`editor.execute-command` → assert the selected candidate **before**
|
||
RET (`accept()` does `session.take()`) → accept → `invoke_interactive`.
|
||
Five of the nine commands open a **second** prompt the pins must drive
|
||
too; a pin that stops after the first RET has tested the palette.
|
||
- **One owner for `*help*` writes — NOT a one-site migration.**
|
||
`src/help.rs` has semantic renderers for command/key/buffer/mode/
|
||
hook/view and **none for settings, lists or apropos**, and
|
||
`_show_help` takes already-flattened text, so a later migration still
|
||
changes each command's subject-specific logic. What the funnel buys is
|
||
the shared policy in one place: reuse-by-name, wholesale
|
||
delete+insert, the `q` binding, and the foreign-`*help*` hazard.
|
||
**`*help*` is ordinary editable content** — no read-only intercept, no
|
||
generated-content invariant. And read-only would **not** fix the
|
||
foreign-buffer hazard either: a user's own `*help*` carries no
|
||
intercept of ours, so the renderer still finds it by name and clears
|
||
it. The missing guarantee is **ownership identity**, which `listview`
|
||
and dired both carry and this mechanism does not. Each command's rendering
|
||
is a named per-subject function so the future Rust work is enumerated
|
||
per-subject (three new renderers) rather than discovered per-call-site.
|
||
- **Deliberately deferred, each with a reason:** richer M-x rows
|
||
(`MinibufferPrompt.candidates` is `Vec<String>` — protocol change);
|
||
`Command` gaining title/category/flags (~147 definition sites);
|
||
predicate evaluation (**read only at `src/help.rs:76` and one test** —
|
||
a behaviour change); help-layer unification; closed-set acceptance;
|
||
and the **help prefix key**, which this stage does not touch because
|
||
#205 recorded why `C-h` is not free.
|
||
- Recovery:
|
||
|
||
```sh
|
||
git fetch githubsucks
|
||
git worktree add ../pmacs-p4-discovery \
|
||
-b discovery-stage1-commands \
|
||
githubsucks/discovery-stage1-commands
|
||
```
|
||
|
||
## Generated-buffer immutability lane (Arc: workbench primitives) — STAGE 1 MERGED; STAGE 2 IS NEXT
|
||
|
||
**Framing #188 (revision 7) and Stage 1 #191 are both on `main` @
|
||
`4cd4a7b`.** Their durable facts are in `docs/agent-handoff.md` §1 —
|
||
including the contract-ownership rule (the framing owns the acceptance
|
||
criteria; an implementation adopts them and may not restate or narrow
|
||
them) and why `dired`/`listview` were the correct first two families.
|
||
|
||
- **Stage 2 is not started and has no branch.** It owns everything with
|
||
new Rust in it: `Buffer::apply_generated_edit` + `GeneratedOutcome` +
|
||
the `{ generated = true }` option and its `run_buffer_edit` arm;
|
||
`set_generated_contents` reimplemented over it; Q#GB10's path-backed
|
||
refusal and `mark_clean`; Q#GB15's `identity_protected`; Q#GB13/GB18
|
||
for `compile.lua` and the search panel; Q#GB5's `ensure_slot` lock;
|
||
the remaining 13 write sites; and the three `compile_mode_acceptance`
|
||
intruder tests converted per Q#GB12.
|
||
- **It collides with dired Stage 2b**, which changes `paint`'s callers.
|
||
Whichever starts second integrates first.
|
||
|
||
|
||
## Bottom-panel lane (Arc 7) — STAGE 2 COMPLETE; STAGE 3 IS THE LAST STEP
|
||
|
||
**Stage 1, the Stage 2 framing, and Stages 2A, 2B-1, 2B-2 and 2B-3 are
|
||
all on `main` @ `4cd4a7b`** (#155, #175, #177, #184, #187, #198). Stage 2
|
||
is complete. Durable facts are in `docs/agent-handoff.md` §1, including
|
||
the v20-baseline / v21-negotiated handshake that Stage 2B-3 made
|
||
compatible.
|
||
|
||
- **Stage 3 — the adopter default flip — is the arc's last step and is
|
||
not started.** This lane stays until it lands; it is not removed at
|
||
2B-3's merge.
|
||
- **DAP waits for Stage 2, not Stage 1** — that dependency is now
|
||
satisfied.
|
||
|
||
## Test ambient-root isolation — FRAMING OPEN, revision 4
|
||
|
||
- **Branch `test-ambient-config-isolation`**, worktree
|
||
`../pmacs-test-isolation`, based on `githubsucks/main` @ `4cd4a7b`.
|
||
**Framing only; no code, no PR yet.**
|
||
`docs/test-ambient-config-isolation-framing.md` revision 4, three review
|
||
rounds closed (eight blocking, six major, all accepted).
|
||
- **What it is.** Integration tests use the developer's real ambient
|
||
roots. `#[cfg(not(test))]` guards config loading against the crate's
|
||
own unit tests only, so the **65** files in `tests/` that construct an
|
||
editor load the real `init.lua` — and, separately, `EditorState::new` materializes bundled
|
||
packages unconditionally into the real `XDG_DATA_HOME`/`$HOME`.
|
||
**It is not read-only**: `~/.local/share/pmacs/builtin-packages/`
|
||
exists on the development machine.
|
||
- **Why it matters now.** `cargo test` is red on any machine with a real
|
||
`~/.config/pmacs/init.lua` (11 of 67 in `compile_mode_acceptance`) and
|
||
green in CI, so the failure is attributed to whatever branch is
|
||
checked out. Every local gate run in this repo currently needs all
|
||
five bootstrap-storage variables controlled as a workaround:
|
||
`XDG_CONFIG_HOME`, `XDG_DATA_HOME`, `XDG_STATE_HOME`,
|
||
`XDG_CACHE_HOME`, and `PMACS_STATE_HOME`.
|
||
- **Round 1's four blocking findings, all confirmed in code:** the
|
||
read-only assumption was already false; the population count was 18
|
||
when 65 of 96 files construct an editor, from a grep that did not
|
||
match `EditorState::new`; 5 files are both in-process and spawned, so
|
||
a file-level partition cannot work; and `EditorState::open` calls
|
||
`Self::new()` while `journey_acceptance` requires that exact entry
|
||
point.
|
||
- **Two constraints any fix must respect.** `std::env::set_var` is
|
||
`unsafe` and the crate forbids it, so in-process tests cannot isolate
|
||
themselves (precedent: `Installer::root_override`). And config loading
|
||
shares one block with `set_init_complete()`, which
|
||
`m8_2_acceptance.rs:75` explicitly depends on — skipping the block
|
||
would leave integration tests permanently in the init phase.
|
||
- Recovery from a clean checkout — **the two-argument form does not
|
||
work**, verified by running it (`git worktree add <path>
|
||
<remote-only-branch>` fails with `fatal: invalid reference`, because
|
||
after a bare fetch no local branch exists):
|
||
|
||
```sh
|
||
git fetch githubsucks
|
||
git worktree add ../pmacs-test-isolation \
|
||
-b test-ambient-config-isolation \
|
||
githubsucks/test-ambient-config-isolation
|
||
```
|
||
|
||
## Reap-ledger silent failures — MERGED (#202); kept for its parked follow-ons
|
||
|
||
- **Branch `reap-ledger-silent-failures`**, worktree
|
||
`../pmacs-reap-ledger`, based on `githubsucks/main` @ `22df6ab`.
|
||
`docs/reap-ledger-silent-failures-framing.md`, **revision 4**;
|
||
approved at revision 3 after two review rounds (round 1: three
|
||
blocking, two major; round 2: two blocking, two major; all accepted).
|
||
Revision 4 records implementation findings, not a new design round.
|
||
- **All four bets resolved.** Bet 1 (every site takes a directed
|
||
outcome) and Bet 2 (every consequence is reachable) hold. **Bet 3
|
||
resolves the shutdown coupling as real and measured** — under 500ms
|
||
with a failed force-kill plus an errored probe, versus the full 2s
|
||
bound with only the force-kill failing. **Bet 4 is falsified: no
|
||
reporting channel exists**, so reporting becomes its own lane.
|
||
- **The in-drain pin's first fixture was vacuous, and the bite caught
|
||
it.** `poll_one` TERMs the group on leader exit, so an untrapped
|
||
descendant died before writing its late marker — absent on *both*
|
||
paths. With the seam reverted the pin failed only the consumed-plan
|
||
check, never the content assertion. Fixed with `trap '' TERM` behind
|
||
the readiness gate.
|
||
- **Gates: 10/10 green** on the pushed tree, all five bootstrap-storage
|
||
variables controlled — fmt, diff-check, clippy, `--lib` (1888),
|
||
`--lib --features crdt` (2073), compile-mode (67), copy-mode in both
|
||
feature configurations (18/19), M4 with the basedpyright skip (149),
|
||
required GPU (221). The five new process pins ran **15/15** as a
|
||
repetition set, since supervisor tests are load-sensitive.
|
||
- **Unparked from PR #200's §5.** #200 retired the premise that
|
||
justified the ledger's leniency and deliberately changed no
|
||
disposition; this lane owns what it refused.
|
||
- **Four sites, not the two #200 named.** In the persistent ledger: a
|
||
probe error of any errno drops the entry and cancels escalation; a
|
||
failed escalating `SIGKILL` is marked as succeeded, so **no later tick
|
||
retries it**; and `shutdown()` discards its own force-kill result the
|
||
same way — on the path that exists specifically to stop a leak at
|
||
editor exit. Those last two are **distinct, not cumulative**:
|
||
`shutdown()` force-kills every entry with no `!entry.killed` guard, so
|
||
a failed escalation still gets one attempt at exit, while a failed
|
||
force-kill leaks the group past exit with nothing left to try.
|
||
**Plus the in-drain
|
||
twin** `final_drain_runtime`, which collapses every errno to "dead"
|
||
while no tick runs; a false "dead" there cancels the readers, so its
|
||
failure mode is truncated output rather than a leaked process.
|
||
- **The blast radius is exactly what the ledger exists for:** a
|
||
TERM-ignoring descendant that outlived its leader with output
|
||
redirected. Neither leader state nor reader state can see it; only
|
||
group liveness can. A silent drop leaks the one process nothing else
|
||
is watching. **Journey step 9 (build/test), not step 8** — the ledger
|
||
arms only for `spec.group`, which spawn rejects for PTY mode, so no
|
||
terminal reaches it; compile mode is the only production caller.
|
||
- **`shutdown()`'s final loop terminates when the ledger empties**,
|
||
which happens via the same silent drop — so the probe error that hides
|
||
a leak can also end the cleanup loop early. That coupling is why the
|
||
probe cannot be made strict on its own. **Its precondition is
|
||
`any_running()` already false**, so the fixture must be a leader that
|
||
exited leaving a survivor; any other shape tests the other arm of the
|
||
disjunction.
|
||
- **None of the three has been observed.** #200 saw an explicit
|
||
`SIGTERM` fail in `signal()`, not a ledger call. The premise is
|
||
falsified and the path exposed; the occurrence is not evidence these
|
||
fire.
|
||
- **They are also untestable today**: `tick_reap_ledger` and
|
||
`shutdown()` call `nix` directly and consult no injection seam, unlike
|
||
`signal()`'s `forced_kill_errno`. All five existing ledger tests
|
||
exercise the success path only. The seam must be **site-directed and
|
||
multi-outcome**: `shutdown()` calls `self.signal()` before its ledger
|
||
force-kill, so a single global slot would be consumed by the wrong
|
||
call, and the coupling test needs two pending outcomes at once. The
|
||
in-drain probe repeats every 1 ms but only cancels readers after 50 ms
|
||
of false "dead", so it needs a directed full-drain override rather
|
||
than a one-shot error. Test state is per-supervisor and shared into
|
||
the drain context, never global; teardown proves its intended site was
|
||
reached. The in-drain SIGKILL's local flag has no independently
|
||
observable outer-path consequence, so it is named but not given a
|
||
dead injection seam. The first PR is the seam **plus** the tests that
|
||
exercise it — a seam without tests does not show it reaches the
|
||
intended calls.
|
||
- **Diagnosis first, no disposition change proposed.** Stage A of the
|
||
signal lane had three tolerance rules rejected across three revisions
|
||
for the same shape of error on the same data structure.
|
||
- Recovery from a clean checkout:
|
||
|
||
```sh
|
||
git fetch githubsucks
|
||
git worktree add ../pmacs-reap-ledger \
|
||
-b reap-ledger-silent-failures \
|
||
githubsucks/reap-ledger-silent-failures
|
||
```
|
||
|
||
## Folding lane (Arc 6) — Stages 1 and 2 MERGED; Stage 3 (GPU) is next
|
||
|
||
Both shipped stages are on `main`; nothing in this arc is in flight. Stage 3
|
||
has **no branch and no framing yet**.
|
||
|
||
- Stage 1 (headless fold engine) merged as **#142**, Stage 2 (grid/daemon
|
||
collapse) as **#149** — both under "Closed since the last snapshot".
|
||
- Retained, carrying nothing unmerged: branches `folding` / `folding-tui`
|
||
and worktrees `../pmacs-folding` / `../pmacs-folding-tui`. The framings
|
||
`docs/folding-framing.md` (rev 5) and `docs/folding-stage2-framing.md`
|
||
(rev 4) are the approved artifacts Stage 3 re-scouts against.
|
||
- **Stage 3 (GPU) obligations, already named by the framings** — the
|
||
starting point for its own framing doc: GPU collapse at TUI parity;
|
||
caret/hit-test fold-awareness; the `BufferSnapshot` **fold-mirror clear**
|
||
(parent R2-4 — without it, empty-after-revert diff suppression leaves
|
||
stale folds on the GPU, the same trap class as #120); CRDT-origin and
|
||
GPU-optimistic interactive unfold (parent R2-3); and flipping
|
||
`FrontendView.fold_projection` to `true` for semantic frontends, which
|
||
Stage 2 deliberately left `false` (Q#FD21).
|
||
|
||
## Parked lane: kill-ring browser + persistence
|
||
|
||
- Portable branch: `githubsucks/kill-ring-browser`
|
||
- Parked framing head: `503c489`
|
||
- State: framing only, revision 2; no implementation and no PR.
|
||
- Status: explicitly parked by the user on 2026-07-20.
|
||
- Its original scout was based on `0efb5cd`. The preserved framing marks
|
||
this ground truth stale and requires a complete re-scout against the
|
||
then-current `githubsucks/main` before implementation.
|
||
- Compile-mode has merged since the original scout, so old
|
||
“compile-mode in flight” keybinding/touch-set assumptions are not
|
||
authoritative.
|
||
|
||
Recovery worktree, only when the user un-parks it:
|
||
|
||
```sh
|
||
git worktree add --track \
|
||
-b kill-ring-browser \
|
||
../pmacs-kill-ring-browser \
|
||
githubsucks/kill-ring-browser
|
||
```
|
||
|
||
## Closed since the last snapshot
|
||
|
||
- **Process-signal diagnostic completeness — MERGED as #200**
|
||
(`main` @ `a2a92bb`), atop Stage A #176. `docs/process-signal-diagnostic-completeness-framing.md`
|
||
revision 6; framing approved at revision 4 after three rounds, then
|
||
five review rounds on the implementation. Durable facts are in
|
||
`docs/agent-handoff.md` §1. **Evidence collection only** — no
|
||
tolerance rule, no retargeting, no disposition change.
|
||
- **Bet 1 was falsified by CI and the framing's own fallback shipped.**
|
||
`bash -m` diverges the terminal's foreground group on Linux and
|
||
never on macOS. The divergent case is pinned by injection
|
||
everywhere; a Linux-only corroboration drives a real shell and is
|
||
the **only** test exercising `pty_foreground_group` end-to-end, so
|
||
**on macOS that lookup has no end-to-end coverage**.
|
||
- **Group identity remains unprovable**, and the pre-kill sample does
|
||
not change that: moving `getpgid` before the `kill` removed a
|
||
post-hoc reading, it did not make the reading contemporaneous.
|
||
- **Still parked, each needing its own lane:** the reap ledger's
|
||
silent cancellation (an EPERM probe drops the entry; a failed
|
||
`SIGKILL` is marked killed) — **being scoped next**; retargeting to
|
||
the measured pgid; any EPERM/ESRCH tolerance rule; Q#PS6; and
|
||
`signal_target`'s read-then-kill of `tcgetpgrp` on the PTY path.
|
||
|
||
- **Six lanes removed by the 2026-07-30 absorption pass**, all merged,
|
||
all with their durable facts in `docs/agent-handoff.md` §1:
|
||
**#190** resource-op delete-guard implementation; **#196** dired
|
||
Stage 2a (rename/delete reconciliation — Stage 2b remains, unstarted
|
||
and without a lane); **#188** generated-buffer immutability framing
|
||
(revision 7, the governing contract); **#194** silent-skip arming;
|
||
**#195** CI timeouts and concurrency; **#197** the process teardown
|
||
stdin deadlock.
|
||
- **#194 and #195 kept their lessons in §3 and §5 rather than §1**,
|
||
which is why a PR-number search of the handoff finds them only once
|
||
each. That is sufficient under rule 3 — durable knowledge has a
|
||
home, not a required section.
|
||
- **A census by PR number is a proxy, not a measurement.** Counting
|
||
`#NNN` in the handoff said five of these lanes had no record at all;
|
||
counting by *content* found most already documented, with the real
|
||
gap being the implementation PRs specifically (#190, #191, #196)
|
||
while their framings were recorded. The absorption written from the
|
||
first count would have duplicated existing entries.
|
||
|
||
- **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 head `1b44c69` — verified by `head_sha`, not by the
|
||
check summary), both 2026-07-26, both with no protocol change.
|
||
Approved framing: `docs/terminal-config-and-copy-mode-framing.md` rev
|
||
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 to `docs/agent-handoff.md` §1 (the arc bullet) and §4 (the
|
||
`set_generated_contents` invariant) per rule 3 below, and to
|
||
`COHERENCE.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 the `a37` footing; the handoff records what it must assert.
|
||
Branches `githubsucks/terminal-config` and
|
||
`githubsucks/terminal-copy-mode` with worktrees
|
||
`../pmacs-terminal-config` and `../pmacs-terminal-copy-mode` are
|
||
retained. The gate-run flake found while gating #178 moved to the CI
|
||
`crdt`-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>*`, a `dired` major mode carrying
|
||
`RET`/`f`, `^`, `n`/`p`, `g`, `q`, `s`. No wire change (v20). The Rust is
|
||
two things — a per-entry-tolerant `read_dir` (Q#DR6), which had to be
|
||
Rust because `read_dir_blocking` fails a whole listing on any of five
|
||
per-entry conditions and a tolerant wrapper cannot be written in Lua at
|
||
all, and `normalize_buffer_path` going `pub` as
|
||
`pmacs.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 abandoned `dired` branch
|
||
(`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 in `docs/agent-handoff.md` §§1/5; the implementation notes are
|
||
`docs/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, and
|
||
`pmacs.fs.rename` has zero production callers, so it can be fixed at
|
||
the primitive; and Q#DR5's seam is the main-thread drain
|
||
`AsyncRuntime::tick`, not `_take_result`, where rename settles as an
|
||
undifferentiated `ReplyKind::FsUnit` and so must be keyed on
|
||
`JobKind::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 a `SIGWINCH` storm that made a GPU terminal untypable while
|
||
output still flowed. `sync_terminal_layout` is now split into a
|
||
frontend-kind-neutral half (panel reconcile + controller liveness) and a
|
||
grid-only geometry half, with the loop body extracted to
|
||
`sync_terminal_layouts_for_tick` so the exclusivity is structural. No
|
||
protocol change (v20). Durable lessons are in `docs/agent-handoff.md` §5;
|
||
the framing (`docs/gpu-terminal-input-framing.md` rev 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).
|
||
Branch `gpu-terminal-input` and worktree `../pmacs-gui-term-input` retained.
|
||
**Its landed-doc pair MERGED as #168** (`main` @ `1b6a084`,
|
||
2026-07-26): #166 recorded as landed, the CI `crdt`-coverage gap
|
||
measured (**264 tests dark workspace-wide**, 177 in the library — a
|
||
reading taken at `1b6a084` and kept here only as history. **The CI
|
||
`crdt`-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 inside
|
||
`pmacs-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 until `cargo build --release -p pmacs-gpu` and a client
|
||
restart; the daemon needs neither. Diagnose with `strings` on 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 into
|
||
`pmacs-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_request` run is created. The ledger previously
|
||
recorded this cause as unidentified; it is not. Check `mergeable` and
|
||
confirm a run exists for the current head SHA.
|
||
- **`m4_5_basedpyright` has no timeout and hangs forever**, parking a
|
||
`--workspace` sweep (observed 2h26m at 38 of 92 suites). It is
|
||
**intermittent**, so an earlier clean sweep proves nothing. Sweep with
|
||
`cargo test --workspace --no-fail-fast -- --skip basedpyright` and
|
||
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_font` family.
|
||
- **Bottom panel Stage 1 — MERGED as #155** (`main` @ `e745068`,
|
||
2026-07-24, after two review rounds). Window placement, window
|
||
parameters, TUI side windows, the divider, and the adopter `display`
|
||
opt-in, with no protocol change. Both rounds found the same class of
|
||
defect and are worth keeping:
|
||
- **Round 1**: the Q#BP6 side-window split guard had *no production
|
||
caller* — `C-x 2` still reached plain `split_active` — and survived
|
||
because the acceptance test called the core method directly.
|
||
- **Round 2**: Q#BP7's terminal growth re-arm had *never been
|
||
implemented*, and the assertion meant to pin it (`at_bottom`) is a
|
||
geometric readout that a still-anchored view satisfies; the anchor
|
||
assertions beside it compared `""` with `""` because the PTY fixture
|
||
emitted LF-only output.
|
||
- **Post-round-2 self-review**, caught by CI going red on all four Test
|
||
jobs: resolving `pmacs.window.buffer()`'s no-argument arm through the
|
||
acting frontend made a total function partial, and six runtime modules
|
||
silently dropped operations (`kill_ring_acceptance` 30/30 → 25/5).
|
||
Fixed in `9110f9f` before merge.
|
||
- Gating fact found on the way: an isolated `XDG_CONFIG_HOME` prevents
|
||
the real user `init.lua` from installing a local package and leaking
|
||
a status message into painted-frame comparisons. **That isolates the
|
||
observed config symptom only, not the gate:** the ambient-root lane
|
||
above establishes that data/state/cache must be controlled too.
|
||
There is also a latent pre-existing `main` bug in the buffer CRDT
|
||
undo path, unrelated to this arc.
|
||
- `compile_mode_acceptance` is load-sensitive under default
|
||
parallelism (~1 run in 3, a different test each time); verified
|
||
pre-existing by swapping in `main`'s `compile.lua`. It is 67/67 at
|
||
`--test-threads=1`.
|
||
|
||
- **GPU initial target — MERGED as #148** (`main` @ `0dd16a5`, 2026-07-24,
|
||
after two review rounds). `pmacs --gpu [--socket …] FILE` opens a target
|
||
before the GPU window appears. Protocol bumped 19 → 20: a semantic-session
|
||
`SessionBootstrapRequest` after `AttachRequest`, plus an appended
|
||
`InstanceMessage::InitialTargetResult` pre-window readiness barrier; v6–v19
|
||
wire encodings are unchanged. Root owns launcher tilde/cwd resolution and
|
||
exact raw-byte path transport; the daemon resolves/dedups/loads the target
|
||
and runs load/switch hooks inside one dispatcher transaction, then
|
||
publishes CRDT-upgraded targets to existing grid replicas (gated on
|
||
`upgraded_to_crdt`, independent of the load/create outcome, so a dedup onto
|
||
a hidden not-yet-backed buffer still reaches pre-attached replicas — round
|
||
2 finding). Semantic replicas receive a publication only when displaying
|
||
that buffer, so a second target launch cannot switch an existing GPU
|
||
window. Round 2 also closed a failure-containment gap: every dispatcher-side
|
||
bootstrap failure now shuts down the socket (a dropped write-half clone
|
||
does not close a shared FD), and the dispatcher drops any event from a
|
||
session that was never installed, rather than reaching absent render/size
|
||
state. Integrated cleanly with Folding Stage 2 (#149): fold projection at
|
||
attach is selected from the same negotiated `semantic_render` bit the
|
||
target bootstrap uses. Its lane, worktree (`../pmacs-gpu-initial-target`),
|
||
and branch (`gpu-initial-target`) are done; the `-framing` branch is kept.
|
||
Durable substrate facts and both review-round lessons live in
|
||
`docs/agent-handoff.md` §§1/5 and `docs/gpu-initial-target-framing.md`
|
||
rev 3.
|
||
|
||
- **Folding Stage 2 (grid/daemon collapse) — MERGED as #149** (`main` @
|
||
`6ed4fe9`, 2026-07-24, after **five** review rounds). The grid TUI now
|
||
renders collapses. Spine (Q#FD12): `src/fold_view.rs`'s `VisibleLineMap`,
|
||
derived from the fold store plus a window's line offsets and **never
|
||
stored**, threaded as `Option<&'a VisibleLineMap>` on a lifetime-bearing
|
||
`Viewport<'a>` that stays `Copy`. No wire schema or protocol change; the
|
||
GPU path is Stage 3. 48 acceptance tests on the real `paint_frame` grid,
|
||
every behavioral claim bite-verified. Durable design points, each a trap
|
||
Stage 3 inherits:
|
||
- the map's unit is a **merged hidden component** (overlapping *or
|
||
adjacent* intervals unioned, keeping the earliest visible head), not a
|
||
fold — folds may cross, and a later fold's own head can be hidden;
|
||
- instances are **per rendered window** and **per command/event
|
||
operation**, never per frame; a command's map follows the operation's
|
||
**target** window, since a wheel event names a pane without activating it;
|
||
- fold projection is **per-frontend** (`FrontendView.fold_projection`) —
|
||
shared `EditorCore` motion would otherwise make a simultaneous unfolded
|
||
GPU session's cursor skip lines it still displays;
|
||
- a hidden cursor normalizes by **position**, not row, and `set_view_top`
|
||
clamps in the setter rather than being repaired at render time;
|
||
- the interactive-Lua unfold keys on the **post-intercept** edit site — a
|
||
managed buffer intercept may legally relocate the op.
|
||
|
||
Process notes worth keeping: `main` moved under the arc, and the merge was
|
||
textually clean but **not semantically clean** (#146 added `Viewport`
|
||
literals the new `folds` field invalidated) — a clean `git merge-tree` does
|
||
not mean the merged tree compiles. CI was red at review on the macOS/luajit
|
||
`outline_5_level_100_entry_renders_within_100ms` budget flake and went
|
||
green on rerun.
|
||
|
||
- **Documentation ledger refresh — MERGED as #147** (`main` @ `0a479ae`,
|
||
2026-07-24). The #142 housekeeping, expanded after review found the ledger
|
||
stale through four merges rather than one. Its own macOS/luajit red was the
|
||
vterm `VTERM_ALT_READY` PTY timeout; green on rerun.
|
||
|
||
- **Web grammars HTML + CSS — MERGED as #146** (`main` @ `47581f4`,
|
||
2026-07-23). `.html/.htm/.xhtml` and `.css` highlight off the official
|
||
`tree-sitter-html` 0.23 / `tree-sitter-css` 0.25 crate query constants (no
|
||
in-repo overlay), and HTML's `INJECTIONS_QUERY` lights up `<script>` → js
|
||
and `<style>` → css. Durable lesson recorded in
|
||
`docs/web-grammars-html-css-framing.md`: the `highlight.rs` capture 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/.cls` highlight via `codebook-tree-sitter-latex` 0.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.io `tree-sitter-latex` is provably broken (no
|
||
`scanner.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/dropping `View`, the structural
|
||
source (derived head line, closer-aware tail), the Lua data API +
|
||
interactive `C-c @` commands, the command-path pre-edit unfold, and
|
||
authoritative-empty `FoldState` production landed with no protocol bump.
|
||
The `folding` branch and worktree (`../pmacs-folding`) are retained but
|
||
carry nothing unmerged; the `folding-framing.md` framing 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 in `docs/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 in `docs/agent-handoff.md` and `docs/vterm-framing.md`.
|
||
|
||
- **Branches deleted 2026-07-22 (authorized):** `vterm-stage3-framing`
|
||
(Revision 8 framing; its content is carried on `vterm-gpu`, verified as a
|
||
superset before deletion — the branch was NOT an ancestor of `vterm-gpu`
|
||
because the framing was copied rather than merged, so it needed a forced
|
||
local delete) and `tab-width-parity` (a clean ancestor of `main` via
|
||
#137). Both removed as worktree + local ref + `githubsucks` ref; the
|
||
`origin` tracking refs were pruned. The `-framing` branches for each are
|
||
deliberately kept.
|
||
|
||
- **Tab-width rendering parity — MERGED as #137** (`main` @ `2625ec7`,
|
||
2026-07-22). One fixed 8-column `TAB_STOP_COLUMNS` in `pmacs-protocol`
|
||
now drives core/TUI columns, GPU code projection, and minimap width;
|
||
source bytes and protocol ranges are unchanged. Its lane, worktree, and
|
||
`tab-width-parity` branch (local + `githubsucks`) are deleted; the
|
||
`tab-width-parity-framing` branch is kept. This closes the long-standing
|
||
"tab width is a rendering-parity
|
||
bug, NOT a config gap" deferral recorded in `docs/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 to `docs/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:
|
||
|
||
1. update its public branch and head/state here;
|
||
2. record new verification and remove superseded caveats;
|
||
3. keep durable architecture in `docs/agent-handoff.md`, not here;
|
||
4. remove the lane after merge or abandonment;
|
||
5. verify every recovery command from a clean worktree before calling
|
||
the transfer complete.
|