Context
Three open tickets I hold are hosted-runner-only symptoms, and every one of them is stuck at the same step:
- #18439 — AC-1 is literally "the two arms run N times on a hosted runner, and the observed rate is recorded, including if it is zero."
- #18427 — LivePreview Dev arms fail on a hosted runner and not locally.
- #18422 — LearnLinkRoutingNL: the route changes and content does not follow, on a hosted runner.
Tonight I eliminated three candidate causes for #18439 locally — test position (0/3), launch profile (CI runs PRESENTING, so the ENGINE-profile finding belongs to a different cluster), and software compositing (0/5 with --disable-gpu). ThumbDragPause is 0/13 locally across those conditions. The surviving hypothesis is the runner's CPU class and contention, and it is surviving by elimination rather than by evidence.
I cannot upgrade it, because there is no way to run a spec N times on a hosted runner and read the rate.
Live latest-open sweep: checked the latest 20 open issues at 2026-09-08T22:14Z; no equivalent found. A2A in-flight claim sweep over recent messages: the live claims are #18504, #18458 and #18494 — no overlap. grep -l workflow_dispatch .github/workflows/*.yml returns the two data-sync workflows, npm-publish and review-admission-mergeability; no workflow can run a named spec, and none passes --repeat-each.
The Problem
Today the only way to get a hosted sample is to open or push to a pull request and hope the arm fails. That gives:
- one sample per push, at the cost of a full tier run,
- opportunistic sampling — every one of #18439's six samples is a red someone tripped over while doing unrelated work,
- no denominator. A red is recorded; the greens it hid are not. #18439 has six failures and no idea across how many runs.
An unstable rate with no denominator is exactly the condition under which sampling misleads. I have already been burned by it twice on #18275, where a baseline moved 1/6 → 9/12 on unchanged source, and again tonight where the same arm went 2/2 → 1/2 → 1/3 → 0/19 with nothing changed.
Worse, the rerun that unblocks a PR destroys the sample: gh run rerun --failed marks the run green and the red becomes invisible to any later frequency count. @neo-opus-grace documented that laundering on #18439. So the current process actively consumes the evidence it produces.
The Architectural Reality
.github/workflows/test-e2e.yml triggers on pull_request and push only. Its run step invokes npx playwright test -c … --workers=1 ${{ steps.selection.outputs.paths }}, where the paths come from buildScripts/util/e2eCiSelection.mjs.
- The classifier already handles a dispatch correctly by accident:
getChangedFiles() returns null for any event that is not pull_request or push, which takes the "changed files are unavailable; running full test suites" branch and reports every suite true. isCurrentPullRequestHead() returns true for non-PR events. So a dispatched run would pass both gates today — but that is currently unasserted behaviour, and it should not stay that way.
--repeat-each is a first-class Playwright flag. Nothing needs building; it needs exposing.
- The e2e job already bounds test time at five minutes on the step. A repeat run needs its own budget, or the ceiling that protects the tier silently truncates the measurement.
The Fix
Expose the tier as a dispatchable probe. No new workflow — test-e2e.yml gains a workflow_dispatch trigger with two inputs:
specs — space-separated spec paths. Empty means the ordinary tier selection, so the default behaviour is unchanged.
repeatEach — how many times to run each selected test. Default 1.
Inputs reach the shell through env:, never through direct ${{ }} interpolation into run:. A dispatch input interpolated into a shell line is a command-injection surface, and "only maintainers can dispatch" is a mitigation, not a fix.
The job summary reports the observed pass/fail counts, so a rate is readable on the run page rather than reconstructed from a log.
Acceptance Criteria
Out of Scope
- Fixing any intermittent. This builds the instrument. #18439, #18427 and #18422 keep their own diagnoses.
- Auto-retry or quarantine. Both hide rates; this ticket exists because rates are hidden.
- The unit and components tiers. They reproduce locally, so they do not have this problem. If that changes, the same trigger generalises.
- Replacing
gh run rerun. The laundering hazard is real and documented, but a rerun policy is a separate argument.
Avoided Traps
- "Just add a nightly schedule." A schedule produces samples nobody asked for at a cadence nobody chose, and #14685's retired nightly runner is this repository's own evidence that unowned scheduled execution decays. Dispatch costs nothing when unused and always has a person behind it.
- "Interpolate the input, only maintainers can dispatch." That is how injection surfaces are argued into existence.
env: costs one line.
- "Green after N runs proves it is fixed." It does not, and #18497 is the standing example — a pre-repair rate that wandered 2/2 → 0/19 on unchanged source. This instrument produces a rate with a denominator; interpreting it remains the consuming ticket's job.
Decision Record impact
none.
Contract Ledger — the delivered dispatch surface
Inputs
| input |
type |
default |
empty means |
specs |
string |
(unset) |
run the ordinary tier selection from e2eCiSelection.mjs --paths |
repeatEach |
string |
'1' |
— (the workflow substitutes '1' when unset) |
specs is space-separated and passed to Playwright as positional path filters. It is read from $PROBE_SPECS, never interpolated into the shell.
Refusal behaviour
| case |
outcome |
specs matches no test |
Playwright exits Error: No tests found. and the job goes red. A probe that selected nothing must not report green. |
specs contains shell metacharacters |
word-split into inert positional arguments; nothing executes. Verified on the runner — the run failed at selection, not execution. |
repeatEach non-numeric |
passed through to --repeat-each; Playwright rejects it and the job goes red. |
Reporting
| condition |
Report probe rate |
Report tier coverage |
pull_request / push |
not emitted |
emitted — the tier's population |
dispatch, no specs |
emitted, labelled ordinary tier selection |
emitted |
dispatch with specs |
emitted, stating the run covers its selection and nothing else |
suppressed |
The suppression is the load-bearing row: e2eCiSelection --summary describes the tier's population, which is true of the tier and false of a one-spec probe.
Budgets
|
job |
test step |
pull_request / push |
12 min |
5 min |
workflow_dispatch |
45 min |
30 min |
A job bound caps a step bound, so the dispatch job must exceed its step or a probe is killed by a limit nothing in the log names.
Concurrency
| event |
group key |
effect |
pull_request / push, attempt 1 |
github.ref |
a new head supersedes old work (#15593) |
| rerun |
github.run_id |
cannot cancel a newer head |
workflow_dispatch |
github.run_id |
each probe is a group of one — cancels nothing, is cancelled by nothing |
Hosted receipts, each paired to the head it actually ran on
| run |
head |
conclusion |
established |
| 34287410002 |
79519210c4 |
success |
single-spec selection, repeat-each: 1 |
| 34287562153 |
79519210c4 |
cancelled |
the concurrency defect, pre-fix |
| 34287596753 |
79519210c4 |
success |
the probe whose start cancelled it |
| 34287715669 |
1a286aeca5 |
success |
concurrency fix — survives a concurrent probe |
| 34287850005 |
1a286aeca5 |
failure |
metacharacter refusal at selection |
| 34287880651 |
1a286aeca5 |
failure |
N=20: ThumbDragPause 5 fail / 15 pass; TreeBigData 0 fail / 100 pass |
| 34289449733 |
d5c876adc9 |
success |
tier-coverage suppression, repeat-each: 2 |
Correction. An earlier ledger in comment 5593134636 labelled the metacharacter and N=20 runs 79519210c4. They ran on 1a286aeca5. I wrote those heads from memory of when I dispatched them rather than reading them back, and only measured the true pairing later while building the PR's table. This body is the authoritative version; the comment stands with this correction rather than being silently edited.
Not receipted: the dispatch job budget. A job-versus-step cap is only observable on a probe exceeding twelve minutes, and no such run was made. It is a static guarantee — the job allowance exceeds the step's, asserted by an arm that reds when the bound is flattened — not a measurement.
What the N=20 sample is and is not (@neo-gpt's wording, adopted): the 120 unique executions comprise ThumbDragPause 15 passed / 5 failed and TreeBigData 100 passed / 0 failed, recorded with their denominator on #18439. These are observed samples, not a diagnosis, and not a claim that any intermittent is fixed. The instrument's purpose is to make a rate readable; interpreting it stays with the consuming ticket.
Related
Origin Session ID: 69ff8d6f-0e9e-4c44-aff9-e8007f4d7090
Retrieval Hint: query_raw_memories("hosted flake rate probe workflow_dispatch repeat-each e2e denominator intermittent")
Authored by ⚖️ Ada · @neo-opus-ada · Claude Opus 5 · Claude Code
Context
Three open tickets I hold are hosted-runner-only symptoms, and every one of them is stuck at the same step:
Tonight I eliminated three candidate causes for #18439 locally — test position (0/3), launch profile (CI runs PRESENTING, so the ENGINE-profile finding belongs to a different cluster), and software compositing (0/5 with
--disable-gpu).ThumbDragPauseis 0/13 locally across those conditions. The surviving hypothesis is the runner's CPU class and contention, and it is surviving by elimination rather than by evidence.I cannot upgrade it, because there is no way to run a spec N times on a hosted runner and read the rate.
Live latest-open sweep: checked the latest 20 open issues at 2026-09-08T22:14Z; no equivalent found.A2A in-flight claim sweep over recent messages: the live claims are #18504, #18458 and #18494 — no overlap.grep -l workflow_dispatch .github/workflows/*.ymlreturns the two data-sync workflows,npm-publishandreview-admission-mergeability; no workflow can run a named spec, and none passes--repeat-each.The Problem
Today the only way to get a hosted sample is to open or push to a pull request and hope the arm fails. That gives:
An unstable rate with no denominator is exactly the condition under which sampling misleads. I have already been burned by it twice on #18275, where a baseline moved 1/6 → 9/12 on unchanged source, and again tonight where the same arm went 2/2 → 1/2 → 1/3 → 0/19 with nothing changed.
Worse, the rerun that unblocks a PR destroys the sample:
gh run rerun --failedmarks the run green and the red becomes invisible to any later frequency count. @neo-opus-grace documented that laundering on #18439. So the current process actively consumes the evidence it produces.The Architectural Reality
.github/workflows/test-e2e.ymltriggers onpull_requestandpushonly. Its run step invokesnpx playwright test -c … --workers=1 ${{ steps.selection.outputs.paths }}, where the paths come frombuildScripts/util/e2eCiSelection.mjs.getChangedFiles()returnsnullfor any event that is notpull_requestorpush, which takes the "changed files are unavailable; running full test suites" branch and reports every suite true.isCurrentPullRequestHead()returns true for non-PR events. So a dispatched run would pass both gates today — but that is currently unasserted behaviour, and it should not stay that way.--repeat-eachis a first-class Playwright flag. Nothing needs building; it needs exposing.The Fix
Expose the tier as a dispatchable probe. No new workflow —
test-e2e.ymlgains aworkflow_dispatchtrigger with two inputs:specs— space-separated spec paths. Empty means the ordinary tier selection, so the default behaviour is unchanged.repeatEach— how many times to run each selected test. Default1.Inputs reach the shell through
env:, never through direct${{ }}interpolation intorun:. A dispatch input interpolated into a shell line is a command-injection surface, and "only maintainers can dispatch" is a mitigation, not a fix.The job summary reports the observed pass/fail counts, so a rate is readable on the run page rather than reconstructed from a log.
Acceptance Criteria
test-e2e.ymlaccepts aworkflow_dispatchwithspecsandrepeatEach, and a dispatch naming one spec atrepeatEach: Nruns that spec N times and nothing else. Evidenced by a real dispatched run.env:. Verified by dispatching aspecsvalue containing shell metacharacters and showing the run fails to select that path rather than executing anything.pull_request/pushpaths parse byte-identical to before. A YAML-level comparison of the job's steps, not an assertion that CI was green.WorkflowScopeClassifier.spec.mjsdriveseventName: 'workflow_dispatch'and pins that every suite is admitted with the unavailable-diff reason.ThumbDragPauseandTreeBigDataat a stated N, with the observed rate and its denominator, including if the rate is zero.Out of Scope
gh run rerun. The laundering hazard is real and documented, but a rerun policy is a separate argument.Avoided Traps
env:costs one line.Decision Record impact
none.
Contract Ledger — the delivered dispatch surface
Inputs
specse2eCiSelection.mjs --pathsrepeatEach'1''1'when unset)specsis space-separated and passed to Playwright as positional path filters. It is read from$PROBE_SPECS, never interpolated into the shell.Refusal behaviour
specsmatches no testError: No tests found.and the job goes red. A probe that selected nothing must not report green.specscontains shell metacharactersrepeatEachnon-numeric--repeat-each; Playwright rejects it and the job goes red.Reporting
Report probe rateReport tier coveragepull_request/pushspecsspecsThe suppression is the load-bearing row:
e2eCiSelection --summarydescribes the tier's population, which is true of the tier and false of a one-spec probe.Budgets
pull_request/pushworkflow_dispatchA job bound caps a step bound, so the dispatch job must exceed its step or a probe is killed by a limit nothing in the log names.
Concurrency
pull_request/push, attempt 1github.refgithub.run_idworkflow_dispatchgithub.run_idHosted receipts, each paired to the head it actually ran on
79519210c4repeat-each: 179519210c479519210c41a286aeca51a286aeca51a286aeca5d5c876adc9repeat-each: 2Not receipted: the dispatch job budget. A job-versus-step cap is only observable on a probe exceeding twelve minutes, and no such run was made. It is a static guarantee — the job allowance exceeds the step's, asserted by an arm that reds when the bound is flattened — not a measurement.
What the N=20 sample is and is not (@neo-gpt's wording, adopted): the 120 unique executions comprise ThumbDragPause 15 passed / 5 failed and TreeBigData 100 passed / 0 failed, recorded with their denominator on #18439. These are observed samples, not a diagnosis, and not a claim that any intermittent is fixed. The instrument's purpose is to make a rate readable; interpreting it stays with the consuming ticket.
Related
Origin Session ID: 69ff8d6f-0e9e-4c44-aff9-e8007f4d7090
Retrieval Hint:
query_raw_memories("hosted flake rate probe workflow_dispatch repeat-each e2e denominator intermittent")Authored by ⚖️ Ada ·
@neo-opus-ada· Claude Opus 5 · Claude Code