Skip to content

feat(governance): cost-check LLM 预算通道(W2-C3 .github#216,ADR-0062) - #231

Merged
randypanding merged 1 commit into
mainfrom
w2c3-cost-llm-channel
Aug 21, 2026
Merged

feat(governance): cost-check LLM 预算通道(W2-C3 .github#216,ADR-0062)#231
randypanding merged 1 commit into
mainfrom
w2c3-cost-llm-channel

Conversation

@randypanding

Copy link
Copy Markdown
Contributor

动机

ADR-0040 决策 6 把 LLM token 预算通道标为 pending(阈值已声明、数据源缺失)。W2-C3(.github#216,ADR-0062)落地数据消费端:cost-check 每小时读取 CI-Workflows 计量账本(metering-ledger 分支,按 invoke 聚合的 JSONL hash 链),经归账引擎按角色档(spec-author/probe/conductor…)汇总当月 token,进入既有 warn 80% / hardstop 100% 熔断分级——替代 pending 声明。数据生产端(wrapper 完整版 + ledger 同步管道)在配套 CI-Workflows PR #54

变更清单

  • governance/policy/automation-limits.yamlcost.llm_tokens.data_source: pending → ciw-metering + metering: 定位三件套(repo/branch/code_path)——复用本段现有结构,不新建配置面;pending 保留为回滚值(脚本仍识别)
  • governance/cost-check.sh
    • policy 读取扩展 LT_M_REPO/LT_M_BRANCH/LT_M_CODE(data_source=ciw-metering 时缺失即 FATAL,fail-closed)
    • 新增 llm_channel_account()@w2c3-llm-channel 标记对,供测试提取):拉 metering-ledger 分支 tarball → metering.py aggregate --since <月初> 按角色档归账(归账前先验链)。三态输出:DATA(当月 token + 角色档 json,进 PCT_TOK 与告警/硬停 issue 正文)、ZERO(账本分支未建/空账本=零用量声明,非 infra)、INFRA(tarball 失败/链断/引擎缺失——不可信数据不入账,exit 2 fail-closed,不盲熔断不静默归零)
    • 顺带修复(同执行路径潜在缺陷,本地 e2e 实测暴露):org 变量读取成功且 value=false(人工复位后的常态)此前落入"非 404 读取失败"INFRA——复位确认路径会每小时误红;现按退出码判读(0=读到值终态 / 404=未建 / 其余=infra)。告警档 ACT 行此前只报 Actions 百分比(token 单独触发时误导),改为双指标
  • .github/workflows/cost-check.yml:sparse checkout CI-Workflows pipeline/metering(归账引擎;该仓公开、自身 CI+org-gate 把关,跟 main 消费——升级破坏会让本检查显式 INFRA 红而非静默)+ COST_LLM_METERING_PY env
  • governance/tests/test-cost-llm-channel.sh:标记对提取被测函数 + 桩 metering.py(与真实 aggregate 退出码契约一致:0/2/3)+ 桩 gh,六形态断言;gate.yml 的 test-*.sh 通配自动纳入 CI

AC 映射(.github#216 AC-4 消费端)

  • AC-4:Given 一周计量数据(真实 metering 账本:spec-author 1 次 invoke 165 token,hash 链验通),When cost-check 读取,Then 按角色档归账并进预算分级。本地 e2e(COST_DRY_RUN=1 + 真实 CI-Workflows metering.py):
OK    LLM token(当月归账): 165 / 250 = 66.0%(角色档 {"spec-author": {..., "total_tokens": 165}}})→ RC=0(未达档)
OK    LLM token(当月归账): 165 / 200 = 82.5%(…)→ ACT 告警档触发: Actions 0.5% / LLM token 82.5%(阈值 80%)→ RC=1
ACT   硬停档触发(Actions=0.5% LLM=100.0%)——置 AUTO_MERGE_DISABLED + 撤 auto-merge + P0 → RC=1(quota=165)

三档(绿/告警/硬停)零 INFRA;角色档分布进告警 issue 正文(W_BODY/P0_BODY)。

单测(bash governance/tests/test-cost-llm-channel.sh):

PASS  DATA 形态:当月 token=207 + 角色档 json 透传
PASS  空账本 → ZERO(零用量声明)
PASS  链断 → INFRA(归账拒绝,fail-closed)
PASS  引擎缺失 → INFRA(sparse checkout 缺失可见)
PASS  账本分支未建 → ZERO(尚无 LLM 调用落账)
PASS  tarball 失败+分支在 → INFRA(fail-closed 出口 2 的判定输入)
test-cost-llm-channel PASS(6/6)

测试方法

  • 单测:bash governance/tests/test-cost-llm-channel.sh(零网络零真实 gh,gate 自动跑)
  • e2e(本地已跑通):真实 wrapper 产账本 → COST_LLM_METERING_DIR/COST_LLM_METERING_PY 注入 + 桩 gh(billing/变量/issue)→ 三档分级与角色档归账断言(输出见上)
  • 注:test-ir0002.sh 在本地 Windows 环境存在 2 处预存失败(origin/main 干净工作树同样失败,与本 PR 无关——本 PR 未触碰 drift-check.sh;CI ubuntu 上为绿)

风险与回滚

  • 回滚:policy data_source 改回 pending 即回到 ADR-0040 决策 6 声明形态(脚本分支保留);摘除通道需同步摘 ADR-0062 数据源声明
  • CI-Workflows metering-ledger 分支不存在时按"零用量"声明(ZERO),不误报 infra;首条账本由 CI-Workflows PR fix: adr-required 换行文件名绕过截断检测——jq 结构化计数(ADR-0016) #54 的 llm-connectivity 同步产生
  • 两仓 PR 互不阻塞:本 PR 先合亦绿(数据源为 ZERO);org 变量读取修复为独立可审小改(复位确认路径的正确性修复)

Card: #216

…github#216,ADR-0062)

- policy llm_tokens:data_source pending→ciw-metering + metering 定位三件套
  (repo/branch/code_path,复用现有结构不新建配置面;pending 保留为回滚值)
- cost-check.sh:新增 llm_channel_account 通道(标记对可提取单测)——拉
  metering-ledger 分支 tarball → CI-Workflows metering.py aggregate 按角色档
  归账(先验链后归账);DATA/ZERO/INFRA 三态:链断=INFRA fail-closed(不可信
  数据不入账不盲熔断)、分支未建/空账本=ZERO 零用量声明;告警/硬停档 issue
  正文带当月归账与角色档分布
- 顺带修复(同路径潜在缺陷,e2e 实测暴露):org 变量读取成功且 value=false
  (人工复位后常态)曾被判'非 404 读取失败'落 INFRA——复位确认路径每小时误红;
  现按退出码判读。告警档 ACT 行此前只报 Actions 百分比(token 单独触发时误导)
- cost-check.yml:sparse checkout CI-Workflows pipeline/metering(归账引擎,
  该仓公开且自身 CI+org-gate 把关;破坏=显式 INFRA 红)
- governance/tests/test-cost-llm-channel.sh:标记对提取 + 桩 metering/桩 gh 六
  形态单测(gate.yml 自动纳入);本地 e2e:真实账本 165 token 走通 66%绿/
  82.5%告警/100%硬停三档
Copilot AI lite review requested due to automatic review settings August 21, 2026 18:07
@coderabbitai

coderabbitai Bot commented Aug 21, 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: 4 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: e82e9b72-50eb-4f97-acbf-3690859626fa

📥 Commits

Reviewing files that changed from the base of the PR and between b0e87f3 and fd4aa0b.

📒 Files selected for processing (4)
  • .github/workflows/cost-check.yml
  • governance/cost-check.sh
  • governance/policy/automation-limits.yaml
  • governance/tests/test-cost-llm-channel.sh

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

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

feat(governance): account LLM token budget via CIW metering ledger

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🐞 Bug fix 🕐 40+ Minutes

Grey Divider

AI Description

• Switch LLM token budget source to CI-Workflows metering-ledger (role-based monthly accounting).
• Add fail-closed DATA/ZERO/INFRA accounting path and improve breaker variable read semantics.
• Wire workflow sparse-checkout for metering engine and add offline contract tests.
Diagram

graph TD
  A[".github/workflows/cost-check.yml"] --> B["governance/cost-check.sh"] --> F{"Budget thresholds"} --> G["GitHub Issues + org variable"]
  B --> C["governance/policy/automation-limits.yaml"]
  B --> D["GitHub API (tarball/branch)"] --> E["CI-Workflows metering-ledger"] --> H["metering.py aggregate"] --> F
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use centralized usage API (llm-gateway) once available
  • ➕ Simpler runtime: no tarball fetch or local aggregation
  • ➕ Single source of truth; less coupling to CI-Workflows internals
  • ➖ Blocked until usage endpoint + auth/tenancy is production-ready (ADR-0040 pending)
  • ➖ Harder to validate provenance vs hash-chained ledger without extra guarantees
2. Publish aggregated monthly counters to a durable store (artifact/metrics)
  • ➕ cost-check consumes a small stable JSON instead of raw ledger
  • ➕ Aggregation can be computed once and cached, reducing runtime and failure modes
  • ➖ Adds new producer pipeline + retention/permissions surface
  • ➖ Still needs integrity model; may reintroduce “silent zero” risks if not carefully designed
3. Vendor a minimal aggregator into this repo
  • ➕ Avoids cross-repo sparse checkout dependency and potential breakage
  • ➕ Reviewers control versioning within the governance repo
  • ➖ Duplicates CI-Workflows logic and increases maintenance burden
  • ➖ Risk of drift between producer and consumer accounting semantics

Recommendation: Given ADR-0062’s ledger+hash-chain design and the need to unblock the budget channel now, the PR’s approach (consume CI-Workflows metering-ledger + run the shared metering.py aggregator) is the best near-term option. The explicit DATA/ZERO/INFRA tri-state and fail-closed behavior appropriately avoid both blind shutdowns and silent zeroing; revisit a centralized usage API once llm-gateway usage is available.

Files changed (4) +234 / -14

Enhancement (1) +97 / -10
cost-check.shImplement ciw-metering LLM token accounting with DATA/ZERO/INFRA contract +97/-10

Implement ciw-metering LLM token accounting with DATA/ZERO/INFRA contract

• Extends policy parsing to include metering repo/branch/code_path and enforces presence when data_source=ciw-metering. Introduces llm_channel_account() that downloads the ledger tarball, runs metering.py aggregate, and returns DATA/ZERO/INFRA outcomes; integrates role-based monthly totals into warning/hardstop messaging. Fixes circuit-breaker variable reads so an existing value=false is treated as a successful read (not INFRA).

governance/cost-check.sh

Tests (1) +116 / -0
test-cost-llm-channel.shAdd offline unit tests for LLM channel accounting contract +116/-0

Add offline unit tests for LLM channel accounting contract

• Adds a new bash test that extracts llm_channel_account() by marker-pair from cost-check.sh and validates DATA/ZERO/INFRA behaviors using stub gh and stub metering.py. Covers six scenarios including empty ledger, broken hash-chain, missing engine, missing branch, and tarball failures.

governance/tests/test-cost-llm-channel.sh

Other (2) +21 / -4
cost-check.ymlSparse-checkout CI-Workflows metering engine and pass metering.py path +11/-1

Sparse-checkout CI-Workflows metering engine and pass metering.py path

• Adds a second checkout step to fetch only CI-Workflows pipeline/metering into the workspace. Exposes the metering.py path to cost-check.sh via COST_LLM_METERING_PY and updates the override input description to reflect precedence over ledger data.

.github/workflows/cost-check.yml

automation-limits.yamlSwitch LLM token data_source from pending to ciw-metering with locator config +10/-3

Switch LLM token data_source from pending to ciw-metering with locator config

• Moves llm_tokens.data_source to ciw-metering and adds metering locator fields (repo/branch/code_path) for the consumer. Documents fail-closed accounting semantics and keeps pending as an explicit rollback option.

governance/policy/automation-limits.yaml

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Action required

1. API失败被判ZERO 🐞 Bug ☼ Reliability
Description
llm_channel_account() 在 tarball 拉取失败后,用 gh api .../branches/... 的成功与否来判断分支是否存在,但对 branches 查询的非
404 失败(限流/网络/权限)会被当作“分支未建”并输出 ZERO,导致用量被静默归零。该路径违背本 PR 声明的 fail-closed(不可信数据不入账),会造成预算检查漏报/假绿。
Code

governance/cost-check.sh[R155-158]

+    if ! "$GH" api "repos/$LT_M_REPO/tarball/$LT_M_BRANCH" >"$led.tar.gz" 2>/dev/null; then
+      if "$GH" api "repos/$LT_M_REPO/branches/$LT_M_BRANCH" >/dev/null 2>&1; then
+        printf 'INFRA\tmetering 账本分支存在但 tarball 拉取失败(%s@%s)\n' "$LT_M_REPO" "$LT_M_BRANCH"
+      else
Relevance

●●● Strong

Accepted precedent requires API responses and failures be distinguished; unavailable verification
must fail closed.

PR-#19

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
代码在 tarball 拉取失败时仅通过 branches API 调用是否成功来分支,但 branches API 的失败并不等价于 404 未建;在失败分支里直接输出 ZERO
会把不可判定场景归零。该模式与仓库既往“API/解析失败必须 fail-closed”的修复经验一致。

governance/cost-check.sh[153-161]
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
`llm_channel_account()` 在 tarball 拉取失败后,用 `gh api repos/.../branches/...` 作为“分支是否存在”的判据,但当前写法把 branches 查询的任何失败都当成“分支不存在”,从而输出 `ZERO`。

这会在 GitHub API 限流/网络抖动/权限异常等场景下把未知状态 fail-open 成 0 用量,导致 LLM 通道预算检查漏报。

### Issue Context
- 该通道在 PR 描述中强调:tarball 失败/链断/引擎缺失应为 INFRA(exit 2 fail-closed)。
- 但当前分支存在性探测没有区分 404 vs 其他错误。

### Fix Focus Areas
- governance/cost-check.sh[155-160]

### Suggested change
- 捕获 branches 查询的 stdout/stderr 与退出码:
 - rc==0 => 分支存在(tarball 失败应为 INFRA)
 - rc!=0 且输出包含 Not Found/404 => 分支不存在(ZERO)
 - 其他任何失败 => INFRA(“无法确认分支是否存在/不可判定”,fail-closed)
- 同时建议不要吞掉 tarball 拉取失败的错误信息(至少截断输出用于诊断)。

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


2. 空账本被判INFRA 🐞 Bug ≡ Correctness
Description
llm_channel_account() 使用 tar 的 wildcard 解包 records-*.jsonl,当分支已建但尚无任何 records 文件时,tar 会因“无匹配成员/Not
found in archive”失败并被当作 INFRA。该场景在 PR 设计中应归类为 ZERO(零用量声明),否则会导致通道上线初期或月初窗口持续误报基础设施故障。
Code

governance/cost-check.sh[R163-166]

+    if ! tar -xzf "$led.tar.gz" -C "$led" --strip-components=1 --wildcards \
+         "*/$LT_M_CODE/records-*.jsonl" 2>/dev/null; then
+      printf 'INFRA\tmetering 账本 tar 解包失败(strip-components=1 + %s/records-*.jsonl)\n' "$LT_M_CODE"
+      return 0
Relevance

●●● Strong

Finding identifies a direct contradiction between documented empty-ledger ZERO behavior and
unconditional tar extraction failure handling.

PR-#195

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
新逻辑在 tar 阶段把解包失败一律当 INFRA,但后续 aggregate rc=2 才是脚本定义的“无周片=ZERO”;因此“分支存在但无 records 文件”的空账本会被前置误判为
INFRA。

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

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

### Issue description
当前 tar 解包逻辑要求归档中必须存在 `*/$LT_M_CODE/records-*.jsonl`,否则直接返回 INFRA。若 metering-ledger 分支已创建但尚未产生任何 records 文件(空账本),这会把“零用量”误判为 INFRA。

### Issue Context
脚本后续已把 `metering.py aggregate` 的 rc=2 视为 ZERO(无周片/零用量)。但在真实数据源路径下,tar 阶段就可能因无 records 文件而提前退出,导致永远到不了 aggregate 的 rc=2 分支。

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

### Suggested change
- 在 tar 前先 `tar -tzf` 列出并检查是否存在匹配 records:
 - 若无匹配:不要报 INFRA,直接让 ledger 目录保持为空并继续执行 aggregate,让其以 rc=2 输出 ZERO;或直接输出 ZERO(带说明“分支已建但无 records 文件”)。
 - 若有匹配但解包失败:仍按 INFRA 处理。
- 或者捕获 tar 的错误输出,仅当错误为“Not found in archive/无匹配”时转 ZERO,其余错误转 INFRA。

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



Remediation recommended

3. Missing Card: metadata line 📘 Rule violation § Compliance
Description
The PR description body does not include the required single Card: <owner>/<repo>#<n> line, so
downstream tooling cannot reliably associate this change with its tracking item.
Code

.github/workflows/cost-check.yml[R52-55]

+      # 归账引擎 sparse checkout(W2-C3 .github#216,ADR-0062):CI-Workflows 是公开仓且
+      # 自身有 CI+org-gate(C1),跟 main 消费;升级若破坏 aggregate 会让本检查显式
+      # INFRA 红(fail-closed 可见),不会被静默吞掉
+      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Relevance

●●● Strong

Explicit active compliance rule requires exactly one Card line; PR description visibly lacks it.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance rule 2825427 requires exactly one Card: metadata line in the PR description body; the
provided PR description text contains no line starting with Card:.

Rule 2825427: Require PR description to include a card metadata line

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

## Issue description
PR description is missing the required card metadata line in the exact format `Card: <owner>/<repo>#<n>`.

## Issue Context
This PR already references `.github#216` / `W2-C3 (.github#216)` in text, but it must be provided as a dedicated `Card:` line (e.g., `Card: Cloudbird-Software/.github#216`).

## Fix Focus Areas
- .github/workflows/cost-check.yml[52-55]

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


4. 外部仓checkout未固定ref 🐞 Bug ⛨ Security
Description
workflow 直接 checkout Cloudbird-Software/CI-Workflows 默认分支来获取 metering.py,未固定到特定
commit/tag,导致成本熔断行为随外部仓 HEAD 变化而漂移,存在供应链与可重复性风险。若外部仓被误推/破坏,预算检查可能被绕过或频繁误报。
Code

.github/workflows/cost-check.yml[R56-60]

+        with:
+          repository: Cloudbird-Software/CI-Workflows
+          sparse-checkout: pipeline/metering
+          path: ciw-metering
+          persist-credentials: false
Relevance

●●● Strong

Recent accepted precedent requires pinning mutable external workflow references to reviewed commit
SHAs.

PR-#73

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
新增 workflow 步骤只指定了 repository/sparse-checkout/path,没有 ref,意味着 metering.py 来源随外部仓变化。

.github/workflows/cost-check.yml[52-60]

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

### Issue description
`actions/checkout` 拉取外部仓 `Cloudbird-Software/CI-Workflows` 时未指定 `ref`,会默认跟随外部仓默认分支最新提交。预算归账/验链的执行逻辑因此不可复现且易受外部变更影响。

### Issue Context
该 PR 将 metering.py 作为“预算通道”的关键执行组件,且 cost-check 拥有治理 token(GH_TOKEN)。即使外部仓有 CI,也仍建议对关键治理逻辑固定版本并通过显式升级流程更新。

### Fix Focus Areas
- .github/workflows/cost-check.yml[55-60]

### Suggested change
- 在 checkout CI-Workflows 步骤增加 `ref: <pinned sha or tag>`(推荐 sha),并在需要升级时通过 PR 明确更新该 ref。
- 若确实要跟随 main,也建议至少在 policy/脚本中记录期望版本并加 drift-check 校验(否则治理行为不可追溯)。

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


5. 告警阈值字段混用 🐞 Bug ≡ Correctness
Description
告警档标题与日志同时展示 Actions 与 LLM token,但括号内阈值仍使用 AM_WARN(Actions 阈值)而非 LT_WARN(LLM
阈值),当两者配置不一致时会误导值守与审计。该问题会导致告警信息描述与实际判定条件不一致。
Code

governance/cost-check.sh[R319-320]

+  W_TITLE="成本告警($YEAR-$MONTH):Actions 分钟 ${PCT_MIN}% / LLM token ${PCT_TOK:--}%(阈值 ${AM_WARN}%)"
  W_EXISTING=$(gov_open_issues cost-budget-warning | grep -m1 "$YEAR-$MONTH" | cut -f1)
-  W_BODY="额度告警(ADR-0040,$(date -u +%FT%TZ)):Actions 分钟($YEAR-$MONTH)$USED_MIN / $AM_QUOTA = ${PCT_MIN}%,达 ${AM_WARN}% 告警档——未硬停;达 ${AM_STOP}% 将置 \`$CB_VARIABLE\` 熔断并撤全部 auto-merge。"
Relevance

●●● Strong

Deterministic metric-field mismatch makes alert text misleading when Actions and LLM thresholds
differ.

PR-#195

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
告警标题与 ACT 日志都引用了 ${AM_WARN}% 作为阈值,但 LLM 的 warn_pct 实际来自 ${LT_WARN},两者并非同一配置源。

governance/cost-check.sh[316-333]

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

### Issue description
`W_TITLE` / `act` 文案在同时展示 Actions 与 LLM 的百分比时,仍把“阈值”字段写成 `${AM_WARN}%`。如果未来 Actions 与 LLM 的 warn_pct 不同,会出现:判定用 LT_WARN,但文案展示 AM_WARN 的不一致。

### Issue Context
脚本已分别计算 `WARN_MIN` vs `WARN_TOK`(LT_WARN),因此文案应与之对齐。

### Fix Focus Areas
- governance/cost-check.sh[318-333]

### Suggested change
- 标题/日志中明确区分阈值:例如 `(阈值 Actions ${AM_WARN}% / LLM ${LT_WARN}%)`。
- 或者将“阈值”文字改成更中性的描述,并在正文里分别列出两套阈值(当前正文已部分包含)。

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


Grey Divider

Context sources
✅ Compliance rules (platform): 16 rules
Review mode: ⚖️ Balanced: Downgraded extended -> standard: change is below the extended eligibility bar (hunks 12/18, lines 248/200; both must reach the floor). Router rationale: 该 PR 修改预算熔断运行时逻辑、CI 工作流与策略配置,新增跨多个数据获取/归账/失败闭锁路径,并涉及告警、硬停和权限相关副作用;12 个独立 hunk 存在较高的多缺陷漏检密度。

Grey Divider

Tip of the day
💡 Did you know, you can tweak Display preferences with a live preview to see your comment before it ships

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +52 to +55
# 归账引擎 sparse checkout(W2-C3 .github#216,ADR-0062):CI-Workflows 是公开仓且
# 自身有 CI+org-gate(C1),跟 main 消费;升级若破坏 aggregate 会让本检查显式
# INFRA 红(fail-closed 可见),不会被静默吞掉
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.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.

Remediation recommended

1. Missing card: metadata line 📘 Rule violation § Compliance

The PR description body does not include the required single Card: <owner>/<repo>#<n> line, so
downstream tooling cannot reliably associate this change with its tracking item.
Agent Prompt
## Issue description
PR description is missing the required card metadata line in the exact format `Card: <owner>/<repo>#<n>`.

## Issue Context
This PR already references `.github#216` / `W2-C3 (.github#216)` in text, but it must be provided as a dedicated `Card:` line (e.g., `Card: Cloudbird-Software/.github#216`).

## Fix Focus Areas
- .github/workflows/cost-check.yml[52-55]

ⓘ 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 +155 to +158
if ! "$GH" api "repos/$LT_M_REPO/tarball/$LT_M_BRANCH" >"$led.tar.gz" 2>/dev/null; then
if "$GH" api "repos/$LT_M_REPO/branches/$LT_M_BRANCH" >/dev/null 2>&1; then
printf 'INFRA\tmetering 账本分支存在但 tarball 拉取失败(%s@%s)\n' "$LT_M_REPO" "$LT_M_BRANCH"
else

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

2. Api失败被判zero 🐞 Bug ☼ Reliability

llm_channel_account() 在 tarball 拉取失败后,用 gh api .../branches/... 的成功与否来判断分支是否存在,但对 branches 查询的非
404 失败(限流/网络/权限)会被当作“分支未建”并输出 ZERO,导致用量被静默归零。该路径违背本 PR 声明的 fail-closed(不可信数据不入账),会造成预算检查漏报/假绿。
Agent Prompt
### Issue description
`llm_channel_account()` 在 tarball 拉取失败后,用 `gh api repos/.../branches/...` 作为“分支是否存在”的判据,但当前写法把 branches 查询的任何失败都当成“分支不存在”,从而输出 `ZERO`。

这会在 GitHub API 限流/网络抖动/权限异常等场景下把未知状态 fail-open 成 0 用量,导致 LLM 通道预算检查漏报。

### Issue Context
- 该通道在 PR 描述中强调:tarball 失败/链断/引擎缺失应为 INFRA(exit 2 fail-closed)。
- 但当前分支存在性探测没有区分 404 vs 其他错误。

### Fix Focus Areas
- governance/cost-check.sh[155-160]

### Suggested change
- 捕获 branches 查询的 stdout/stderr 与退出码:
  - rc==0 => 分支存在(tarball 失败应为 INFRA)
  - rc!=0 且输出包含 Not Found/404 => 分支不存在(ZERO)
  - 其他任何失败 => INFRA(“无法确认分支是否存在/不可判定”,fail-closed)
- 同时建议不要吞掉 tarball 拉取失败的错误信息(至少截断输出用于诊断)。

ⓘ 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 +163 to +166
if ! tar -xzf "$led.tar.gz" -C "$led" --strip-components=1 --wildcards \
"*/$LT_M_CODE/records-*.jsonl" 2>/dev/null; then
printf 'INFRA\tmetering 账本 tar 解包失败(strip-components=1 + %s/records-*.jsonl)\n' "$LT_M_CODE"
return 0

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

3. 空账本被判infra 🐞 Bug ≡ Correctness

llm_channel_account() 使用 tar 的 wildcard 解包 records-*.jsonl,当分支已建但尚无任何 records 文件时,tar 会因“无匹配成员/Not
found in archive”失败并被当作 INFRA。该场景在 PR 设计中应归类为 ZERO(零用量声明),否则会导致通道上线初期或月初窗口持续误报基础设施故障。
Agent Prompt
### Issue description
当前 tar 解包逻辑要求归档中必须存在 `*/$LT_M_CODE/records-*.jsonl`,否则直接返回 INFRA。若 metering-ledger 分支已创建但尚未产生任何 records 文件(空账本),这会把“零用量”误判为 INFRA。

### Issue Context
脚本后续已把 `metering.py aggregate` 的 rc=2 视为 ZERO(无周片/零用量)。但在真实数据源路径下,tar 阶段就可能因无 records 文件而提前退出,导致永远到不了 aggregate 的 rc=2 分支。

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

### Suggested change
- 在 tar 前先 `tar -tzf` 列出并检查是否存在匹配 records:
  - 若无匹配:不要报 INFRA,直接让 ledger 目录保持为空并继续执行 aggregate,让其以 rc=2 输出 ZERO;或直接输出 ZERO(带说明“分支已建但无 records 文件”)。
  - 若有匹配但解包失败:仍按 INFRA 处理。
- 或者捕获 tar 的错误输出,仅当错误为“Not found in archive/无匹配”时转 ZERO,其余错误转 INFRA。

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

Comment on lines +56 to +60
with:
repository: Cloudbird-Software/CI-Workflows
sparse-checkout: pipeline/metering
path: ciw-metering
persist-credentials: false

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

4. 外部仓checkout未固定ref 🐞 Bug ⛨ Security

workflow 直接 checkout Cloudbird-Software/CI-Workflows 默认分支来获取 metering.py,未固定到特定
commit/tag,导致成本熔断行为随外部仓 HEAD 变化而漂移,存在供应链与可重复性风险。若外部仓被误推/破坏,预算检查可能被绕过或频繁误报。
Agent Prompt
### Issue description
`actions/checkout` 拉取外部仓 `Cloudbird-Software/CI-Workflows` 时未指定 `ref`,会默认跟随外部仓默认分支最新提交。预算归账/验链的执行逻辑因此不可复现且易受外部变更影响。

### Issue Context
该 PR 将 metering.py 作为“预算通道”的关键执行组件,且 cost-check 拥有治理 token(GH_TOKEN)。即使外部仓有 CI,也仍建议对关键治理逻辑固定版本并通过显式升级流程更新。

### Fix Focus Areas
- .github/workflows/cost-check.yml[55-60]

### Suggested change
- 在 checkout CI-Workflows 步骤增加 `ref: <pinned sha or tag>`(推荐 sha),并在需要升级时通过 PR 明确更新该 ref。
- 若确实要跟随 main,也建议至少在 policy/脚本中记录期望版本并加 drift-check 校验(否则治理行为不可追溯)。

ⓘ 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 +319 to 320
W_TITLE="成本告警$YEAR-$MONTH):Actions 分钟 ${PCT_MIN}% / LLM token ${PCT_TOK:--}%(阈值 ${AM_WARN}%)"
W_EXISTING=$(gov_open_issues cost-budget-warning | grep -m1 "$YEAR-$MONTH" | cut -f1)

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

5. 告警阈值字段混用 🐞 Bug ≡ Correctness

告警档标题与日志同时展示 Actions 与 LLM token,但括号内阈值仍使用 AM_WARN(Actions 阈值)而非 LT_WARN(LLM
阈值),当两者配置不一致时会误导值守与审计。该问题会导致告警信息描述与实际判定条件不一致。
Agent Prompt
### Issue description
`W_TITLE` / `act` 文案在同时展示 Actions 与 LLM 的百分比时,仍把“阈值”字段写成 `${AM_WARN}%`。如果未来 Actions 与 LLM 的 warn_pct 不同,会出现:判定用 LT_WARN,但文案展示 AM_WARN 的不一致。

### Issue Context
脚本已分别计算 `WARN_MIN` vs `WARN_TOK`(LT_WARN),因此文案应与之对齐。

### Fix Focus Areas
- governance/cost-check.sh[318-333]

### Suggested change
- 标题/日志中明确区分阈值:例如 `(阈值 Actions ${AM_WARN}% / LLM ${LT_WARN}%)`。
- 或者将“阈值”文字改成更中性的描述,并在正文里分别列出两套阈值(当前正文已部分包含)。

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR implements the “LLM token budget channel” consumption side for governance/cost-check.sh by reading CI-Workflows metering ledger data (ciw-metering) and integrating it into the existing warn/hardstop budget enforcement flow, plus adds an offline unit test harness for the channel contract.

Changes:

  • Switch cost.llm_tokens.data_source from pending to ciw-metering and add metering locator config (repo/branch/code_path).
  • Add llm_channel_account() to pull and aggregate metering-ledger data (DATA/ZERO/INFRA tri-state) and wire results into warning/hardstop issue content.
  • Update the workflow to sparse-checkout the metering engine and add a focused shell test for the channel contract.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
governance/tests/test-cost-llm-channel.sh Adds offline unit tests that extract and validate llm_channel_account() contract via marker-pair.
governance/policy/automation-limits.yaml Enables ciw-metering as the LLM token data source and declares metering locator fields.
governance/cost-check.sh Implements the LLM metering consumption path and integrates it with warn/hardstop flows.
.github/workflows/cost-check.yml Adds sparse checkout of CI-Workflows metering engine and wires COST_LLM_METERING_PY.
Suppressed comments (1)

governance/cost-check.sh:166

  • 这里把 tar 解包失败一律当作 INFRA,但空账本/首月无 records-.jsonl 时,按当前的 tar ... "*/$LT_M_CODE/records-*.jsonl" 写法会因为通配无匹配而失败,从而把“空账本应 ZERO(零用量)”误判为 INFRA。建议仅在 tarball 不可读或确实包含 records-.jsonl 但解包失败时才 INFRA;若 tarball 可读且不含 records 文件,则继续让 aggregate 返回 rc=2 → ZERO。
    if ! tar -xzf "$led.tar.gz" -C "$led" --strip-components=1 --wildcards \
         "*/$LT_M_CODE/records-*.jsonl" 2>/dev/null; then
      printf 'INFRA\tmetering 账本 tar 解包失败(strip-components=1 + %s/records-*.jsonl)\n' "$LT_M_CODE"
      return 0

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread governance/cost-check.sh
Comment on lines +155 to +162
if ! "$GH" api "repos/$LT_M_REPO/tarball/$LT_M_BRANCH" >"$led.tar.gz" 2>/dev/null; then
if "$GH" api "repos/$LT_M_REPO/branches/$LT_M_BRANCH" >/dev/null 2>&1; then
printf 'INFRA\tmetering 账本分支存在但 tarball 拉取失败(%s@%s)\n' "$LT_M_REPO" "$LT_M_BRANCH"
else
printf 'ZERO\t计量账本分支 %s@%s 未建(尚无经 wrapper 的 LLM 调用落账)——当月用量记 0\n' "$LT_M_REPO" "$LT_M_BRANCH"
fi
return 0
fi
Comment thread governance/cost-check.sh
Comment on lines +319 to +323
W_TITLE="成本告警$YEAR-$MONTH):Actions 分钟 ${PCT_MIN}% / LLM token ${PCT_TOK:--}%(阈值 ${AM_WARN}%)"
W_EXISTING=$(gov_open_issues cost-budget-warning | grep -m1 "$YEAR-$MONTH" | cut -f1)
W_BODY="额度告警(ADR-0040,$(date -u +%FT%TZ)):Actions 分钟($YEAR-$MONTH)$USED_MIN / $AM_QUOTA = ${PCT_MIN}%,达 ${AM_WARN}% 告警档——未硬停;达 ${AM_STOP}% 将置 \`$CB_VARIABLE\` 熔断并撤全部 auto-merge。"
W_BODY="额度告警(ADR-0040,$(date -u +%FT%TZ)):Actions 分钟($YEAR-$MONTH)$USED_MIN / $AM_QUOTA = ${PCT_MIN}%,达 ${AM_WARN}% 告警档——未硬停;达 ${AM_STOP}% 将置 \`$CB_VARIABLE\` 熔断并撤全部 auto-merge。
${LLM_SUMMARY:+$LLM_SUMMARY
}(LLM 阈值 ${LT_WARN}%/${LT_STOP}%,ADR-0062 归账通道)"
@randypanding
randypanding merged commit 1fd7ec0 into main Aug 21, 2026
12 checks passed
@randypanding
randypanding deleted the w2c3-cost-llm-channel branch August 21, 2026 18:13
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.

2 participants