From 2c59fac5100b803f6a8d969ab2b5f4d7ce66788a Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Thu, 6 Aug 2026 10:17:46 +0200 Subject: [PATCH 1/4] docs: retire two arcs, and file two reds the scan turned up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RULE 4 REMOVES A LANE AFTER MERGE, and two were overdue: the tree primitive (#217) and macOS CI signal integrity (#215, #216). The macOS arc could not simply be deleted. It owned R1 and R3, and a lane removed while it still owns undone work does not close that work, it hides it. R1 goes to a new async-runtime block; R3 goes to the reap-ledger lane, which already parks every disposition change pending exactly its question. Re-homing first is why this did not happen at merge. AN OCCURRENCE SCAN (last 25 main runs: 23 green, 2 red) found both reds unrecorded, and I misattributed both on the first pass — by theme rather than by required fragment, which is the exact error the registry exists to prevent. - Run 30710662474 is NOT R3. Same test, same EPERM, same measured_group=unobservable(ESRCH...) — but R3 requires `leader=live` and this reads `leader=exited(signal SIGUSR1)`, R2's exact fragment. It is a second R2 occurrence, four days BEFORE R2's retirement, so it corroborates the row rather than falsifying its disposition. It also adds something: macOS luajit where R2's evidence was lua54, so the mechanism was never flavor-specific. Filing it as R3 would have attached a live possible product defect to an occurrence of a fixed test race. - Run 30555667095 is NOT R1. Different test, different module, different assertion; they share only "supersede under a deadline on macOS". Sharing a subject is not sharing a signature. Filed as R5, undiagnosed. R6 is the acc28 readiness timeout from #217's CI. Its scope is the AUDIT, not the call site: three independently written readiness helpers now exist and they disagree, with bottom_panel_stage1's carrying only the zero-byte half of #216's hardening. R4's disposition predicted this recurrence under a new selector. Patching acc28 alone repeats the mistake this arc already made once, when the empty-file predicate was fixed in one helper and left in its neighbour. The registry carries a rate for the first time — a floor, not a measurement: main only, 25 runs, readable reds only. Four durable lessons to the handoff, two from the tree review: an optional field the shape depends on is not optional, and a contract two mechanisms must honour is only as strong as the weaker one. Verified: fmt, diff-check, --lib, listview 26/26. Docs-only. Co-Authored-By: Claude Opus 5 --- docs/active-work.md | 323 ++++++++++++-------------------------- docs/agent-handoff.md | 44 ++++++ docs/ci-red-signatures.md | 90 ++++++++++- 3 files changed, 233 insertions(+), 224 deletions(-) diff --git a/docs/active-work.md b/docs/active-work.md index 4c596b4..80ba9bb 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -180,87 +180,38 @@ 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. -## Tree primitive (P5) — PR #217 OPEN, awaiting review +## Tree primitive (P5) — MERGED as #217; adoption is the open work -**PR #217** — https://github.com/levineuwirth/pmacs/pull/217. It was -held through four framing rounds plus a review round; the hold is over -and the work is with the user. Full gate suite green at the tip, -including `PMACS_REQUIRE_GPU=1 -p pmacs-gpu` 221/0. +**The lane is gone, not the work.** Rule 4 removes a lane after merge, +and the primitive is merged: `listview` rows take optional `depth` and +`id`, collapse is primitive-owned, selection re-seats by id, and the +LSP outline is the one adopter. `COHERENCE.md` §14 is ◐ and §20 says +adoption rather than construction, which is where the remaining work is +recorded — **not here**, because none of it is in flight. -- **Branch `tree-primitive-framing`**, base `githubsucks/main` @ - `12f2970`. **Pushed and in sync** — held means no PR is open, not - that the work is stranded locally, so it is portable per the - handoff's rule. `githubsucks/tree-primitive-framing` is the - authoritative tip; `d92f0ad` was it as of the line above, and any - edit to this lane advances past it, which is why the ref and not a - pinned SHA is the thing to trust. Recover with `git fetch githubsucks - && git checkout tree-primitive-framing`. Framing - `docs/tree-primitive-framing.md` **revision 5** — approved after four - review rounds, with Q#TR1–TR4 decided. -- **The substantive arc** (not an exhaustive log — read `git log - 12f2970..githubsucks/tree-primitive-framing` for that): `61b1062` - framing through `5186bfd` rev 4, which carried the `COHERENCE.md` §14 - call-site correction; `49a42ec` the primitive; `8f64c3b` - byte-identity coverage plus the verification record; `38e94dc` this - lane, §14's ✗ → ◐ and the handoff facts; `c59de95` the review round — - moving-selection witness, flat-panel TAB delegation, §20's build → - adopt correction. +What review found is worth carrying forward, since all four were +invisible to a passing suite: -### What it ships +- **A selection test that toggled the root proved nothing.** The root + sits on line 1 before and after collapsing, so it passed unchanged + under the line-keyed re-seating that id-keyed re-seating replaced. + A moving-node witness (`tr_4`) was the fix. +- **TAB was bound on every listview**, so flat panels lost their + fall-through to the global binding and the Q#P3 read-only intercept. + Delegation restored it. +- **`item` was effectively required.** `line_to_item` is sparse when a + row omits the optional `item`, and `seat_cursor` took `#` of it — a + display-only tree stranded the cursor on the header. Every existing + test supplied `item`, so none could reach it. +- **"Opaque, compared by equality" was two contracts.** Selection uses + `==`; collapse keys a table, which consults no `__eq`. Narrowed to + string-or-number, and with it uniqueness and not-NaN, all enforced + where rows enter. -`listview` gains **optional** `depth` and `id` on rows; absent, a row -behaves exactly as before, which is what leaves the flat consumers -untouched. Collapse state is **primitive-owned**, keyed by -consumer-supplied id. Selection is re-seated **by id, not by line**. -`TAB` toggles; a leaf reports rather than silently doing nothing. - -**The observation that made it cheap:** collapse only ever *hides* rows -and never changes a surviving row's depth, and consumers emit parents -before children, so descendants are a **contiguous run**. Folding is -therefore **local projection state, not a refresh protocol** — the -primitive re-renders from its own array without calling the consumer, -which is why the anchor consumer works at all: **the outline has no -`on_refresh`**. - -The LSP outline adopts, supplying `depth` and `id = line:col`; its -`text` stays consumer-rendered per Q#TR4. - -### Verification - -| gate | result | -|---|---| -| luajit sweep | **3453 / 0** (= `main` 3450 + 3 listview tests) | -| crdt sweep, isolated ×2 | **3722 / 0** (= `main` 3718 + 4 tests) | -| `listview_acceptance` | 22 / 22 | -| `m4_acceptance` | 150 / 150 (basedpyright skipped) | -| `--lib` | 1896 / 0 | -| fmt · diff-check · clippy ±crdt | pass | - -Both behavioural claims are **bite-verified**: disabling the ancestor -filter fails the fold test; an unconditional gutter fails the -byte-identity test. - -**One unclassified occurrence** — the first crdt sweep reported 7 -failures whose signatures were destroyed before being read. It is -recorded in the framing's §6a, **not** as a row in -`docs/ci-red-signatures.md`, because it has no normalized signature to -match. Two non-causal hypotheses are recorded there; neither is testable -now. - -### Recovery, once pushed - -```sh -git fetch githubsucks -git worktree add ../pmacs-tree \ - -b tree-primitive-framing \ - githubsucks/tree-primitive-framing -``` - -### Not in scope - -dired's `i` insert-subdirectory (the second consumer, its own stage); -the other four §14 consumers; DAP's variables view; giving the outline a -refresh, which is LSP request-lifecycle work. +The last two are the durable lesson and it is in the handoff: **an +optional field that a data structure's shape depends on is not +optional**, and a contract that two mechanisms must honour is only as +strong as the weaker mechanism. ## Leaked daemons from `gpu_invocation_acceptance` — NEEDS A LANE @@ -301,163 +252,74 @@ reap, or does the daemon fail to exit when its socket disappears? Those have different fixes, and the second would be a product defect rather than a test one. -## macOS CI signal integrity — STAGES 1 AND 2 MERGED (#215, #216) +## macOS CI signal integrity — ARC RETIRED (#215, #216); residue re-homed -**This file requires a lane for every open PR** (see the #171/#174 note -above: an open PR is exactly the volatile work this file records, and -#171 drifted 153 commits while invisible here). #215 had none until -review caught it; **Stage 2's lane is written with its first commit**, -which is where the same defect stops recurring. +**Both stages merged and the arc is done**, so rule 4 removes the lane. +Stage 1 built `docs/ci-red-signatures.md` and audited the incumbents; +Stage 2 retired **R2** and **R4** with discriminating witnesses. The +framing `docs/macos-ci-signal-integrity-framing.md` and the registry +both survive the lane — the registry is the durable artifact this arc +existed to produce. -Framing `docs/macos-ci-signal-integrity-framing.md` **revision 3**, -already approved. Stage 1 is acceptance 1–5; Stage 2 is acceptance 6–9. +**Retiring it required re-homing the residue first**, which is why this +did not happen at merge. A lane removed while it still owns undone work +does not close that work, it hides it. What it owned: -### Stage 2 — hardening, MERGED as #216 +- **R1** (supersede cancellation budget, *measurement design*) → the + **async-runtime lane**, below. Its retirement condition is Q#MCI3: + replace or justify the measurement. Widening the budget would make it + pass and measure nothing more. +- **R3** (live-leader EPERM, **UNRESOLVED — possible product defect**) + → the **reap-ledger lane**, below, which already parks every + disposition change pending exactly this question. +- **R5** and **R6**, added 2026-08-06 and neither diagnosed → the + async-runtime lane and a **readiness-helper audit** respectively. -- **Branch `ci-signal-hardening`**, worktree `../pmacs-ci-signals`, base - `githubsucks/main` @ `12f2970` (the #215 merge). Opened from an - **isolated worktree** because the shared checkout was on another - lane's branch with clean-but-foreign state; never switch it. -- **PR: , MERGED** - 2026-08-05 as `2657568`, all 14 checks green at head `8ab20b5`. This - block was written *with* the work, before the PR existed, so the row - below was filled in rather than invented. **Checkpoints, newest - last**, because a lane that records only one head goes stale on the - next push: +### The rows are the state now, not this block - | head | CI run | result | - |---|---|---| - | `2d9c678` | [31003333581](https://github.com/levineuwirth/pmacs/actions/runs/31003333581) | **14/14 green** — the opening head, and the last one carrying code. Includes **both macOS legs**: R2's job (macOS / lua54) and R4's (macOS / luajit) | - | `668fc72` | [31006160334](https://github.com/levineuwirth/pmacs/actions/runs/31006160334) | **14/14 green** — this ledger block, docs only | - | *(tip)* | — | the checkpoint row for `668fc72`; a table can never carry the head that adds it | +An occurrence scan on 2026-08-06 (last 25 `main` runs: 23 green, 2 red) +turned up two things worth recording as method rather than as trivia. - **The branch tip is authoritative over any row here.** Verify with - `git rev-parse githubsucks/ci-signal-hardening` rather than trusting - the newest line. -- **What it ships — exactly two fixes, both at the readiness - predicate**, plus four witnesses: - - **R4.** `wait_for_file` takes the **expected bytes** and waits while - the file holds a *strict prefix* of them, so a zero-byte or torn - read is no longer a readiness signal. Four callers updated; **the - identical mechanism in `wait_for_published_file`**, one function - away in the same suite, is fixed with it — leaving it would have let - R4 recur under a different selector. - - **R2.** The USR1 fixture publishes a marker **after** `trap '' USR1` - and the test waits for that marker's **content**; `exec` replaces - the forked `sleep`, so the group holds one process and the ignored - disposition survives by POSIX rather than by a shell's - fork-suppression optimization. -- **The witnesses were verified by REVERTING each fix**, not by - reasoning. With the old predicate restored, - `wait_for_file_does_not_return_a_zero_byte_readiness_file` fails - `left: []`, `right: [49]` — **R4's two required fragments, verbatim** - — and with the readiness wait removed, - `usr1_readiness_waits_for_the_trap_not_for_the_spawn` fails - `left: Some("SIGUSR1")`, `right: Some("SIGTERM")`. -- **R1 is NOT touched** — referred to the async-runtime lane (Q#MCI3); - widening its budget would make it pass and measure nothing more. - **R3 is NOT touched** and stays **unresolved**; it belongs to the - process-signal / reap-ledger lanes and nothing here may make it look - otherwise. -- **Repetition sets, not single runs:** the two `--lib` process tests - **15/15**, the whole `vterm_stage2_acceptance` suite (9 tests, default - parallelism) **15/15**. -- **Verification:** fmt, diff-check, clippy with and without `crdt`, - `--lib` 1897, `--lib --features crdt` 2082, vterm Stage 2 9, m4 149, - required GPU 221. Each `--lib` figure is exactly one above #215's - (1896 / 2081): the R2 witness. -- **Full serialized sweep in BOTH feature configurations, baseline - first** — the blindness handoff §5 warns about, and the baseline is - what makes any failure attributable: +**A signature very nearly got misfiled by theme.** `main` run +30710662474 is the same test as R3, with `EPERM` and +`measured_group=unobservable(ESRCH…)` — and R3 requires `leader=live` +where that run reads `leader=exited(signal SIGUSR1)`, which is R2's +exact fragment. Read by test name and shared fragments it looks like +R3; read by required fragment it is R2, four days before R2's +retirement, on the *other* macOS flavor. **Attaching a live unresolved +product-defect row to an occurrence of a retired test race is precisely +the error the exact-fragment rule prevents**, and it was caught by +checking the fragment rather than the resemblance. - | tree | config | suites | passed | failed | - |---|---|---|---|---| - | `main` @ `12f2970` | luajit | 108 | 3691 | 0 | - | `main` @ `12f2970` | luajit,crdt | 108 | 3959 | 0 | - | branch | luajit | 108 | 3695 | 0 | - | branch | luajit,crdt | 108 | 3962 | **1 — see below** | +**A second red matched nothing at all** and became R5, rather than +being folded into R1 because both involve supersede under a deadline on +macOS. Sharing a subject is not sharing a signature. - **Every total is exactly +4 on its baseline** — 3691→3695 and - 3959→3963 (3962 passed plus the one failure). Four witnesses, and - nothing else moved. -- **The one branch-`crdt` failure is recorded rather than rerun away.** - `lsp_dispatch_seams_acceptance - acc33_apply_edit_still_handled_with_a_response_subscriber` — - *"the executeCommand response reaches its one-shot, left: 0, right: - 1"*. It is a **new incident** by the registry's rules (no row's - fragments match), and it is **not attributable to this branch**, on a - structural argument rather than on the green rerun: - - the only Rust change is inside `#[cfg(test)] mod tests` in - `src/process.rs`, which is compiled **only into the lib test - target** — an integration-test binary links the non-`cfg(test)` - lib, so this suite's artifact is what `main` builds; - - the other change is a different test binary entirely; - - CI's **`Test (crdt)` job passed at this same head**, and it runs - this suite; - - it occurred while a **second full workspace sweep** was running - concurrently in another worktree, against a deadline-based fake-LSP - one-shot; - - the suite then ran **15/15 as a repetition set** — which, per this - project's own rerun rule, establishes **intermittence only** and is - the weakest of these five points, not the argument. +### Async-runtime lane — NOT STARTED, owns R1 and R5 - **No registry row is opened**, because the registry judges red **CI** - runs and keys on linked CI occurrences; this is a local observation - under known contention. If it appears in CI, it is a first recorded - occurrence and gets a row then. -- **A SHARED `CARGO_TARGET_DIR` MAKES A LOCAL SWEEP UNATTRIBUTABLE, and - it bit this lane.** The branch `crdt` sweep first reported 7 failures - in three suites while the baseline `crdt` sweep was clean. All three - spawn the **real `pmacs` binary out of the target directory**, and the - failure text named its own cause: *"daemon does not advertise required - capabilities … start the daemon built with the `crdt` feature"*. A - concurrent `cargo test --workspace` in a **different worktree**, with - default (non-`crdt`) features and the same `CARGO_TARGET_DIR`, had - overwritten `target/debug/pmacs` mid-sweep. Confirmed by `pgrep` while - it was happening, and discriminated by re-running the same three - suites from the same tree with a **dedicated** `CARGO_TARGET_DIR`: - 41/41 green, then the whole config swept clean the same way. **Give a - sweep its own target directory whenever another worktree is live** — - a feature-flavored binary is a shared mutable file, not a build - artifact private to your invocation. +No branch, no framing. Owns **R1** (Q#MCI3: the 50ms budget starts +before the second dispatch and is consumed by the test's own pump, so +it measures when the test was scheduled) and **R5** +(`stream_supersede_delivers_cancelled_to_on_close`, `async pump +deadline exceeded`, undiagnosed). Filed together because both are the +async runtime under a deadline; they are **not** assumed to share a +cause. -Recovery from a clean checkout: +### Readiness-helper audit — NOT STARTED, owns R6 -```sh -git fetch githubsucks -git worktree add ../pmacs-ci-signals \ - -b ci-signal-hardening \ - githubsucks/ci-signal-hardening -``` +No branch, no framing. **Three independently written readiness helpers +now exist**, and they disagree: `vterm_stage2_acceptance`'s waits for +expected content (hardened by #216), `wait_for_published_file` was +fixed with it, and +`tests/bottom_panel_stage1_acceptance.rs:2446` carries only the +zero-byte half. R4's disposition predicted this recurrence under a new +selector, and R6 is it. -### Stage 1 — MERGED as #215 - -- **Branch `macos-ci-signal-integrity`**, merged to `main` @ `12f2970` - after two review rounds. Docs only. Final CI checkpoint: - - | head | CI run | result | - |---|---|---| - | `d33bf4d` | [30950108477](https://github.com/levineuwirth/pmacs/actions/runs/30950108477) | 14/14 green — the reviewed head | - | `f76897c` | [30990816890](https://github.com/levineuwirth/pmacs/actions/runs/30990816890) | review round 1 | - | `2e0617f` | — | review round 2: framing wording, the missing quote, the checkpoint table | - -- **What Stage 1 shipped:** `docs/ci-red-signatures.md`, the single - authority for judging a red CI run. Rows key on **signature** — - selector + job/flavor + every required fragment, normalized — so **a - test-name match confers nothing**. The rerun rule is replaced: a green - rerun establishes **intermittence only**; the same signature again is - a second occurrence and stays blocking; a different signature is a new - incident. Retirement is **causal**, never a count of green runs. -- **Four incidents, three tests, four signatures.** The process test - produced two — one a test race (R2), one an **unresolved possible - product defect** (R3) that no rerun can clear. Counting by test name - would have hidden the second behind the first. -- **The audit found a third state the framing did not allow**, which is - why the framing is at revision 3: two incumbents have a historical - claim but **no linked occurrence ever**. They are **audit notes A1/A2**, - not rows — unmatchable by construction, so a red in either is a new - incident. The registry is therefore *stricter* than the list it - replaces: nothing is pre-excused. +**Scope is the audit, not the call site**: how many helpers exist, +whether they can be one, what each promises. Patching `acc28` alone +leaves the question open under a fourth selector — which is the same +mistake as fixing `wait_for_file` and leaving +`wait_for_published_file`, already made once in this arc. ## CI CRDT coverage — MERGED (#209); kept for its three follow-ons @@ -678,6 +540,23 @@ them) and why `dired`/`listview` were the correct first two families. - **Unparked from PR #200's §5.** #200 retired the premise that justified the ledger's leniency and deliberately changed no disposition; this lane owns what it refused. +- **Now also owns R3**, re-homed 2026-08-06 when the macOS CI + signal-integrity arc retired. `docs/ci-red-signatures.md` R3 is a + group-directed `kill` returning **EPERM while the leader was observed + live**, with `measured_group` — the one field able to disagree — + unreadable. It is the **same group-target question** #176 and #200 + circled and this lane parks every disposition change pending, which + is why it lands here rather than staying with a retired CI lane. Its + registry entry is explicit that it is an **unresolved possible + product defect** and that **a green rerun never retires it**; that + constraint travels with the row, not with whoever inherits it. + + Note what it is *not*: R2 is the same test with `leader=exited(signal + SIGUSR1)`, a test race, retired. An occurrence scan on 2026-08-06 + nearly filed a second R2 occurrence here as R3 on the strength of the + shared test name and shared `EPERM` fragment. **`leader=live` is the + fragment that separates a product-defect candidate from a fixed + fixture bug**, and it is the whole reason the row lists it. - **Four sites, not the two #200 named.** In the persistent ledger: a probe error of any errno drops the entry and cancels escalation; a failed escalating `SIGKILL` is marked as succeeded, so **no later tick diff --git a/docs/agent-handoff.md b/docs/agent-handoff.md index 2b3e3c8..b34fc5e 100644 --- a/docs/agent-handoff.md +++ b/docs/agent-handoff.md @@ -2236,6 +2236,50 @@ round-trip cannot detect a discriminant shift. ## 5. Hard-won ops lessons +- **An optional field that a data structure's shape depends on is not + optional.** `listview` rows may omit `item`, so + `line_to_item[n] = row.item` leaves that map **sparse** — and + `seat_cursor` took `#` of it, which for an all-display-only tree is + 0, stranding the cursor on the header where TAB finds no row. The + whole existing suite missed it because **every test supplied + `item`**: the field was optional in the API and mandatory in + practice, and nothing in a passing run distinguishes those. When a + field is optional, write one test that omits it — the sparse-table + `#` is a Lua-specific trap, but "optional in the signature, required + by the shape" is not. +- **A contract two mechanisms must honour is only as strong as the + weaker mechanism.** `listview` ids were documented "opaque, compared + by equality". Selection compares with `==`, honouring `__eq`; collapse + state stores ids as **table keys**, and Lua indexes tables by raw + identity, consulting no metamethod. A table id satisfied one half and + silently failed the other — a refresh would restore the cursor and + lose the fold, surfacing arbitrarily later with nothing pointing at + the id. **Narrow the contract to what both halves can honour and + enforce it where the data enters**, rather than generalizing the + strong half to match the weak one: equality-aware collapse lookup + would have turned a linear render quadratic to support a key type no + consumer wanted. The same pass found the contract said "identity" + while accepting duplicates (every lookup takes the first match, so + selecting the later row toggles the earlier) and "number" while + accepting NaN (a number Lua refuses as a key). **Enforcement and + documentation drift apart silently; only the enforcement is real.** +- **Match a red by its required fragment, never by test name or + resemblance.** An occurrence scan on 2026-08-06 nearly filed `main` + run 30710662474 as **R3** — same test, same `EPERM`, same + `measured_group=unobservable(ESRCH…)`. R3 requires `leader=live`; + that run reads `leader=exited(signal SIGUSR1)`, which is **R2's** + exact fragment, four days before R2 was retired. The two rows are a + live possible **product defect** and a fixed **test race** in the + same test, and only the fragment separates them. A second red in the + same scan was filed as **R5** rather than folded into R1 for the same + reason: both are supersede under a deadline on macOS, and **sharing a + subject is not sharing a signature**. +- **A retired row's recurrence rule is about time, not just text.** A + red matching a retired row falsifies the retirement **only if it + postdates it**. An earlier occurrence found later corroborates the + row instead — and can still add something: 30710662474 is macOS + *luajit* where R2's evidence was *lua54*, so the mechanism was never + flavor-specific. - **A gate summary assembled through a pipe can report success over a failure.** `cmd | tail -2` returns **`tail`'s** exit status, not `cmd`'s — in `fish` and `bash` alike — so a chain of diff --git a/docs/ci-red-signatures.md b/docs/ci-red-signatures.md index 4d4ff28..669ec5d 100644 --- a/docs/ci-red-signatures.md +++ b/docs/ci-red-signatures.md @@ -89,8 +89,17 @@ row, not a weaker one. Reopen the row rather than rerunning. ## Live rows -**Two of the four evidenced rows are live.** R2 and R4 were retired on +**Four of the six evidenced rows are live.** R2 and R4 were retired on 2026-08-05 and are below, under "Retired rows", with their dispositions. +R5 and R6 were added on 2026-08-06 from an occurrence scan and a live +red; neither is diagnosed. + +**Rate, as of 2026-08-06.** Of the last 25 `main` runs, **23 green and +2 red** — the two reds being R2's second occurrence (30710662474) and +R5 (30555667095). R6 has **no** occurrence in that window; it was first +seen on a PR branch. This is the first rate this file has carried, and +it is a floor rather than a measurement: it counts only `main`, only 25 +runs, and only reds that were still readable. ### R1 — supersede cancellation budget @@ -152,6 +161,64 @@ as a falsification of that claim rather than as a fresh mystery. Both were retired **causally** — the mechanism removed, plus a discriminating witness that fails without the fix — never by a count of green runs. +### R5 — async pump deadline exceeded in the supersede close path + +| field | value | +|---|---| +| **selector** | `--lib editor::tests::stream_supersede_delivers_cancelled_to_on_close` | +| **job / flavor** | macOS / lua54 | +| **required fragments** | `async pump deadline exceeded` | +| **causal status** | **UNRESOLVED — no diagnosis** | +| **evidence** | `main` [run 30555667095](https://github.com/levineuwirth/pmacs/actions/runs/30555667095), 2026-07-30 | +| **retirement** | diagnosis by the async-runtime lane, alongside R1's measurement question (Q#MCI3). **Never a green rerun.** | + +**Not R1, though it is the nearest thing to it.** R1 is +`async_runtime::tests::supersede_cancels_in_flight_job_within_50ms` +failing `supersede did not cancel within 50ms`; this is a different +test in a different module failing a different assertion. They share a +subject — supersede, under a deadline, on macOS — and sharing a subject +is not sharing a signature. Filed separately so that a fix for one is +not read as a disposition for the other. + +What the row does **not** claim: that the pump is slow, that the +deadline is wrong, or that this is the same measurement-design problem +R1 has. Nothing here has been diagnosed. It is recorded because it +happened and had a signature, which is the entire bar for a row. + +### R6 — readiness file never published in the panel terminal fixture + +| field | value | +|---|---| +| **selector** | `--test bottom_panel_stage1_acceptance acc28_child_input_and_the_c_c_escape_work_unchanged_in_a_panel` | +| **job / flavor** | macOS / lua54 | +| **required fragments** | `timed out waiting for` **and** `/ready` | +| **causal status** | **UNRESOLVED — no diagnosis** | +| **evidence** | #217 [run 31023651701](https://github.com/levineuwirth/pmacs/actions/runs/31023651701), 2026-08-05 | +| **retirement** | the readiness helpers are audited and reconciled, with a witness. **Never a green rerun** — the next push was green and that retires nothing. | + +**A THIRD copy of the readiness helper.** R4's disposition already +recorded that the empty-file predicate lived in a second helper +(`wait_for_published_file`) and warned that leaving it would let the +mechanism recur under a different selector as a new incident. +`tests/bottom_panel_stage1_acceptance.rs:2446` is a third, +independently written `wait_for_file` carrying only *half* the +hardening: it rejects a zero-byte file (`!bytes.is_empty()`) but never +waits for the expected content. + +So the honest statement is narrow. This red is a **timeout**, not R4's +`left: [] / right: [49]`, and a timeout is what a *correct* predicate +does when the content never arrives — it is not evidence of the R4 bug. +What is established is only that a third copy exists and diverges from +the other two. **Whether this occurrence is a slow runner, a child that +never published, or something in the panel path is not known**, and the +suite passed 5/5 locally on Linux, which reproduces nothing about a +macOS runner and is not evidence about this occurrence. + +The scope this row implies is the audit, not the test: how many +readiness helpers exist, whether they can be one, and what each +promises. Patching this call site alone would leave the same question +open under a fourth selector. + ### R2 — USR1 delivered before the trap is installed — RETIRED 2026-08-05 | field | value | @@ -160,7 +227,7 @@ witness that fails without the fix — never by a count of green runs. | **job / flavor** | macOS / lua54 | | **required fragments** | `leader=exited(signal SIGUSR1)` — **one exact fragment, not two loose ones**. Split into `leader=exited(` and `SIGUSR1` it would match a child that exited by some *other* disposition while `SIGUSR1` appeared elsewhere in the output | | **causal status** | **test race** | -| **evidence** | [#213 run 30927084982 attempt 1](https://github.com/levineuwirth/pmacs/actions/runs/30927084982/attempts/1) | +| **evidence** | [#213 run 30927084982 attempt 1](https://github.com/levineuwirth/pmacs/actions/runs/30927084982/attempts/1); **and `main` [run 30710662474](https://github.com/levineuwirth/pmacs/actions/runs/30710662474), 2026-08-01, macOS / *luajit*** — same exact fragment on the **other flavor**, found by an occurrence scan on 2026-08-06 | | **retirement condition** | the fixture proves the trap is installed, with a witness that fails without it | | **disposition** | **met.** The child publishes a readiness marker *after* `trap '' USR1`, the test waits for that marker's **content**, and `process::tests::usr1_readiness_waits_for_the_trap_not_for_the_spawn` is the witness | @@ -186,6 +253,25 @@ Three things the fix and the witness settled that the row did not say: disposition** — a child that took the USR1 reports `Signaled { signal: "SIGUSR1" }` — rather than by an absence observed within a window. +- **The second occurrence is pre-retirement, and that is the whole + question.** `main` run 30710662474 predates the 2026-08-05 retirement + by four days, so it corroborates the row rather than falsifying its + disposition — a red matching a retired row is a recurrence **only if + it postdates the retirement**, and the rule above is about the claim + a retirement makes going forward. It does add something: this row's + evidence was macOS / lua54 and this one is macOS / **luajit**, so the + mechanism was never flavor-specific, which is what the fix already + assumed when it widened the window deliberately rather than hoping a + loaded runner supplied one. + + *It was also very nearly filed as R3.* Same test, same `EPERM`, same + `measured_group=unobservable(ESRCH…)` — and R3 explicitly requires + `leader=live`, where this reads `leader=exited(signal SIGUSR1)`. + Matching on the shared fragments and the shared test name would have + attached a live, unresolved possible-product-defect row to an + occurrence of a retired test race. **This is why the rows key on + exact fragments and why R2's says "one exact fragment, not two loose + ones."** - **The fixture had a second, unnamed dependency.** These signals are group-directed, so a forked `sleep` would be an *untrapped* member of the same group. That it survived at all depended on the shell From d4931acba6cd8cc30e42d414e16bc25ea230e754 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Thu, 6 Aug 2026 10:18:05 +0200 Subject: [PATCH 2/4] docs: this lane's own block, written before its PR The ledger requires a lane for every open PR. #171 drifted 153 commits while invisible here and #215 had no lane until review caught it, so the block goes in with the lane's own commits rather than after someone asks for it. Co-Authored-By: Claude Opus 5 --- docs/active-work.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/active-work.md b/docs/active-work.md index 80ba9bb..7290492 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -180,6 +180,34 @@ 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. +## Docs absorption after #217 — IN FLIGHT + +**This block is written with the lane's first commit, before the PR +exists.** That is the standing correction from #171 (153 commits of +drift while invisible here) and #215 (no lane until review caught it): +this file requires a lane for every open PR, and the way that stops +recurring is writing it now rather than after someone asks. + +- **Branch `docs-absorption-217`**, base `githubsucks/main` @ `db1bbe9` + (the #217 merge). `githubsucks/docs-absorption-217` is the + authoritative tip — any edit to this block advances past whatever SHA + it records, so the ref is the thing to trust. Recover with `git fetch + githubsucks && git checkout docs-absorption-217`. +- **Docs only.** No `src/`, no crate, no manifest, no test changes. + Gates run are fmt, `git diff --check`, `--lib`, and + `listview_acceptance`; the full suite is not re-run for a change that + cannot reach it, and that is stated rather than left as a gap. +- **Scope:** retire the tree and macOS-CI arcs per rule 4, re-home + their residue first, file R5 and R6, and carry four durable lessons + into the handoff. + +### Not in scope + +Diagnosing R5 or R6, or auditing the three readiness helpers — those +are the lanes this one creates, not work it does. Retiring the +CI-CRDT, Distribution, or reap-ledger lanes: each still owns undone +work and rule 4 does not apply to them. + ## Tree primitive (P5) — MERGED as #217; adoption is the open work **The lane is gone, not the work.** Rule 4 removes a lane after merge, From d4d7ea605cfceee5eb4e638ed6dc6f14a50662d2 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Thu, 6 Aug 2026 10:18:43 +0200 Subject: [PATCH 3/4] docs: the absorption lane records PR #218 Co-Authored-By: Claude Opus 5 --- docs/active-work.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/active-work.md b/docs/active-work.md index 7290492..4778d4f 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -180,10 +180,11 @@ 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. -## Docs absorption after #217 — IN FLIGHT +## Docs absorption after #217 — PR #218 OPEN -**This block is written with the lane's first commit, before the PR -exists.** That is the standing correction from #171 (153 commits of +**PR #218** — https://github.com/levineuwirth/pmacs/pull/218. **This +block was written with the lane's first commit, before the PR +existed**, so the row above was filled in rather than invented. That is the standing correction from #171 (153 commits of drift while invisible here) and #215 (no lane until review caught it): this file requires a lane for every open PR, and the way that stops recurring is writing it now rather than after someone asks. From b1723479457733246c92765e44e18a73d898501a Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Thu, 6 Aug 2026 10:40:54 +0200 Subject: [PATCH 4/4] docs: four review defects, and a documented error that never happens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. THE CENTRAL RULE LACKED ITS TIME QUALIFIER. It said any red matching a retired row is a recurrence — under which this PR's own R2 finding reopens R2. Now: a match POSTDATING the retirement challenges the disposition; a match predating it corroborates. That is not a technicality. An occurrence scan reaches backwards by construction, so most matches it finds are the earlier kind, and the old wording would reopen every retired row the first time anyone scanned. 2. R5 AND R6 SAT UNDER "RETIRED ROWS" while declaring themselves live and undiagnosed. I inserted them before R2's heading, which put them in the wrong section — presentation contradicting classification in a file whose whole job is classification. Moved under Live rows. 3. RECOVERY ANCHORS STILL DECLARED 12f2970, so the check accepted a checkout lacking #216 and #217 while the same file described both as complete. Advanced to db1bbe9, with the ancestry verified rather than assumed. 4. THE HANDOFF'S DURABLE TREE FACTS STILL SAID IDS ARE OPAQUE AND COMPARED BY EQUALITY — the contract ef99b64 deliberately narrowed. A §5 lesson explaining a correction does not fix a summary that still states the uncorrected fact; the summary is what a new agent reads first. Corrected there and in the header, which also still called Stage 2 in flight and anchored main at f186253. AND ONE FINDING FROM RE-EXERCISING THE RECOVERY PATH RATHER THAN SWAPPING ITS SHA. This file claimed `git worktree add githubsucks/` fails with "fatal: invalid reference". It does not fail. On git 2.55.0 it SUCCEEDS and leaves a detached HEAD — no branch, no upstream. Still use -b, but the reason is the opposite of the one recorded: the hazard is not an error that stops you, it is that nothing stops you. Work committed there sits on no branch and is not pushed by a bare `git push` — the "uncommitted work does not travel" hazard wearing the shape of committed work. A documented error message that never appears is worse than no documentation, because the reader waits for a signal that is not coming. Verified: fmt, diff-check, --lib, listview 26/26. Recovery path re-run from an empty directory at the new base; all four steps clean. Co-Authored-By: Claude Opus 5 --- docs/active-work.md | 42 +++++++++++++++++++--------- docs/agent-handoff.md | 59 +++++++++++++++++++++++++++++---------- docs/ci-red-signatures.md | 41 ++++++++++++++++++--------- 3 files changed, 101 insertions(+), 41 deletions(-) diff --git a/docs/active-work.md b/docs/active-work.md index 4778d4f..8ee9881 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -111,8 +111,10 @@ lesson, §1 for the two framings). are identical on every machine. Remote names are otherwise machine-local: `origin` may name this canonical URL, a release mirror, or something else, and therefore has no authority by name alone. -- Canonical base at this snapshot: **`githubsucks/main` @ `12f2970`** — - the macOS CI signal-integrity registry **#215**, atop `f186253`: +- Canonical base at this snapshot: **`githubsucks/main` @ `db1bbe9`** — + the tree primitive **#217**, atop `2657568` the macOS CI + signal-integrity **Stage 2 #216** (which retired R2 and R4), atop + `12f2970` its Stage 1 registry **#215**, atop `f186253`: bottom-panel Stage 3 **#213**, which completes Arc 7, atop the post-release accuracy pass **#212**, Distribution Stage 1 **#211** (released as **v1.1.0**, the first release with prebuilt binaries), @@ -125,8 +127,9 @@ lesson, §1 for the two framings). `Hello` still advertises v20** — two different facts, and #184 landed only the first. **The recovery floor advances with the base**, so the check below - now requires `12f2970` or newer; a tree at `f186253` no longer - passes. That is deliberate — a check accepting an older commit than + now requires `db1bbe9` or newer; a tree at `12f2970` no longer + passes — it would lack #216 and #217, both of which this file + describes as complete. That is deliberate — a check accepting an older commit than the declared base passes on a tree the rest of this file does not describe. **Lanes below that name an older base have not been re-based; derive @@ -164,21 +167,34 @@ git worktree list git status --short --branch ``` -The `git log` command must expose `12f2970` — the base named above — or a +The `git log` command must expose `db1bbe9` — 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 -`f186253` 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. +**This path was exercised, not asserted, at this snapshot** — re-run +from an empty directory on 2026-08-06 when the base advanced to +`db1bbe9`, rather than having its SHA swapped. `git clone` the +canonical URL, add the `githubsucks` alias, `git fetch githubsucks +--prune`, confirm `db1bbe9` 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. + +**Correction, found by re-running it.** This file claimed the +two-argument form fails for a remote-only branch with `fatal: invalid +reference`. **It does not fail.** On git 2.55.0 it *succeeds* and +leaves a **detached HEAD** — no branch, no upstream, `git status` +reporting `## HEAD (no branch)`. + +Still use `-b`, but for the opposite reason to the one recorded: the +danger is not an error that stops you, it is that nothing stops you. +Work committed in that worktree sits on no branch, is not pushed by a +bare `git push`, and is exactly the "uncommitted work does not travel" +hazard in a shape that looks committed. **A documented error message +that never appears is worse than no documentation**, because the reader +waits for a signal that is not coming. ## Docs absorption after #217 — PR #218 OPEN diff --git a/docs/agent-handoff.md b/docs/agent-handoff.md index b34fc5e..e94db13 100644 --- a/docs/agent-handoff.md +++ b/docs/agent-handoff.md @@ -1,11 +1,19 @@ # Agent handoff — cross-machine continuity -**Last updated: 2026-08-05.** The live CI-triage rule in §5 now points -at `docs/ci-red-signatures.md` (**PR #215, merged as `main` @ -`12f2970`**; its Stage 2 hardening is in flight — see -`docs/active-work.md`), which keys on -signature rather than test name; the hazards list this file used to -carry is retired, and its two unevidenced entries are audit notes there. +**Last updated: 2026-08-06.** `main` is **`db1bbe9`** — the tree +primitive **#217**, atop **#216**, which completed the macOS CI +signal-integrity arc by retiring R2 and R4 with discriminating +witnesses, atop **#215**, which built the registry. **That arc is +retired**; its live residue (R1, R3, and the newer R5 and R6) is +re-homed to the async-runtime, reap-ledger, and readiness-helper-audit +lanes in `docs/active-work.md`. + +The live CI-triage rule in §5 points at `docs/ci-red-signatures.md`, +which keys on **signature, not test name** — and, since 2026-08-06, +on **signature plus date**: a red matching a retired row is a +recurrence only if it *postdates* the retirement, and an earlier one +corroborates instead. The hazards list this file used to carry is +retired, and its two unevidenced entries are audit notes there. Previously **2026-08-04, as bottom-panel Stage 3 #213 — the adopter default flip, which COMPLETES ARC 7: omitting `display` now means the panel, and the workbench's panel half is done on both frontends. Beneath @@ -75,9 +83,16 @@ 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-08-01) +## 1. Where the project stands (2026-08-06) -- **`main` @ `f186253`.** Bottom-panel Stage 3 **#213** completes Arc 7, +- **`main` @ `db1bbe9`.** The **tree primitive #217** — `listview` rows + take optional `depth`/`id`, collapse is primitive-owned, folding is + **local projection state and not a refresh protocol**, and the LSP + outline is the sole adopter (`COHERENCE.md` §14 ◐; §20 says adoption, + not construction). Atop `2657568` **#216**, which completed the macOS + CI signal-integrity arc, atop `12f2970` **#215**, which built + `docs/ci-red-signatures.md`. Beneath those, `f186253`: bottom-panel + Stage 3 **#213** completes Arc 7, atop the post-release accuracy pass #212 and `000b6cd` / **v1.1.0**. Beneath that, Distribution Stage 1 #211 lands atop the docs absorption #210, the CI CRDT coverage lane #209, @@ -137,13 +152,27 @@ commands, read `docs/active-work.md` immediately after this file. reason the anchor consumer works, because **the outline has no `on_refresh` at all**. A design requiring the consumer to re-supply rows on every fold would have fitted no existing consumer. - - **Identity is consumer-supplied and compared by equality; the - primitive never derives one.** `item` is opaque by design. The - outline uses `line:col`, because the `::` parent chain collides on - overloads and same-named siblings — exactly where a stale expansion - would reattach to the wrong node. **Selection is re-seated by id, - not by line**, since a fold inserts or removes rows above the - cursor. + - **Identity is consumer-supplied and the primitive never derives + one**, but it is **not opaque**: `row.id` must be a **string or + number, unique among rows, and not NaN**, enforced by `check_ids` + where rows enter (`open` and `refresh`). Review narrowed this from + "opaque, compared by equality", which was **two contracts wearing + one name** — selection compares with `==`, honouring `__eq`, while + collapse state stores ids as **table keys**, and Lua indexes tables + by raw identity consulting no metamethod. A table id satisfied one + half and silently failed the other. Uniqueness and not-NaN came + from the same pass: every lookup resolves to the **first** match, + so a duplicate makes selecting the later row toggle the earlier, + and `0/0` is a `number` Lua refuses as a key. The outline uses + `line:col`, because the `::` parent chain collides on overloads and + same-named siblings — exactly where a stale expansion would + reattach to the wrong node. **Selection is re-seated by id, not by + line**, since a fold inserts or removes rows above the cursor. + - **`item` is optional in the API and was mandatory in practice.** + `line_to_item` is sparse when a row omits it, and `seat_cursor` + took `#` of that map — a display-only tree stranded the cursor on + the header. It counts visible rows explicitly now. Every existing + test supplied `item`, so none could reach it. - **`has_children` must read the FULL row array, not the rendered subset.** A collapsed node's children are absent from the rendered map by construction, so asking the view would answer "no" for every diff --git a/docs/ci-red-signatures.md b/docs/ci-red-signatures.md index 669ec5d..66e9949 100644 --- a/docs/ci-red-signatures.md +++ b/docs/ci-red-signatures.md @@ -79,11 +79,26 @@ Main-branch greens are **occurrence evidence** and accumulate toward a rate. They retire nothing by themselves. Retired rows stay in this file with their disposition, so a recurrence is recognisable. -**A red matching a RETIRED row is a recurrence, and it puts the -retirement in question — it is not a known flake.** The claim a -retirement makes is that the mechanism is gone; the same signature -afterwards falsifies that claim, which is a stronger finding than a live -row, not a weaker one. Reopen the row rather than rerunning. +**A red matching a RETIRED row AND POSTDATING ITS RETIREMENT is a +recurrence, and it puts the retirement in question — it is not a known +flake.** The claim a retirement makes is that the mechanism is gone +*from the retirement forward*; the same signature afterwards falsifies +that claim, which is a stronger finding than a live row, not a weaker +one. Reopen the row rather than rerunning. + +**A matching red that PREDATES the retirement corroborates the row +instead.** It is an additional occurrence of the mechanism the fix +removed, so it strengthens the evidence and challenges nothing. Add it +to the row's evidence; do not reopen. This is not a technicality — an +occurrence scan reaches backwards by construction, so most matches it +finds will be of this kind, and treating them as recurrences would +reopen every retired row the first time anyone looked. + +**Both halves need the date, which means a row is unmatchable without +one.** R2's second occurrence (`main` run 30710662474, 2026-08-01, four +days before its 2026-08-05 retirement) is the worked example, and it +also shows the flavor field is not part of matching: R2's first +evidence is macOS / lua54 and that one is macOS / luajit. --- @@ -153,14 +168,6 @@ diagnosis by the process-signal / reap-ledger lanes. --- -## Retired rows - -**These stay here on purpose.** A retirement is a claim that a mechanism -is gone; keeping the signature is what makes a recurrence recognisable -as a falsification of that claim rather than as a fresh mystery. Both -were retired **causally** — the mechanism removed, plus a discriminating -witness that fails without the fix — never by a count of green runs. - ### R5 — async pump deadline exceeded in the supersede close path | field | value | @@ -219,6 +226,14 @@ readiness helpers exist, whether they can be one, and what each promises. Patching this call site alone would leave the same question open under a fourth selector. +## Retired rows + +**These stay here on purpose.** A retirement is a claim that a mechanism +is gone; keeping the signature is what makes a recurrence recognisable +as a falsification of that claim rather than as a fresh mystery. Both +were retired **causally** — the mechanism removed, plus a discriminating +witness that fails without the fix — never by a count of green runs. + ### R2 — USR1 delivered before the trap is installed — RETIRED 2026-08-05 | field | value |