Skip to content

Budget breaker depends on scheduled ticks that this repository barely receives: add a default-branch-only event trigger before arming #2259

Closed

Description

@don-petry

Problem

The budget breaker ("Budget breaker writer", #2153) is schedule-only, every 10 minutes, so that only default-branch code ever runs with its write credential. But this repository is receiving almost no scheduled ticks.

Observed 2026-10-11: the workflow merged at 01:55 UTC and is listed as active. By 03:10 UTC it had not run once (seven slots). Over the same period, across every scheduled workflow in this repository, GitHub delivered 2 scheduled run(s) since 01:00 UTC; the twice-hourly budget poller last ran at 01:00, and a 15-minute sweep ran once. "GitHub drops about 60% of scheduled ticks here" (#2160) measured 40% delivery; under load it is far lower.

A breaker that may not run for an hour or more cannot react to a quota outage in time, and once armed it also could not resume the fleet on time, which is worse: its own pause would outlive the reset until a tick happens to arrive.

Proposal

Keep the schedule as a backstop and add an event trigger that still runs only default-branch code:

  1. Add a workflow_run trigger on completion of the agent workflows that post quota markers (dev-lead first). GitHub runs a workflow_run workflow from the default branch's definition, so the property the schedule-only rule protects (no branch code with the write credential) is kept. Confirm that against GitHub's documentation and state it in the workflow header; the checkout must stay on the default branch and must never check out the triggering run's ref.
  2. Bound the cost: the existing budget-breaker concurrency group already allows one running and one pending run. Add a cheap early exit when the previous breaker run finished less than a couple of minutes ago and the fleet is not paused.
  3. For resume specifically, a pause must not depend on a tick arriving: consider having the paused agents' own fleet-paused notice jobs or the retry cron's next run be able to wake the breaker, or accept and document the worst-case delay.
  4. Report, in the run summary, how long it has been since the previous breaker run, so poor delivery is visible.

Acceptance criteria

  1. The static test that pins "no trigger that can run branch code" is updated to allow exactly schedule and workflow_run, and still rejects workflow_dispatch, pull_request, push and the rest.
  2. A test that the checkout step never uses the triggering run's ref or SHA.
  3. The documented worst-case reaction time and worst-case resume delay, with the measurement they are based on.

This should be decided before arming ("Auto-pause companion", #1565). Related: #2160, #2251.

Activity

  1. github-actions commented on Oct 11, 2026

    @github-actions
    Contributor

    Thanks for the detailed write-up and the delivery measurements, which make the problem easy to see. Before this is picked up, one question: for the resume case (point 3), do you prefer having the fleet-paused notice jobs or the retry cron wake the breaker, or would you rather accept and document a worst-case resume delay? That choice decides how much new wiring is needed and what the documented worst-case numbers will be.

  2. don-petry commented on Oct 11, 2026

    @don-petry
    CollaboratorAuthor

    Second data point, 2026-10-11 04:55 UTC: three hours after merging, the breaker workflow has still not run once (eighteen 10-minute slots). Across every scheduled workflow in this repository, one scheduled run has been delivered since 02:00 UTC. The workflow is listed as active, so this is delivery, not configuration. Whatever cadence the schedule asks for, it cannot be relied on for reaction or for resume; the event trigger proposed above is the substance of this issue, not an optimisation.

  3. don-petry commented on Oct 11, 2026

    @don-petry
    CollaboratorAuthor

    dev-lead is withholding action on this item.

    It is labeled dev-lead:hands-off (deliberately kept off-limits to dev-lead (e.g. changes to the dev-lead automation itself)), so dev-lead will not pick it up while that label is present. This notice is posted once so the withhold is visible rather than looking like a stalled run.

    To re-enable automated pickup: remove the dev-lead:hands-off label.

  4. don-petry commented on Oct 11, 2026

    @don-petry
    CollaboratorAuthor

    First 84 minutes under the new trigger (17:35 to 18:59 UTC, 2026-10-11)

    It works as designed, and it costs more than the pull request estimated.

    Measure Observed Estimate in #2293
    Breaker runs created 309 (about 220 an hour) not estimated
    of which executed 115 (about 82 an hour) about 47 an hour
    of which cancelled while waiting (replaced by a newer arrival) 193 "superseded runs are harmless"
    Scheduled runs delivered 0 —
    Busiest ten minutes 77 created —

    No self-trigger loop. Lined up second by second over a busy stretch (18:10 to 18:12), every breaker run was created 1 to 2 seconds after a Dev-Lead Agent run completed, and no Dev-Lead Agent run was created by a breaker run completing: the Dev-Lead Agent runs in that window were issue_comment, pull_request_review, pull_request_review_comment, pull_request and repository_dispatch events, with one check_run. The volume is real fleet activity, which was far burstier than the day's average the estimate was built on (21 Dev-Lead Agent runs in 3.5 minutes there).

    The early exit engages. Of six consecutive runs read (38162668958 onward), the first of the two-minute slot scanned and decided (0 trusted, 0 dropped, decision none), and the next five took the early exit. The first run after the merge (38160312163) could not list earlier runs because the login's API quota was exhausted at that moment; it failed open and decided, as intended.

    What it costs while the breaker is disarmed and protecting nothing:

    • Run history: on this evidence, several thousand "Budget Breaker" entries a day, about two thirds of them cancelled.
    • One runner slot busy for 10 to 20 seconds per executed run; in bursts that is a third of one slot.
    • API calls on the shared maintainer login: two per early-exit run, plus one organisation scan per two-minute slot. Roughly 500 to 600 an hour at this rate, a tenth of the hourly quota, on a day that quota ran out twice (mostly from other reads).

    Not yet observed: a breaker run following a Dev-Lead Agent run that ended skipped (arming precondition 6). One check_run Dev-Lead Agent run ended skipped at about 18:10; I have not matched it to a breaker run.

    Options are with the maintainer: leave it and review after a day; lengthen the early exit's slot with the repository variable BUDGET_BREAKER_MIN_INTERVAL_MIN (fewer organisation scans, same number of runs); or change the design so that fewer runs are created at all.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions