diff --git a/docs/active-work.md b/docs/active-work.md index e09481f..a303151 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -21,11 +21,13 @@ number appears in `git log --first-parent githubsucks/main`, it has landed regardless of what a lane says. **Two open PRs had no lane here at all before this snapshot** — #174 and -#171 — and both now have one. An open PR is exactly the volatile work -this file exists to record, so its absence is a ledger defect rather -than a tidy omission: #171 drifted **153 commits** while invisible here, -and its still-green old CI run describes a tree nobody has looked at -since. When a PR is opened, give it a lane. +#171. An open PR is exactly the volatile work this file exists to +record, so its absence is a ledger defect rather than a tidy omission: +#171 drifted **153 commits** while invisible here, and its still-green +old CI run describes a tree nobody has looked at since. **When a PR is +opened, give it a lane.** #174 has since merged, so per rule 4 its lane +is gone again and its durable lesson is in `docs/agent-handoff.md` §5; +#171's lane is below. ## Repository authority @@ -36,14 +38,16 @@ since. When a PR is opened, give it a lane. 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` @ `6bee09d` (bottom-panel Stage 2B-1 #184, atop the - Journey/GPU directory-target ratchet #183, Journey Stage 1a #182 and - the previously recorded landed work). **Protocol schema support is + `githubsucks/main` @ `0442d78` (the M4 config-sink race fix #174, atop + bottom-panel Stage 2B-1 #184, the Journey/GPU directory-target ratchet + #183, Journey Stage 1a #182 and the previously recorded landed work). + **Protocol schema support is `v6..=v21`; the production server-first `Hello` still advertises v20** — two different facts, and #184 landed only the first. The previous snapshot named `7fd646d`, and **the recovery floor advances with it**: the check below now requires - `6bee09d` or newer, so a tree at `7fd646d` no longer passes. That is + `0442d78` or newer, so a tree at `7fd646d` — or at `6bee09d` — 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. @@ -82,7 +86,7 @@ git worktree list git status --short --branch ``` -The `git log` command must expose `6bee09d` — the base named above — or a +The `git log` command must expose `0442d78` — 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 @@ -224,15 +228,24 @@ If it does not, stop and repair the remote/fetch configuration. never been enforced. Any CI job that compiles the `crdt` targets has to fix them first or it will be red on arrival. -## Bottom-panel lane (Arc 7) — 2B-1 MERGED; 2B-2 IS THE NEXT SLICE +## Bottom-panel lane (Arc 7) — 2B-1 MERGED; 2B-2 IN FLIGHT Stage 1, the Stage 2 framing, Stage 2A, and **Stage 2B-1 are all on `main`**. Framing revision 5's three-way split of 2B was explicitly approved on 2026-07-27; revision 6 records PR #184's review correction. -Nothing in this arc is in flight. **2B-2 — the daemon panel projection -and epoch machine — is the next slice, and it branches fresh from -`githubsucks/main` @ `6bee09d` or newer. Do not stack it on the 2B-1 -branch.** +**2B-2 — the daemon panel projection and epoch machine — is under way** +on branch `bottom-panel-stage2b2`, worktree `../pmacs-bp-stage2b2`, +branched fresh from `githubsucks/main` @ `6bee09d`. It is *not* stacked +on the 2B-1 branch, which is the rule for every slice in this arc. Note +that `main` has since advanced to `0442d78`; the only difference is the +test-only #174, so the slice's integration surface is unchanged. + +**2B-2's boundaries, restated because they are easy to overrun:** the +production `Hello` stays at v20 and `panel_capable` stays `false`. The +slice is dark/test-only capability exactly as 2B-1 was. Compatible v21 +activation, the GPU band, and the negotiated capability flip are all +2B-3's, and 2B-3 may **not** simply change the unsolicited `Hello` to +21. - **Stage 2B-1 MERGED as #184** (`main` @ `6bee09d`, 2026-07-28; all twelve checks green on the reviewed head `5539b6e`; two review rounds @@ -446,64 +459,6 @@ has **no branch and no framing yet**. `FrontendView.fold_projection` to `true` for semantic frontends, which Stage 2 deliberately left `false` (Q#FD21). -## M4 config-sink race lane — PR #174 OPEN, REVIVED AND REGATED - -> **This ledger had no lane for #174 at all until this snapshot, and -> that is the defect worth naming.** An open PR is precisely the -> volatile work this file exists to record; two of them were invisible -> here (see the dired framing lane below), which is how one of them -> drifted 153 commits without anyone noticing. - -- Portable branch: `githubsucks/fix-m4-sink-races`; worktree - `../pmacs-m4-sink-race`. **PR #174**, base `main`. -- **Revived 2026-07-28**: it was 130 commits behind, so canonical `main` - @ `6bee09d` was merged in at `302c21c` and the whole gate suite rerun. - The diff against `main` is unchanged at **25 lines in one file** - (`tests/m4_acceptance.rs`). -- **The fix is one predicate.** - `m4_5_initial_config_pushed_via_did_change_configuration` waited for - `contains("probe")` and then asserted `"probe":true` — six bytes - further on. The sink is JSONL written by a *separate process*, so the - test could read a half-written line; Linux wins that race reliably and - macOS/lua54 did not, reporting `{"rust":{"probe":`. It now waits for a - trailing newline, which is true only once a whole record has landed. - **Any "wait until the file mentions X, then assert Y about the file" - shape races whenever Y is stricter than X.** -- **The structural claim was re-verified against current `main`, not - inherited from the original scout.** `src/bin/pmacs_fake_lsp.rs` *did* - change upstream (Lean 4 stages 3b, `1e1be67` / `cdaea66`), so the "one - `writeln!` is the only writer" argument could have rotted. It has not: - the config sink is still a single `writeln!` at `:433` on a - `create+append` handle. `tests/m4_acceptance.rs` itself has **zero** - upstream commits since the merge base `ccf29e3`, which is why the - merge was clean. -- **The race cannot be falsified locally, but the wait can be — and was, - two ways.** Replacing the predicate with an unsatisfiable one fails - the test at `m4_acceptance.rs:317` with "async pump deadline - exceeded", proving `pump_async` asserts rather than falling through - and that the new predicate genuinely has to become true. Restoring the - *old* predicate leaves the test passing locally, which confirms rather - than assumes that a green local run cannot tell the two apart. Treat - the local suite as a regression check and the structural argument as - the reason. -- **The sibling weak predicate is deliberately left alone.** `m4_26` - (`:5487`) has the same shape, and the obvious fix makes it *worse* — - waiting for the expected value turns a genuine `rootUri` regression - into a five-second timeout with a misleading message, trading a - precise diff for a vague hang. Closing it properly means giving that - sink a record terminator in the fake server, and it has never been - observed failing. The reasoning is a comment at the call site so the - next person does not repeat the first instinct. -- Gates on `302c21c`: fmt; strict workspace Clippy; library **1,849 + 3 - ignored** default and **2,034 + 4 ignored** CRDT; m4 **121 passed + 3 - ignored + 1 filtered** with basedpyright skipped; the two touched - tests run explicitly **2/2**; required GPU **202/202**; - `git diff --check`. -- **No ledger entry was added on the PR branch itself** — this file was - owned by the landed-state sync while #174 was revived, and a - drive-by lane there would have re-conflicted it for no benefit. This - is that entry. - ## dired Stage 2 framing lane — PR #171 OPEN, STALE, DO NOT MERGE AS-IS - Portable branch: `githubsucks/dired-stage2-framing` (head `ab42a79`, @@ -536,7 +491,11 @@ has **no branch and no framing yet**. fan-out a rename transaction has to survive. Re-scout against `6bee09d`, publish a new revision, and get explicit - framing approval before any implementation. + framing approval before any implementation. **The re-scout is under + way** on the existing branch, so PR #171 keeps its three-round + history; the product is a revision 5, not a new document. (`main` has + since advanced to `0442d78`, but the only difference is the test-only + #174, so no re-scout conclusion turns on it.) - **The rename problem the framing must still answer**, restated because it is the hard part: a rename is a transaction across **five** path owners — the buffer path, the buffer name, the URI-keyed LSP stores diff --git a/docs/agent-handoff.md b/docs/agent-handoff.md index ea5319f..6c99502 100644 --- a/docs/agent-handoff.md +++ b/docs/agent-handoff.md @@ -1,7 +1,9 @@ # Agent handoff — cross-machine continuity -**Last updated: 2026-07-28, after bottom-panel Stage 2B-1 (#184) merged; -the canonical landed base is that merge — the reserved protocol-v21 +**Last updated: 2026-07-28, after the M4 config-sink race fix (#174) and +bottom-panel Stage 2B-1 (#184) merged; the canonical landed base is +`0442d78`. #174 is test-only. #184 is the substantive one — the reserved +protocol-v21 bottom-panel wire family, dark by construction, with the production handshake deliberately still advertising v20 — following the Journey/GPU directory-target ratchet (#183), following Journey Stage 1a (#182), @@ -49,7 +51,8 @@ commands, read `docs/active-work.md` immediately after this file. ## 1. Where the project stands (2026-07-28) -- `main` @ `6bee09d` (bottom-panel Stage 2B-1 #184, atop the Journey/GPU +- `main` @ `0442d78` (the M4 config-sink race fix #174 — test-only — + atop bottom-panel Stage 2B-1 #184, the Journey/GPU directory-target ratchet #183, Journey Stage 1a #182, incorporating terminal configuration + copy mode landed docs #180, Lean 4 Stage 4b #181, the dired Stage 1 landed @@ -528,7 +531,7 @@ commands, read `docs/active-work.md` immediately after this file. negotiated `panel_capable` flip. Production attachment remains v20 through 2B-2. Parent acceptance 37–55 remains authoritative. Stage 3 is the adopter default flip. **2B-2 is the next slice, and - it branches from `6bee09d` or newer.** + it branches from `0442d78` or newer.** - **The §1.3 census is CLASSIFIED, not uniformly redirected.** Only the Projection class (#1–#12, #21–#22) routes through `primary_document_window`; focus/input (#13–#15, #23), focus chrome @@ -1412,6 +1415,33 @@ round-trip cannot detect a discriminant shift. 0/15 failures with all 16 cores saturated versus 1/10 for the ticking helper.** The general rule: an observation helper that advances the system is not an observation. +- **A wait predicate that is WEAKER than the assertion it guards is a + race, on every platform that happens to lose it.** #174: the m4 config + sink test waited for `contains("probe")` and then asserted + `"probe":true` — six bytes further on. The sink is JSONL written by a + *separate process*, so the test could read a half-written line; Linux + won that race reliably and macOS/lua54 did not, reporting the + truncated `{"rust":{"probe":`. **Any "wait until the file mentions X, + then assert Y about the file" shape races whenever Y is stricter than + X.** The fix is to wait for the exact unit the assertion reads — here + a trailing newline, true only once a whole `writeln!` record has + landed, and still correct if the payload's field order or spelling + changes. Two things generalize beyond the fix: + - **A race you cannot reproduce can still be bitten at one remove.** + The failure needs a scheduler outcome Linux does not produce, so no + local run falsifies it. But `pump_async` *asserts* on its deadline, + so replacing the predicate with an unsatisfiable one proves the wait + is load-bearing rather than decorative — and restoring the *old* + predicate, which still passes locally, proves a green local run + cannot distinguish the two. Bite what you can reach and say plainly + which claim rests on structure instead of evidence. + - **The obvious fix is sometimes worse.** The sibling `m4_26` has the + same weak shape and was deliberately left alone: waiting for the + expected value would convert a genuine `rootUri` regression — what + its `assert_ne!`s exist to catch — into a five-second timeout with a + misleading "server didn't initialize?" message, trading a precise + assertion diff for a vague hang. Closing it properly needs a record + terminator in the fake server first. - **Proving a child has exited is harder than it looks on this codebase.** A fixed sleep is not proof; nix's `waitid` is unavailable on macOS; and `libc::waitid` needs `unsafe`, which