Revision 4 of `docs/macos-ci-signal-integrity-framing.md` records
implementation findings, not a new design round: §4's acceptance is
unchanged and was approved at revision 3. Two of the four findings
correct this document.
- **§1.3 named the right window and the wrong assertion.**
`leader=exited(signal SIGUSR1)` is rendered only on a FAILED `kill`,
and the USR1 cannot be the call that failed — it is the call that did
the killing. The failing call is the SIGTERM that follows, so
`.expect("TERM delivers")` is what blew up, not the `Running` state
check; `ProcessState` never carries that value and nothing ticks
between the two calls. The row's fragment and mechanism were both
right. Why a group-directed TERM found no group is NOT established
here, and the fix does not depend on it.
- **§1.5 scoped the fix one function too narrowly.**
`wait_for_published_file` gates the real-TUI smoke on the identical
predicate. §1.5's note about the bottom-panel helper is about a
different file and correctly refuses creep there; it does not reach
this one.
- The fixture also had an unnamed second dependency: these signals are
group-directed, so a forked `sleep` is an untrapped group member, and
survival depended on bash and dash suppressing the fork for the last
command of a `-c` script.
- R2's witness could not reproduce the row on Linux, so it widens the
pre-trap window deliberately and proves survival by exit disposition
rather than by an absence observed within a window.
`docs/ci-red-signatures.md` gains the sentence R3 needs most: **R2's
retirement does not touch it and must not be read as touching it.** The
hardening changed a fixture and no product code; the same
group-directed `kill` runs. Because the fixture changes the shape of the
signalled group, a change in how often R3 appears would be evidence
about FREQUENCY, not about cause — and its retirement is still a
diagnosis by the process-signal / reap-ledger lanes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Stage 2 of `docs/macos-ci-signal-integrity-framing.md` revision 3
(acceptance 6–9). Two test races, both the same shape: the thing waited
for was weaker than the thing asserted, so the wait could return inside
the window the assertion needs closed.
R4 — `wait_for_file` returned as soon as `fs::read` succeeded, which
succeeds on a ZERO-BYTE FILE. The probe publishes with
`open(path,'wb').write(b'1')` and `open()` creates the file before
`write()` fills it, so the helper handed `[]` to a caller asserting
`== b"1"`. It now takes the expected bytes and waits while the file
holds a STRICT PREFIX of them — the states a write in flight can be
observed in — returning anything else immediately so the caller's
`assert_eq!` stays the discriminating assertion rather than becoming a
timeout inside a helper that does not know what was expected.
All four callers pass their expectation. `wait_for_published_file`, one
function away in the same suite, gated the real-TUI smoke's
`assert_eq!(…, b"1")` on the identical predicate and is fixed with it:
leaving it would have let R4 recur under a different selector, which the
registry would then have had to judge a new incident.
R2 — the USR1 fixture waited on `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 child now publishes a marker AFTER the trap and the test
waits for that marker's CONTENT (the same zero-byte trap applies to a
shell's `>` redirection). `exec` replaces the forked `sleep`, so the
group holds exactly one process and the ignored disposition survives by
POSIX rather than by the shell's fork-suppression optimization — an
unstated dependency the old fixture had, since these signals are
group-directed and a forked `sleep` is an untrapped group member.
Four witnesses, each verified by REVERTING the fix and observing the
failure rather than by reasoning about it:
- `wait_for_file_does_not_return_a_zero_byte_readiness_file` fails
`left: []`, `right: [49]` — R4's two required fragments, verbatim;
- `wait_for_file_does_not_return_a_partial_write` fails on the torn read
a length check alone would admit;
- `wait_for_file_returns_divergent_content_rather_than_timing_out` fails
against an over-strict helper that waits for an exact match;
- `usr1_readiness_waits_for_the_trap_not_for_the_spawn` fails
`left: Some("SIGUSR1")`, `right: Some("SIGTERM")` with the readiness
wait removed. Its fixture sleeps before `trap` so the pre-trap window
is deliberate rather than load-dependent, and it proves survival by
the child's EXIT DISPOSITION rather than by an absence observed within
a window.
R1 is NOT touched — referred to the async-runtime lane (Q#MCI3), because
widening its budget would make it pass and measure nothing more. R3 is
NOT touched and remains UNRESOLVED, owned by the process-signal /
reap-ledger lanes.
`docs/ci-red-signatures.md` moves R2 and R4 to a "Retired rows" section
with their dispositions and adds the rule the file needed and lacked: a
red matching a retired row is a RECURRENCE that puts the retirement in
question, never a known flake. `docs/active-work.md` carries this lane
from its first commit rather than after review asks for it.
Repetition sets, not single runs: the two `--lib` process tests 15/15,
the whole `vterm_stage2_acceptance` suite 15/15 at default parallelism.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Five corrections, one blocking.
BLOCKING — the contract, not the implementation, was what needed
changing. Revision 2's acceptance 3 offered a binary: carry an incumbent
in with a signature and evidence, or remove it as never substantiated.
Stage 1 shipped a THIRD state because both incumbents are neither. The
framing is now revision 3 and names all three states, because a
governing criterion that says "two" while the branch does five is the
framing describing something that does not exist.
"MECHANISM NAMED" OVERSTATED THE EVIDENCE, and the phrase is retired.
The a33 audit proves an assertion string exists; the m6_8 audit proves a
test is timing-based. NEITHER ESTABLISHES A FAILURE MECHANISM — no
occurrence was ever observed, so nothing is known about how, or whether,
either fails. They are now AUDIT NOTES A1/A2 rather than registry rows,
with `R`-numbers reserved for signatures carrying linked evidence. The
distinction is not row-versus-weaker-row: a row says "this was seen,
here is the evidence", a note says "someone recorded a belief and no
occurrence backs it". Both remain unmatchable, so a red in either test
is a new incident.
R2's SIGNATURE WAS WEAKENED AND IS RESTORED. Splitting
`leader=exited(signal SIGUSR1)` into `leader=exited(` plus `SIGUSR1`
would match a child that exited by some OTHER disposition while SIGUSR1
appeared elsewhere in the output — precisely the name-style
over-matching this registry exists to refuse, reintroduced one level
down as fragment-style over-matching. It is one exact fragment again,
and the row says why.
THE HANDOFF STILL ISSUED LIVE IMPERATIVES. Its historical block opened
with "rerun isolated before treating a sweep failure as a regression"
and closed with "rerun the test alone before investigating", so the
supersession note I added sat between two instructions it contradicted.
Both are rewritten as record: the block now reports what that lane
OBSERVED, and the retired instruction is marked retired with its reason
— an isolated green reproduces nothing about a load-sensitive failure.
#215 HAD NO LANE, in the file that requires one for every open PR and
records why: #171 drifted 153 commits while invisible there. That is the
same defect, caught in review rather than 153 commits later. The lane
now carries branch, base, PR, reviewed head, what Stage 1 ships, the
verification, what Stage 2 owes, and the recovery worktree command. Both
snapshot headers are bumped.
Also narrowed a claim the PR body overstated: the registry is NOT
macOS-only. All four EVIDENCED rows are macOS, which is a property of
these occurrences; A1's job is `GPU Render (headless)`, which runs on
Ubuntu, and A2's job was never recorded. A future row from any job
belongs in the same table.
Verified: fmt, diff-check, clippy, --lib 1896, --lib --features crdt
2081.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Stage 1 of the macOS CI signal-integrity lane. `docs/ci-red-signatures.md`
is now the single authority for judging a red CI run.
NOT NAMED "FLAKES", DELIBERATELY. One of its rows is a possible product
defect, and a filename calling it a flake would confer immunity the
evidence does not support.
A ROW MATCHES ON SIGNATURE, NEVER ON TEST NAME. All three of selector,
job/flavor and every required fragment must hold. Fragments are
normalized rather than pasted: PIDs, elapsed times and rendered
OS-error suffixes vary between runs, so a verbatim key would match
nothing, and where a fragment lists alternatives (ESRCH / "No such
process") those are one condition rendered differently by platform. A
failure in a listed test WITHOUT that row's fragments is a new incident.
The process test is why that rule exists: it produced TWO signatures
with different mechanisms and different causal status, and only one is a
test bug. Four incidents, three tests, four signatures — the registry
counts signatures.
THE RERUN RULE IS REPLACED, NOT SOFTENED. A green rerun establishes
INTERMITTENCE ONLY — never environmental cause, harmlessness, or
retirement. The same signature again is a second occurrence and stays
blocking pending investigation or a merge-base control. A different
signature is a new incident.
RETIREMENT IS CAUSAL. A test race retires by hardening that removes the
mechanism plus a discriminating witness; a measurement-design row by its
owning lane replacing or justifying the measurement; an unresolved row
by diagnosis and disposition. Main-branch greens accumulate as
occurrence evidence and retire nothing.
THE AUDIT FOUND A THIRD CATEGORY the framing's acceptance 3 did not
anticipate. It said each incumbent is either carried in with a signature
and evidence, or removed as never substantiated. Both incumbents are in
between: the tests are real and the mechanisms plausible — a33's "blue
pixels" is a genuine assertion string at pmacs-gpu/src/main.rs:17973,
and m6_8 exists and is timing-based — but NEITHER HAS A LINKED
OCCURRENCE. Deleting them would discard a real observation; carrying
them as peers of the evidenced rows would grant exactly the reputation
this lane exists to deny.
They are recorded as "mechanism named, no occurrence recorded", with it
stated that nothing there confers known-flaky status and that a red
matching one is a FIRST recorded occurrence to be investigated. R6 goes
further: with no signature ever captured it CANNOT BE MATCHED AT ALL, so
a red in that test is a new incident by default. That is the correct
outcome for an entry that never carried evidence.
LIVE POLICY IS CENTRALIZED; HISTORICAL EVIDENCE IS NOT MOVED. The
handoff's hazards rule becomes a pointer. Its landed-lesson block at
§5 keeps its own evidence and gains a note that the registry's rerun
rule supersedes "rerun isolated". active-work.md's two mentions are
verification records for the reap-ledger and bottom-panel lanes and are
left alone; only the CI-weakness block's triage half points at the
registry, because the job-cost question there is genuinely separate.
Verified: fmt, diff-check, clippy with and without crdt, --lib 1896,
--lib --features crdt 2081, pmacs-protocol 19, m4 149, required GPU 221.
All three tests the registry names pass locally on Linux, which is
consistent with every row being macOS-only and is not evidence about
any of them.
Docs only. Stage 2 (hardening) is a separate PR.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>