Repository navigation
fix(triage-bot): stop a cleanup run that exits early from wedging the daemon - #5244
Merged
Merged
Conversation
… daemon On 2026-09-26 the PR #5216 cleanup backgrounded its pre-commit run, ended on "I'll push and reply once it finishes", and exited 0 with its fixes uncommitted. The daemon took exit 0 as success and cleared BOT_CLEANUP; the next sync_repo() then failed at `git checkout main` because the leftover edits would be overwritten - and since every flow starts with sync_repo(), every flow after it failed the same way. - claude_env() now sets CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 for every claude invocation, which removes run_in_background from the Bash tool (checked against Claude Code 2.1.283). A -p session cannot come back for a background result. - sync_repo() stashes anything left uncommitted (aborting an unfinished merge first) before checking out main, so leftovers are kept but can no longer block the checkout. - process_bot_cleanup_pr() checks the clone after a clean exit and marks the PR BOT_FAILED, with the reason, when work was left uncommitted or unpushed. - pr-cleanup SKILL.md: run the quality gate in the foreground, and drop the false claim that an unpushed commit is discarded on the next run. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
springfall2008
deleted the
fix/triage-bot-cleanup-leaves-dirty-clone
branch
September 26, 2026 09:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What happened
On 2026-09-26 the bot stopped working partway through a poll cycle:
/pr-cleanuprun for fix(givtcp): fill per-inverter arg slots by REST endpoint index, not discovery order (#5209) #5216 editedgivtcp.py, its tests and the debug journal, and mergedorigin/mainlocally. It then started its pre-commit run in the background and ended with "The pre-commit quick suite is still running in the background. I'll push and reply once it finishes." Inclaude -pmode nothing comes back to finish the job. The session exited 0, the background job died with it, and nothing was committed, pushed or posted.process_bot_cleanup_pr()treated exit 0 as success and removedBOT_CLEANUP. The PR was left with no push, no reply, and nothing marking it for a retry.sync_repo()rangit checkout main, and git refused because the uncommitted edits would be overwritten. Every flow starts withsync_repo(), so every flow failed at that step from then on.Changes
claude_env()setsCLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1for everyclaudeinvocation. This removesrun_in_backgroundfrom the Bash tool, so the setting is enforced by the tool itself. It does not depend on a prompt instruction. I checked the effect against Claude Code 2.1.283 by asking a-psession whether its Bash schema has the parameter:YESwithout the variable,NOwith it. The value is assigned rather than set withsetdefault, so an exported value cannot turn backgrounding back on.claude_env()therefore now always returns a dict; on the default route it is the daemon's environment plus this one key.sync_repo()stashes leftovers before the checkout (newset_aside_leftovers()). The stash message is labelledtriage-daemon: …and git records the branch the changes came from. If a merge is in progress it is aborted first, because bothstashandcheckoutrefuse a tree mid-merge. Leftovers are stashed rather than force-discarded, because the daemon cannot tell a half-finished fix from noise.unfinished_cleanup_reason()). Uncommitted tracked changes, unpushed commits, or no upstream to compare against all lead tomark_pr_cleanup_failed(pr, reason), which now accepts a reason in the same waymark_pr_review_faileddoes. This follows the same principle as the review flow's comment count: exit status is not evidence that the work was done.pr-cleanup/SKILL.md: run the quality gate in the foreground and never end a turn waiting on it. The skill also stated that an unpushed local commit "is discarded automatically the next time this runs". That was false, becausesync_repo()only resets main. It now describes the new check.Consequence to be aware of
A cleanup that stops at a failed quality gate, posts its "didn't push" summary and leaves the local merge commit behind is now reported
BOT_FAILED. Before this change it was silently counted as success. This is intended, because the cleanup did not complete.Testing
python3 tools/test_triage_daemon.py: 284 tests pass (270 before). The new tests cover the stash before the checkout, the merge abort, silence on a clean tree, each unfinished-run reason, the orchestrator marking failure with a reason, and the environment variable on the Claude,--ollamaand--ollama_reviewroutes. Existing tests that assertedclaude_env()returnsNonenow assert that no Ollama overrides are present.git checkout mainrefuses; the newsync_repo()stashes (On fix/x: triage-daemon: …) and lands on main;unfinished_cleanup_reason()reports both the uncommitted file and the unpushed commits.The daemon runs from its own clone of main, so it picks up this change only after merge and a daemon restart.
🤖 Generated with Claude Code