# CI red signatures — the triage registry **This file is the single authority for judging a red CI run.** It is an occurrence ledger, not a flake list: a row records what was seen, what is known about why, and what would retire it. **A row is not a claim that the failure is harmless.** Deliberately not named "flakes". One of its rows is a possible product defect, and a filename that called it a flake would confer immunity the evidence does not support. Read this before attributing any red run to the environment. Landed framing documents keep their own historical evidence and reasoning — that is not duplication, and it is not superseded by this file. What lives here is **live triage policy**. --- ## How a row matches **A test-name match is never sufficient.** A red run matches a row only when *all three* hold: 1. the **exact test selector** matches; 2. the **job / flavor** matches; 3. **every required fragment** is present in the failure output. Where a fragment lists alternatives (`ESRCH` / `No such process`), any one satisfies that requirement — those are the same condition rendered differently by platform or libc. Fragments are **normalized**, never pasted verbatim. PIDs, elapsed times, thread ids and rendered OS-error suffixes vary between runs; a verbatim key would match nothing. The evidence link preserves the exact occurrence. **A failure in a listed test that does not carry that row's fragments is a NEW incident**, judged on its own. The process test below is why this rule exists: it produced two signatures with different mechanisms and different causal status, and only one of them is a test bug. --- ## The rerun rule This replaces "rerun before concluding", which conflated three different outcomes. - **A green rerun after a red establishes INTERMITTENCE ONLY.** It does not establish environmental cause, harmlessness, or retirement. - **The same signature on the rerun is a SECOND OCCURRENCE.** It remains blocking pending investigation or a merge-base control. - **A different signature is a NEW INCIDENT**, judged independently. A merge-base control — running the same command on the merge base — is what distinguishes "this branch caused it" from "this tree has it". It is cheaper than argument and is the correct response to a second occurrence. --- ## What retires a row **Retirement is causal, never a count of green runs.** A row is retired by removing or explaining its mechanism: | causal status | retirement condition | |---|---| | **test race** | hardening that removes the named mechanism, plus a discriminating witness for the stronger predicate | | **measurement design** | the owning lane replaces or justifies the measurement and pins the resulting claim | | **unresolved** | diagnosis and an explicit disposition | **Audit notes (`A`-numbers) have no retirement condition**, because they have nothing to retire — see that section. A linked occurrence promotes one into an `R` row; absence retires nothing, because nothing was ever measured. 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. --- ## Live rows ### R1 — supersede cancellation budget | field | value | |---|---| | **selector** | `--lib async_runtime::tests::supersede_cancels_in_flight_job_within_50ms` | | **job / flavor** | macOS / luajit | | **required fragments** | `supersede did not cancel within 50ms` | | **causal status** | **measurement design** | | **evidence** | [#213 run 30826884642](https://github.com/levineuwirth/pmacs/actions/runs/30826884642) | | **retirement** | the async-runtime lane replaces or justifies the measurement (Q#MCI3) | The test's premise is `thread::sleep(15ms)`, asserted by comment to mean "the worker picked the job up"; under load it may not have, in which case the test measures the *queued* path while claiming the running one. And its 50ms clock starts before the second dispatch and is consumed by the test's own `tick()` + `sleep(1ms)` pump, so the interval is dominated by when *the test* was scheduled. **Widening the budget would make it pass and measure nothing more.** ### R2 — USR1 delivered before the trap is installed | field | value | |---|---| | **selector** | `--lib process::tests::a_successful_signal_disposition_depends_on_whether_it_is_fatal` | | **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) | | **retirement** | the fixture proves the trap is installed, with a witness that fails without it | Readiness is `ProcessEventKind::Started`, emitted at **spawn** — not when `/bin/sh` has parsed `trap '' USR1`. SIGUSR1's default disposition is terminate, so a signal inside that window kills the child. The fixture's own comment states the requirement it does not enforce. ### R3 — live-leader EPERM with an unobservable group | field | value | |---|---| | **selector** | `--lib process::tests::a_successful_signal_disposition_depends_on_whether_it_is_fatal` | | **job / flavor** | macOS / lua54 | | **required fragments** | `EPERM` **and** `measured_group=unobservable(` **and** (`ESRCH` / `No such process`) **and** `leader=live` | | **causal status** | **UNRESOLVED — possible product defect** | | **evidence** | [#214 run 30932558752 attempt 1](https://github.com/levineuwirth/pmacs/actions/runs/30932558752/attempts/1) | | **retirement** | **diagnosis and disposition by the process-signal / reap-ledger lanes. Never a green rerun.** | **Same test as R2, different mechanism, different status.** This is the group-target behaviour #176 and #200 circled and the reap-ledger lane parked every disposition change pending: a group-directed `kill` returned EPERM while the leader was observed live, and `measured_group` — the one field able to disagree — could not be read at all. **Do not treat a red matching this row as environmental.** A green rerun changes nothing about it. ### R4 — readiness predicate satisfied by an empty file | field | value | |---|---| | **selector** | `--test vterm_stage2_acceptance terminal_escape_gates_local_bindings_and_double_escape_sends_interrupt` | | **job / flavor** | macOS / luajit | | **required fragments** | `left: []` **and** `right: [49]` | | **causal status** | **test race** | | **evidence** | [#214 run 30932558752 attempt 1](https://github.com/levineuwirth/pmacs/actions/runs/30932558752/attempts/1) | | **retirement** | `wait_for_file` requires the expected content, with a witness that fails against a zero-byte file | `wait_for_file` returns as soon as `fs::read` succeeds — which succeeds on a **zero-byte file**. The probe writes readiness with `open(path,'wb').write(b'1')`, and `open()` creates the file before `write()` fills it. The predicate is "readable"; the assertion is "contains `1`" (`49` is ASCII `'1'`). --- ## Audit notes — historical claims with no linked occurrence **These are NOT registry rows.** They carry `A`-numbers, not `R`-numbers, because nothing here can be matched against a red run and nothing here confers any status. They were named in the handoff's hazards list without evidence. The audit found the tests real and the claims recorded in good faith — but **an assertion string existing is not a mechanism, and "timing-based" is not an observation.** No occurrence of either was ever linked, so nothing is known about how either fails, or whether either has failed. Deleting them would discard a real recorded belief. Listing them beside the evidenced rows would grant the reputation this file exists to deny. So they are stated as what they are: **claims awaiting a first occurrence.** A red in either test is a first recorded occurrence, to be investigated and then promoted to an `R` row — not matched against anything here. ### A1 — GPU terminal cell background did not paint | field | value | |---|---| | **selector** | `-p pmacs-gpu a33_headless_terminal_frame_paints_cells_without_document_layers` | | **job / flavor** | GPU Render (headless), under parallel load | | **required fragments** | `the terminal cell background did not paint` + `blue pixels` | | **status** | **historical claim, no linked occurrence** | | **what IS established** | the test exists and the assertion string is real (`pmacs-gpu/src/main.rs:17973`). That is all | | **what is NOT** | any mechanism, and any occurrence. No run was ever cited | | **promotion** | a linked occurrence makes this an `R` row with a signature. Absence retires nothing, because nothing was measured | ### A2 — supervisor reap across cycles | field | value | |---|---| | **selector** | `--test m6_8_multi_repl_acceptance m6_8_supervisor_reaps_all_children_across_cycles` | | **job / flavor** | not recorded | | **required fragments** | **not recorded** — no signature was ever captured | | **status** | **historical claim, no linked occurrence** | | **what IS established** | the test exists and runs 10 cycles; the handoff called it "timing-based" | | **what is NOT** | any mechanism, any signature, any occurrence | | **promotion** | a linked occurrence *with a captured signature* makes this an `R` row | **A2 cannot be matched, and neither can A1** — that is what makes them notes rather than rows. A red in either test is a new incident by default. That is the correct outcome for an entry that never carried evidence, and it means this file is **stricter** than the list it replaces: nothing is pre-excused. --- ## Occurrence log | date | run | row | outcome | |---|---|---|---| | 2026-08-04 | [30826884642](https://github.com/levineuwirth/pmacs/actions/runs/30826884642) | R1 | rerun green — intermittence only | | 2026-08-04 | [30927084982 att.1](https://github.com/levineuwirth/pmacs/actions/runs/30927084982/attempts/1) | R2 | rerun green — intermittence only | | 2026-08-04 | [30932558752 att.1](https://github.com/levineuwirth/pmacs/actions/runs/30932558752/attempts/1) | R3, R4 | rerun green — intermittence only; **R3 remains unresolved** | Four incidents, three tests, **four signatures**. Count signatures: the process test contributed two, and only one of them is a test bug. **All four *evidenced* rows (R1–R4) are macOS.** That is a property of these occurrences, not of the file: **A1's job is `GPU Render (headless)`, which runs on Ubuntu**, and **A2's job was never recorded**. Nothing here is macOS-only by construction, and a future row from any job belongs in the same table. The #214 occurrence is the strongest available evidence that these are not caused by the PRs they appeared on — that PR is **docs-only and its tree is byte-identical to a green `main`**. It is not evidence that any of them is harmless.