fix(recovery): terminate exhausted recovery assessments with one actionable notice #251

Merged
severus-snape merged 1 commit from fix/exhausted-recovery-attention into main 2026-11-04 14:34:44 +00:00
Collaborator

Fixes the silent exhaustion path diagnosed in development-evidence/validator-agent-stall/PR165-APOLOGY-STALL-TRACE.md (installed/base 95cf3a4).

Bug: assessRetainedTask handled retry exhaustion in two places (entry with recoveryRetry.attempts >= 3, and the third technical/invalid evaluation) via reconcileUnassessedTask only. The task became blocked with a host placeholder next, but no termination, no recoveryAttentionId and no operator notice. controlTaskBudget requires termination === "recovery_assessment_failed", so the supported reset rejected it ("Task is not host budget-blocked"). Live tasks $iwrda06z…/$T48P6I6… show exactly this signature.

Fix (reuse only): both exhaustion paths now call the existing recoveryAttention(s, task, "exhausted") after the existing placeholder reconciliation. Persisted under the existing barrier: termination, dedup id task-recovery-exhausted:…, one outbox notice naming the supported reset. That terminal notice is delivered even with default-off recoveryDiagnostics (same class as task-budget escalation; documented in docs/gateway-progress.md). Other technical diagnostics stay opt-in.

Not changed: no new retry machine, no admission/queue entry, no worker execution/replay, no cross-task linking, no deadline extension; stop/pause/expiry/semantic-blocked tasks untouched (assessRetainedTask only acts on active). Legacy tasks already persisted as silent blocked are deliberately not inferred terminal or migrated (test asserts reset still rejects them); see PR result evidence for the documented limitation.

Tests: red on base (9 failures incl. both branches), green with fix. New: technical + invalid exhaustion across JSON StateStore reloads → one termination, one notice delivered once, no fourth assessment, no worker turn/queue, reset commits idempotently and keeps deadline; holds/legacy unchanged. Updated tests that encoded the silent block. Full serial 1422/1422, typecheck, build. Independent exact-diff plugin_review: passed, 0 findings (fingerprint 75810489…).

Fixes the silent exhaustion path diagnosed in `development-evidence/validator-agent-stall/PR165-APOLOGY-STALL-TRACE.md` (installed/base `95cf3a4`). **Bug:** `assessRetainedTask` handled retry exhaustion in two places (entry with `recoveryRetry.attempts >= 3`, and the third technical/invalid evaluation) via `reconcileUnassessedTask` only. The task became `blocked` with a host placeholder `next`, but **no** `termination`, **no** `recoveryAttentionId` and **no** operator notice. `controlTaskBudget` requires `termination === "recovery_assessment_failed"`, so the supported reset rejected it ("Task is not host budget-blocked"). Live tasks `$iwrda06z…`/`$T48P6I6…` show exactly this signature. **Fix (reuse only):** both exhaustion paths now call the existing `recoveryAttention(s, task, "exhausted")` after the existing placeholder reconciliation. Persisted under the existing barrier: `termination`, dedup id `task-recovery-exhausted:…`, one outbox notice naming the supported reset. That terminal notice is delivered even with default-off `recoveryDiagnostics` (same class as task-budget escalation; documented in `docs/gateway-progress.md`). Other technical diagnostics stay opt-in. **Not changed:** no new retry machine, no admission/queue entry, no worker execution/replay, no cross-task linking, no deadline extension; stop/pause/expiry/semantic-blocked tasks untouched (`assessRetainedTask` only acts on `active`). **Legacy** tasks already persisted as silent `blocked` are deliberately **not** inferred terminal or migrated (test asserts reset still rejects them); see PR result evidence for the documented limitation. **Tests:** red on base (9 failures incl. both branches), green with fix. New: technical + invalid exhaustion across JSON StateStore reloads → one termination, one notice delivered once, no fourth assessment, no worker turn/queue, reset commits idempotently and keeps deadline; holds/legacy unchanged. Updated tests that encoded the silent block. Full serial 1422/1422, typecheck, build. Independent exact-diff `plugin_review`: passed, 0 findings (fingerprint `75810489…`).
Both retained-assessment exhaustion paths (exhausted retry on entry and the
third invalid/technical evaluation) silently parked the task as blocked
without recovery_assessment_failed, attention id or operator notice. The
supported budget reset therefore rejected it ("not host budget-blocked").

Reuse the existing recoveryAttention("exhausted"): persist the terminal
reason and the dedup id under the same barrier, and deliver its single notice
even with default-off recovery diagnostics, like task-budget escalation.
No new retry machine, no work admission, no replay. Legacy tasks already
persisted as silent blocked are not inferred terminal or migrated.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
harness/core!251
No description provided.