A second tbd_ship for the same change no longer errors on "branch exists":
CreateBranch conflict is tolerated, and if a PR is already open for the head,
CreatePullRequest's conflict/validation error resolves it via ListPullRequests
(matching head.ref). Identical file content on the branch skips the write, so a
resume produces no redundant empty-diff commit. Then the same CI gate runs and
merges if now green — so "poll or re-invoke" (the #40 UX) actually works.
Test: TestTBDShip_Resume_ExistingBranchAndPR (branch+PR exist, content
unchanged → no write, merges when green). First-call paths unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An intent verb for the trunk-based loop: branch from base → write the file →
open a PR → auto-merge (squash) → delete the branch. One call instead of
orchestrating file_write_branch + pr_create + workflow_run_status + pr_merge +
branch_delete and remembering the conventions each time.
The load-bearing safety is a pure, fail-closed CI gate (evaluateShipGate):
merge=true ONLY when every workflow run for the PR head commit is
completed+success AND the base branch is not review-protected. Every other
state — CI pending / red / absent, review-required base, or an unclean merge —
fails closed to PR-only and returns the PR with a reason. A change with no CI
gate is never auto-merged to trunk (cf. agentsquad#36: non-compiling code
reviewer-approved straight to main with no CI wall).
- ci_timeout_seconds polls the head commit's runs to completion (default 0 =
snapshot, returns pending right after opening the PR).
- Derives a deterministic short-lived branch (tbd/<slug>-<hash>); handles new
and existing files (fetches the blob sha for updates).
- Adds head.sha + mergeable to the PR struct.
- Tests: full gate matrix (green/pending/red/cancelled/none/mixed/protected) +
Call happy-merge, no-CI fail-closed, red fail-closed, allowlist.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>