From 493e8ee36be23901428e622409a841f9750b5c39 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 29 Aug 2026 16:43:37 +0200 Subject: [PATCH 1/8] feat(async): parse budgets report the measurement that failed Both `duration_ms < 100` assertions --- src/async_runtime.rs and tests/m4_acceptance.rs --- now print the observed value and the budget. Neither budget moves. dispatch_parse_round_trips_a_rust_source_file redded twice on macOS/lua54, as U11 and again on #243, and BOTH margins were unrecoverable: the message said only "trivial parse should be fast", so the second red could not be compared with the first. A 1ms overshoot and a 900ms overshoot are different failures that produced identical logs. U11 predicted that cost in writing, and it came true once. D1 and D2 are verified against REAL PANIC MESSAGES, by forcing only the comparison bound to 0 in a scratch build: trivial parse should be fast: took 0ms against a 100ms budget 200-line parse should be quick: took 11ms against a 100ms budget The second is the stronger demonstration --- a non-zero observed value cannot be mistaken for a literal. That scratch panic proves only half. It exercises a budget of 0 while printing 100ms, so it says nothing about the committed threshold. D3 carries that half separately by pinning the literal 100 in both files. The two are a proof together and neither substitutes for the other. The framing withdraws revision 1's claim that the async_runtime assertion was the codebase's sole measurement-omitting one. It was not: tests/m4_acceptance.rs:244 is the same measurement against the same budget. The claim is withdrawn rather than repaired, because a sweep wide enough to be complete also catches `Instant::now() < deadline` loop guards and `eval::` turbofish, while one narrow enough to be accurate proves nothing about completeness. This is not an assertion-hygiene audit. workflow_dispatch was split out per the ledger's own recorded decision; it and U9's still-unrun discriminating control remain separate lanes. Framing docs/parse-budget-diagnosability-framing.md revision 2, approved. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai --- docs/active-work.md | 46 ++++++ docs/parse-budget-diagnosability-framing.md | 150 ++++++++++++++++++++ src/async_runtime.rs | 6 +- tests/m4_acceptance.rs | 6 +- 4 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 docs/parse-budget-diagnosability-framing.md diff --git a/docs/active-work.md b/docs/active-work.md index 8ec8666..faeb276 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -270,6 +270,52 @@ 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. +## Parse-budget diagnosability — ACTIVE + +**Written with the branch's FIRST commit**, per the standing correction +from #171 and #215. + +- **Branch `parse-budget-diagnosability`**, base `githubsucks/main` @ + **`3557779`** exactly, in worktree + `/home/jeans/Repos/personal/pmacs-parse-budget`. + **`githubsucks/parse-budget-diagnosability` is the authoritative + tip** (the ref, not a SHA). Recover with `git fetch githubsucks && + git checkout parse-budget-diagnosability`. +- **Framing `docs/parse-budget-diagnosability-framing.md`, revision 2 — + APPROVED.** Revision 1 was reviewed and had two defects worth + keeping: it claimed `dispatch_parse_round_trips_a_rust_source_file` + was the codebase's **sole** measurement-omitting assertion (false — + `tests/m4_acceptance.rs:244` is the same budget on the same + measurement), and it bundled `workflow_dispatch`, which this ledger + had already recorded as its own lane. +- **What it does:** both `duration_ms < 100` assertions now report the + observed value and the budget. **Neither budget moves.** +- **Why:** `dispatch_parse_round_trips_a_rust_source_file` redded twice + on macOS/`lua54` — U11, then again on #243 — and **both margins were + unrecoverable**, so the second red could not be compared with the + first. A 1ms overshoot and a 900ms overshoot are different failures + and produced identical logs. +- **D1/D2 verified against REAL PANIC MESSAGES**, by forcing only the + comparison bound to `0` in a scratch build: + - `trivial parse should be fast: took 0ms against a 100ms budget` + - `200-line parse should be quick: took 11ms against a 100ms budget` + The second is the stronger demonstration: a non-zero observed value + cannot be mistaken for a literal. +- **THE SCRATCH PANIC PROVES ONLY HALF.** It exercises a budget of + **0** while printing `100ms`, so it says nothing about the committed + threshold. **D3 carries that half separately** by pinning the literal + `100` in both files. The two are a proof together; neither + substitutes for the other. +- **NOT an assertion-hygiene audit.** The framing's §3 withdraws + revision 1's completeness claim rather than repairing it: a sweep + wide enough to be complete also catches `Instant::now() < deadline` + loop guards and `eval::` turbofish, and a sweep narrow enough + to be accurate proves nothing about completeness. +- **Still owed, separately:** `workflow_dispatch` on `ci.yml`, and U9's + discriminating control — pin test-binary concurrency to 1, then load + a lone `--lib` binary — which has been named since 2026-08-09 and + never run. + ## Panel-pointer replay (parent acceptance 48) — MERGED as #243 (`6c9bae6`) - **MERGED 2026-08-29T10:37:10Z** at approved head `b8c51b7`, merge diff --git a/docs/parse-budget-diagnosability-framing.md b/docs/parse-budget-diagnosability-framing.md new file mode 100644 index 0000000..8a6274a --- /dev/null +++ b/docs/parse-budget-diagnosability-framing.md @@ -0,0 +1,150 @@ +# Parse-budget diagnosability — reporting the measurement that failed + +**Status: revision 2 — AWAITING APPROVAL. Nothing implemented.** + +Revision 2 answers review of 1. Three of its four changes are +corrections, and one is a scope reduction: + +- **SPLIT.** Revision 1 bundled this with adding `workflow_dispatch` to + `ci.yml`. `docs/active-work.md` already records those as "two + follow-ups … both their own lanes", and a shared thesis is not an + atomic feature boundary. **`workflow_dispatch` is no longer in this + framing**; it gets its own. +- **The census was WRONG.** Revision 1 called + `dispatch_parse_round_trips_a_rust_source_file` the sole outlier. + `tests/m4_acceptance.rs:244` asserts the same measurement against the + same budget and also omits it. **Both are now in scope.** +- **The completeness claim is WITHDRAWN**, not repaired. See §3. +- The concurrency reasoning revision 1 got backwards belonged to the + other change and leaves with it. + +## 1. What this fixes + +Two assertions compare a parse duration against a 100ms budget and +report **nothing** about what they measured: + +| where | assertion | message | +|---|---|---| +| `src/async_runtime.rs:3328` | `duration_ms < 100` | `"trivial parse should be fast"` | +| `tests/m4_acceptance.rs:244` | `duration_ms < 100` | `"200-line parse should be quick"` | + +When either reds, the log says the parse was slow and stops there. + +## 2. Why now, and the evidence + +The first has redded **twice** on `Test (macos-latest / lua54)`, and +**both margins are unrecoverable**: + +| occurrence | where | outcome | +|---|---|---| +| U11 | PR #242, run `32393462318`, job `96504773333` | red, green on rerun | +| U11's recurrence | PR #243, run `33156571314`, job `98800645872` | red, green on rerun | + +`src/async_runtime.rs` was **byte-identical to `main`** for both — blob +`9310ce3fca8c5fd8ebd39a68c29ad6985e256049`. U11's own row predicted the +cost: + +> *A recurrence is not another instance of this row. Because the margin +> was never captured, a second red cannot be compared with the first.* + +That came true once. Nothing prevents a third. + +**A 1ms overshoot and a 900ms overshoot are different failures** — one +says a threshold is marginal, the other says something stalled — and +today they produce identical logs. + +## 3. Scope, and a claim this framing does NOT make + +**In scope: the two assertions in §1.** They are the same measurement +against the same budget, in the same subsystem, and it would be strange +to fix one and leave the other to produce the next unreadable red. + +**Out of scope, and deliberately: everything else.** Revision 1 claimed +these were the only measurement-omitting assertions in the codebase. +**That claim is withdrawn and is not replaced by a corrected one.** A +sweep wide enough to be complete also catches `Instant::now() < +deadline` loop guards and `eval::` turbofish, which are not +budget assertions at all; a sweep narrow enough to be accurate proves +nothing about completeness. **This lane is not an assertion-hygiene +audit and should not be read as one.** + +Several nearby budget assertions do already report their measurements — +`composition_overhead_under_ten_percent`, `criterion_1_end_of_line_typing…` +(`optimistic.rs:989`), `dired_open_renders_10k_entries_under_200ms`, +`m6_2_pty_streaming_respects_byte_ceiling` (`observed {in_flight}`). +They are cited as **precedent for the shape**, not as evidence that the +set is exhausted. + +## 4. What lands + +```rust +assert!( + duration_ms < 100, + "trivial parse should be fast: took {duration_ms}ms against a \ + 100ms budget" +); +``` + +and, in `tests/m4_acceptance.rs`: + +```rust +assert!( + duration_ms < 100, + "200-line parse should be quick: took {duration_ms}ms against a \ + 100ms budget" +); +``` + +**Both budgets stay at 100ms.** Widening is what R1 already rejected, +and it would discard the signal these reds carry. + +## 5. Acceptance + +| # | contract | witness | mutation | +|---|---|---|---| +| D1 | the `async_runtime` assertion reports the observed ms **and** the budget | a scratch build with the comparison bound forced to `0` panics with a message containing the observed value and `100ms` | restore the bare message → the row cannot separate a 1ms overshoot from a 900ms one | +| D2 | the `m4_acceptance` assertion does the same | same method, same row | same | +| D3 | both budgets are still `100` | the comparison literal is unchanged in both files | widen either → R1's rejected remedy returns | + +**D1 and D2 are asserted against a real panic message, not by reading +the source.** A row that greps the format string would pass while the +assertion it describes had been deleted — which is the same +read-the-code-not-the-effect failure this project has repeatedly +caught. + +**How the fault is injected, and what each half proves.** Temporarily +force **only the comparison bound** to `0` in a scratch build. The +resulting panic then proves two things and no more: that the observed +value is interpolated, and that the budget text reads `100ms`. + +**It does NOT prove the exercised budget was 100ms — it was 0.** The +scratch build compares against `0` while the message still says `100ms`, +so that panic alone says nothing about the committed threshold. **D3 +carries that half separately**, by pinning the comparison literal in +both files. The two rows are only a proof together, and neither +substitutes for the other. + +This is the **smallest deterministic fault injection** available, not +the only conceivable one. A row that waited for a genuine 100ms +overshoot would be exactly as intermittent as the thing it documents. + +## 6. Coherence impact (`COHERENCE.md` §20) + +- **Journey steps touched: NONE.** No product behaviour changes. What + moves is **evidence quality** — whether a failing check can be + reasoned about — the same axis the `scripts/gate` SIGINT guard sat + on. +- **Interaction islands: none added.** +- **Config registry: no entry.** A test budget is not a user-tunable. +- **Background work: none started**, and no attribution moves. + +## 7. What this does NOT do + +- **It does not fix the intermittence**, and makes no claim about + cause. It makes the next occurrence *comparable*. +- **It does not widen, relax or `#[ignore]` any budget.** Two + intermittent reds are not evidence a threshold is wrong. +- **It does not add `workflow_dispatch`.** That is its own lane, per + the ledger's recorded decision. +- **It does not run U9's discriminating control**, which remains + unrun and is a third, separate piece of work. diff --git a/src/async_runtime.rs b/src/async_runtime.rs index 9310ce3..5f8a163 100644 --- a/src/async_runtime.rs +++ b/src/async_runtime.rs @@ -3325,7 +3325,11 @@ mod tests { .expect("parse handoff must hold a bundle on Complete"); match rt.take_result(id) { Some(JobOutcome::Complete(JobResult::Parse { duration_ms })) => { - assert!(duration_ms < 100, "trivial parse should be fast"); + assert!( + duration_ms < 100, + "trivial parse should be fast: took {duration_ms}ms \ + against a 100ms budget" + ); } other => panic!("unexpected outcome: {other:?}"), } diff --git a/tests/m4_acceptance.rs b/tests/m4_acceptance.rs index 58f6930..5e66eed 100644 --- a/tests/m4_acceptance.rs +++ b/tests/m4_acceptance.rs @@ -241,7 +241,11 @@ fn m4_1_dispatch_parse_round_trips_via_runtime() { .expect("parse handoff holds bundle on Complete"); match rt.take_result(id) { Some(JobOutcome::Complete(JobResult::Parse { duration_ms })) => { - assert!(duration_ms < 100, "200-line parse should be quick"); + assert!( + duration_ms < 100, + "200-line parse should be quick: took {duration_ms}ms \ + against a 100ms budget" + ); } other => panic!("unexpected outcome: {other:?}"), } From 45d438ccb332a413782b6fedc26608faad299353 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 29 Aug 2026 16:59:10 +0200 Subject: [PATCH 2/8] docs(ci-reds): R7 sixth occurrence, and a gate run that is not a result The gpu stage failed with all three of R7's required fragments --- `transient sequence must attach`, `Handshake(Io(`, `BrokenPipe` --- at pmacs-gpu/src/attach.rs:1889, 283 passed and 1 failed. Isolated selector green three times afterwards, which is this row's established control. The tree exclusion is as strong as the fifth occurrence's: this lane's entire diff is src/async_runtime.rs, tests/m4_acceptance.rs and two docs. No pmacs-gpu file is touched, and the change is two assert! message strings. Records the run's other half honestly. The gate was in a background task killed at 314s and 07-sweep.log ends in Terminated, so the run is NOT a gate result. Stage 6 is evidence because it completed and reported; stage 7's absence is evidence of nothing. Distinguishing those is the same rule the handoff already carries about timeout-wrapped gates. Causal status unchanged: UNRESOLVED. A sixth lane touching a sixth unrelated surface saw the same three fragments, which strengthens "not lane-correlated" and settles nothing else. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai --- docs/active-work.md | 10 ++++++++++ docs/ci-red-signatures.md | 31 ++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/active-work.md b/docs/active-work.md index faeb276..abd8c36 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -311,6 +311,16 @@ from #171 and #215. wide enough to be complete also catches `Instant::now() < deadline` loop guards and `eval::` turbofish, and a sweep narrow enough to be accurate proves nothing about completeness. +- **GATE RUN 1 IS NOT A RESULT.** It ran in a background task that was + killed at 314s; `07-sweep.log` ends in `Terminated`. Stages 1–5 and 8 + were green and stage 6 (`gpu`) genuinely failed — that one completed + and reported — but the run as a whole proves nothing and must not be + read as a gate outcome. +- **That `gpu` failure is R7's SIXTH occurrence**, recorded in + `docs/ci-red-signatures.md`. All three required fragments, isolated + selector green three times, and **this lane touches no `pmacs-gpu` + file at all** — its whole diff is two `assert!` message strings and + two docs. - **Still owed, separately:** `workflow_dispatch` on `ci.yml`, and U9's discriminating control — pin test-binary concurrency to 1, then load a lone `--lib` binary — which has been named since 2026-08-09 and diff --git a/docs/ci-red-signatures.md b/docs/ci-red-signatures.md index 7c7a7f7..ec84773 100644 --- a/docs/ci-red-signatures.md +++ b/docs/ci-red-signatures.md @@ -496,12 +496,41 @@ Stage 4; the lane touches no `pmacs-gpu` code at all. | **selector** | `-p pmacs-gpu attach::tests::managed_retry_survives_transients_and_uses_the_successful_stream` | | **job / flavor** | local (Linux), `cargo test --workspace --features crdt --no-fail-fast`, i.e. under full-sweep load | | **required fragments** | `transient sequence must attach` + `Handshake(Io(` + `BrokenPipe` (or `code: 32`) | -| **status** | **FIFTH OCCURRENCE 2026-08-15 — causal status still UNRESOLVED.** The fifth carries the strongest tree exclusion this row has had: a **documentation-only diff** | +| **status** | **SIXTH OCCURRENCE 2026-08-29 — causal status still UNRESOLVED.** The fifth carries the strongest tree exclusion this row has had: a **documentation-only diff** | | **what IS established** | **three** occurrences at `pmacs-gpu/src/attach.rs:1680`, the second and third with all three fragments **verified** rather than inferred; the test drives a scripted transient-then-success sequence over a real socket pair. **The added GPU test is not the mechanism** — see the third-occurrence control below | | **what is NOT** | whether the broken pipe is the *fixture's* writer closing early or a real retry-path defect. **This row is not a claim that it is harmless** | | **rerun evidence** | occurrence 1: 6 isolated runs green, plus a full `--workspace --features crdt` sweep green (113 targets). Occurrence 2: **30 green on the observing branch** (15 isolated selector, 15 full `-p pmacs-gpu`) **plus a 15-run merge-base control, also green**. Occurrence 3: 5 isolated selector runs green, 10 full `-p pmacs-gpu` runs green **with** the added test, and **1 failure in 10 with the added test `#[ignore]`d** — the first rerun in this row's history that reproduced anything. Per the rerun rule the green runs establish intermittence only; the red control run is what carries the exclusion | | **retirement** | hardening that removes the named mechanism plus a discriminating witness — or a diagnosis showing the fixture, not the code, closes the pipe | +**Sixth occurrence — the parse-budget diagnosability lane, 2026-08-29, +local (Linux), `gpu` step.** All three required fragments present in the +durable log +(`pmacs-parse-budget-9c27ecfe/gate-logs/20260829T144541Z-350549/06-gpu.log`): + +``` +transient sequence must attach: Attach(Handshake(Io(Os { code: 32, +kind: BrokenPipe, message: "Broken pipe" }))) +``` + +at `pmacs-gpu/src/attach.rs:1889`, `283 passed; 1 failed`. + +* **The tree exclusion is as strong as the fifth's.** The observing + lane's entire diff is `src/async_runtime.rs`, + `tests/m4_acceptance.rs` and two docs — **no `pmacs-gpu` file, and no + file `pmacs-gpu` links against beyond the workspace it always did.** + The change is two `assert!` message strings. +* **Rerun: isolated selector green three times** (`1 passed`, 0.01 s + each), which is this row's established control shape. +* **THE SAME RUN'S `sweep` STAGE WAS TRUNCATED, and is not evidence.** + The gate was running in a background task that was killed at 314s; + `07-sweep.log` ends in `Terminated`. That stage's absence says + nothing, and the run as a whole is **not** a gate result. Only the + `gpu` stage's failure is, because it completed and reported. +* **Causal status: still UNRESOLVED**, and this occurrence does not + change it. It adds one more data point to "not lane-correlated" — a + sixth lane, touching a sixth unrelated surface, seeing the same + three fragments. + **Fifth occurrence — panel cell-mapping generation (§5b) framing, 2026-08-15, local (Linux).** The `scripts/gate` **`gpu` step** again, the same flavor as occurrence 2, inside a `--protocol` run From 68a16f905841e67b2b3fc9afdf3ef71fb1aa1d85 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 29 Aug 2026 17:16:11 +0200 Subject: [PATCH 3/8] docs(ci-reds): R7 seventh occurrence, and four hypotheses excluded The next gate run of the same worktree reproduced R7 immediately --- same selector, same three fragments --- and that run's other seven stages were green, sweep included and complete, so the pair is not confounded by the truncation that marred the sixth. Two consecutive in-gate failures is new for a row whose prior five were spread across lanes and months, so it prompted a narrowing. Seventeen green runs at the failing head on the failing worktree exclude four hypotheses: the selector being flaky (3 isolated), the gpu binary's own concurrency (6 full runs), the gate's isolated TMPDIR (6 runs under a gate-shaped 61-character path, tested because this project already knows socket-path length matters), and residue from the m4 stage the gate runs immediately before gpu (2 back-to-back pairs). So the discriminator is inside scripts/gate versus outside it, and it is not the TMPDIR, not the preceding stage, and not the binary's concurrency. The row does not guess at what remains --- the gate's ambient root, its exported environment, and process state carried across stage boundaries are named as uneliminated, not as suspects. Causal status stays UNRESOLVED, but the question is sharper than it was: previous entries compared trees and lanes, and this one locates the difference in the runner. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai --- docs/active-work.md | 8 ++++++++ docs/ci-red-signatures.md | 40 +++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/docs/active-work.md b/docs/active-work.md index abd8c36..ad1a40a 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -321,6 +321,14 @@ from #171 and #215. selector green three times, and **this lane touches no `pmacs-gpu` file at all** — its whole diff is two `assert!` message strings and two docs. +- **GATE RUN 2: 7/8, `gpu` red on R7 again** (head `45d438c`, log + `20260829T150011Z-429115`). `sweep` ran fully green this time. + **Two consecutive in-gate R7 failures prompted a narrowing**: 17 green + runs outside the gate excluded the selector being flaky, the gpu + binary's own concurrency, the gate's TMPDIR, and residue from the + preceding `m4` stage. The discriminator is in-gate versus out, and + the three obvious runner differences are ruled out. Recorded in the + registry; **not this lane's to solve**. - **Still owed, separately:** `workflow_dispatch` on `ci.yml`, and U9's discriminating control — pin test-binary concurrency to 1, then load a lone `--lib` binary — which has been named since 2026-08-09 and diff --git a/docs/ci-red-signatures.md b/docs/ci-red-signatures.md index ec84773..f4fd7ce 100644 --- a/docs/ci-red-signatures.md +++ b/docs/ci-red-signatures.md @@ -526,10 +526,42 @@ at `pmacs-gpu/src/attach.rs:1889`, `283 passed; 1 failed`. `07-sweep.log` ends in `Terminated`. That stage's absence says nothing, and the run as a whole is **not** a gate result. Only the `gpu` stage's failure is, because it completed and reported. -* **Causal status: still UNRESOLVED**, and this occurrence does not - change it. It adds one more data point to "not lane-correlated" — a - sixth lane, touching a sixth unrelated surface, seeing the same - three fragments. +**A SEVENTH OCCURRENCE FOLLOWED IMMEDIATELY**, on the next gate run of +the same worktree at head `45d438c` +(`20260829T150011Z-429115/06-gpu.log`), same selector, same three +fragments, `283 passed; 1 failed`. **That run's other seven stages were +green**, `sweep` included and complete — 121 result lines, none with a +failure — so this pair is not confounded by a truncation the way the +sixth was. + +**Two consecutive in-gate failures is new for this row**, whose prior +five were spread across lanes and months. It prompted a narrowing, and +the narrowing is the useful part. + +**Four hypotheses EXCLUDED, 17 green runs, all at head `45d438c` on the +failing worktree:** + +| hypothesis | test | result | +|---|---|---| +| the selector is simply flaky | isolated selector | **3 green** | +| the full `-p pmacs-gpu` binary's own concurrency | full binary, outside the gate | **6 green** | +| the gate's isolated `TMPDIR` (this project already knows socket-path length matters) | full binary under a gate-shaped 61-character `TMPDIR` | **6 green** | +| residue from `m4`, which the gate runs immediately before `gpu` and which spawns fake LSP servers and PTYs | `m4` then `gpu` back to back, twice | **2 pairs green** | + +**So the discriminator is "inside `scripts/gate`" versus "outside it", +and it is NOT the TMPDIR, NOT the preceding stage, and NOT the +binary's own concurrency.** Something else about the gate's execution +context remains, and this row does not guess at it. + +**Causal status: still UNRESOLVED.** What these two occurrences add is +a sharper question than the row had before: previous entries compared +lanes and trees, and this one says the difference is in the *runner*, +with the three most obvious runner differences already ruled out. + +* **Next occurrence should start from that**, not from another tree + exclusion. The remaining candidates are the gate's ambient root, its + exported environment, and process state carried across stage + boundaries — none of which has been isolated. **Fifth occurrence — panel cell-mapping generation (§5b) framing, 2026-08-15, local (Linux).** The `scripts/gate` **`gpu` step** again, From 2d769846dc4554ca59574855c00000b167eb9ed0 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 29 Aug 2026 17:27:43 +0200 Subject: [PATCH 4/8] docs(ci-reds): R7 --- a third in-gate run was green, and my "exclusions" were not The gate run immediately after the seventh occurrence passed all eight stages with zero failures anywhere, R7's selector included. So "in-gate always fails" is false, and the previous entry --- written before that run --- is corrected rather than deleted. The correction that matters is a reasoning error, not a data one. I called seventeen green out-of-gate runs "four hypotheses excluded". They exclude nothing: NOTHING outside the gate has ever reproduced this failure, so matching one gate condition at a time outside the gate cannot isolate an in-gate cause. All those runs establish is that none of the four conditions reproduces it BY ITSELF. Stated at the strength it carries: in-gate is 2 failures in 3 runs, out-of-gate is 0 in 17. Suggestive, and not a clean split. The method follows from that and is written down for the next occurrence: varying conditions outside the gate cannot answer this question, so the gate's ambient root, its exported environment, and process state across stage boundaries each need a gate run with that one thing changed. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai --- docs/active-work.md | 13 ++++++---- docs/ci-red-signatures.md | 53 +++++++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/docs/active-work.md b/docs/active-work.md index ad1a40a..37a382f 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -324,11 +324,14 @@ from #171 and #215. - **GATE RUN 2: 7/8, `gpu` red on R7 again** (head `45d438c`, log `20260829T150011Z-429115`). `sweep` ran fully green this time. **Two consecutive in-gate R7 failures prompted a narrowing**: 17 green - runs outside the gate excluded the selector being flaky, the gpu - binary's own concurrency, the gate's TMPDIR, and residue from the - preceding `m4` stage. The discriminator is in-gate versus out, and - the three obvious runner differences are ruled out. Recorded in the - registry; **not this lane's to solve**. + runs outside the gate, in four configurations, never reproduced it. + **A third in-gate run was then GREEN**, so "in-gate always fails" is + false and the registry's first write-up — which called those runs + *exclusions* — is corrected there: nothing outside the gate has ever + reproduced this, so matching one gate condition at a time outside it + cannot isolate an in-gate cause. In-gate is 2 failures in 3; + out-of-gate is 0 in 17. Recorded in the registry; **not this lane's + to solve**. - **Still owed, separately:** `workflow_dispatch` on `ci.yml`, and U9's discriminating control — pin test-binary concurrency to 1, then load a lone `--lib` binary — which has been named since 2026-08-09 and diff --git a/docs/ci-red-signatures.md b/docs/ci-red-signatures.md index f4fd7ce..5685ed3 100644 --- a/docs/ci-red-signatures.md +++ b/docs/ci-red-signatures.md @@ -538,30 +538,45 @@ sixth was. five were spread across lanes and months. It prompted a narrowing, and the narrowing is the useful part. -**Four hypotheses EXCLUDED, 17 green runs, all at head `45d438c` on the -failing worktree:** +**A THIRD IN-GATE RUN WAS GREEN** (head `68a16f9`, log +`20260829T152024Z-563254`, all eight stages, zero failures anywhere). +So "in-gate always fails" is **false**, and the paragraph below was +written before that run and is corrected rather than deleted. -| hypothesis | test | result | +**Seventeen green runs outside the gate, in four configurations, all at +head `45d438c` on the failing worktree:** + +| condition reproduced outside the gate | runs | result | |---|---|---| -| the selector is simply flaky | isolated selector | **3 green** | -| the full `-p pmacs-gpu` binary's own concurrency | full binary, outside the gate | **6 green** | -| the gate's isolated `TMPDIR` (this project already knows socket-path length matters) | full binary under a gate-shaped 61-character `TMPDIR` | **6 green** | -| residue from `m4`, which the gate runs immediately before `gpu` and which spawns fake LSP servers and PTYs | `m4` then `gpu` back to back, twice | **2 pairs green** | +| isolated selector | 3 | green | +| full `-p pmacs-gpu` binary | 6 | green | +| full binary under a gate-shaped 61-character `TMPDIR` (tested because this project already knows socket-path length matters) | 6 | green | +| `m4` then `gpu` back to back, as the gate orders them | 2 pairs | green | -**So the discriminator is "inside `scripts/gate`" versus "outside it", -and it is NOT the TMPDIR, NOT the preceding stage, and NOT the -binary's own concurrency.** Something else about the gate's execution -context remains, and this row does not guess at it. +**THESE ARE NOT EXCLUSIONS, and an earlier version of this entry called +them that.** The reasoning was wrong: **nothing outside the gate has +ever reproduced this failure**, in 17 runs across four configurations — +so matching one gate condition at a time *outside* the gate cannot +isolate an in-gate cause. All these runs establish is that none of the +four conditions **by itself** reproduces the failure. They do not show +that any of them is uninvolved when the gate supplies the rest. -**Causal status: still UNRESOLVED.** What these two occurrences add is -a sharper question than the row had before: previous entries compared -lanes and trees, and this one says the difference is in the *runner*, -with the three most obvious runner differences already ruled out. +**What the observations actually support**, stated at the strength they +carry: in-gate is **2 failures in 3 runs**; out-of-gate is **0 failures +in 17**. That asymmetry is suggestive and it is not a clean split, +because the third in-gate run passed. -* **Next occurrence should start from that**, not from another tree - exclusion. The remaining candidates are the gate's ambient root, its - exported environment, and process state carried across stage - boundaries — none of which has been isolated. +**The method for the next occurrence follows from that.** Varying +conditions outside the gate cannot answer this question. It has to be +varied INSIDE — the gate's ambient root, its exported environment, and +process state carried across stage boundaries are the uneliminated +candidates, and each would need a gate run with that one thing changed. + +**Causal status: still UNRESOLVED.** What these occurrences add is a +sharper question and a method, not a cause: previous entries compared +lanes and trees, and these locate the asymmetry in the *runner* while +showing that the obvious way to probe it — reproducing gate conditions +outside the gate — cannot work. **Fifth occurrence — panel cell-mapping generation (§5b) framing, 2026-08-15, local (Linux).** The `scripts/gate` **`gpu` step** again, From acd6cee693762bb3fde95861fedd3348fddf776f Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 29 Aug 2026 18:02:52 +0200 Subject: [PATCH 5/8] docs: reconcile the lane's records with its final state Four stale claims, all in durable documentation that would have shipped as written. The framing still said "AWAITING APPROVAL. Nothing implemented" while the ledger and the PR both said otherwise. R7's status line said SIXTH OCCURRENCE while the body below it recorded the seventh. Its tree-exclusion bullet said the observing lane's diff is two docs; the lane carries three. And its cumulative figure said in-gate is 2 failures in 3 runs, which omitted this lane's final head-exact verification gate --- a fourth in-gate run, green. The figure is 2 in 4, and the fourth is named so a reader can tell which run it was. Active-work carried the same stale docs count and the same 2-in-3, and recorded neither PR #244, nor the final head 2d76984, nor the gate log 20260829T152824Z-673477. It now carries all three, with the gate's result read from its eight stage logs rather than inferred from stage exits. The sweep that found the last 2-in-3 also printed "(none = consistent)" unconditionally, which is the read-success-from-absence shape this project keeps catching. It now prints the hit count and only claims CLEAN when that count is zero. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai --- docs/active-work.md | 18 +++++++++++++----- docs/ci-red-signatures.md | 11 +++++++---- docs/parse-budget-diagnosability-framing.md | 2 +- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/docs/active-work.md b/docs/active-work.md index 37a382f..5cc98a0 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -316,11 +316,11 @@ from #171 and #215. were green and stage 6 (`gpu`) genuinely failed — that one completed and reported — but the run as a whole proves nothing and must not be read as a gate outcome. -- **That `gpu` failure is R7's SIXTH occurrence**, recorded in +- **Those `gpu` failures are R7's SIXTH and SEVENTH occurrences**, in `docs/ci-red-signatures.md`. All three required fragments, isolated selector green three times, and **this lane touches no `pmacs-gpu` file at all** — its whole diff is two `assert!` message strings and - two docs. + three docs. - **GATE RUN 2: 7/8, `gpu` red on R7 again** (head `45d438c`, log `20260829T150011Z-429115`). `sweep` ran fully green this time. **Two consecutive in-gate R7 failures prompted a narrowing**: 17 green @@ -329,9 +329,17 @@ from #171 and #215. false and the registry's first write-up — which called those runs *exclusions* — is corrected there: nothing outside the gate has ever reproduced this, so matching one gate condition at a time outside it - cannot isolate an in-gate cause. In-gate is 2 failures in 3; - out-of-gate is 0 in 17. Recorded in the registry; **not this lane's - to solve**. + cannot isolate an in-gate cause. In-gate is 2 failures in 4; + out-of-gate is 0 in 17 — **and a fourth in-gate run, this lane's + final verification gate, was also green, so the cumulative figure is + 2 in 4**. Recorded in the registry; **not this lane's to solve**. +- **PR #244** — https://github.com/levineuwirth/pmacs/pull/244, opened + at head **`2d76984`**, which is exactly the head the gate ran on. +- **GATE GREEN, head-exact**: all 8 stages, log + `20260829T152824Z-673477`, with `HEAD` and a clean worktree captured + before and after. Zero `FAILED` lines and zero non-zero-failure + result lines across all eight stage logs, read from them rather than + inferred from stage exits. - **Still owed, separately:** `workflow_dispatch` on `ci.yml`, and U9's discriminating control — pin test-binary concurrency to 1, then load a lone `--lib` binary — which has been named since 2026-08-09 and diff --git a/docs/ci-red-signatures.md b/docs/ci-red-signatures.md index 5685ed3..5445593 100644 --- a/docs/ci-red-signatures.md +++ b/docs/ci-red-signatures.md @@ -496,7 +496,7 @@ Stage 4; the lane touches no `pmacs-gpu` code at all. | **selector** | `-p pmacs-gpu attach::tests::managed_retry_survives_transients_and_uses_the_successful_stream` | | **job / flavor** | local (Linux), `cargo test --workspace --features crdt --no-fail-fast`, i.e. under full-sweep load | | **required fragments** | `transient sequence must attach` + `Handshake(Io(` + `BrokenPipe` (or `code: 32`) | -| **status** | **SIXTH OCCURRENCE 2026-08-29 — causal status still UNRESOLVED.** The fifth carries the strongest tree exclusion this row has had: a **documentation-only diff** | +| **status** | **SEVENTH OCCURRENCE 2026-08-29 — causal status still UNRESOLVED.** The sixth and seventh came back to back on one lane and are written up together below; the fifth carries the strongest tree exclusion this row has had, a **documentation-only diff** | | **what IS established** | **three** occurrences at `pmacs-gpu/src/attach.rs:1680`, the second and third with all three fragments **verified** rather than inferred; the test drives a scripted transient-then-success sequence over a real socket pair. **The added GPU test is not the mechanism** — see the third-occurrence control below | | **what is NOT** | whether the broken pipe is the *fixture's* writer closing early or a real retry-path defect. **This row is not a claim that it is harmless** | | **rerun evidence** | occurrence 1: 6 isolated runs green, plus a full `--workspace --features crdt` sweep green (113 targets). Occurrence 2: **30 green on the observing branch** (15 isolated selector, 15 full `-p pmacs-gpu`) **plus a 15-run merge-base control, also green**. Occurrence 3: 5 isolated selector runs green, 10 full `-p pmacs-gpu` runs green **with** the added test, and **1 failure in 10 with the added test `#[ignore]`d** — the first rerun in this row's history that reproduced anything. Per the rerun rule the green runs establish intermittence only; the red control run is what carries the exclusion | @@ -516,7 +516,7 @@ at `pmacs-gpu/src/attach.rs:1889`, `283 passed; 1 failed`. * **The tree exclusion is as strong as the fifth's.** The observing lane's entire diff is `src/async_runtime.rs`, - `tests/m4_acceptance.rs` and two docs — **no `pmacs-gpu` file, and no + `tests/m4_acceptance.rs` and three docs — **no `pmacs-gpu` file, and no file `pmacs-gpu` links against beyond the workspace it always did.** The change is two `assert!` message strings. * **Rerun: isolated selector green three times** (`1 passed`, 0.01 s @@ -562,8 +562,11 @@ four conditions **by itself** reproduces the failure. They do not show that any of them is uninvolved when the gate supplies the rest. **What the observations actually support**, stated at the strength they -carry: in-gate is **2 failures in 3 runs**; out-of-gate is **0 failures -in 17**. That asymmetry is suggestive and it is not a clean split, +carry: in-gate is **2 failures in 4 runs** — the failing pair, then two +greens, the second of which is this lane's final head-exact +verification gate (`20260829T152824Z-673477`) — while out-of-gate is +**0 failures in 17**. That asymmetry is suggestive and it is not a +clean split, because the third in-gate run passed. **The method for the next occurrence follows from that.** Varying diff --git a/docs/parse-budget-diagnosability-framing.md b/docs/parse-budget-diagnosability-framing.md index 8a6274a..dbf7b66 100644 --- a/docs/parse-budget-diagnosability-framing.md +++ b/docs/parse-budget-diagnosability-framing.md @@ -1,6 +1,6 @@ # Parse-budget diagnosability — reporting the measurement that failed -**Status: revision 2 — AWAITING APPROVAL. Nothing implemented.** +**Status: revision 2 — APPROVED 2026-08-29, IMPLEMENTED, PR #244.** Revision 2 answers review of 1. Three of its four changes are corrections, and one is a scope reduction: From 054a427e749c1ccee142c4e8ffe2b456a37c60e8 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 29 Aug 2026 18:11:02 +0200 Subject: [PATCH 6/8] docs: bound R7's observation window so the tally stops re-counting itself The previous commit recorded "in-gate 2 failures in 4 runs" and named the final verification gate. Re-running the gate on that very commit made both wrong: a fifth in-gate run, green, and a new log id. Every docs fix forces a re-gate, and the re-gate invalidates the docs fix. R7's window is now explicitly the four in-gate runs of 2026-08-29 up to and including the first green, plus the 17 out-of-gate runs taken between them. Later verification gates are excluded by definition. A ratio that grows with review activity measures review activity, not the phenomenon. The ledger stops naming the head and log id at all. It records that every review round ends with a green head-exact gate and points at the PR body for the current values --- which is the same reason SS5b stopped recording an ahead-count: a line naming a moving value is stale the moment it is written, and that lane learned it twice. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai --- docs/active-work.md | 21 +++++++++++---------- docs/ci-red-signatures.md | 18 ++++++++++++------ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/docs/active-work.md b/docs/active-work.md index 5cc98a0..2f542bc 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -330,16 +330,17 @@ from #171 and #215. *exclusions* — is corrected there: nothing outside the gate has ever reproduced this, so matching one gate condition at a time outside it cannot isolate an in-gate cause. In-gate is 2 failures in 4; - out-of-gate is 0 in 17 — **and a fourth in-gate run, this lane's - final verification gate, was also green, so the cumulative figure is - 2 in 4**. Recorded in the registry; **not this lane's to solve**. -- **PR #244** — https://github.com/levineuwirth/pmacs/pull/244, opened - at head **`2d76984`**, which is exactly the head the gate ran on. -- **GATE GREEN, head-exact**: all 8 stages, log - `20260829T152824Z-673477`, with `HEAD` and a clean worktree captured - before and after. Zero `FAILED` lines and zero non-zero-failure - result lines across all eight stage logs, read from them rather than - inferred from stage exits. + out-of-gate is 0 in 17, over a **bounded observation window** the + registry defines so the ratio cannot drift with review activity. + Recorded there; **not this lane's to solve**. +- **PR #244** — https://github.com/levineuwirth/pmacs/pull/244. +- **Every review round ends with a head-exact 8-stage gate**, green + each time, with `HEAD` and a clean worktree captured before and after + and the result read from the eight stage logs rather than inferred + from stage exits. **The PR body carries the current head and log + id**; they are not duplicated here, because a docs commit answering a + review moves both, and a ledger line naming them is stale the moment + it is written — the lesson §5b learned twice. - **Still owed, separately:** `workflow_dispatch` on `ci.yml`, and U9's discriminating control — pin test-binary concurrency to 1, then load a lone `--lib` binary — which has been named since 2026-08-09 and diff --git a/docs/ci-red-signatures.md b/docs/ci-red-signatures.md index 5445593..7d8a315 100644 --- a/docs/ci-red-signatures.md +++ b/docs/ci-red-signatures.md @@ -561,12 +561,18 @@ isolate an in-gate cause. All these runs establish is that none of the four conditions **by itself** reproduces the failure. They do not show that any of them is uninvolved when the gate supplies the rest. -**What the observations actually support**, stated at the strength they -carry: in-gate is **2 failures in 4 runs** — the failing pair, then two -greens, the second of which is this lane's final head-exact -verification gate (`20260829T152824Z-673477`) — while out-of-gate is -**0 failures in 17**. That asymmetry is suggestive and it is not a -clean split, +**THE OBSERVATION WINDOW IS BOUNDED, deliberately.** It is the four +in-gate runs of 2026-08-29 up to and including the first green — +`20260829T144541Z`, `T150011Z`, `T152024Z`, `T152824Z` — plus the 17 +out-of-gate runs taken between them. **Later head-exact verification +gates on this lane are NOT part of it and do not move these numbers.** +Without that boundary the tally re-counts itself every time a review +round adds a docs commit and the gate is re-run, which is a ratio that +drifts with review activity rather than with the phenomenon. + +**What the window supports**, at the strength it carries: in-gate +**2 failures in 4**; out-of-gate **0 failures in 17**. That asymmetry +is suggestive and it is not a clean split, because the third in-gate run passed. **The method for the next occurrence follows from that.** Varying From 756c2b87df7b2aa82dc3232255a8238b17413659 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 29 Aug 2026 19:15:41 +0200 Subject: [PATCH 7/8] docs(ci-reds): bound R7 window without misnaming its endpoint The four-run observation window contains two green in-gate runs, so it cannot be described as ending at the first green. Name it directly as the first four in-gate runs; the timestamps continue to define the boundary exactly and later verification gates remain excluded. --- docs/ci-red-signatures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ci-red-signatures.md b/docs/ci-red-signatures.md index 7d8a315..255767f 100644 --- a/docs/ci-red-signatures.md +++ b/docs/ci-red-signatures.md @@ -561,8 +561,8 @@ isolate an in-gate cause. All these runs establish is that none of the four conditions **by itself** reproduces the failure. They do not show that any of them is uninvolved when the gate supplies the rest. -**THE OBSERVATION WINDOW IS BOUNDED, deliberately.** It is the four -in-gate runs of 2026-08-29 up to and including the first green — +**THE OBSERVATION WINDOW IS BOUNDED, deliberately.** It is the first +four in-gate runs of 2026-08-29 — `20260829T144541Z`, `T150011Z`, `T152024Z`, `T152824Z` — plus the 17 out-of-gate runs taken between them. **Later head-exact verification gates on this lane are NOT part of it and do not move these numbers.** From 7b50682eace42570827fbbbac558f935cddb4e0c Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Sat, 29 Aug 2026 22:53:36 +0200 Subject: [PATCH 8/8] docs(ci-reds): record empty prune-output sweep red as U13 The head-exact review gate reached sweep with six green stages, then `skipped_directories_are_reported_with_a_reason` observed empty child stdout. The durable failure cannot say why because the row discards the child status and stderr. Record the exact signature, the structurally unrelated branch diff, and the isolated-selector and full-binary green reruns at their actual strength: intermittence only. Diagnostic hardening remains a separate lane. --- docs/active-work.md | 9 +++++++++ docs/ci-red-signatures.md | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/docs/active-work.md b/docs/active-work.md index 2f542bc..581b6dc 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -341,6 +341,15 @@ from #171 and #215. id**; they are not duplicated here, because a docs commit answering a review moves both, and a ledger line naming them is stale the moment it is written — the lesson §5b learned twice. +- **Review follow-up found U13, separately recorded.** On signed head + `756c2b8`, gate `20260829T171606Z-1087848` was 7/8: `07-sweep` + reddened only + `gate_script_acceptance::skipped_directories_are_reported_with_a_reason`. + The child command's stdout was empty, but the row discards its stderr + and status, so the mechanism is unrecoverable. The exact selector and + the full 36-test binary both passed immediately afterwards — + intermittence only. This lane changes neither the gate script nor + that acceptance binary; diagnostic hardening is a separate lane. - **Still owed, separately:** `workflow_dispatch` on `ci.yml`, and U9's discriminating control — pin test-binary concurrency to 1, then load a lone `--lib` binary — which has been named since 2026-08-09 and diff --git a/docs/ci-red-signatures.md b/docs/ci-red-signatures.md index 255767f..86859b6 100644 --- a/docs/ci-red-signatures.md +++ b/docs/ci-red-signatures.md @@ -1076,3 +1076,20 @@ discriminating control U9 named is STILL UNRUN**: pin test-binary concurrency to 1, and separately load a lone `--lib` binary. Four incidents is enough evidence that the family will keep costing review rounds until someone runs it. + +### U13 — gate prune-reporting row receives empty child stdout in `sweep` + +Recorded during PR #244 review, 2026-08-29, on signed head `756c2b8`. +The first six gate stages were green; `07-sweep` produced this one red; +`08-diff-check` remained green. + +| field | value | +| --- | --- | +| **selector** | `--test gate_script_acceptance skipped_directories_are_reported_with_a_reason` | +| **job / step** | local (Linux), `scripts/gate` step `07-sweep`, log `20260829T171606Z-1087848` | +| **required fragments** | `skipped_directories_are_reported_with_a_reason` + `the unmarked directory must be named with its reason; output was:` followed by empty output | +| **status** | **one occurrence; exact selector and the full 36-test binary both green on immediate rerun** | +| **what IS established** | the test observed empty stdout from its `scripts/gate --prune` child even though its fixture had created an unmarked lookalike which a successful prune must name. The row discards the child's stderr and success status (`let (out, _, _)`), so the durable failure cannot distinguish wrong successful output from a refused or failed child invocation | +| **what is NOT** | any mechanism. Another row in the same test binary mutates shared git-worktree metadata, but concurrent execution is only a candidate; this occurrence did not capture the child status or stderr needed to support it | +| **why this branch is excluded** | the lane changes two assertion-message strings and three docs. `scripts/gate` and `tests/gate_script_acceptance.rs` are byte-identical to the base; the `async_runtime` edit is inside its unit-test module, and the other Rust edit belongs to a different integration-test binary | +| **next occurrence** | preserve the child success status and stderr in the assertion before rerunning. A green rerun establishes intermittence only; it cannot reconstruct why stdout was empty |