feat(pipeline): push-on-retire — no stranded commits when a stage completes

The pipeline retires a stage the instant its completion marker appears; if the agent wrote the marker
before its final 'git push' landed, its last commits were stranded locally (observed: a review stage
left 7 unpushed commits). Now, before retiring a COMPLETED stage, the watchdog does a best-effort
'git push origin HEAD:main' in that stage's dir (never raises; 90s timeout). Guarantees the deliverable
reaches the remote.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UWTdUq2bsic7JZGqJp3nD6
This commit is contained in:
2026-07-04 17:47:23 +00:00
parent b739504e1f
commit 08fd58ccc0

View File

@ -977,6 +977,25 @@ def _pipeline_marker(cfg, stage):
p = Path(stage["done"])
return p if p.is_absolute() else Path(cfg["project_dir"]) / stage["done"]
def _pipeline_push_on_retire(agent):
"""Best-effort `git push` in a completing stage's dir before it's retired, so its final commits
aren't stranded locally if it wrote its completion marker before its last push landed (a race:
the pipeline retires a stage the instant its marker appears). Never raises; logs the outcome."""
d = agent.get("dir")
if not d or not (Path(d) / ".git").exists():
return
try:
r = subprocess.run(["git", "-C", d, "push", "origin", "HEAD:main"],
env={**os.environ, "GIT_SSH_COMMAND": "ssh -o BatchMode=yes"},
capture_output=True, text=True, timeout=90)
if r.returncode == 0:
log(f"pipeline: pushed {agent['name']!r} before retiring (no stranded commits)")
elif "up-to-date" not in (r.stderr + r.stdout).lower():
log(f"pipeline: push-on-retire for {agent['name']!r} nonzero (non-fatal): "
f"{(r.stderr or r.stdout).strip().splitlines()[-1:] }")
except Exception as e:
log(f"pipeline: push-on-retire for {agent['name']!r} failed (non-fatal): {e}")
def pipeline_check(cfg):
"""Reconcile the [pipeline]: run the first stage whose completion marker is absent, retire every
other pipeline agent. Returns the ACTIVE stage's agent dict (so the watchdog stall/heal-watches it),
@ -1000,6 +1019,10 @@ def pipeline_check(cfg):
log(f"pipeline: advancing → start stage {ag['name']!r}")
start_agent(cfg, ag, force=True)
elif alive:
# Retiring a non-active stage. If it's a COMPLETED stage (its marker exists), push its repo
# first so final commits written just before the marker aren't stranded locally.
if _pipeline_marker(cfg, st).exists():
_pipeline_push_on_retire(ag)
log(f"pipeline: retire stage {ag['name']!r} (complete or not yet active)")
kill_session(ag["session"])
if active is None: