Skip to content

fix: when errors happen, put ai-rework, and do not automatically call again (#30) - #34

Merged
ngthluu merged 8 commits into
mainfrom
ai/issue-30
Jul 25, 2026
Merged

fix: when errors happen, put ai-rework, and do not automatically call again (#30)#34
ngthluu merged 8 commits into
mainfrom
ai/issue-30

Conversation

@ngthluu

@ngthluu ngthluu commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Closes #30

Automated by loope (bug flow). Spec and plan, if any, are committed in this branch under docs/.

A failed run was retried unattended, forever. classifyCause marked
usage/rate limits, turn/budget ceilings and network errors "resumable",
so ResumeParked re-ran Rework on them every poll cycle (5m backoff
doubling to 60m) — one issue that kept failing kept re-running the whole
pipeline and burning tokens with nobody watching.

Now a failure is terminal: the issue is parked as ai-rework with the
error commented, and nothing touches it until a human removes the label,
which re-queues it (reusing the preserved worktree and branch, per the
continue-don't-reset rule). The only resume left is interruptedCause —
the deliberate hand-off written by SweepOrphans after a daemon restart
and by the dashboard's Continue — which is not a failure. The backoff
still covers a resume that fails before it re-parks (e.g. GitHub
unreachable), so its resumable cause survives on disk.

The park comment now carries the real error instead of a generic
snippet:
- Errors are clipped, not tail-truncated: 6000 chars keeping BOTH ends,
  so the head (which step failed) and the tail (the API's own message)
  both survive, inside a collapsed fenced block.
- A claude transport/parse failure reports stderr AND stdout, each
  labelled and explicitly "(empty)" when blank — claude often writes its
  diagnostics to stdout, and the old "(stderr: )" said nothing.
- failureSummary adds turns, cost and wall time, so the comment shows how
  far the session got before it died.
- Every park comments, including a re-park: the silent-repark rule only
  existed to keep the backoff loop from spamming the issue, and that loop
  is gone.

Judgment calls made headlessly:
- The tooling-failure path (abort) also parks now. It used to strip
  ai-wip and leave the issue eligible, which re-triaged and re-attempted
  it every cycle — the same unattended retry loop, paid for in triage
  tokens. gh/git calls are already retried in-band, so reaching abort
  means the failure is persistent. It no longer deletes the worktree or
  branch either.
- The park comment stopped advertising `loop -rework <N>` (that flag does
  not exist) and does not offer Continue (the dashboard shows it only for
  ai-stopped tickets). It names the one action that works: remove the
  label.

Docs (operations, how-it-works, configuration, README) updated to match.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
ngthluu and others added 4 commits July 25, 2026 16:35
# Conflicts:
#	ai/prompts/comments.md.tmpl
Nothing retries an issue automatically any more. The resume stage
(ResumeParked, shouldResume, per-issue exponential backoff, `loop -rework`
and rework.go) re-ran the whole pipeline every cycle for a parked issue,
so one broken ticket burned tokens indefinitely on work nobody watched.

A parked issue now waits for a human, who removes the rework label to
queue another attempt; the preserved worktree, branch, logs and session
are reused by the next run per the continue-not-reset rule. classifyCause
keeps recognizing causes purely to explain the park in the comment, and
park-cause on disk is now a diagnostic nothing reads back.

Continue drops its session-resume branch: it clears the stopped label and
local state so the normal cycle re-picks the issue in the same worktree.

preflight stops asking repos to create ai-failed (recognized, never
applied) and starts asking for ai-stopped, which the dashboard does apply.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
It was already deprecated — recognized but never applied. Drop the
labelFailed const, the StateLabels.Failed field and its "failed" JSON key,
the hasStateLabel and stateKind cases, the "failed" sidebar rank tier, the
dashboard badge branch, and every mention across docs and the example
config.

Behavior change: an issue still carrying a literal ai-failed label from an
older version is no longer recognized as being in a state, so it becomes
eligible again and the loop will pick it up. Remove the label, or relabel
those issues ai-rework, before upgrading if that is not what you want.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
abort() now delegates to park(), so a failure in wt.Create parks the issue
before any worktree or branch exists. The park comment told the human
unconditionally that "the preserved worktree, branch and logs are reused",
which on that one path names work that was never created. Reword it to
cover whatever the failed run actually produced, and align the same claim
in docs/operations.md.

Also document why readParkCause has no caller in the daemon: it is a
test/inspection accessor, kept so the read goes through the same path
logic as the write.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@ngthluu ngthluu self-assigned this Jul 25, 2026
ngthluu and others added 3 commits July 25, 2026 17:26
stripeClass had a "failed" branch that stateKind can no longer return, and
readParkCause lost its only production caller when the resume scan went away.
The park-cause marker is still written and cleared as an operator diagnostic,
so the reader moves to a test helper to keep the existing assertions honest.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
readSession lost its last production caller when the auto-resume path was
removed, while scanIssueDir kept its own inlined ReadFile + Unmarshal of the
same file — dead code sitting next to a duplicate of itself. Point scanIssueDir
at readSession so SessionInfo parsing has one implementation, and pull the
"session" filename into a sessionFile const alongside stateFile/titleFile/
parkCauseFile so the writer and reader can't drift.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
rework.md.tmpl and guidance-interrupted were deleted earlier on this
branch, but their entries lingered in the map. The test only errors on
templates missing an entry, never on extra ones, so nothing failed —
they were just dead weight.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@ngthluu
ngthluu merged commit 82ddb16 into main Jul 25, 2026
1 check passed
@ngthluu
ngthluu deleted the ai/issue-30 branch July 25, 2026 10:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: when errors happen, put ai-rework, and do not automatically call again

1 participant