fix(forge): do not let a killed campaign's leftovers fail the next task - #1564
Conversation
Every in-place task in one repository is handed the same forge_experiments
directory. The release archives it, but a run the host killed never reaches
that, and the leftover then does two things: forge-loop refuses the workspace
outright ('already contains a Forge campaign; pass --resume to continue it'),
failing the next task at dispatch, and recovery reads the stale manifest as
that task's own best result -- reporting the dead campaign's commit as a
missing base commit, a reason with nothing to do with the task it lost.
Session 20260914T115354Z-5262e947 lost chunk_gated_delta_rule this way to a
fusion campaign left behind at the timeout an hour earlier.
Archive a leftover on the way in as well as on the way out, and require a
trusted manifest's commit to still exist in the repository before believing
it describes this task.
|
Reviewed the dispatch/recovery paths against 1. with contextlib.suppress(OSError, shutil.Error):
destination.mkdir(parents=True, exist_ok=True)
target = destination / f"stale_{FORGE_LOOP_OUTPUT_DIRNAME}"
shutil.rmtree(target, ignore_errors=True)
shutil.move(str(source), str(target))
log.warning(...)The 2. No
No other blocking issues: the |
The log line announcing the archive sat inside the contextlib.suppress that guards it, so it only ever fired on success. A stale workspace.lock still held open, an unwritable operator directory or a cross-device move left the borrow continuing as though the leftover were gone; forge-loop then failed the task at dispatch with "already contains a Forge campaign" and nothing in the log said an archive had been attempted -- the undiagnosable failure this archive exists to end. Report the failure instead, and name the remedy. CHANGELOG records the fix.
…ampaign-leftovers # Conflicts: # CHANGELOG.md
|
Re-reviewed at One blocker left. The PR description still describes the first commit. The diff has since gained a behaviour change and two files the description does not mention:
Please refresh both before merge. |
xiaofei-zheng
left a comment
There was a problem hiding this comment.
Approving at 236b0544b. Both blockers from the first pass are fixed: _archive_stale_campaign_output reports a failed archive and names the manual remedy instead of swallowing it, with the success log moved out of the guarded block and test_an_archive_that_failed_says_so covering the failure path; the CHANGELOG entry is in Unreleased / Fixed in the existing format.
What I checked:
_borrow_live_repositoryordering — reclaim, checkout, archive,_ignore_forge_loop_output— and that the archive clears exactly whathas_run_artifactsincli.py:1041-1050refuses a workspace for.- The release side (
_archive_campaign_output,_restore_tree_to_base,remove_foreign_untracked) for a collision betweenstale_forge_experimentsand the archive_trusted_manifestreads back: there is none. - The
commit_existsgate on both recovery entry points againstgit merge-base origin/main <branch>. It does not reject a live result: a private worktree's commits stay resolvable fromrepo_root's shared object store, and in-place recovery atdispatcher.py:215/273runs while the campaign branch is still alive. - The CHANGELOG merge conflict resolution: 2113 lines = main's 2098 plus the 15 added here, so nothing from main was dropped.
Tests were not run locally (no environment here); CI covers them.
The description still describes the first commit — it does not mention the archive-failure warning, and Test plan predates the three tests added since. Worth refreshing before merge, but not holding the approval.
…ampaign-leftovers # Conflicts: # CHANGELOG.md
…ampaign-leftovers # Conflicts: # CHANGELOG.md
Problem
Every in-place task in one repository is handed the same
forge_experimentsdirectory. The release archives it, but a run the host killed never reaches
that, and the leftover then does two things:
already contains a Forge campaign; pass --resume to continue it), failing the next task at dispatch.the dead campaign's commit as a missing base commit -- a reason with nothing
to do with the task it lost.
Session
20260914T115354Z-5262e947lostchunk_gated_delta_rulethis way to afusion campaign left behind at a timeout an hour earlier.
Fix
Archive a leftover on the way in as well as on the way out, and require a
trusted manifest's commit to still exist in the repository before believing it
describes this task. The leftover is archived rather than deleted: for a run
that published nothing it is the only account of what that run did.
An archive that cannot be made now says so. The log line announcing it sat
inside the
contextlib.suppressthat guards the move, so it only ever fired onsuccess; a stale
forge_experiments/workspace.lockstill held open, anunwritable operator directory or a cross-device move left the borrow continuing
as though the leftover were gone. forge-loop then failed the task at dispatch
and nothing in the log said an archive had been attempted -- the undiagnosable
failure this change exists to end.
Test plan
src/kernelforge/tests/kernel_rewrite_controller/(275 passed), includinga new test that a failed archive is reported rather than swallowed
ruff check .+ruff format --check .CHANGELOG.mdUnreleased / Fixed entry