fix(cost-check): metering tar 单 pattern 终修(#258,ADR-0083 关联) - #340
fix(cost-check): metering tar 单 pattern 终修(#258,ADR-0083 关联)#340randypanding wants to merge 10 commits into
Conversation
…thub#275,ADR-0083) - gate.yml 新增 t14-spec-suite job:specs/** PR 必含含断言的 suite/(fail-closed); 全部 specs/*/suite 在 CI 真实执行(可执行性证明) - specs/ISSUE-263/suite/:IR 自有验收套件(9 项断言——T5/T6、T-14/T-15、AR-10、 ruleset adversary 无 integration_id、verifier_app 登记、holdout 申报;自反断言) - main-protection.json 剔除 adversary 条目非法 integration_id:0(致 apply 422、 线上 ruleset 长期缺 adversary——独立验证发现的断链根因) - drift-check §1 fail-closed:ruleset 清单非数组 → FATAL exit 2(GOVERNANCE_TOKEN 失效期间曾把四个真实 ruleset 全报『不存在』) - g060-guard cron 错峰(0 → 17 分,对齐 workflow 规范)
…adversary check 永缺(队列 60min 超时弹回根因)
…cess check 前核验审计 run 绿+workflow+判定行)
…运行致条目恒挂(#95 实测),结构性冲突先摘除(ADR-0083 关联)
…rg-gate job 对中心仓自跳,required 化后产生永不满足的 skipped)+ 钉点 ref=main(SHA 形式被 ruleset API 拒绝)
…实测);expected-state/W1-C5 自检同步;adversary-gate 纳入钉点面
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (11)
📝 WalkthroughWalkthrough本次变更更新 adversary 门禁、跨仓审计 relay、spec 套件校验、治理规则及治理脚本,并调整定时任务。 Changes治理门禁
Suggested labels: ✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Comment |
PR Summary by QodoFix metering tar extraction and harden governance gates (ADR-0083)
AI Description
Diagram
High-Level Assessment
Files changed (12)
|
Code Review by Qodo
1. Tar 多 pattern 仍失败
|
| if ! tar -xzf "$led.tar.gz" -C "$led" --strip-components=1 --wildcards \ | ||
| "*-records-*.jsonl" "records-*.jsonl" 2>/dev/null; then | ||
| "*/records-*.jsonl" "records-*.jsonl" 2>/dev/null; then |
There was a problem hiding this comment.
2. Tar 多 pattern 仍失败 🐞 Bug ≡ Correctness
governance/cost-check.sh 仍在同一次 tar 解包里传入两个 pattern;当第二个 pattern("records-*.jsonl")未命中时,tar 仍会以错误状态退出,导致整个解包被判 INFRA。该 PR 的根因叙述就是“任一未命中即 exit 2”,所以当前改动仍可能在“仅带前缀目录”的 tarball 上复现。
Agent Prompt
### Issue description
`tar -x ... pattern1 pattern2` 在任一 pattern 未命中时可能返回非 0(常见为 2),从而让“命中的文件也被整体判失败”。当前代码仍传入两个 pattern("*/records-*.jsonl" 和 "records-*.jsonl"),与 PR 描述中的根因(任一未命中即 exit 2)相矛盾。
### Issue Context
目标是兼容两类 tarball:
- 形如 `<repo>-<sha>/records-*.jsonl`(需要 `*/records-*.jsonl`)
- 形如 `records-*.jsonl`(不带目录前缀)
### Fix Focus Areas
- governance/cost-check.sh[163-172]
### Proposed fix
把一次 tar 调用拆成两次“单 pattern”尝试(或启用能忽略未命中的 tar 选项/行为),例如:
1) 先尝试 `tar ... "*/records-*.jsonl"`
2) 若失败再尝试 `tar ... "records-*.jsonl"`
并确保只有在两次都失败时才判 INFRA。
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| FILES=$(gh api "repos/$REPO/compare/$BASE_SHA...$HEAD_SHA" --jq '[.files[].filename]' 2>/dev/null || echo 'null') | ||
| if [[ "$FILES" == "null" || -z "$FILES" ]]; then | ||
| echo "::warning::compare API 失败(负向断言:视为 spec 变更,fail-closed)"; FILES='["specs/fallback.md"]' | ||
| fi | ||
| HASSPECS=$(echo "$FILES" | python3 -c "import json,sys;files=json.load(sys.stdin);print('true' if any(f.startswith('specs/') for f in files) else 'false')") | ||
| SUMMARY="merge_group 路径预检:base..head specs/** 变更=$HASSPECS" | ||
| if [[ "$HASSPECS" == "true" ]]; then | ||
| echo "::error::merge queue 提交含 specs/** 变更——需人工确认 survived 审计后重试(fail-closed)" | ||
| exit 1 |
There was a problem hiding this comment.
4. Compare 文件数截断误放行 🐞 Bug ⛨ Security
merge_group 路径用 compare API 的 .files 判断是否改动 specs/**,但该接口的 changed files 最多只返回 300 个文件;当改动文件数超过上限时可能漏掉 specs/** 变更并错误写回 adversary success。这样会把本应 fail-closed 的合并错误放行。
Agent Prompt
### Issue description
`GET /compare/{base}...{head}` 的响应里 `.files` 在大比较下会被截断(最多 300 个 changed files),当前逻辑只基于 `.files[].filename` 是否以 `specs/` 开头来决定放行/阻断。若 specs 变更落在被截断部分,将被误判为“无 specs 变更”并直接写回 success check。
### Issue Context
此 gate 被设计为 fail-closed;任何“文件清单不完整”的路径判断都会变成可绕过点。
### Fix Focus Areas
- .github/workflows/adversary-gate.yml[55-64]
### Proposed fix
不要用 compare API 的 `.files` 作为唯一来源。可选方案:
1) checkout + fetch base/head 后用 `git diff --name-only $BASE_SHA $HEAD_SHA`(全量、无 300 限制);或
2) 若继续用 compare API:显式检测“可能截断”的条件并 fail-closed(例如 `.files|length == 300` 时直接按 specs 变更处理),同时记录告警。
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| AUDIT_RUN_ID: ${{ github.event.inputs.audit_run_id }} | ||
| AUDIT_REPO: ${{ github.event.inputs.audit_repo || 'Cloudbird-Software/CI-Workflows' }} | ||
| PR_NUMBER: ${{ github.event.inputs.pr_number }} | ||
| HEAD_SHA: ${{ github.event.inputs.head_sha }} | ||
| AUDIT_HEAD_NOTE: ${{ github.event.inputs.audit_head_note }} | ||
| run: | | ||
| set -euo pipefail | ||
| # 1) 审计 run 存在 + 绿 | ||
| RUN=$(gh api "repos/$AUDIT_REPO/actions/runs/$AUDIT_RUN_ID" 2>/dev/null) \ | ||
| || { echo "::error::审计 run $AUDIT_RUN_ID 不存在(fail-closed)"; exit 1; } |
There was a problem hiding this comment.
5. 跨仓 relay 永远 404 🐞 Bug ☼ Reliability
adversary-relay.yml 用本仓 github.token 通过 gh api/gh run view 读取 Cloudbird-Software/CI-Workflows 的 Actions run 信息,但 GITHUB_TOKEN 默认只能访问当前工作流所在仓库资源。若 CI-Workflows 为私有仓(或未对该 token 授权),该 workflow 会稳定失败并无法写回 survived check。
Agent Prompt
### Issue description
workflow 期望跨仓读取 CI-Workflows 的 run(`gh api repos/$AUDIT_REPO/...` / `gh run view -R $AUDIT_REPO`),但当前仅配置了 `${{ github.token }}`。GitHub 官方文档指出 `GITHUB_TOKEN` 只能访问 workflow 所在仓库的资源,跨仓访问需要 GitHub App 安装 token 或更宽权限的凭据。
### Issue Context
该 relay 是 specs PR 的“survived 背书”写回路径;若跨仓读取始终失败,会导致 specs PR 永远无法满足 required adversary check(实际变成永久 fail-closed)。
### Fix Focus Areas
- .github/workflows/adversary-relay.yml[40-63]
### Proposed fix
引入可跨仓读取 CI-Workflows 的最小权限凭据:
- 优先:GitHub App installation token(限制仅 CI-Workflows actions:read + 本仓 checks:write)
- 备选:fine-grained PAT(同样限制仓库范围与 actions:read)
并将该 token 用于 gh/curl 调用 `$AUDIT_REPO` 的读取接口。
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| "repository_id": 1337911551, | ||
| "path": ".github/workflows/org-gate.yml", | ||
| "ref": "main", | ||
| "workflows": [ |
There was a problem hiding this comment.
6. Drift-check 恒报钉点漂移 🐞 Bug ≡ Correctness
expected-state.json 删除 org_required_workflows.ref_commit 并将 ref 改为分支名后,governance/drift-check.sh 仍无条件读取 .ref_commit 并做“钉点移动”比较;由于 WANT_COMMIT 会变成 null,drift-check 会把任何正常状态都判为漂移。
Agent Prompt
### Issue description
`expected-state.json` 中 `org_required_workflows` 已不再包含 `ref_commit`,但 `drift-check.sh` §15 仍执行 `WANT_COMMIT=$(jq -r '.ref_commit' ...)` 并用它与解引用得到的 commit SHA 比较。字段缺失时 `jq -r` 返回 `null`,导致比较恒不相等,从而把 org-required-workflows 误判为“钉点已移动”。
### Issue Context
PR 注释称“内容完整性改由 drift-check 后验承载”,但 drift-check 目前实现仍依赖 `ref_commit`。
### Fix Focus Areas
- governance/drift-check.sh[684-732]
- governance/expected-state.json[197-212]
### Proposed fix
调整 drift-check §15:
- 若 expected-state 不提供 `ref_commit`(或 `ref` 不是 tag/SHA 而是分支名),则不要做 commit-binding 比较;改为:
- 仅校验 ruleset 的 path/ref/repository_id/workflows 列表一致;并
- (可选)从分支 ref 解引用到当前 commit,但仅作为信息输出,不作为“移动”漂移判断,或引入新的、可行的完整性机制(例如对 CI-Workflows 工作流文件内容做哈希对账)。
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
动机
两轮实测定位:tar 多 pattern 任一未命中即 exit 2——
"records-*.jsonl"裸兜底模式在带目录前缀的 tarball 里恒未命中,连累命中的"*/records-*.jsonl"整体判失败 → cost-check 恒 INFRA。测试
本地实测:单 pattern 提取 metering-ledger tarball 成功(records-2026-W34/W35 落盘)。
Card: #263
Ref: ADR-0083
Summary by CodeRabbit
新功能
错误修复
治理与测试