tbd_ship (v0.6.0, #40) derives a deterministic branch tbd/<slug>-<hash> from the change. With ci_timeout_seconds=0 (default) it returns pending/PR-only right after opening the PR, and the issue's UX is "let the caller poll or re-invoke". But re-invoking the same change collides: CreateBranch fails because the branch already exists, so the second call errors instead of resuming.
Desired behavior
Make tbd_ship idempotent / resumable: if the derived (or provided) branch + its PR already exist for this change, skip creation and jump straight to the CI gate + merge decision on the existing PR's head commit. So a caller can:
tbd_ship(...) → pending (CI not done)
tbd_ship(...) again later → same branch/PR detected → re-evaluate CI → merge if now green.
Sketch
On CreateBranch conflict (or a pre-check via branch_list/pr_list head=branch), don't error — look up the open PR for head=branch, use its head sha, run the gate, merge-or-report.
Only (re)write the file if the content differs from what's already on the branch (the sha-fetch path already exists).
Keep the first-call path unchanged.
Acceptance
Second tbd_ship with the same change resumes the existing PR and merges when CI is now green (no "branch exists" error).
Content unchanged → no redundant commit.
First call behavior unchanged; tests for the resume path.
Refs
Follow-up to #40 (v0.6.0). internal/tools/tbd_ship.go.
## Context
`tbd_ship` (v0.6.0, #40) derives a deterministic branch `tbd/<slug>-<hash>` from the change. With `ci_timeout_seconds=0` (default) it returns `pending`/PR-only right after opening the PR, and the issue's UX is "let the caller poll or re-invoke". But **re-invoking the same change collides**: `CreateBranch` fails because the branch already exists, so the second call errors instead of resuming.
## Desired behavior
Make `tbd_ship` idempotent / resumable: if the derived (or provided) branch + its PR already exist for this change, **skip creation and jump straight to the CI gate + merge decision** on the existing PR's head commit. So a caller can:
1. `tbd_ship(...)` → `pending` (CI not done)
2. `tbd_ship(...)` again later → same branch/PR detected → re-evaluate CI → merge if now green.
## Sketch
- On `CreateBranch` conflict (or a pre-check via `branch_list`/`pr_list head=branch`), don't error — look up the open PR for `head=branch`, use its head sha, run the gate, merge-or-report.
- Only (re)write the file if the content differs from what's already on the branch (the sha-fetch path already exists).
- Keep the first-call path unchanged.
## Acceptance
- [ ] Second `tbd_ship` with the same change resumes the existing PR and merges when CI is now green (no "branch exists" error).
- [ ] Content unchanged → no redundant commit.
- [ ] First call behavior unchanged; tests for the resume path.
## Refs
Follow-up to #40 (v0.6.0). `internal/tools/tbd_ship.go`.
CreateBranch conflict tolerated — a re-invoke on an existing branch resumes instead of erroring.
Existing PR reused — if CreatePullRequest reports one already open for the head (409/422), it's resolved via ListPullRequests (matching head.ref).
No redundant commit — if the branch already has byte-identical content at the path, the write is skipped.
Then the same fail-closed CI gate runs and merges if now green.
So the #40 "poll or re-invoke" UX actually works: first call returns pending; a later call with the same change resumes the PR and merges once CI is green — no "branch exists" error.
Test: TestTBDShip_Resume_ExistingBranchAndPR (branch+PR exist, content unchanged → no write, merges when green). First-call paths unchanged; task check green (exit 0, 0 FAIL — verified, not grep-filtered).
Done in v0.7.0.
`tbd_ship` is now idempotent / resumable:
- **`CreateBranch` conflict tolerated** — a re-invoke on an existing branch resumes instead of erroring.
- **Existing PR reused** — if `CreatePullRequest` reports one already open for the head (409/422), it's resolved via `ListPullRequests` (matching `head.ref`).
- **No redundant commit** — if the branch already has byte-identical content at the path, the write is skipped.
- Then the same fail-closed CI gate runs and merges if now green.
So the #40 "poll or re-invoke" UX actually works: first call returns `pending`; a later call with the same change resumes the PR and merges once CI is green — no "branch exists" error.
Test: `TestTBDShip_Resume_ExistingBranchAndPR` (branch+PR exist, content unchanged → no write, merges when green). First-call paths unchanged; `task check` green (exit 0, 0 FAIL — verified, not grep-filtered).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Context
tbd_ship(v0.6.0, #40) derives a deterministic branchtbd/<slug>-<hash>from the change. Withci_timeout_seconds=0(default) it returnspending/PR-only right after opening the PR, and the issue's UX is "let the caller poll or re-invoke". But re-invoking the same change collides:CreateBranchfails because the branch already exists, so the second call errors instead of resuming.Desired behavior
Make
tbd_shipidempotent / resumable: if the derived (or provided) branch + its PR already exist for this change, skip creation and jump straight to the CI gate + merge decision on the existing PR's head commit. So a caller can:tbd_ship(...)→pending(CI not done)tbd_ship(...)again later → same branch/PR detected → re-evaluate CI → merge if now green.Sketch
CreateBranchconflict (or a pre-check viabranch_list/pr_list head=branch), don't error — look up the open PR forhead=branch, use its head sha, run the gate, merge-or-report.Acceptance
tbd_shipwith the same change resumes the existing PR and merges when CI is now green (no "branch exists" error).Refs
Follow-up to #40 (v0.6.0).
internal/tools/tbd_ship.go.Done in v0.7.0.
tbd_shipis now idempotent / resumable:CreateBranchconflict tolerated — a re-invoke on an existing branch resumes instead of erroring.CreatePullRequestreports one already open for the head (409/422), it's resolved viaListPullRequests(matchinghead.ref).So the #40 "poll or re-invoke" UX actually works: first call returns
pending; a later call with the same change resumes the PR and merges once CI is green — no "branch exists" error.Test:
TestTBDShip_Resume_ExistingBranchAndPR(branch+PR exist, content unchanged → no write, merges when green). First-call paths unchanged;task checkgreen (exit 0, 0 FAIL — verified, not grep-filtered).