Three points from the PR #41 Copilot review, each a robustness gap on a path
the happy case never takes:
- phases._mine() gated the beat on the assignee even when this checkout has
no git name — but agents.claim_for_launch() cannot gate a launch there and
lets it through, so a phase could start (branch cut, run recorded) and then
advance nowhere. _mine() now treats "no local identity" as the lone actor,
matching the launch it mirrors.
- phases._record() ignored whether the log line landed. The log is the
durable memory a restart reads to tell "already started" from "not reached
yet"; a launch or merge with no line behind it is what a restart repeats.
_record() now raises _Halt when the write fails — before the action it was
meant to precede — split from a best-effort _write_log() the halt path and
_start() use so recording a halt can never itself raise.
- github._pr_base() switched a member PR's base to the phase branch only when
it existed locally. A board that did not run the phase knows it only through
the remote (sync fetches origin/main and nothing else), so _pr_base() now
also honours a phase branch the remote carries, and _open_pr() only pushes
the base when it is a local branch.
Four new tests in tests/test_phase_runs.py cover each. Full suite green (740).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>