Repository navigation
Action budget, slice 3b: resume from a checkpoint on re-dispatch (or say why not) #1706
Description
Story
As a maintainer re-dispatching an issue after a timeout,
I want the new pass to resume from the checkpoint branch rather than starting from zero,
so that a timeout costs the remaining work, not all of it.
Split from #1660 (slice 3a), which delivers the checkpoint push itself. Blocked by #1660 — there is nothing to resume from until checkpoints exist.
Scope
Given a marked, incomplete checkpoint branch left by a timed-out pass (#1660), a re-dispatch of the same issue either resumes from it or states explicitly that it is starting clean and why. Silently ignoring an existing checkpoint is the failure this closes.
Acceptance Criteria
- On dispatch, the run detects an existing checkpoint branch for the same issue (the marker convention Action budget, slice 3a: checkpoint-push partial work before the budget expires #1660 establishes — reuse it, do not invent a second one).
- When resuming is safe, the pass starts from the checkpoint branch and its opening notice says so, naming the branch and the checkpoint commit.
- When resuming is not safe, the run starts clean and says why, in the issue notice — never silently. At minimum, treat these as unsafe: the checkpoint's base has diverged from the target branch; the checkpoint fails a structural sanity check; or the issue body changed materially since the checkpoint.
- A resumed pass is still subject to every completion gate — resuming must not inherit "already addressed" state. In particular it must not allow review-thread resolution on the strength of the earlier partial pass (dev-lead resolves review threads without addressing them — unfixed findings merge through the thread-resolution gate #1621/[#1621 S1] Harness-only thread resolution: remove the model's resolveReviewThread path #1691: resolution is harness-only and gated on a complete pass).
- Stale checkpoints are bounded: a checkpoint older than a configurable age is not resumed, and is reported as skipped with its age.
- Fail safe — any inability to evaluate the checkpoint results in a clean start with a stated reason, never a silent resume of unknown state.
- Tests: resume from a valid checkpoint; refuse a diverged one; refuse an over-age one; a clean start when none exists; and the resolution gate stays closed for a resumed pass.
Tasks / Subtasks
- Detect the checkpoint branch via Action budget, slice 3a: checkpoint-push partial work before the budget expires #1660's marker convention (AC: test issue from agent #1)
- Implement the safety evaluation (divergence, sanity, issue-body drift, age) (AC: Add @claude delegation, auto-merge, and rebase handling #3, feat: add Copilot engine support via REVIEW_ENGINE toggle #5, feat: feature ideation framework — adversarial proposer/challenger/synthesizer #6)
- Wire resume vs clean-start, with the stated-reason notice on both paths (AC: Go-live improvements for PR review agent #2, Add @claude delegation, auto-merge, and rebase handling #3)
- Ensure completion gates are not inherited (AC: Optimize review: small-PR and incremental fast paths #4)
- Tests for all five cases (AC: Workflow should skip no-op PRs in MAX_PRS count #7)
Dev Notes
- Correctness beats reuse. A wrong resume is far worse than a clean restart: it produces a PR that looks complete and is not — the dev-lead merges zero-diff PRs that close their issues — 10 confirmed across 5 repos since 2026-07-17 (latest 2026-09-09) #1620 failure class. Every ambiguous case must start clean with a reason.
- Do not widen scope into changing how work is split; this issue only handles what already exists on a checkpoint branch.
- Watch the budget. Action budget, slice 3a: checkpoint-push partial work before the budget expires #1660's first attempt died at 3600/3600 carrying both halves, which is why this is separate. If this pass runs long, land detection + clean-start-with-reason (AC1, AC3, AC6) and split the actual resume — that ordering is already a net win over today.
References
- Action budget, slice 3a: checkpoint-push partial work before the budget expires #1660 (slice 3a — the checkpoint push and its marker convention), parent Measure and tune the dev-lead action budget: #1609 died 1s over ACTION_TIMEOUT_SEC and lost all its work — near-miss alerting + checkpoint push before tuning the number #1619
- dev-lead resolves review threads without addressing them — unfixed findings merge through the thread-resolution gate #1621 / [#1621 S1] Harness-only thread resolution: remove the model's resolveReviewThread path #1691 — why a resumed or partial pass must never satisfy the resolution gate
- Timeout evidence: Follow-up to #1567: threads resolved with NO commit at all — the shipped evidence bar gates status=applied, not thread resolution (near-miss: fleet-breaking merge on .github#1024) #1609, Shadow-mode dual-run — dispatch half: run the shadow lane + suppress its PR output (follow-up to #605) #1269,
petry-projects/.github#1047, and Action budget, slice 3a: checkpoint-push partial work before the budget expires #1660's own 3600/3600 run34162481686
Split 2026-09-13: this is now a tracking parent
The single pass timed out at the 2700s action budget (run 34473748744, 2026-09-10). Following this issue's own Dev Notes, it is split along the safe seam: detection plus clean-start-with-reason first, actual resume second.
- [#1706 split 1/2] Detect a checkpoint on re-dispatch and start clean with a stated reason (no resume yet) #1784 — split 1/2: detect the checkpoint, evaluate it, always start clean with a stated verdict. Released to dev-lead.
- [#1706 split 2/2] Resume from a safe checkpoint without inheriting completion state #1785 — split 2/2: resume from a checkpoint judged safe, inheriting no completion state. Blocked by [#1706 split 1/2] Detect a checkpoint on re-dispatch and start clean with a stated reason (no resume yet) #1784.
Do not implement from this parent. It closes when both slices land.
Activity
don-petry commentedon Sep 10, 2026
Dev-Lead Implementation Plan[RETRACTED]Issue: #1706 — Action budget, slice 3b: resume from a checkpoint on re-dispatch (or say why not)
Scope
On dispatch,
dev-lead-fix-issue.shdetects an existing checkpoint branch (the #1660checkpoint(dev-lead): INCOMPLETE —marker), evaluates it for safety (base divergence, structural sanity, issue-body drift, age), and either resumes from it with a stated notice or starts clean with a stated reason — never silently — while keeping every completion gate intact.Implementation Checklist
scripts/lib/checkpoint-resume.sh(new) — PURE helpers:checkpoint_body_fingerprint,checkpoint_humanize_age,checkpoint_decide(verdict+reason from gathered facts). Fail-safe: any unknown fact → clean start.scripts/dev-lead-fix-issue.sh— source the lib; addCHECKPOINT_MAX_AGE_HOURS/DEV_LEAD_TARGET_BRANCHconfig; record an issue-body-SHA256 trailer on the checkpoint commit incheckpoint_push_partial_work(reuses the same Action budget, slice 3a: checkpoint-push partial work before the budget expires #1660 checkpoint commit — no second marker); add impurefind_checkpoint_branch+maybe_resume_from_checkpoint+ resume/clean-start notices; branch to resume-vs-fresh inmain()before branch creation.pre_engine_shaat the checkpoint HEAD so a durable completion claim / PR only appears after a fresh pass adds work; checkpoint opens no PR and posts no completion claim.Tests to Write (
tests/dev-lead/unit/)test_checkpoint_resume.bats(new) — pure-lib unit tests: fingerprint stability, age humanize,checkpoint_decidefor resume / diverged / stale / sanity-fail / body-drift / body-unknown.test_fix_issue.bats— integration cases: resume from a valid checkpoint (notice names branch+commit); refuse a diverged checkpoint (clean start + reason); refuse an over-age checkpoint (reason names age); clean start when none exists (silent, no notice); resolution gate stays closed for a resumed pass (nostatus=completedinherited from the partial pass).Test Command
bats tests/dev-lead/unit/andbash .dev-lead/scripts/dev-lead-lint.shdon-petry commentedon Sep 10, 2026
Dev-Lead: cannot implement issue #1706 — needs human attention
A stage timed out (exit 124) while implementing this issue. A timeout is treated as a first-class, non-retryable condition — retrying with the same budget would just time out again — so this escalates for human review on the first occurrence instead of wasting a same-budget retry.
timeoutThis hit the max action budget. Split this issue into smaller, independently-implementable pieces, or raise
vars.ACTION_TIMEOUT_SEC, then re-apply thedev-leadlabel.