idle-draft: clear attempts on success so forced redos start fresh

Claude-Session: https://claude.ai/code/session_01YQDoWNM7XPPii28khFWoMc
This commit is contained in:
Paul O'Reilly
2026-08-02 21:54:53 +12:00
parent 1f008eebcb
commit ec25f02f01
3 changed files with 67 additions and 6 deletions

View File

@@ -1096,11 +1096,30 @@ def log_gates(repo: Path, gates: dict, credential_ok: dict) -> None:
def apply_success_state(state: dict, candidate: dict) -> None:
# Successful dispatch doesn't itself set a human gate; nothing to flip here.
# (human_edit_done / research_sampled / approved are human-only via `mark`.)
# Present for symmetry with apply_failure_state and as the extension point
# if a future work type needs to record something on success.
return
"""On success, clear any failed-attempt history for this (item, work_type).
Without this, a success following one or more content failures leaves
attempts[work_type] at its prior nonzero value; if a human later deletes
the produced file to force a redo, the item would start already one
content failure away from `blocked` instead of fresh. Pruning (rather
than zeroing) the key/subkey keeps state minimal and matches how
get_item_state()/validate_state() already treat an absent attempts entry
as equivalent to zero -- nothing else (human_edit_done / research_sampled
/ approved) is a success-path concern; those are human-only via `mark`."""
if candidate["kind"] != "item":
return
key = candidate["item_key"]
work_type = candidate["work_type"]
if work_type not in ALLOWED_ATTEMPT_WORK_TYPES:
return
entry = state.get("items", {}).get(key)
if entry is None:
return
attempts = entry.get("attempts")
if not attempts or work_type not in attempts:
return
del attempts[work_type]
if not attempts:
del entry["attempts"]
def apply_failure_state(state: dict, candidate: dict, max_attempts: int) -> str | None: