Skip to content

feat(run): durable halt-and-resume for waitable rate-limit windows — write resumeAt, release the lock, survive reboots #892

Description

@admarble

Motivation

#860 fixed the classification that kept --auto-wait (#804) inert, and recorded an explicit decision (docs/investigations/auto-wait-vs-halt-resume-860.md) to keep the in-process wait for now. That decision names two structural costs the in-process design cannot fix from inside:

  • The per-issue lock is held for the entire wait (up to 5h). Nothing else can touch the issue; after a crash mid-wait, a stale lock whose PID is recycled reads as fresh (Nested phase claude that hangs after its turn is killed at ~106s, taking down the parent sequant run #856) and blocks the issue until manual locks clear or 6h age-based recovery.
  • A reboot or power loss kills the run and buys nothing. The claude subprocess died at the failure, so the wait only ever purchases resumption, not continuity — after a reboot the phase restarts from scratch anyway, minus the run, plus a stale lock.

Halt-and-resume inverts both: exit cleanly at the window halt, record when to come back, and let re-entry (cheap since #837 skips completed issues) continue the run.

Proposed shape (from the #860 decision record)

  1. resumeAt in issue state — written at the billing/window fail-fast (fix(run): -Q quality loop retries after an "Out of credits" exec instead of failing fast (billing cascades into "unparseable verdict") #799 path) when the payload carries a waitable window (isWaitableWindow, Five-hour window exhaustion is misclassified as terminal billing, so --auto-wait is inert on subscription plans #860). Machine-readable; sequant status shows "resumable at 07:00" instead of a bare failure.
  2. Re-entry pathsequant run --resume (or equivalent) that no-ops until resumeAt, then re-runs the halted issues, leaning on fix(chain): --chain --ready-gate resume re-executes already-gated links (COMPLETED_STATUSES misses waiting_for_human_merge/blocked) #837's completed-issue skipping.
  3. Lock hygiene — release the per-issue lock at halt; re-acquire on re-entry. Removes the Nested phase claude that hangs after its turn is killed at ~106s, taking down the parent sequant run #856-adjacent stale-lock exposure for waits entirely.
  4. Re-entry bound — a counter mirroring AUTO_WAIT_MAX_WAITS so a window that never reopens cannot ping-pong a scheduler.
  5. Scheduler recipe — ~20 documented lines of launchd/cron invoking the re-entry command; no daemon inside sequant.

Relationship to --auto-wait

Not a replacement initially. --auto-wait stays for the attended/overnight-laptop case (context preserved in memory, zero infrastructure). This issue serves the unattended case where reboot survival and lock release matter. If it proves strictly better in practice, deprecating the in-process wait is a later, separate decision.

Acceptance criteria

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestno-browser-testSkip the /test browser phase (no web surface)plannedImplementation plan approved

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions