docs: absorption pass at cfc1710 — the whole board, before a machine move

Nine PRs landed since the handoff's anchor (#199-#207) and it named
only four of them, so a fresh machine would not have learned that
ambient-root isolation exists, that the journey arc's 1b split
completed, or that the reap-ledger diagnostic landed. The ledger's
canonical base and recovery floor were five merges stale, and three
lanes described merged work as in flight.

Taken now because ZERO PRs are open. The ledger's own rule is never to
open a standalone refresh PR — because with several PRs open a lane
written on `main` re-conflicts at every merge — and this is the one
window where that cost is nil.

The handoff gains §1a, "Outstanding work — the whole board", which is
the point of the pass rather than a by-product: every arc against §20's
priority order with its next step, every open lane, every deferred item
attributed to the framing that parked it, and the standing hazards
someone running gates on a new machine needs — the three known flakes
by name, the basedpyright skip, the crdt sweep's build prerequisite,
and why a green a37 means nothing alone.

Two arcs completed, so per rule 4 their lanes are removed and their
facts are in the handoff: Journey Stage 1 and test ambient-root
isolation. Discovery and reap-ledger merged a stage each, so their
lanes are rewritten to the remaining plan rather than deleted — the
discovery lane now enumerates Stage 2 in dependency order and carries
the two Stage-1 facts a Stage-2 author would otherwise rediscover.

Base and floor advance together to `cfc1710`, per the file's own rule
that a floor accepting an older commit than the declared base passes on
a tree the document does not describe.

The recovery path was EXERCISED rather than asserted: from an empty
directory, clone, alias, fetch, floor check, and a lane worktree all
ran clean. The two-argument `git worktree add` still fails for a
remote-only branch, which is why every lane spells out the `-b` form.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lv428Fth9LRtffwJSsqH7T
This commit is contained in:
Levi Neuwirth 2026-07-31 21:03:33 -04:00
parent cfc1710cf9
commit c8f111413f
2 changed files with 178 additions and 307 deletions

View File

@ -1,10 +1,22 @@
# Active work — cross-machine resume ledger # Active work — cross-machine resume ledger
**Snapshot: 2026-07-30.** This file records volatile work that has not **Snapshot: 2026-08-01.** This file records volatile work that has not
landed on `main`. Read it after `docs/agent-handoff.md`. Remove completed 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 entries when their PR merges; do not let this become a second permanent
backlog. backlog.
**This snapshot is an absorption pass, taken with ZERO open PRs** —
the one window in which a ledger refresh has nothing to re-conflict
with, and taken deliberately before a machine move. Nine PRs landed
since the previous anchor (#199#207). Two arcs completed and their
lanes are **removed**, their durable facts now in
`docs/agent-handoff.md` §1: **Journey Stage 1** (1a plus the whole 1b
split) and **test ambient-root isolation**. Discovery and reap-ledger
merged a stage each and keep lanes rewritten to what remains.
`docs/agent-handoff.md` §1a now carries the whole board — every arc,
open lane, deferred item and standing hazard in one view.
*(Earlier note, retained because its reasoning still governs:)*
**This snapshot is an absorption pass.** Eight PRs merged on 2026-07-29 **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 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 ledger had drifted to 1,854 lines carrying six lanes whose work was
@ -62,19 +74,17 @@ lesson, §1 for the two framings).
machine-local: `origin` may name this canonical URL, a release mirror, machine-local: `origin` may name this canonical URL, a release mirror,
or something else, and therefore has no authority by name alone. or something else, and therefore has no authority by name alone.
- Canonical base at this snapshot: - Canonical base at this snapshot:
`githubsucks/main` @ `fbcf235` (the reap-ledger diagnostic #202, atop `githubsucks/main` @ `cfc1710` (discovery Stage 1 #207, atop the
the test ambient-root isolation framing #201, the reap-ledger framing ambient-root isolation implementation #206, Journey Stage 1b-3 #205,
#200, the ledger absorption #199, the M5.5 protocol-version pin #198, 1b-2 #204 and 1b-1 #203, the reap-ledger diagnostic #202, the
the docs-only coherence listview correction #189, the docs-only isolation framing #201, the process-signal diagnostic #200, the ledger
landed-state refresh #185, the M4 config-sink race fix #174, absorption #199, and the previously recorded landed work).
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 **Protocol schema support is
`v6..=v21`; the production server-first `Hello` still advertises `v6..=v21`; the production server-first `Hello` still advertises
v20** — two different facts, and #184 landed only the first. The v20** — two different facts, and #184 landed only the first. The
previous snapshot named `7586905`, and **the previous snapshot named `fbcf235`, and **the
recovery floor advances with it**: the check below now requires recovery floor advances with it**: the check below now requires
`fbcf235` or newer, so a tree at `7586905` no longer passes. That is `cfc1710` or newer, so a tree at `fbcf235` no longer passes. That is
deliberate — the floor moves with the base, because a check that deliberate — the floor moves with the base, because a check that
accepts an older commit than the declared base passes on a tree the accepts an older commit than the declared base passes on a tree the
rest of this file does not describe. rest of this file does not describe.
@ -113,13 +123,22 @@ git worktree list
git status --short --branch git status --short --branch
``` ```
The `git log` command must expose `fbcf235` — the base named above — or a The `git log` command must expose `cfc1710` — the base named above — or a
newer intentional main. Keep this threshold and the canonical-base line in 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 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 declares canonical will pass on a tree the rest of this file does not
describe. describe.
If it does not, stop and repair the remote/fetch configuration. If it does not, stop and repair the remote/fetch configuration.
**This path was exercised, not asserted, at this snapshot** — ahead of a
machine move. From an empty directory: `git clone` the canonical URL,
add the `githubsucks` alias, `git fetch githubsucks --prune`, confirm
`cfc1710` is an ancestor of `githubsucks/main`, and recover a lane with
the three-argument `git worktree add <path> -b <local> githubsucks/<branch>`
form. All four steps ran clean. **The two-argument form still does not
work** for a remote-only branch (`fatal: invalid reference`), which is
why every lane below spells out the `-b` form.
## The CRDT half of the test corpus is dark in CI — NEEDS A LANE ## 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 - **No branch, no framing yet.** Found while gating #166, then measured
@ -255,230 +274,50 @@ If it does not, stop and repair the remote/fetch configuration.
never been enforced. Any CI job that compiles the `crdt` targets has to never been enforced. Any CI job that compiles the `crdt` targets has to
fix them first or it will be red on arrival. fix them first or it will be red on arrival.
## Journey lane (P1) — 1a, 1b-1, 1b-2 MERGED; 1b-3 PR #205 OPEN ## Discovery lane (P4) — STAGE 1 MERGED (#207); STAGE 2 IS NEXT
**Rewritten, not removed.** Rule 4 removes a lane when its ARC is done; **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 this arc is not — Stage 1 built the command surface and everything that
still open. Stages 1a (#182/#183), 1b-1 (#203) and **1b-2 (#204)** are needs a Rust change is still ahead. Stage 1's durable facts are in
all on `main` and their durable facts are in `docs/agent-handoff.md` §1, `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` - **Landed:** eleven `help.*` commands over the existing registries,
§2's step-9 row reads **Works**, §2's keybinding-inversion paragraph indexed by `M-x help`, with `editor.describe-command` /
records all three examples answered (the quote itself deliberately `editor.describe-setting` kept as forwarders. No Rust, no protocol
unchanged), §20 Priority 1 and its arc list say "landed", and the change. §5 moved substrate-without-surface → **Partial**.
handoff bullet says LANDED. **#204's is discharged too**: §2's step-6 - **Stage 2 candidates, in rough dependency order:**
row names it as landed and stays **Partial** for a reason that landing 1. **Richer M-x rows** — a **protocol change**:
did not touch — a server that starts and then *crashes* is still `MinibufferPrompt.candidates` is `Vec<String>`, while
unsurfaced. Both rode this branch rather than standalone docs PRs, `CompletionPopupRow` already carries `kind`/`detail`, so the wire
which would have re-conflicted on every merge. pattern is solved and the bump is the work.
2. **`Command` gains title / category / aliases / flags /
### Stage 1b-3 — IMPLEMENTED, PR #205 open arg-schema** — a Rust type change across ~147 definition sites.
MCP currently works around the missing schema by stuffing rendered
- **Branch `journey-stage1b3-welcome`**, worktree `../pmacs-journey-1b3`, JSON into the description string.
based on `githubsucks/main` @ `1f290d5`, **integrated with `main` @ 3. **Predicate evaluation.** `Command.predicate` is read at
`5376af1`** (#204). **Implemented; PR #205 open.** `src/help.rs:76` and one test, and **evaluated nowhere**. Starting
`docs/journey-stage1b3-welcome-framing.md` revision 4, three to evaluate it makes commands stop being invocable, so it needs its
review rounds closed (round 1: four findings; round 2: two acceptance own decision about what "unavailable" means at each call site —
holes plus a doc correction; round 3: a visibility mismatch and an M-x, dispatch, menu. `discovery_acceptance`'s `d9` pins today's
unobservable assertion; all accepted). The last of the 1b split behaviour with a *raising* predicate, so that stage must change the
(1b-1 landed #203, 1b-2 landed #204 at `5376af1`). pin knowingly.
- **What it is.** Journey step 4 / `COHERENCE.md` §18: a fresh `pmacs` 4. **Help-layer unification.** `src/help.rs` is still orphaned. Stage 1
greets the user with an empty buffer, an empty status line, and no funnels every command through `pmacs.editor._show_help` and renders
indication that `M-x` exists. The stage renders a three-line welcome via named per-subject functions, so the work is enumerated:
into `*scratch*` and adds a minimal `M-x help`. replace the four subjects `src/help.rs` covers (key, mode, hook,
- **`EditorState::new()` is NOT the no-argument entry point**, and buffer) and **write three new Rust renderers** for settings, lists
revision 1 rested every criterion on the assumption that it was. and apropos.
`EditorState::open` calls it *before* resolving the target 5. **The help prefix.** Deliberately untouched by Stage 1 — the
(`src/editor.rs:944`), the daemon constructs one too, user config runs decision is one for the whole family, and `C-h` is **not** free
*inside* it, and desktop restore happens much later (non-kitty terminals cannot disambiguate Ctrl+Backspace from
(`restore_desktop_if_armed`, `:3637`, inside `run()`'s `RunLocal` Ctrl+H; both produce byte 0x08). `F1` / `C-c ?` / a rebind are the
arm). The stage now adds a **launch-finalization seam** called right candidates.
after desktop restore, with `had_file` — already threaded to that - **Two Stage-1 facts a Stage-2 author needs.** Completion is
point for the same kind of question — as the no-target signal. **assistance, not validation**`resolve_accepted_value` returns the
**Round 2 closed the wiring hole rather than disclaiming it.** literal typed text when no candidate is selected, so closed-set
Revision 2 called `run()`'s call to the seam an untestable residual — acceptance is unbuilt Rust work. And **`invoke_interactive` is not the
but deleting that one line left every proposed pin green while M-x path**; the forwarders learned that the hard way in CI, since
shipping no welcome, because the pins called the seam by hand. The `pmacs.command.invoke` is a real caller of the old names.
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) — IMPLEMENTED, PR OPEN
- **Branch `discovery-stage1-commands`**, worktree `../pmacs-p4-discovery`,
based on `githubsucks/main` @ `54a092e`. **Implemented; PR open.**
`docs/discovery-stage1-command-family-framing.md` revision 6,
three review rounds closed (round 1: two blocking, two major; round 2:
two blocking, two major; round 3: two factual corrections; all
accepted), Q#D2 / Q#D3 decided by the user, and the final review's
acceptance corrections applied.
- **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
`test.apropos-subsequence-fixture` whose `qzjx` letters occur as `q z
j x`, only after asserting no registered name or description contains
`qzjx` as a substring; it must find nothing, whereas fuzzy finds the
fixture.
- **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`.
Six of the eleven canonical commands (`help.describe-command`,
`help.describe-setting`, `help.describe-key`, `help.describe-hook`,
`help.where-is`, and `help.apropos`) 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.
- **Implementation notes.** `runtime/help.lua` is new and owns the
family plus the `help` index, which **moved out of `welcome.lua`** so
the greeting file keeps only the greeting; it loads after welcome.lua
so the index can read `pmacs.welcome.entries`.
- **The seam-counting pin caught a real bypass immediately.** The two
renamed commands were still calling the file-local `show_help_text`,
so the funnel the framing promised was fiction for exactly the two
commands that predate the seam. They now call
`pmacs.editor._show_help`, with a comment saying why the in-scope
local is deliberately not used.
- **Bites, all directed** — six mutations, each failing exactly one pin:
fuzzy apropos, name-only apropos, static where-is, a dropped
forwarder, an unindexed family command, and one command writing
`*help*` itself (seam count 10 vs 11).
- **§5 moves substrate-without-surface → Partial ON THIS PR** per §25,
with the remaining gaps named in the row: packages and workers have no
surface, `Command` still lacks title/category/flags, M-x rows are bare
names, and the Rust help layer is still orphaned.
- 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 ## Generated-buffer immutability lane (Arc: workbench primitives) — STAGE 1 MERGED; STAGE 2 IS NEXT
@ -514,72 +353,6 @@ compatible.
- **DAP waits for Stage 2, not Stage 1** — that dependency is now - **DAP waits for Stage 2, not Stage 1** — that dependency is now
satisfied. satisfied.
## Test ambient-root isolation — IMPLEMENTATION OPEN
- **Framing MERGED (#201)**;
`docs/test-ambient-config-isolation-framing.md` **revision 5**
(revision 4 approved after three review rounds — eight blocking, six
major, all accepted; revision 5 rides the implementation PR and records
findings, not a new design round).
- **PR #206 OPEN**, one review round closed. **§7's branch plan was
consciously exceeded**: the whole-corpus migration rides this PR rather
than a follow-up lane, because splitting would either leave 65 suites
writing the real data root or ship acceptance 12's ratchet with a
~65-file allowlist that does not ratchet. Recorded in framing revision
5; accepted in review.
- **Implementation branch `test-ambient-isolation-impl`**, worktree
`../pmacs-test-isolation-impl`, based on `githubsucks/main` @
`54a092e`. The framing-only worktree `../pmacs-test-isolation`
(branch `test-ambient-config-isolation`) is spent — its doc is on
`main`.
- **What landed on the branch.** `pmacs::bootstrap::BootstrapRoots`
(config/data/state/cache, `ambient()` for production), reachable from
`EditorState::new_with_roots` **and** `open_with_roots` and consulted
again by `install_state_dirs`; all 342 in-process construction sites
in 65 files migrated; `journey_acceptance` keeps the ambient `open`
and re-execs itself per test with controlled roots instead; the shared
daemon and PTY spawners now set all five storage variables;
`tests/ambient_isolation_acceptance.rs` carries the hostile-environment
proof and the adoption ratchet.
- **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-impl \
-b test-ambient-isolation-impl \
githubsucks/test-ambient-isolation-impl
```
## Reap-ledger silent failures — MERGED (#202); kept for its parked follow-ons ## Reap-ledger silent failures — MERGED (#202); kept for its parked follow-ons
- **Branch `reap-ledger-silent-failures`**, worktree - **Branch `reap-ledger-silent-failures`**, worktree

View File

@ -58,16 +58,114 @@ reads it the way you just did.
For volatile branches, checkpoints, verification, and recovery For volatile branches, checkpoints, verification, and recovery
commands, read `docs/active-work.md` immediately after this file. commands, read `docs/active-work.md` immediately after this file.
## 1. Where the project stands (2026-07-30) ## 1. Where the project stands (2026-08-01)
- **`main` @ `4cd4a7b`.** Eight PRs landed since the previous anchor, in - **`main` @ `cfc1710`.** Nine PRs landed since the previous anchor, in
this order: the generated-buffer immutability **framing** #188, the this order: the ledger absorption #199, the process-signal diagnostic
resource-op delete-guard **implementation** #190, silent-skip arming #200, the test ambient-root isolation **framing** #201, the
#192/#193/#194, CI timeouts and concurrency #195, the process teardown reap-ledger diagnostic #202, Journey Stage **1b-1** #203, **1b-2**
stdin-deadlock fix #197, dired Stage 2a #196, generated-buffer #204 and **1b-3** #205, the ambient-root isolation **implementation**
immutability **Stage 1** #191, and bottom-panel **Stage 2B-3** #198. #206, and discovery Stage 1 #207. Each has its own bullet below; this
Each has its own bullet below; this line is the head-of-`main` anchor line is the head-of-`main` anchor and nothing else.
and nothing else. - **Two arcs completed in that run, and their lanes are gone from
`docs/active-work.md` accordingly** (rule 4 removes a lane once its
arc is done *and* its facts are here): **Journey Stage 1** — 1a plus
the whole 1b split — and **test ambient-root isolation**. Discovery
and reap-ledger merged a stage each and keep rewritten lanes.
### 1a. Outstanding work — the whole board, 2026-08-01
**Read this before picking anything up.** It is the only place the
remaining work is enumerated in one view; the per-arc bullets below give
the detail. Nothing here is in flight: **zero PRs are open** at this
anchor, so every item is startable.
#### Arc state, against `COHERENCE.md` §20's priority order
| P | Arc | State | What is next |
|---|---|---|---|
| 1 | **Journey** | **Stage 1 COMPLETE** (1a #182/#183; 1b-1 #203, 1b-2 #204, 1b-3 #205) | Journey runs to step 10. The thin end is now steps **1** (install — that is P8), **11** (background work: visible but no ownership model, §9) and **12** (session restore: desktop-save is opt-in *and* a documented no-op under a daemon) |
| 2 | Workspace + location | Missing; model gap | The long-lead arc. Start before a fifth subsystem grows its own root convention — four have already diverged (§7) |
| 3 | Extension ownership | Missing; prerequisite-shaped | **`pmacs.hook.remove` does not exist.** That one bug-sized gap blocks §13's disable/uninstall, §10's trust classes, and package-scoped cancellation |
| 4 | **Discovery** | **Stage 1 MERGED (#207)** | Stage 2 candidates, in rough dependency order: richer M-x rows (**protocol change** — `MinibufferPrompt.candidates` is `Vec<String>`; `CompletionPopupRow` already proves the pattern), `Command` gaining title/category/aliases/flags/arg-schema (~147 definition sites), predicate evaluation, help-layer unification, and the help-prefix decision |
| 5 | Workbench convergence | Partial, best trajectory | Bottom panel done both frontends; **Stage 3 = flip the adopter default**. Then the tree primitive — build it *before* dired and the worker tree invent two |
| 6 | Config productization | Foundation only | Value provenance, then layering, then adoption migration (**table-valued settings are the hard prerequisite** — `ConfigValue` is four scalars) |
| 7 | Package lifecycle | Not started | Correctly sequenced after P3 |
| 8 | Distribution | Zero | Independent of everything, startable any time. **Every other priority's value is invisible until this one exists** |
#### Open lanes (branch exists, work not finished)
- **CRDT half of the corpus is dark in CI — still no lane, no owner.**
CI never enables `crdt`, so those tests are *not compiled*, not merely
skipped. **Re-measure before quoting a number**; the ledger's figure
moves with every merge.
- **Generated-buffer immutability** — Stage 1 merged (#191); Stage 2
not started. Four writer mechanisms have still not adopted
`set_generated_contents`; key the inventory by *writer*, not buffer.
- **Bottom panel** — Stage 2 complete; Stage 3 is the adopter default
flip.
- **Folding** — Stages 12 merged; Stage 3 (GPU) next, with
mirror-clear-on-snapshot a named obligation.
- **Reap-ledger** — diagnostic merged (#202); every *disposition* change
is still parked (below).
- **Kill-ring browser + persistence** — parked by choice.
#### Deferred work, by the framing that parked it
Each was a deliberate decision with a stated reason; none is a to-do
someone forgot.
- **From LSP guidance (#204):** build `pmacs.error` (**fifteen guarded
call sites reporting through a channel that does not exist**);
promote the spawn-failure record into Rust's status model so `*lsp*`
shows failures natively; surface `LspEventKind::Crashed` — a server
that *started then died* is unsurfaced and is a different message.
- **From the welcome (#205):** the help prefix — **`C-h` is not free**;
it deletes a word because non-kitty terminals cannot disambiguate
Ctrl+Backspace from Ctrl+H (both byte 0x08), so rebinding breaks
Ctrl+Backspace on every legacy terminal. Also: make `show_help_text`
adopt the generated-buffer write invariant; onboarding proper (§18's
ten-step sequence).
- **From discovery (#207):** everything in the P4 row above, plus
settings **value provenance** (§11) and **closed-set acceptance
semantics** — completion today is *assistance*, not validation:
`resolve_accepted_value` returns the literal typed text when no
candidate is selected, so a typo still reaches the handler.
- **From the reap ledger (#202):** Q#RL1's strict-`ESRCH` probe and
Q#RL2's retry policy — **neither can move alone**, because
`shutdown()`'s loop exits when the ledger empties, so a strict probe
without a loop change converts a silent early exit into a guaranteed
2 s stall at every editor exit. Also parked: retargeting to the
measured pgid, `signal_target`'s read-then-kill of `tcgetpgrp` (the
"most likely real fix site"), and **why** a group-directed `kill`
returns `EPERM` against a live owned child — still unknown.
- **From ambient isolation (#206):** production still resolves ambient
roots by default (only *construction* gained a parameter); nothing
audits what previously wrote into `~/.local/share/pmacs`.
#### Side quests and standing hazards
- **`pmacs.error` is undefined in production.** Fifteen `if pmacs.error
then` guards make the silence look deliberate. Report through
`pmacs.editor.set_status` until the channel is built.
- **Flakes that are not your change.** Judge a red run against these
before bisecting: `process::tests::a_successful_signal_disposition_...`
(macOS-only, signal timing); `a33_headless_terminal_frame_paints_...`
(GPU under parallel load, "0 blue pixels"); `m6_8_supervisor_reaps_...`
(load-sensitive). **Rerun before concluding.**
- **`basedpyright` hangs forever** — always
`cargo test --test m4_acceptance -- --skip basedpyright`.
- **The crdt sweep needs `cargo build --workspace` first**, or twelve
`gpu_invocation_acceptance` tests fail on a missing `pmacs-gpu`
binary.
- **A green a37 means nothing on its own** — the vterm real-daemon
acceptance returns `ok` without running unless `pmacs-gpu` is built.
- **Previous anchor, retained for provenance:** `4cd4a7b` (the
generated-buffer immutability framing #188, the resource-op
delete-guard implementation #190, silent-skip arming #192/#193/#194,
CI timeouts and concurrency #195, the process teardown stdin-deadlock
fix #197, dired Stage 2a #196, generated-buffer immutability Stage 1
#191, and bottom-panel Stage 2B-3 #198).
- **Previous anchor, retained for provenance:** `6c9e765` (the dired - **Previous anchor, retained for provenance:** `6c9e765` (the dired
Stage 2 framing #171 and the resource-op Stage 2 framing #171 and the resource-op
delete guard framing #186 — both framing-only, no runtime code, no delete guard framing #186 — both framing-only, no runtime code, no