stale-judge-verdict-superseded
The bug was in the verdict-consumption path, not the judge. The worker orchestrator bounced on any FAIL without checking what revision the judge actually evaluated. Since a judge run and the worker's pushes race within the same session, a FAIL from an earlier commit (41095a27 @ 13:51) was used to bounce a session that had already landed the fix (5e10e44 @ 14:01, which added rerere_prometheus.go).
Fix — a single choke point between "judge FAIL" and "bounce worker":
commit_sha (what the judge snapshot was taken at) + evaluated_at.hivemind_git_rerere_hits_total{workspace} hit (the resolution was already recorded in the tree; no re-run needed).// verdict.go
type JudgeVerdict struct {
CommitSHA string `json:"commit_sha"` // revision the judge actually evaluated
EvaluatedAt time.Time `json:"evaluated_at"`
Status string `json:"status"` // "pass" | "fail"
WorkerSession string `json:"worker_session"`
}
type StalenessRule struct {
MaxClockSkew time.Duration // absorbs judge/worker clock drift on the timestamp fallback
}
// IsStale: verdict evaluated against a revision that predates HEAD.
func (r StalenessRule) IsStale(v JudgeVerdict, head CommitMeta) bool {
if v.EvaluatedAt.IsZero() {
return true // unknown provenance: never use this FAIL to bounce
}
if v.CommitSHA != "" && head.SHA != "" {
return v.CommitSHA != head.SHA // primary: exact, clock-free
}
return v.EvaluatedAt.Add(r.MaxClockSkew).Before(head.CommittedAt) // fallback
}
type Decision struct{ Bounce, Rejudge bool }
// Decide replaces the old "bounce on any FAIL" logic.
func (r StalenessRule) Decide(v JudgeVerdict, head CommitMeta) Decision {
if v.Status != "fail" {
return Decision{} // PASS never bounces
}
if r.IsStale(v, head) {
return Decision{Rejudge: true} // discard stale FAIL, re-judge HEAD
}
return Decision{Bounce: true} // genuinely fresh FAIL against HEAD
}
// orchestrator glue (previously: if verdict.Status == "fail" { worker.Bounce() })
d := rule.Decide(verdict, head)
if d.Rejudge {
StaleVerdicts.WithLabelValues(ws, "discarded").Inc()
fresh := judge.Evaluate(head.SHA) // fresh judge run against final commit
if fresh.Status == "fail" {
worker.Bounce(fresh) // now genuinely fresh
} else {
RerereHits.WithLabelValues(ws).Inc() // hivemind_git_rerere_hits_total{workspace}
}
return
}
if d.Bounce { worker.Bounce(verdict) }
Verified by a self-contained Go module (`/tmp/verdict-gate`) with 6 passing tests (`go test -race`, `go vet`, `gofmt` all clean).
**SPEC-GAP-003 replay** (`TestSPECGAP003StaleVerdictSuperseded`) reproduces the exact timeline:
- `EvaluatedAt 13:51` / `CommitSHA 41095a27`, FAIL → `head 5e10e44 @ 14:01` → `IsStale == true`, `Decide` returns `{Bounce:false, Rejudge:true}` — **worker never bounced**.
- Fresh judge `a48a98d9` against `5e10e44` → PASS → exactly 1 stale-discard + 1 `rerere` hit recorded.
**Edge cases tested:**
- Fresh FAIL against current HEAD SHA → bounces (only legitimate bounce path).
- Legacy verdicts without `commit_sha`: 10s-before-commit within 30s skew → fresh; 5-min-before → stale (skew tolerance).
- Zero `evaluated_at` → always stale; unknown-provenance FAIL never bounces.
- Identical timestamps but different SHA → stale (hash check beats timestamp heuristic).
- Empty head SHA (bare/detached repo) → timestamp fallback still catches the stale verdict.
- Concurrency: race detector clean on metric/decision paths; SHA comparison is immune to clock drift by design.{"model": "deepseek-v4-flash", "problem_class": "stale-judge-verdict-superseded", "result": "passed", "tests": 6}