Skip to content

[SWR-SEC-DEPENDABOT-STAGING] Shipped template's clobber-merge sweep cannot work — GITHUB_TOKEN can never push .github/workflows/* #60

Description

@MelbourneDeveloper

templates/gh-actions/dependabot-automerge.yml and the [SWR-SEC-DEPENDABOT-STAGING] spec section both still describe the clobber-merge generation. That design cannot work, and the spec's own reference implementation has already moved off it.

The defect

The template sweeps with:

git merge -X theirs --no-edit "origin/${PR_HEAD}"
git push origin "HEAD:dependabot-upgrades"

and subscribes to both dependabot-upgrades and main.

Security updates are the only Dependabot PRs that reach main — GitHub ignores target-branch for them, which the spec correctly documents as fixed behaviour. But a security bump is branched off current main. Merging it into a staging branch that has drifted behind main drags main's intervening commits along, including .github/workflows/* edits.

GITHUB_TOKEN can never push workflow files. There is no permissions: key that grants the workflows scope — it is a GitHub App scope the Actions token does not have. So the push is rejected:

! [remote rejected] HEAD -> dependabot-upgrades (refusing to allow a GitHub App
  to create or update workflow `.github/workflows/ci.yml` without `workflows` permission)

The rejection is permanent, and the template retries it five times before failing. The staging branch drifts behind main by construction — that is what a staging branch does — so this transitions from working to permanently broken the first time a workflow file changes on main, and never recovers.

Observed impact in a repo running the shipped template

Nimblesite/NimblesiteAgenticPlatform, template generation agent-pmo:372ce7f. Identical failure on every main-based bump from 2026-07-22 to 2026-09-04 — six weeks, always the same rejection, never once surfaced, because a merge bot going red is silent.

The damage compounds well past PR noise:

Security PRs never retired 10
npm PR quota 5/5 — saturated at open-pull-requests-limit
uv PR quota 5/5 — saturated
Open Dependabot alerts 92 (56 HIGH)
Routine bumps stranded on staging 54 commits
Consolidation PRs ever opened 0

Because nothing is ever retired, both ecosystems pin at their PR cap and Dependabot stops opening new security PRs altogether. A direct dependency (GitPython) accumulated 17 HIGH advisories without ever getting a PR.

Two further defects in the same template

  1. No drain. The template flows bumps into staging and has nothing that flows them out. Nothing opens the dependabot-upgrades → main consolidation PR the spec relies on, so it depends entirely on a maintainer noticing. In the repo above, zero were opened in three months while 54 commits accumulated.

  2. The main base filter hangs a dead check on every human PR. The job can only be narrowed to Dependabot with a job-level if:, and an if:-skipped job still materialises as a check run with conclusion skipped. Subscribing to PRs against main therefore pins a permanently-skipped check onto every human PR forever — one that by construction can never run.

The fix already exists

Nimblesite/deslop runs agent-pmo:0b21609, which resolves all three:

  • Path-restricted sweep. Copy only the files the PR itself changed (diff against its merge-base with the PR base) onto the staging tip. Immune to staging drift, and clobber semantics survive with no merge machinery left to conflict. A bump that edits .github/workflows/* itself is squash-merged server-side, since the merge API is not a push.
  • Base filter dependabot-upgrades only, so the workflow is never instantiated for a human PR.
  • A drain step that opens the consolidation PR whenever staging is ahead and none is open, on a weekly schedule so it runs from the default branch's copy of the file.

Nimblesite/Basilisk — named in the spec as the reference implementation — is on 0b21609 too, but only partially: its dependabot.yml header still describes the clobber-merge behaviour, and its ci.yml still carries the Dependabot actor skip.

Suggested changes

  1. Replace templates/gh-actions/dependabot-automerge.yml with the 0b21609 generation.
  2. Rewrite the [SWR-SEC-DEPENDABOT-STAGING] bullet "The sweep clobber-merges, latest wins" — it currently prescribes the broken mechanism.
  3. Add the drain to the spec as a load-bearing part; it is currently absent, and "a consolidation PR that the maintainer opens periodically" is the part that does not happen on its own.
  4. Reconsider the "CI runs once, on the consolidation PR" bullet. With the base filter narrowed to staging, routine bumps never trigger a main workflow at all, so the github.actor == 'dependabot[bot]' skip saves nothing on them — it applies only to security bumps, making the vulnerability fix the single PR class that reaches main with no build, no test and no dependency review. deslop has removed those skips.
  5. Finish Basilisk's migration so the reference implementation matches the template.

Fix as applied to NAP: Nimblesite/NimblesiteAgenticPlatform#324

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions