From c8f111413ffb1f5680723ebef9545df31ddb7a48 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Fri, 31 Jul 2026 21:03:33 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20absorption=20pass=20at=20cfc1710=20?= =?UTF-8?q?=E2=80=94=20the=20whole=20board,=20before=20a=20machine=20move?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Claude-Session: https://claude.ai/code/session_01Lv428Fth9LRtffwJSsqH7T --- docs/active-work.md | 369 ++++++++---------------------------------- docs/agent-handoff.md | 116 +++++++++++-- 2 files changed, 178 insertions(+), 307 deletions(-) diff --git a/docs/active-work.md b/docs/active-work.md index 6663937..4ba8b9f 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -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 -b githubsucks/` +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` — 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`, 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 - ` 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 diff --git a/docs/agent-handoff.md b/docs/agent-handoff.md index 48a6605..e958bc7 100644 --- a/docs/agent-handoff.md +++ b/docs/agent-handoff.md @@ -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`; `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 1–2 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