flake-update-cascade: read protected-branches by the same rule as the rails #237

Closed
opened 2026-09-27 17:48:49 +01:00 by allod-agent · 0 comments
Member

Bring flake-update-cascade up to the same standard as allod change and the protected-refs-policy hook when it decides whether a repository's default branch is protected, so a direct run cannot commit and push to a protected branch because of how a line in the list is written or where the repository is checked out.

Primary goals:

  • One rule, three readers — the cascade answers "is this branch protected" the way the tool and the hook do since allod/tools#235: by checkout path first, then by the owner/repo that origin names.
  • Tolerant of how the line is written — extra spaces, a tab, or a trailing slash on the path no longer turn a protected repository into an unprotected one.
  • Loud on a list that cannot be read — a list that is present but unreadable stops the run and names the file; an absent list stays "nothing is protected here".
  • A misplaced checkout is refused — a repository whose origin names a listed repository but which sits at another path is skipped with a message naming both paths, in direct mode and in --pr mode.
  • Pinned by the shared table — the cascade's answer is tested against tests/fixtures/protection-cases.tsv, the table that already holds the Go tool and the bash hook together.

Current state and specifics

cmd/flake-update-cascade/main.go:416 decides protection with fileHasLine(c.protectedFile, "work/"+repo+" "+defaultBranch). fileHasLine (main.go:306) compares each whole line of ~/.config/git/protected-branches with that string, byte for byte.

Input What the cascade concludes What the tool and the hook conclude
work/allod/tools master (two spaces) or a tab not protected protected
work/allod/tools/ master not protected protected
the list is a directory or otherwise unreadable not protected error naming the file
the repository is checked out at another path, origin names a listed repository not protected misplaced checkout, refused
an entry whose path is not work/<name> not protected protected when the path or the origin matches

In a direct run a repository judged unprotected is committed to and pushed on its default branch. For an agent the forge rejects that push. For a repository admin it does not: branch protection on the forge does not restrain an admin, so for a human running the cascade the local list is the only thing in the way.

The rule to share is lookupProtection in cmd/allod/workspace.go, with readBranchList beside it and RepoFromURL in internal/gitremote. The default is to move the lookup into an internal package that both commands import, since both are Go; duplicating it a third time is the alternative only if the move turns out to drag cmd/allod plumbing with it.

Validation

  • The cascade's protection answer runs against every row of tests/fixtures/protection-cases.tsv that applies to it.
  • A direct run over a fixture repository whose entry is written with two spaces skips it as protected; the same run on the current code commits to it. One sabotage run shows the test going red.
  • The existing cascade suites under tests/flake/flake-update-cascade/ stay green.

Scope

In scope: how flake-update-cascade reads protected-branches, and the package move that lets it share the rule. Out of scope: the contents of protected-branches, which stay a hand-maintained copy of the forge's rules; active-pr-branches and allowed-external-remotes, which the cascade already reads by remote.

Bring `flake-update-cascade` up to the same standard as `allod change` and the `protected-refs-policy` hook when it decides whether a repository's default branch is protected, so a direct run cannot commit and push to a protected branch because of how a line in the list is written or where the repository is checked out. Primary goals: - **One rule, three readers** — the cascade answers "is this branch protected" the way the tool and the hook do since allod/tools#235: by checkout path first, then by the `owner/repo` that `origin` names. - **Tolerant of how the line is written** — extra spaces, a tab, or a trailing slash on the path no longer turn a protected repository into an unprotected one. - **Loud on a list that cannot be read** — a list that is present but unreadable stops the run and names the file; an absent list stays "nothing is protected here". - **A misplaced checkout is refused** — a repository whose origin names a listed repository but which sits at another path is skipped with a message naming both paths, in direct mode and in `--pr` mode. - **Pinned by the shared table** — the cascade's answer is tested against `tests/fixtures/protection-cases.tsv`, the table that already holds the Go tool and the bash hook together. ### Current state and specifics `cmd/flake-update-cascade/main.go:416` decides protection with `fileHasLine(c.protectedFile, "work/"+repo+" "+defaultBranch)`. `fileHasLine` (`main.go:306`) compares each whole line of `~/.config/git/protected-branches` with that string, byte for byte. | Input | What the cascade concludes | What the tool and the hook conclude | | --- | --- | --- | | `work/allod/tools master` (two spaces) or a tab | not protected | protected | | `work/allod/tools/ master` | not protected | protected | | the list is a directory or otherwise unreadable | not protected | error naming the file | | the repository is checked out at another path, origin names a listed repository | not protected | misplaced checkout, refused | | an entry whose path is not `work/<name>` | not protected | protected when the path or the origin matches | In a direct run a repository judged unprotected is committed to and pushed on its default branch. For an agent the forge rejects that push. For a repository admin it does not: branch protection on the forge does not restrain an admin, so for a human running the cascade the local list is the only thing in the way. The rule to share is `lookupProtection` in `cmd/allod/workspace.go`, with `readBranchList` beside it and `RepoFromURL` in `internal/gitremote`. The default is to move the lookup into an internal package that both commands import, since both are Go; duplicating it a third time is the alternative only if the move turns out to drag `cmd/allod` plumbing with it. ### Validation - The cascade's protection answer runs against every row of `tests/fixtures/protection-cases.tsv` that applies to it. - A direct run over a fixture repository whose entry is written with two spaces skips it as protected; the same run on the current code commits to it. One sabotage run shows the test going red. - The existing cascade suites under `tests/flake/flake-update-cascade/` stay green. ### Scope In scope: how `flake-update-cascade` reads `protected-branches`, and the package move that lets it share the rule. Out of scope: the contents of `protected-branches`, which stay a hand-maintained copy of the forge's rules; `active-pr-branches` and `allowed-external-remotes`, which the cascade already reads by remote.
vnprc closed this issue 2026-09-29 00:41:07 +01:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
allod/tools#237
No description provided.