fix(retry): charge gate budget on any repeated failure fingerprint (#460)
gateFailureFingerprints held one slot per gate, so the budget only charged when a failure repeated back to back. Whack-a-mole (fix A breaks B, fix B breaks A) alternates two fingerprints forever, every round read as progress, and the per-gate budget never triggered. Track the full set seen per gate and charge when the current fingerprint is a repeat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+6
-3
@@ -19,9 +19,12 @@ data class OrchestrationState(
|
||||
// brief_grounding, brief_echo, contract, plan_compile, static_analysis, execution, review, ...)
|
||||
// exhausts its own budget rather than sharing retryCount session-wide. Attempts charged so far.
|
||||
val gateRetryBudgets: Map<String, Int> = emptyMap(),
|
||||
// Last-seen failure fingerprint per gate, used to tell a no-progress retry (same fingerprint,
|
||||
// charged) from a genuine-progress retry (changed fingerprint, free).
|
||||
val gateFailureFingerprints: Map<String, String> = emptyMap(),
|
||||
// Every failure fingerprint seen per gate, used to tell a no-progress retry (a fingerprint
|
||||
// already seen for this gate, charged) from a genuine-progress retry (an unseen fingerprint,
|
||||
// free). A set, not a single slot: whack-a-mole (fix A breaks B, fix B breaks A) alternates two
|
||||
// fingerprints forever and a single slot reads every round as progress, so nothing is ever
|
||||
// charged and the budget never triggers.
|
||||
val gateFailureFingerprints: Map<String, Set<String>> = emptyMap(),
|
||||
// Gates that have already spent their one hybrid-exhaustion salvage reset (review gate only,
|
||||
// see RetrySalvageDecidedEvent) — a second exhaustion for that gate is terminal.
|
||||
val gateSalvageUsed: Set<String> = emptySet(),
|
||||
|
||||
Reference in New Issue
Block a user