Skip to content

governance: auto-fix 上限执法 + 额度/成本熔断(P2-8,ADR-0040) - #116

Merged
randypanding merged 8 commits into
mainfrom
p2-8/autofix-limit-cost-breaker
Aug 20, 2026
Merged

governance: auto-fix 上限执法 + 额度/成本熔断(P2-8,ADR-0040)#116
randypanding merged 8 commits into
mainfrom
p2-8/autofix-limit-cost-breaker

Conversation

@randypanding

Copy link
Copy Markdown
Contributor

摘要(ADR-0040 实现载体,工作卡 #93 / 计划 #81 P2-8)

无人值守自动化的两道生存护栏:

  1. auto-fix 上限(N=3)——governance/auto-fix-limit.sh(小时级 cron):统计 agent PR 各 commit 上 gate check run 的失败结论数(真源 = Checks API,无内存态——崩溃/重启计数续接,T3 由构造保证);达上限 → 撤 auto-merge + 关 PR + auto-fix-limit-exhausted 标签 + 说明 issue(含失败 run 链接);reopen 的超限 PR 下轮再关。
  2. 成本熔断——governance/cost-check.sh(6h cron):GET /orgs/{org}/settings/billing/usage(旧端点 410 已迁移)汇总当月 Actions 分钟 vs policy/automation-limits.yaml 声明预算:≥80% 告警 issue(同日去重);≥100% 置 org 变量 AUTO_MERGE_DISABLED + 撤全部 open PR auto-merge + P0 issue。复位仅人工(owner PATCH 变量 + P0 留评论),脚本观察到复位后自动关 P0。
  3. 消费点:AGENTS.md 硬规则(派发/automerge 前检查变量 + 重试上限 + 禁 reopen)+ auto-fix-limit 每轮机器执法撤 auto-merge(旁路窗口 ≤1h)。
  4. fail-closed:API 读失败 → exit 2 + 专属 label issue(auto-fix-infra / cost-infra),不盲置熔断(假熔断要求人工复位会停摆流水线);非数值输入 → infra 通道。
  5. LLM token 预算:policy 声明 + 注入通道可测(LLM_TOKENS_USED),数据源(llm-gateway usage)pending。

刻意不改 governance/expected-state.json:熔断变量是运行态标志非期望状态(纳入会造成复位后被 drift-check 反向改回的对账冲突——ADR-0040 后果节)。

变更文件

  • governance/policy/automation-limits.yaml(新)——阈值真源:max_attempts=3 / 20000 分钟月预算 / 80·100 档位 / 熔断变量与复位人
  • governance/auto-fix-limit.sh(新)——上限执法扫描器
  • governance/cost-check.sh(新)——用量检查 + 熔断置位/复位确认
  • .github/workflows/auto-fix-limit.yml(新)——cron 18 * * * * + dispatch 注入(max_attempts/check_name/repos/all_prs/dry_run)
  • .github/workflows/cost-check.yml(新)——cron 42 */6 * * * + dispatch 注入(usage/quota/llm/dry_run——T2 注入式测试入口)
  • AGENTS.md——硬规则 (a)-(d)(熔断前置检查/重试上限/禁 reopen/infra issue 补盲)+ 索引 + 常用命令
  • .github/workflows/gate.yml——bash -n 纳入两个新脚本

本地已验证(dry-run,零写入)

  • bash -n × 2;YAML 全量解析(policy + 3 workflow)
  • auto-fix-limit:agent-registry 实扫(PR#54/fix: 移除误提交的 agent-registry gitlink,修复 gate/scorecard checkout 失败 #52 计数 0/3 正确;head 未完成 run 不计);超限路径用历史真实失败数据驱动(PR#43 1 次失败 + max=1 注入 → 关闭动作 + exit 1)
  • cost-check T2 注入:真实基线 59%→exit 0;79%→exit 0;85%→告警档 exit 1;100%→硬停档 exit 1(识别到 mutual#14 真实 automerge PR 作为执法对象,DRY 拦截实际撤销);LLM 注入 100%→硬停档;非数值注入→exit 2
  • 验证零副作用:mutual#14 auto-merge 原样、org 变量/issue 无新增

待执行(合并后,按卡内测试方法)

  • T1 全链路:测试仓构造必红 PR → 实际关 PR + issue + 无第 N+1 次 run(Actions 用量佐证)
  • T2.3 真置位:非 dry-run 注入 100% → 变量真置位 + P0 + 模拟派发被拒;T2.4 人工复位 → 自动关 P0
  • T3:T1 重跑中途 cancel → 计数续接(真源=API,预期直接通过)

@randypanding

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@randypanding, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Limit details: You’ve used all 10 included reviews currently available.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 201967d4-69f1-4a56-971c-53eb2c859cf3

📥 Commits

Reviewing files that changed from the base of the PR and between c034fe9 and 7806ace.

📒 Files selected for processing (7)
  • .github/workflows/auto-fix-limit.yml
  • .github/workflows/cost-check.yml
  • .github/workflows/gate.yml
  • AGENTS.md
  • governance/auto-fix-limit.sh
  • governance/cost-check.sh
  • governance/policy/automation-limits.yaml

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Enforce auto-fix limits and organization cost circuit breakers

✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Enforces a three-failure cap on agent pull-request repair loops.
• Trips organization-wide auto-merge circuit breakers at configured cost thresholds.
• Adds scheduled enforcement, fail-closed alerts, operator rules, and dry-run controls.
Diagram

graph TD
  W["Scheduled workflows"] --> A["Auto-fix guard"] --> E["PR enforcement"]
  W --> C["Cost monitor"] --> B["Org breaker"] --> A
  P["Limit policy"] --> A
  P --> C
  K["Checks API"] --> A
  U["Billing API"] --> C
  C --> E
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Event-driven per-repository enforcement
  • ➕ Can stop retries immediately after the final failed check.
  • ➕ Avoids the scanner's maximum one-hour enforcement delay.
  • ➖ Requires workflow changes across every managed repository.
  • ➖ Makes reopen protection and organization-wide breaker enforcement harder to centralize.
  • ➖ Increases configuration drift and maintenance surface.
2. Persist counters in an external store
  • ➕ Supports richer attempt metadata and explicit state transitions.
  • ➕ Avoids repeatedly querying check runs across all commits.
  • ➖ Introduces state reconciliation, availability, and credential requirements.
  • ➖ Can lose consistency with GitHub after crashes or manual PR operations.
  • ➖ Adds infrastructure for a small bounded counter.

Recommendation: Keep the centralized scheduled scanners and GitHub APIs as durable truth sources. This approach avoids external state, covers reopened PRs and all active repositories, and centralizes privileged enforcement; the bounded polling delay is preferable to distributing mutable governance workflows across repositories.

Files changed (7) +665 / -2

Enhancement (2) +492 / -0
auto-fix-limit.shEnforce persistent per-PR repair attempt limits +219/-0

Enforce persistent per-PR repair attempt limits

• Adds a stateless scanner that counts failed gate checks from commit history and closes exhausted agent or opted-in PRs. It also removes auto-merge while the organization breaker is active and reports API failures through a dedicated fail-closed issue channel.

governance/auto-fix-limit.sh

cost-check.shMonitor budgets and enforce the cost circuit breaker +273/-0

Monitor budgets and enforce the cost circuit breaker

• Adds Actions-minute and injectable LLM-token budget evaluation with warning and hard-stop tiers. Hard stops set the organization breaker, remove auto-merge across managed repositories, create deduplicated issues, and preserve owner-only manual reset semantics.

governance/cost-check.sh

Tests (1) +3 / -1
gate.ymlValidate new governance shell scripts +3/-1

Validate new governance shell scripts

• Extends the existing shell syntax gate to cover both auto-fix and cost-check scripts. It also restores a trailing newline at the end of the workflow.

.github/workflows/gate.yml

Documentation (1) +5 / -1
AGENTS.mdDefine mandatory automation survival guardrails +5/-1

Define mandatory automation survival guardrails

• Requires agents to check breaker and infrastructure issue state before dispatching work or enabling auto-merge. It also documents retry limits, prohibits reopening exhausted PRs, and indexes the new policy and operator commands.

AGENTS.md

Other (3) +165 / -0
auto-fix-limit.ymlSchedule hourly auto-fix limit enforcement +59/-0

Schedule hourly auto-fix limit enforcement

• Adds an hourly serialized workflow that runs the auto-fix scanner with the governance token. Manual dispatch inputs support focused scans, threshold overrides, all-PR coverage, and dry-run validation.

.github/workflows/auto-fix-limit.yml

cost-check.ymlSchedule organization cost circuit checks +58/-0

Schedule organization cost circuit checks

• Adds a six-hour serialized workflow for billing-budget evaluation and circuit-breaker enforcement. Dispatch inputs expose usage, quota, LLM-token, and dry-run injection channels for threshold testing.

.github/workflows/cost-check.yml

automation-limits.yamlCentralize retry and cost thresholds +48/-0

Centralize retry and cost thresholds

• Introduces the canonical policy for auto-fix attempts, failed conclusions, Actions and LLM budgets, warning and hard-stop percentages, breaker variable naming, and reset ownership.

governance/policy/automation-limits.yaml

@qodo-code-review

qodo-code-review Bot commented Aug 20, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (11) 📘 Rule violations (2) 📜 Skill insights (0)

Grey Divider


Action required

1. False breaker looks unavailable 🐞 Bug ≡ Correctness
Description
Both scripts accept only a value containing true or a 404 response, so a valid manually reset
value of false is recorded as an infrastructure failure. Cost checks then exit 2 instead of
confirming reset, and auto-fix scans also remain permanently red after the documented PATCH-to-false
reset.
Code

governance/cost-check.sh[R125-131]

+VERR=$("$GH" api "orgs/$ORG/actions/variables/$CB_VARIABLE" --jq .value 2>&1) || true
+if [[ "$VERR" == *"true"* && "$VERR" != *"Not Found"* ]]; then
+  BREAKER_SET=1
+elif grep -q "Not Found" <<<"$VERR"; then
+  :
+else
+  infra "org 变量 $CB_VARIABLE 读取失败(非 404)——熔断状态未知"
Relevance

●●● Strong

The documented false reset path is deterministically treated as infrastructure failure instead of a
valid reset state.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The reset instructions explicitly permit setting the variable to false, but each reader only
recognizes text containing true or Not Found; false reaches infra().

governance/cost-check.sh[123-132]
governance/cost-check.sh[191-194]
governance/auto-fix-limit.sh[92-103]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A valid organization variable value of `false` is treated as an API failure.

## Issue Context
The documented reset procedure explicitly PATCHes the variable to false. Parse the command status separately and accept exact true and false values; handle 404 from a reliable status check rather than matching error text.

## Fix Focus Areas
- governance/cost-check.sh[123-132]
- governance/auto-fix-limit.sh[92-103]
- governance/cost-check.sh[191-194]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. New breaker P0 closes immediately 🐞 Bug ≡ Correctness
Description
BREAKER_SET is read before set_breaker and never updated, so the first hard-stop run enters the
BREAKER_SET == 0 reset branch after creating the P0 and immediately closes it. The same branch can
also close breaker issues when the variable read failed and its state is unknown.
Code

governance/cost-check.sh[R244-248]

+elif [[ $BREAKER_SET -eq 0 ]]; then
+  # 复位确认:变量未置位 + 用量 < 硬停档 + P0 issue 开着 → 自动关闭(人工复位已发生且留痕在评论)
+  for row in $(gov_open_issues cost-circuit-breaker | cut -f1); do
+    mutate "$GH" issue close "$row" --repo "$GOV_REPO" --comment \
+      "复位确认:$CB_VARIABLE 已未置位且用量 ${PCT_MIN}% < ${AM_STOP}%——全流程恢复(agent 派发/automerge 前置检查放行)。自动关闭。" \
Relevance

●●● Strong

Stale state causing a newly created P0 to close is a deterministic lifecycle bug, matching the
team’s recent fail-closed reliability focus.

PR-#19
PR-#50

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The script captures BREAKER_SET=0 before hard-stop handling, sets the variable and creates the P0,
then unconditionally closes all breaker issues whenever the stale value remains zero.

governance/cost-check.sh[123-132]
governance/cost-check.sh[176-203]
governance/cost-check.sh[234-251]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The first breaker activation immediately triggers the reset cleanup using stale pre-activation state.

## Issue Context
`BREAKER_SET` is initialized from the variable before hard-stop processing. `set_breaker` does not update it, and the reset branch does not require both metrics to be below hard-stop or the state read to have succeeded.

## Fix Focus Areas
- governance/cost-check.sh[123-132]
- governance/cost-check.sh[176-203]
- governance/cost-check.sh[234-251]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. PR scan failures report success 🐞 Bug ☼ Reliability
Description
The PR list actually consumed by the enforcement loop runs in a process substitution whose exit
status is discarded and whose errors are suppressed. If that request fails, the loop processes no
PRs, INFRA remains unchanged, and the workflow can exit 0 claiming the scan completed.
Code

governance/auto-fix-limit.sh[R197-199]

+  done < <("$GH" pr list --repo "$ORG/$repo" --state open --limit 200 \
+    --json number,author,labels,autoMergeRequest \
+    --jq '.[] | [.number, .author.login, (if (.labels | length) > 0 then (.labels | map(.name) | join(",")) else "-" end), (if .autoMergeRequest != null then "1" else "0" end)] | @tsv' 2>/dev/null)
Relevance

●●● Strong

Historical governance precedents favor fail-closed handling when API results are incomplete or
failures are silently treated as success.

PR-#19

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Lines 107-117 inspect one request, while lines 197-199 issue a separate request inside process
substitution. Nothing observes the latter command's failure, and the final status depends only on
INFRA and closure count.

governance/auto-fix-limit.sh[105-118]
governance/auto-fix-limit.sh[197-218]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Failure of the PR-list request used by the enforcement loop is silently interpreted as an empty scan.

## Issue Context
The earlier `PRJSON` request is checked but never consumed. Fetch and validate once, record any command or JSON failure as infrastructure failure, and iterate over that validated response.

## Fix Focus Areas
- governance/auto-fix-limit.sh[105-118]
- governance/auto-fix-limit.sh[197-200]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (6)
4. Failed closures count as enforced 🐞 Bug ☼ Reliability
Description
Auto-merge removal, label application, and PR closure failures are swallowed, after which
EXHAUSTED_CLOSED is incremented unconditionally. The workflow can therefore report that an
over-limit PR was closed and return the over-limit status while the PR remains open and mergeable.
Code

governance/auto-fix-limit.sh[R172-176]

+    mutate "$GH" pr edit "$num" --repo "$ORG/$repo" --add-label "$AF_EXHAUSTED_LABEL" >/dev/null 2>&1 || true
+    mutate "$GH" pr close "$num" --repo "$ORG/$repo" --comment \
+      "auto-fix 上限触发(ADR-0040):$CHECK_NAME 失败 $FAILS 次 ≥ 上限 $MAX_ATTEMPTS。自动关闭并不再重试;失败历史见 .github 仓说明 issue。人工续作:修复后新开 PR(勿 reopen 本 PR)。" \
+      >/dev/null 2>&1 || true   # 竞态(本轮已被关/合)不算 infra;issue 通道已有去重兜底
+    EXHAUSTED_CLOSED=$((EXHAUSTED_CLOSED+1))
Relevance

●●● Strong

Swallowing critical mutation failures while counting closure is a direct reliability defect in
fail-closed enforcement.

PR-#19

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Every critical mutation is followed by || true, but lines 176-177 always increment and announce a
closure. Exit 2 is only produced for errors that increment INFRA.

governance/auto-fix-limit.sh[167-177]
governance/auto-fix-limit.sh[203-218]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Failed PR enforcement mutations are reported as successful closures.

## Issue Context
Only benign, explicitly identified races should be tolerated. Other failures must increment infrastructure errors, and the closure counter should update only after closure is confirmed.

## Fix Focus Areas
- governance/auto-fix-limit.sh[167-177]
- governance/auto-fix-limit.sh[203-218]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Breaker removal failures are silent 🐞 Bug ☼ Reliability
Description
strip_all_automerge neither checks failure of each repository's PR-list process substitution nor
records failed DELETE requests. A hard-stop can therefore leave existing auto-merges enabled while
the P0 body falsely states that all were removed.
Code

governance/cost-check.sh[R158-163]

+      if mutate "$GH" api -X DELETE "repos/$ORG/$r/pulls/$n/auto-merge" >/dev/null 2>&1; then
+        act "熔断执法: 撤销 $r#$n 的 auto-merge"
+      fi
+    done < <("$GH" pr list --repo "$ORG/$r" --state open --limit 200 \
+      --json number,autoMergeRequest \
+      --jq '.[] | [.number, (if .autoMergeRequest != null then "1" else "0" end)] | @tsv' 2>/dev/null)
Relevance

●●● Strong

Recent accepted precedents require fail-closed handling for API failures and prevent silent
governance enforcement gaps.

PR-#19
PR-#49

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The DELETE is guarded only by an if with no failure branch, and the gh pr list command runs in
an unchecked process substitution with stderr discarded. The generated P0 nevertheless says all
open-PR auto-merges were revoked.

governance/cost-check.sh[143-165]
governance/cost-check.sh[176-189]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The hard-stop silently ignores failures while enumerating PRs or deleting auto-merge requests.

## Issue Context
Fetch and validate each PR list outside process substitution, record API failures through `infra`, and verify or fail on each DELETE before claiming complete enforcement.

## Fix Focus Areas
- governance/cost-check.sh[143-165]
- governance/cost-check.sh[176-189]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Gate shell command is invalid ✓ Resolved 🐞 Bug ≡ Correctness
Description
The added && starts a new shell command after a completed newline, which is a Bash syntax error.
Every gate run reaches this step and fails before either new script is checked.
Code

.github/workflows/gate.yml[R89-90]

+          # ADR-0040:生存护栏脚本纳入同一语法门(新增脚本不登记=语法检查盲区)
+          && bash -n governance/auto-fix-limit.sh && bash -n governance/cost-check.sh
Relevance

●●● Strong

The standalone leading && is an immediate shell syntax error; this is a trivial deterministic fix.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The run block ends the existing command on line 88 and begins line 90 with the binary && operator,
so the shell cannot parse the step.

.github/workflows/gate.yml[86-91]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The syntax-check step starts a new line with `&&`, which Bash rejects.

## Issue Context
The preceding command has no line continuation, and the intervening comment terminates the command line.

## Fix Focus Areas
- .github/workflows/gate.yml[87-90]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Breaker creation uses wrong endpoint 🐞 Bug ≡ Correctness
Description
When the variable does not yet exist, set_breaker POSTs to the item endpoint ending in
/$CB_VARIABLE instead of the organization variable collection. The fallback therefore fails and
the hard-stop can leave AUTO_MERGE_DISABLED unset.
Code

governance/cost-check.sh[R170-172]

+    mutate "$GH" api -X POST "orgs/$ORG/actions/variables/$CB_VARIABLE" \
+      -f name="$CB_VARIABLE" -F value=true -f visibility=all >/dev/null 2>&1 \
+      || infra "org 变量 $CB_VARIABLE 置位失败"
Relevance

●●● Strong

Wrong REST endpoint is a deterministic correctness bug; the fallback cannot create the organization
variable as written.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code POSTs to orgs/$ORG/actions/variables/$CB_VARIABLE, whereas GitHub documents the create
endpoint without the variable-name suffix.

governance/cost-check.sh[167-173]
🌐 GitHub documents organization-variable creation as POST /orgs/{org}/actions/variables, with the variable name supplied in the body.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The breaker fallback cannot create a missing organization variable because it POSTs to the wrong REST path.

## Issue Context
PATCH uses the item path, but creation must POST to `/orgs/{org}/actions/variables` with name, value, and visibility in the request body.

## Fix Focus Areas
- governance/cost-check.sh[167-173]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Missing checks escape counting 🐞 Bug ≡ Correctness
Description
When a commit has no matching check run, last is null but the expression wraps it as `[null,
"-"], so the outer // ["absent", "-"]` fallback never runs. Historical commits with no gate check
are consequently not recognized as absent and evade the script's intended fail-closed attempt
count.
Code

governance/auto-fix-limit.sh[R148-149]

+        if ! ROW=$("$GH" api "repos/$ORG/$repo/commits/$sha/check-runs" \
+          --jq "([.check_runs[] | select(.name == \"$CHECK_NAME\")] | sort_by(.id) | last | [.conclusion // \"none\", (.html_url // \"-\")]) // [\"absent\", \"-\"] | @tsv" 2>/dev/null); then
Relevance

●●● Strong

The jq fallback is applied after array construction, making absent check runs observable as null
rather than absent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The jq fallback applies to the constructed array, which is non-null even when its first member is
null. The counter only recognizes configured failures plus literal none or absent.

governance/auto-fix-limit.sh[145-160]
governance/policy/automation-limits.yaml[21-23]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
An empty matching check-run list does not produce the expected `absent` conclusion.

## Issue Context
Apply the null fallback to `last` before reading `.conclusion`, or branch explicitly on an empty result, and test commits with zero matching runs.

## Fix Focus Areas
- governance/auto-fix-limit.sh[145-160]
- governance/policy/automation-limits.yaml[21-23]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Dispatch grants org-wide mutations 🐞 Bug ⛨ Security
Description
Any repository writer who can manually dispatch these workflows can supply hard-stop or all-PR
inputs that execute with the organization-admin GOVERNANCE_TOKEN. For example,
usage_minutes_override can trip the organization breaker, while all_prs=true and
max_attempts=0 can close every listed open PR without requiring dry-run.
Code

.github/workflows/cost-check.yml[R10-13]

+  workflow_dispatch:
+    # 注入入口(T2 注入式测试:79%/85%/100% 全场景不依赖真实超支):空=真实 API/真源
+    inputs:
+      usage_minutes_override:
Relevance

●● Moderate

Privileged dispatch-input abuse is plausible, but history lacks a close workflow-dispatch
authorization precedent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Both workflows expose enforcement overrides through workflow_dispatch, default dry_run to an
empty/non-dry value, and pass the inputs into scripts authenticated with the privileged governance
secret. GitHub documents that repository write access is sufficient to manually run such a workflow.

.github/workflows/cost-check.yml[10-32]
.github/workflows/cost-check.yml[50-58]
.github/workflows/auto-fix-limit.yml[10-32]
.github/workflows/auto-fix-limit.yml[51-59]
🌐 GitHub states that write access to the repository is sufficient to trigger a workflow_dispatch workflow.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Manual dispatch inputs permit repository writers to invoke destructive organization-wide actions with an admin secret.

## Issue Context
GitHub permits users with repository write access to trigger `workflow_dispatch`; dry-run defaults to disabled and the supplied inputs directly control enforcement.

## Fix Focus Areas
- .github/workflows/cost-check.yml[10-32]
- .github/workflows/cost-check.yml[50-58]
- .github/workflows/auto-fix-limit.yml[10-32]
- .github/workflows/auto-fix-limit.yml[51-59]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

10. Exactly 100 commits force closure 🐞 Bug ≡ Correctness
Description
The commit request returns at most 100 entries, but the script treats >= 100 as proof that the PR
is over limit. A PR with exactly 100 commits and no failed gate attempts is therefore forcibly
closed, even though the comment says only PRs with more than 100 commits should receive that
treatment.
Code

governance/auto-fix-limit.sh[R140-142]

+    if [[ "${#SHAS[@]}" -ge 100 ]]; then
+      # >100 commit 的 agent PR 本身即失控形态——直接按超限处置(fail-closed 方向)
+      FAILS=$MAX_ATTEMPTS; FAIL_ROWS="(commit 数 ≥100,逐条省略——按失控处置)"
Relevance

●●● Strong

The condition contradicts the documented greater-than-100 behavior and deterministically mishandles
the API page boundary.

PR-#19

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The API request is capped with per_page=100, and the >= 100 condition assigns
FAILS=$MAX_ATTEMPTS without examining any check runs.

governance/auto-fix-limit.sh[133-143]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A PR containing exactly 100 commits is classified as an overflow and closed regardless of check results.

## Issue Context
Request a sentinel 101st commit or paginate before deciding that the PR exceeds 100 commits; process exactly 100 normally if that is the intended boundary.

## Fix Focus Areas
- governance/auto-fix-limit.sh[133-143]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Enforcement stops after 200 PRs 🐞 Bug ☼ Reliability
Description
Both breaker scanners explicitly limit each repository to 200 open PRs, despite promising to revoke
auto-merge from all open PRs. Any matching auto-merge request beyond that limit remains active
during a hard stop.
Code

governance/cost-check.sh[R161-163]

+    done < <("$GH" pr list --repo "$ORG/$r" --state open --limit 200 \
+      --json number,autoMergeRequest \
+      --jq '.[] | [.number, (if .autoMergeRequest != null then "1" else "0" end)] | @tsv' 2>/dev/null)
Relevance

●●● Strong

Recent accepted precedents explicitly accept findings about capped, unpaginated governance
enumeration missing resources.

PR-#19
PR-#49

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The hard-stop function uses gh pr list --limit 200; auto-fix-limit uses the same cap. Both scripts
describe their breaker behavior as applying to all open PRs.

governance/cost-check.sh[143-165]
governance/auto-fix-limit.sh[12-13]
governance/auto-fix-limit.sh[197-200]
PR-#19

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Organization-wide breaker enforcement scans only the first 200 open PRs per repository.

## Issue Context
Paginate until exhaustion in both scripts and treat any page failure as infrastructure failure before claiming complete revocation.

## Fix Focus Areas
- governance/cost-check.sh[143-165]
- governance/auto-fix-limit.sh[105-118]
- governance/auto-fix-limit.sh[197-200]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

12. cost-check uses broad token 📘 Rule violation ⛨ Security
Description
The workflow authenticates billing, organization-variable, issue, and cross-repository operations
directly with GOVERNANCE_TOKEN rather than obtaining single-repository cloudbrid-agent tokens
through scripts/gh-app-token.sh. This introduces the prohibited generic organization-wide
credential path.
Code

.github/workflows/cost-check.yml[R50-53]

+      - name: 用量检查与熔断(exit 1=触发告警/熔断 2=基础设施故障)
+        env:
+          GH_TOKEN: ${{ secrets.GOVERNANCE_TOKEN }}
+          COST_USAGE_MINUTES_OVERRIDE: ${{ inputs.usage_minutes_override }}
Relevance

● Weak

Recent precedent rejected replacing the existing governance token for cross-repository governance
API calls.

PR-#73

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2778539 prohibits generic organization-wide tokens and requires tokens generated
through scripts/gh-app-token.sh with single-repository scope. The new workflow sources GH_TOKEN
from secrets.GOVERNANCE_TOKEN, and its script uses that credential for organization-level and
multi-repository API operations.

Rule 2778539: Agent operations must use the cloudbrid-agent GitHub App identity via scripts/gh-app-token.sh with single-repo tokens
.github/workflows/cost-check.yml[50-58]
governance/cost-check.sh[143-173]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The cost-check workflow directly injects a generic organization-wide governance token into automation that performs GitHub API reads and writes.

## Issue Context
Authenticated repository operations must use short-lived `cloudbrid-agent` credentials generated by `scripts/gh-app-token.sh`, with one token scoped to each target repository. Separate or redesign organization-admin billing and variable operations so the workflow does not expose a broad token contrary to the authentication rule.

## Fix Focus Areas
- .github/workflows/cost-check.yml[50-58]
- governance/cost-check.sh[13-32]
- governance/cost-check.sh[143-173]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. auto-fix-limit uses broad token 📘 Rule violation ⛨ Security
Description
The workflow authenticates GitHub API operations directly with the generic GOVERNANCE_TOKEN secret
instead of minting a single-repository cloudbrid-agent token through scripts/gh-app-token.sh.
This bypasses the required identity, scope, and short-lived token controls.
Code

.github/workflows/auto-fix-limit.yml[R51-54]

+      - name: 扫描并执法(超限关 PR;exit 1=有超限 2=基础设施故障)
+        env:
+          GH_TOKEN: ${{ secrets.GOVERNANCE_TOKEN }}
+          AUTOFIX_MAX_ATTEMPTS: ${{ inputs.max_attempts }}
Relevance

● Weak

Recent precedent rejected requiring App-token replacement for governance API calls in this
repository.

PR-#73

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2778539 requires authenticated automation to invoke scripts/gh-app-token.sh and
restrict each token to one repository. The new workflow instead assigns GH_TOKEN directly from
secrets.GOVERNANCE_TOKEN, while the invoked script performs API mutations across managed
repositories.

Rule 2778539: Agent operations must use the cloudbrid-agent GitHub App identity via scripts/gh-app-token.sh with single-repo tokens
.github/workflows/auto-fix-limit.yml[51-59]
governance/auto-fix-limit.sh[15-33]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The auto-fix workflow supplies a broad governance token directly to GitHub API automation instead of obtaining short-lived, single-repository tokens through `scripts/gh-app-token.sh`.

## Issue Context
Repository mutations must run as the `cloudbrid-agent` GitHub App. Because the scanner targets multiple repositories, mint a separately scoped token for each repository and avoid exposing a generic organization-wide token to the script; redesign organization-level operations through a compliant narrowly scoped mechanism if necessary.

## Fix Focus Areas
- .github/workflows/auto-fix-limit.yml[51-59]
- governance/auto-fix-limit.sh[15-33]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


14. Check runs omit later pages 🐞 Bug ≡ Correctness
Description
The Checks API request reads only one page before selecting the latest same-name run. Commits with
enough check runs can have the relevant gate run outside that page, producing an absent or
incorrect conclusion and therefore a wrong attempt count.
Code

governance/auto-fix-limit.sh[R148-149]

+        if ! ROW=$("$GH" api "repos/$ORG/$repo/commits/$sha/check-runs" \
+          --jq "([.check_runs[] | select(.name == \"$CHECK_NAME\")] | sort_by(.id) | last | [.conclusion // \"none\", (.html_url // \"-\")]) // [\"absent\", \"-\"] | @tsv" 2>/dev/null); then
Relevance

● Weak

A same-day precedent rejected requiring pagination for a per_page=100 check-runs query in governance
scanning.

PR-#111

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The check-runs request has neither pagination nor aggregation, yet enforcement selects the latest
run solely from that response. Prior accepted reviews in this repository establish that one-page
governance API scans are incomplete and must fail closed.

governance/auto-fix-limit.sh[145-160]
PR-#19

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Attempt counting selects from only the first page of check runs for each commit.

## Issue Context
Retrieve every page, validate all responses, then choose the newest matching check run from the aggregated result.

## Fix Focus Areas
- governance/auto-fix-limit.sh[145-160]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 8 rules
✅ Web pages:
  +7 more
Review mode: ⚖️ Balanced: Downgraded extended -> standard: change is below the extended eligibility bar (hunks 9/18, lines 667/200; both must reach the floor). Router rationale: This is a high-blast-radius governance change with two substantial shell enforcement paths, multiple API mutation flows, workflow permissions/concurrency, fail-closed handling, and many independent edge cases where redundant review is materially valuable.

Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/gate.yml
Comment on lines +10 to +13
workflow_dispatch:
# 注入入口(T2 注入式测试:79%/85%/100% 全场景不依赖真实超支):空=真实 API/真源
inputs:
usage_minutes_override:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. Dispatch grants org-wide mutations 🐞 Bug ⛨ Security

Any repository writer who can manually dispatch these workflows can supply hard-stop or all-PR
inputs that execute with the organization-admin GOVERNANCE_TOKEN. For example,
usage_minutes_override can trip the organization breaker, while all_prs=true and
max_attempts=0 can close every listed open PR without requiring dry-run.
Agent Prompt
## Issue description
Manual dispatch inputs permit repository writers to invoke destructive organization-wide actions with an admin secret.

## Issue Context
GitHub permits users with repository write access to trigger `workflow_dispatch`; dry-run defaults to disabled and the supplied inputs directly control enforcement.

## Fix Focus Areas
- .github/workflows/cost-check.yml[10-32]
- .github/workflows/cost-check.yml[50-58]
- .github/workflows/auto-fix-limit.yml[10-32]
- .github/workflows/auto-fix-limit.yml[51-59]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread governance/cost-check.sh
Comment on lines +244 to +248
elif [[ $BREAKER_SET -eq 0 ]]; then
# 复位确认:变量未置位 + 用量 < 硬停档 + P0 issue 开着 → 自动关闭(人工复位已发生且留痕在评论)
for row in $(gov_open_issues cost-circuit-breaker | cut -f1); do
mutate "$GH" issue close "$row" --repo "$GOV_REPO" --comment \
"复位确认:$CB_VARIABLE 已未置位且用量 ${PCT_MIN}% < ${AM_STOP}%——全流程恢复(agent 派发/automerge 前置检查放行)。自动关闭。" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

5. New breaker p0 closes immediately 🐞 Bug ≡ Correctness

BREAKER_SET is read before set_breaker and never updated, so the first hard-stop run enters the
BREAKER_SET == 0 reset branch after creating the P0 and immediately closes it. The same branch can
also close breaker issues when the variable read failed and its state is unknown.
Agent Prompt
## Issue description
The first breaker activation immediately triggers the reset cleanup using stale pre-activation state.

## Issue Context
`BREAKER_SET` is initialized from the variable before hard-stop processing. `set_breaker` does not update it, and the reset branch does not require both metrics to be below hard-stop or the state read to have succeeded.

## Fix Focus Areas
- governance/cost-check.sh[123-132]
- governance/cost-check.sh[176-203]
- governance/cost-check.sh[234-251]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread governance/cost-check.sh
Comment on lines +170 to +172
mutate "$GH" api -X POST "orgs/$ORG/actions/variables/$CB_VARIABLE" \
-f name="$CB_VARIABLE" -F value=true -f visibility=all >/dev/null 2>&1 \
|| infra "org 变量 $CB_VARIABLE 置位失败"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

6. Breaker creation uses wrong endpoint 🐞 Bug ≡ Correctness

When the variable does not yet exist, set_breaker POSTs to the item endpoint ending in
/$CB_VARIABLE instead of the organization variable collection. The fallback therefore fails and
the hard-stop can leave AUTO_MERGE_DISABLED unset.
Agent Prompt
## Issue description
The breaker fallback cannot create a missing organization variable because it POSTs to the wrong REST path.

## Issue Context
PATCH uses the item path, but creation must POST to `/orgs/{org}/actions/variables` with name, value, and visibility in the request body.

## Fix Focus Areas
- governance/cost-check.sh[167-173]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread governance/cost-check.sh
Comment on lines +125 to +131
VERR=$("$GH" api "orgs/$ORG/actions/variables/$CB_VARIABLE" --jq .value 2>&1) || true
if [[ "$VERR" == *"true"* && "$VERR" != *"Not Found"* ]]; then
BREAKER_SET=1
elif grep -q "Not Found" <<<"$VERR"; then
:
else
infra "org 变量 $CB_VARIABLE 读取失败(非 404)——熔断状态未知"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

7. False breaker looks unavailable 🐞 Bug ≡ Correctness

Both scripts accept only a value containing true or a 404 response, so a valid manually reset
value of false is recorded as an infrastructure failure. Cost checks then exit 2 instead of
confirming reset, and auto-fix scans also remain permanently red after the documented PATCH-to-false
reset.
Agent Prompt
## Issue description
A valid organization variable value of `false` is treated as an API failure.

## Issue Context
The documented reset procedure explicitly PATCHes the variable to false. Parse the command status separately and accept exact true and false values; handle 404 from a reliable status check rather than matching error text.

## Fix Focus Areas
- governance/cost-check.sh[123-132]
- governance/auto-fix-limit.sh[92-103]
- governance/cost-check.sh[191-194]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +172 to +176
mutate "$GH" pr edit "$num" --repo "$ORG/$repo" --add-label "$AF_EXHAUSTED_LABEL" >/dev/null 2>&1 || true
mutate "$GH" pr close "$num" --repo "$ORG/$repo" --comment \
"auto-fix 上限触发(ADR-0040):$CHECK_NAME 失败 $FAILS 次 ≥ 上限 $MAX_ATTEMPTS。自动关闭并不再重试;失败历史见 .github 仓说明 issue。人工续作:修复后新开 PR(勿 reopen 本 PR)。" \
>/dev/null 2>&1 || true # 竞态(本轮已被关/合)不算 infra;issue 通道已有去重兜底
EXHAUSTED_CLOSED=$((EXHAUSTED_CLOSED+1))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

9. Failed closures count as enforced 🐞 Bug ☼ Reliability

Auto-merge removal, label application, and PR closure failures are swallowed, after which
EXHAUSTED_CLOSED is incremented unconditionally. The workflow can therefore report that an
over-limit PR was closed and return the over-limit status while the PR remains open and mergeable.
Agent Prompt
## Issue description
Failed PR enforcement mutations are reported as successful closures.

## Issue Context
Only benign, explicitly identified races should be tolerated. Other failures must increment infrastructure errors, and the closure counter should update only after closure is confirmed.

## Fix Focus Areas
- governance/auto-fix-limit.sh[167-177]
- governance/auto-fix-limit.sh[203-218]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +148 to +149
if ! ROW=$("$GH" api "repos/$ORG/$repo/commits/$sha/check-runs" \
--jq "([.check_runs[] | select(.name == \"$CHECK_NAME\")] | sort_by(.id) | last | [.conclusion // \"none\", (.html_url // \"-\")]) // [\"absent\", \"-\"] | @tsv" 2>/dev/null); then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

10. Missing checks escape counting 🐞 Bug ≡ Correctness

When a commit has no matching check run, last is null but the expression wraps it as `[null,
"-"], so the outer // ["absent", "-"]` fallback never runs. Historical commits with no gate check
are consequently not recognized as absent and evade the script's intended fail-closed attempt
count.
Agent Prompt
## Issue description
An empty matching check-run list does not produce the expected `absent` conclusion.

## Issue Context
Apply the null fallback to `last` before reading `.conclusion`, or branch explicitly on an empty result, and test commits with zero matching runs.

## Fix Focus Areas
- governance/auto-fix-limit.sh[145-160]
- governance/policy/automation-limits.yaml[21-23]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread governance/cost-check.sh
Comment on lines +158 to +163
if mutate "$GH" api -X DELETE "repos/$ORG/$r/pulls/$n/auto-merge" >/dev/null 2>&1; then
act "熔断执法: 撤销 $r#$n 的 auto-merge"
fi
done < <("$GH" pr list --repo "$ORG/$r" --state open --limit 200 \
--json number,autoMergeRequest \
--jq '.[] | [.number, (if .autoMergeRequest != null then "1" else "0" end)] | @tsv' 2>/dev/null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

11. Breaker removal failures are silent 🐞 Bug ☼ Reliability

strip_all_automerge neither checks failure of each repository's PR-list process substitution nor
records failed DELETE requests. A hard-stop can therefore leave existing auto-merges enabled while
the P0 body falsely states that all were removed.
Agent Prompt
## Issue description
The hard-stop silently ignores failures while enumerating PRs or deleting auto-merge requests.

## Issue Context
Fetch and validate each PR list outside process substitution, record API failures through `infra`, and verify or fail on each DELETE before claiming complete enforcement.

## Fix Focus Areas
- governance/cost-check.sh[143-165]
- governance/cost-check.sh[176-189]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +140 to +142
if [[ "${#SHAS[@]}" -ge 100 ]]; then
# >100 commit 的 agent PR 本身即失控形态——直接按超限处置(fail-closed 方向)
FAILS=$MAX_ATTEMPTS; FAIL_ROWS="(commit 数 ≥100,逐条省略——按失控处置)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

12. Exactly 100 commits force closure 🐞 Bug ≡ Correctness

The commit request returns at most 100 entries, but the script treats >= 100 as proof that the PR
is over limit. A PR with exactly 100 commits and no failed gate attempts is therefore forcibly
closed, even though the comment says only PRs with more than 100 commits should receive that
treatment.
Agent Prompt
## Issue description
A PR containing exactly 100 commits is classified as an overflow and closed regardless of check results.

## Issue Context
Request a sentinel 101st commit or paginate before deciding that the PR exceeds 100 commits; process exactly 100 normally if that is the intended boundary.

## Fix Focus Areas
- governance/auto-fix-limit.sh[133-143]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread governance/cost-check.sh
Comment on lines +161 to +163
done < <("$GH" pr list --repo "$ORG/$r" --state open --limit 200 \
--json number,autoMergeRequest \
--jq '.[] | [.number, (if .autoMergeRequest != null then "1" else "0" end)] | @tsv' 2>/dev/null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

14. Enforcement stops after 200 prs 🐞 Bug ☼ Reliability

Both breaker scanners explicitly limit each repository to 200 open PRs, despite promising to revoke
auto-merge from all open PRs. Any matching auto-merge request beyond that limit remains active
during a hard stop.
Agent Prompt
## Issue description
Organization-wide breaker enforcement scans only the first 200 open PRs per repository.

## Issue Context
Paginate until exhaustion in both scripts and treat any page failure as infrastructure failure before claiming complete revocation.

## Fix Focus Areas
- governance/cost-check.sh[143-165]
- governance/auto-fix-limit.sh[105-118]
- governance/auto-fix-limit.sh[197-200]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@randypanding
randypanding merged commit 7ed737c into main Aug 20, 2026
7 checks passed
@randypanding
randypanding deleted the p2-8/autofix-limit-cost-breaker branch August 20, 2026 06:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant