Merge pull request #208 from levineuwirth/docs-absorption-2026-08-01

docs: absorption pass at cfc1710 — the whole board before a machine move
This commit is contained in:
Levi Neuwirth 2026-07-31 21:21:16 -04:00 committed by GitHub
commit 4223dd3476
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 178 additions and 307 deletions

View File

@ -1,10 +1,22 @@
# 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
entries when their PR merges; do not let this become a second permanent
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
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
@ -62,19 +74,17 @@ lesson, §1 for the two framings).
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).
`githubsucks/main` @ `cfc1710` (discovery Stage 1 #207, atop the
ambient-root isolation implementation #206, Journey Stage 1b-3 #205,
1b-2 #204 and 1b-1 #203, the reap-ledger diagnostic #202, the
isolation framing #201, the process-signal diagnostic #200, the ledger
absorption #199, 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
previous snapshot named `fbcf235`, 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
`cfc1710` or newer, so a tree at `fbcf235` 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.
@ -113,13 +123,22 @@ git worktree list
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
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.
**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
- **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
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;
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".
this arc is not — Stage 1 built the command surface and everything that
needs a Rust change is still ahead. Stage 1's durable facts are in
`docs/agent-handoff.md` §1.
**#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) — 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
```
- **Landed:** eleven `help.*` commands over the existing registries,
indexed by `M-x help`, with `editor.describe-command` /
`editor.describe-setting` kept as forwarders. No Rust, no protocol
change. §5 moved substrate-without-surface → **Partial**.
- **Stage 2 candidates, in rough dependency order:**
1. **Richer M-x rows** — a **protocol change**:
`MinibufferPrompt.candidates` is `Vec<String>`, while
`CompletionPopupRow` already carries `kind`/`detail`, so the wire
pattern is solved and the bump is the work.
2. **`Command` gains title / category / aliases / flags /
arg-schema** — a Rust type change across ~147 definition sites.
MCP currently works around the missing schema by stuffing rendered
JSON into the description string.
3. **Predicate evaluation.** `Command.predicate` is read at
`src/help.rs:76` and one test, and **evaluated nowhere**. Starting
to evaluate it makes commands stop being invocable, so it needs its
own decision about what "unavailable" means at each call site —
M-x, dispatch, menu. `discovery_acceptance`'s `d9` pins today's
behaviour with a *raising* predicate, so that stage must change the
pin knowingly.
4. **Help-layer unification.** `src/help.rs` is still orphaned. Stage 1
funnels every command through `pmacs.editor._show_help` and renders
via named per-subject functions, so the work is enumerated:
replace the four subjects `src/help.rs` covers (key, mode, hook,
buffer) and **write three new Rust renderers** for settings, lists
and apropos.
5. **The help prefix.** Deliberately untouched by Stage 1 — the
decision is one for the whole family, and `C-h` is **not** free
(non-kitty terminals cannot disambiguate Ctrl+Backspace from
Ctrl+H; both produce byte 0x08). `F1` / `C-c ?` / a rebind are the
candidates.
- **Two Stage-1 facts a Stage-2 author needs.** Completion is
**assistance, not validation**`resolve_accepted_value` returns the
literal typed text when no candidate is selected, so closed-set
acceptance is unbuilt Rust work. And **`invoke_interactive` is not the
M-x path**; the forwarders learned that the hard way in CI, since
`pmacs.command.invoke` is a real caller of the old names.
## 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
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
- **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
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
this order: 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.
Each has its own bullet below; this line is the head-of-`main` anchor
and nothing else.
- **`main` @ `cfc1710`.** Nine PRs landed since the previous anchor, in
this order: the ledger absorption #199, the process-signal diagnostic
#200, the test ambient-root isolation **framing** #201, the
reap-ledger diagnostic #202, Journey Stage **1b-1** #203, **1b-2**
#204 and **1b-3** #205, the ambient-root isolation **implementation**
#206, and discovery Stage 1 #207. Each has its own bullet below; this
line is the head-of-`main` anchor 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
Stage 2 framing #171 and the resource-op
delete guard framing #186 — both framing-only, no runtime code, no