Summary
openhands-phase-eval.yml's status-cleanup step removes status:* labels read from the event payload
snapshot rather than from live state. When two dispatch runs fire seconds apart for the same head, the
first removes status:impl and the second's removeLabel returns 404 Label does not exist, failing the
whole step and turning the workflow run red — on a PR whose evaluation actually succeeded.
This is a trust defect, not a correctness defect: exactly-once dispatch held. What it produces is a
false red that costs investigation time, and it recurs on any PR that gets two near-simultaneous dispatch
events.
Observed instance — PR #1541, head 0503991ab
31596291515 completed/failure event=pull_request created=12:24:36Z <- raced, red
31596293364 completed/success event=pull_request created=12:24:38Z <- posted the authoritative trigger
Two seconds apart, same head. Failure log, step "Enter IMPL-EVAL status on ready transition":
DELETE /repos/rickylabs/netscript/issues/1541/labels/status%3Aimpl - 404
retry-exempt-status-codes: 400,401,403,404,422
404 is deliberately retry-exempt, which is correct — a retry cannot help — so the step fails hard.
Exactly-once was not violated. Verified: exactly one trigger marker exists for that head —
generation=29339092792 phase=impl head=0503991ab…, model openrouter/deepseek/deepseek-v4-flash-0731.
The generation-dedup mechanism did its job. Only the label cleanup raced.
Root cause
.github/workflows/openhands-phase-eval.yml, "Enter IMPL-EVAL status on ready transition":
const labels = context.payload.pull_request.labels.map((label) => label.name);
for (const label of labels.filter((name) => name.startsWith('status:'))) {
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: label });
}
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['status:impl-eval'] });
context.payload.pull_request.labels is a snapshot taken when the event was created. The loop then
issues unconditional deletes against live state. Any concurrent path that removed a status: label between
event creation and this step makes the delete 404.
This is the same defect class as #1564 — acting on a payload snapshot instead of live state. There it is
pull_request.base.sha feeding a diff range; here it is pull_request.labels feeding a delete loop. Worth
noting because the two fixes rhyme: read live, or compute from something current.
Why it matters more than a stray red
A red run on a PR whose evaluation succeeded is the precise stimulus that teaches operators to discount red
runs. It has already cost real time in this milestone: this defect is what produced the apparent
"duplicate evaluator run" that had to be investigated and then corrected with the finding that extra raw
workflow entries were no-op/skip events and exactly-once was intact. The investigation was necessary
because a red run implied something had gone wrong with dispatch. Nothing had.
Fix
- Make status cleanup idempotent. Either fetch live labels immediately before removing
(issues.listLabelsOnIssue) and remove only what is actually present, or tolerate the missing-label
case — narrowly.
- Narrowly means narrowly. Tolerate only a
404 whose body is the missing-label error for the label
being removed. A blanket try {} catch {} around removeLabel would swallow a 403 from a permissions
regression or a 404 from a wrong issue_number, converting a real failure into a silent pass. Rethrow
anything else, and keep the existing retry-exempt-status-codes behaviour unchanged.
- Preserve generation deduplication exactly as it is. It already works — one trigger for one head under
a genuine race — and it is the property that keeps this defect cosmetic. Do not restructure it.
Acceptance criteria
Boundaries
Provenance
Observed on PR #1541 at head 0503991ab, 2026-08-12, runs 31596291515 (red) and 31596293364 (green).
Run facts, the 404 body, and the exactly-once verification were reproduced from the Actions API and the PR's
trigger markers by the 0.0.6 internals lane before filing. No pre-existing issue covers it (searched).
Summary
openhands-phase-eval.yml's status-cleanup step removesstatus:*labels read from the event payloadsnapshot rather than from live state. When two dispatch runs fire seconds apart for the same head, the
first removes
status:impland the second'sremoveLabelreturns 404 Label does not exist, failing thewhole step and turning the workflow run red — on a PR whose evaluation actually succeeded.
This is a trust defect, not a correctness defect: exactly-once dispatch held. What it produces is a
false red that costs investigation time, and it recurs on any PR that gets two near-simultaneous dispatch
events.
Observed instance — PR #1541, head
0503991abTwo seconds apart, same head. Failure log, step "Enter IMPL-EVAL status on ready transition":
404 is deliberately retry-exempt, which is correct — a retry cannot help — so the step fails hard.
Exactly-once was not violated. Verified: exactly one trigger marker exists for that head —
generation=29339092792 phase=impl head=0503991ab…, modelopenrouter/deepseek/deepseek-v4-flash-0731.The generation-dedup mechanism did its job. Only the label cleanup raced.
Root cause
.github/workflows/openhands-phase-eval.yml, "Enter IMPL-EVAL status on ready transition":context.payload.pull_request.labelsis a snapshot taken when the event was created. The loop thenissues unconditional deletes against live state. Any concurrent path that removed a
status:label betweenevent creation and this step makes the delete 404.
This is the same defect class as #1564 — acting on a payload snapshot instead of live state. There it is
pull_request.base.shafeeding a diff range; here it ispull_request.labelsfeeding a delete loop. Worthnoting because the two fixes rhyme: read live, or compute from something current.
Why it matters more than a stray red
A red run on a PR whose evaluation succeeded is the precise stimulus that teaches operators to discount red
runs. It has already cost real time in this milestone: this defect is what produced the apparent
"duplicate evaluator run" that had to be investigated and then corrected with the finding that extra raw
workflow entries were no-op/skip events and exactly-once was intact. The investigation was necessary
because a red run implied something had gone wrong with dispatch. Nothing had.
Fix
(
issues.listLabelsOnIssue) and remove only what is actually present, or tolerate the missing-labelcase — narrowly.
404whose body is the missing-label error for the labelbeing removed. A blanket
try {} catch {}aroundremoveLabelwould swallow a403from a permissionsregression or a
404from a wrongissue_number, converting a real failure into a silent pass. Rethrowanything else, and keep the existing
retry-exempt-status-codesbehaviour unchanged.a genuine race — and it is the property that keeps this defect cosmetic. Do not restructure it.
Acceptance criteria
status:implbetween event creation and the cleanup step no longer fails theworkflow: the step completes and
status:impl-evalis applied exactly once.causes the step's logic to throw — and passes after.
removeLabel(for example403, or a404for a different resource) still fails the step. A blanket catch fails this box.comment is posted. Asserted by test rather than by inspection.
status:label, per the taxonomy'ssingle-status rule.
gate:the phase-eval script's tests pass, and.github/scripts/type-check, lint and format via thescoped wrappers.
Boundaries
on:types) or its dispatch conditions. This is thecleanup step only.
eval:model:*label mapping, or the trusted-base-ref logic thatfix(agentic): resolve evaluator prompt from current trusted base #1552 landed.
would consume evaluator capacity to reproduce a cosmetic failure.
base.sharange computations; this one ownsthe label-snapshot race. Same class, different surface, separate fixes.
Provenance
Observed on PR #1541 at head
0503991ab, 2026-08-12, runs31596291515(red) and31596293364(green).Run facts, the 404 body, and the exactly-once verification were reproduced from the Actions API and the PR's
trigger markers by the 0.0.6 internals lane before filing. No pre-existing issue covers it (searched).