feat(git): env-ladder rung protection at the shared remote-delete chokepoint (#651)

Rung protection lived only in delete_task_branch; the post-merge PR-
source cleanup (and the stale-branch sweep's shared primitive) could
still delete a branch that IS a ladder rung. _protected_branches_for_
deletion(slug) — field ∪ rung names, null-ladder shim included — now
feeds _delete_remote_branch_best_effort, so every remote deletion path
is covered; delete_task_branch's local rung check is removed as exactly
subsumed (verified byte-identical comparison semantics). Bonus closed
gap: a renamed trunk (default_branch 'trunk', null ladder) is now
delete-protected, which the hardcoded main/master floor never covered.

Per adversarial review, the deletion lookup fails CLOSED: a raised
project lookup skips the delete with a warning (a skipped best-effort
delete just retries next sweep — free safety), while a genuinely-gone
project proceeds with the hardcoded floor (its ladder is meaningless).
The rebase/sync resolver stays fail-open — a refused rebase on a DB
blip would wrongly block work, a different tradeoff, now documented.
Panel tooltip updated to the new truth. 29 tests.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
Renzo F
2026-07-23 00:05:33 +02:00
committed by GitHub
co-authored by Renn F
parent 3806317aa7
commit fa459998b4
4 changed files with 320 additions and 36 deletions
@@ -612,7 +612,7 @@ function EditProjectForm({
{/* Protected Branches */}
<div className="grid gap-2">
<HelpTip label="Branches the fleet refuses to rebase onto, sync (force-push) as a task's own branch, or delete on the remote, in addition to the always-protected master/main defaults — matched exactly, case-sensitive. Environment-ladder rungs get separate protection, but only for task-branch cleanup, not a PR's source-branch cleanup after merge.">
<HelpTip label="Branches the fleet refuses to rebase onto or sync (force-push) as a task's own branch, in addition to the always-protected master/main defaults — matched exactly, case-sensitive. Every remote branch delete (task-branch cleanup, the stale-branch sweep, and a merged PR's source-branch cleanup) additionally refuses any environment-ladder rung, even one not listed here.">
<Label htmlFor="protected_branch_input">Protected Branches</Label>
</HelpTip>
{protectedBranches.length > 0 && (