232 lines
11 KiB
Markdown
232 lines
11 KiB
Markdown
# 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.
|