-
Notifications
You must be signed in to change notification settings - Fork 0
fix: IR-0006 验收缺陷五连修——预算执法/metering 源/gate 逐测/retention/checkpoint(ADR-0103) #474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| name: evidence-checkpoint | ||
| # IR-0006 BEH-02 月度 checkpoint 调度(#469 2B 补件,ADR-0103 / BUDGET-02) | ||
| # | ||
| # 为什么:checkpoint 生成能力已存在(archive 仓 scripts/write_evidence.py | ||
| # --checkpoint,attest-drill/eval-wave 演练时调用过)但无月度 cron 承载—— | ||
| # BEH-02「每月末做 checkpoint」此前靠人工/演练顺带,一旦忘记,账本月度 | ||
| # hash 锚点断档(篡改检测窗口拉长)。本工作流补上调度面+新鲜度守卫: | ||
| # - 月度(每月 1 号 07:33 UTC):生成当月 checkpoint → verify 链完整 → | ||
| # 有变化则以 PR 形态提交 archive(PR 面 archive gate 再跑一轮 | ||
| # verify_evidence——双保险);无变化(幂等)=绿跳过。 | ||
| # - 周度(每周三 07:11 UTC):新鲜度守卫——最新 checkpoint 超 40 天=红 | ||
| # (fail-closed:API 失败亦红,防月度跑飞无人知)。 | ||
| # 节奏声明真源=governance/policy/retention.yaml(BUDGET-02 声明位,#468)。 | ||
| on: | ||
| schedule: | ||
| - cron: "33 7 1 * *" # 月度 checkpoint 生成(BEH-02) | ||
| - cron: "11 7 * * 3" # 周三新鲜度守卫(40 天窗口) | ||
| workflow_dispatch: | ||
| inputs: | ||
| mode: | ||
| { description: "run 模式(generate=生成+PR / freshness=新鲜度守卫)", type: string, required: false, default: "generate" } | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: evidence-checkpoint | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| generate: | ||
| if: github.event_name == 'workflow_dispatch' || github.event.schedule == '33 7 1 * *' | ||
|
Comment on lines
+31
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 7. Freshness mode is unreachable The workflow advertises mode: freshness, but every manual dispatch runs generate regardless of the input and the freshness job only permits scheduled events. An operator selecting freshness therefore performs checkpoint generation/PR actions instead of the requested read-only freshness check. Agent Prompt
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/evidence-checkpoint.yml | sed -n '1,115p'
printf '%s\n' '--- applicable repository knowledge ---'
find /tmp/coderabbit-repo-knowledge/cloudbird-software-github-0dfcc7f5 -maxdepth 2 -type f -name '*.md' -print
printf '%s\n' '--- first five lines of convention files ---'
for f in /tmp/coderabbit-repo-knowledge/cloudbird-software-github-0dfcc7f5/*/*.md; do
case "$f" in
*/learnings/*|*/architecture/*) continue ;;
esac
printf '\n### %s\n' "$f"
head -5 "$f"
done
printf '%s\n' '--- relevant learnings ---'
for f in /tmp/coderabbit-repo-knowledge/cloudbird-software-github-0dfcc7f5/learnings/*.md; do
[ -e "$f" ] || continue
printf '\n### %s\n' "$f"
cat "$f"
doneRepository: Cloudbird-Software/.github Length of output: 7827 按 当 🤖 Prompt for AI Agents |
||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - name: 拉取 archive 仓(判定层账本+脚本) | ||
| env: | ||
| GOVERNANCE_TOKEN: ${{ secrets.GOVERNANCE_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
| git clone --depth 1 \ | ||
| "https://x-access-token:${GOVERNANCE_TOKEN}@github.com/Cloudbird-Software/archive.git" archive | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: printf '%s\n' '--- applicable repository guidance ---'
head -5 /tmp/coderabbit-repo-knowledge/cloudbird-software-github-0dfcc7f5/*/*.md 2>/dev/null
printf '%s\n' '--- workflow excerpt ---'
cat -n .github/workflows/evidence-checkpoint.yml | sed -n '1,115p'
printf '%s\n' '--- archive script references ---'
rg -n --glob 'scripts/*' 'remote|origin|GOVERNANCE_TOKEN|git|subprocess|os\.environ' .github/workflows/evidence-checkpoint.yml . 2>/dev/null | head -120Repository: Cloudbird-Software/.github Length of output: 16019 🌐 Web query:
💡 Result: When you perform a git clone, Git automatically initializes the configuration variable remote.origin.url to the URL of the repository you are cloning [1]. This information is stored in the local repository configuration file located at.git/config [2][3]. In the context of GitHub Actions, the actions/checkout step is responsible for checking out your repository into the workspace [4][5]. By default, this action configures the repository such that the remote origin is set correctly to allow for subsequent Git operations [4][6]. Key points regarding this process in GitHub Actions: 1. Storage: The remote URL is indeed stored in the.git/config file within the workspace, just as it is in any standard Git repository [2][3]. You can verify this in your workflow using the command: git config --get remote.origin.url [3][7]. 2. Configuration: The actions/checkout action manages credentials and remote configuration automatically [6]. By default, it sets the persist-credentials option to true, which configures the token or SSH key with the local Git config to allow subsequent actions (like git push) to authenticate [6]. 3. Accessing the URL: If you need to access this URL within a workflow step, you can use the following command [3][8]: git config --get remote.origin.url This command will output the current URL configured for the remote named origin [3][7]. If you encounter scenarios where this returns an unexpected value, it is often due to the way the actions/checkout step was configured or because the workspace was cleared/re-initialized between steps [9][10]. Citations:
[严重级别:高] 在执行 archive 脚本前清除持久化令牌。
请在 clone 后运行: git -C archive remote set-url origin "https://github.com/Cloudbird-Software/archive.git"保留 Line 70 的一次性推送 URL。 🤖 Prompt for AI AgentsSource: Path instructions |
||
| - name: 生成当月 checkpoint(write_evidence --checkpoint,幂等只前进) | ||
| run: | | ||
| set -euo pipefail | ||
| cd archive | ||
| python3 scripts/write_evidence.py --checkpoint \ | ||
| --ledger evidence/ledger.jsonl --checkpoints evidence/checkpoints | ||
| - name: 独立复算(链完整+checkpoint 对账,fail-closed) | ||
| run: | | ||
| set -euo pipefail | ||
| cd archive | ||
| python3 scripts/verify_evidence.py --ledger evidence/ledger.jsonl | ||
| - name: 有变化 → 分支+PR(archive gate 面再验一轮) | ||
| env: | ||
| GOVERNANCE_TOKEN: ${{ secrets.GOVERNANCE_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
| cd archive | ||
| if git diff --quiet; then | ||
| echo "OK 幂等——当月 checkpoint 已在(无新链头),跳过提交" | ||
| exit 0 | ||
|
Comment on lines
+60
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 4. New checkpoints are skipped The generate job checks git diff --quiet before staging, but a new monthly evidence/checkpoints/YYYY-MM.json file is untracked and therefore invisible to that command. The job exits as “idempotent” without committing or opening a PR, so the newly added monthly checkpoint schedule does not publish its primary artifact. Agent Prompt
|
||
| fi | ||
| git config user.name "cloudbrid-agent[bot]" | ||
| git config user.email "cloudbrid-agent[bot]@users.noreply.github.com" | ||
| BR="evidence-checkpoint-$(date -u +%Y-%m)" | ||
| git checkout -b "$BR" | ||
| git add evidence/checkpoints/ | ||
| git commit -m "evidence: 月度 checkpoint ${BR}(BEH-02,evidence-checkpoint.yml 调度)" | ||
| git push "https://x-access-token:${GOVERNANCE_TOKEN}@github.com/Cloudbird-Software/archive.git" HEAD:refs/heads/"$BR" | ||
|
Comment on lines
+66
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 8. Monthly retries collide The workflow always pushes to evidence-checkpoint-YYYY-MM without fetching or updating an existing monthly branch. A retry while that month's PR/branch exists will be rejected as a non-fast-forward push or fail when creating a duplicate PR, making the scheduled job non-rerunnable during the period when retries are most needed. Agent Prompt
|
||
| export GH_TOKEN="$GOVERNANCE_TOKEN" | ||
| gh pr create --repo Cloudbird-Software/archive --base main --head "$BR" \ | ||
| --title "evidence: 月度 checkpoint ${BR}(BEH-02 自动调度)" \ | ||
| --body "月度 checkpoint 自动生成(evidence-checkpoint.yml,#469 2B)。verify_evidence 独立复算通过后提交;PR 面 archive gate 将再跑一轮链完整性+checkpoint 对账(fail-closed 双保险)。" | ||
|
|
||
| freshness: | ||
| if: github.event_name == 'schedule' && github.event.schedule == '11 7 * * 3' | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
| steps: | ||
| - name: 最新 checkpoint 新鲜度(>40 天=红,API 失败=红) | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GOVERNANCE_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
| # fail-closed:拉取失败=守卫面失明,红 | ||
| LATEST=$(gh api repos/Cloudbird-Software/archive/contents/evidence/checkpoints \ | ||
| --jq '[.[].name | select(test("^\\d{4}-\\d{2}\\.json$"))] | max') \ | ||
| || { echo "::error::checkpoints 目录拉取失败(fail-closed)"; exit 1; } | ||
| [[ -n "$LATEST" ]] || { echo "::error::无任何 checkpoint(fail-closed)"; exit 1; } | ||
| MONTH="${LATEST%.json}" | ||
| python3 - "$MONTH" <<'EOF' | ||
| import datetime, sys | ||
| month = sys.argv[1] | ||
| ckpt = datetime.datetime.strptime(month, "%Y-%m").replace(tzinfo=datetime.timezone.utc) | ||
| # 月内任一天即可信:按月初计龄(保守——若按月末会更宽松) | ||
| age = (datetime.datetime.now(datetime.timezone.utc) - ckpt).days | ||
| print(f"最新 checkpoint {month},年龄 {age} 天(窗口 40 天)") | ||
| if age > 40: | ||
| print(f"::error::checkpoint 过期({age} 天 > 40 天窗口)——月度生成跑飞,人工介入") | ||
| sys.exit(1) | ||
| EOF | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -183,13 +183,70 @@ jobs: | |
| # IR-0002 #143 收口:§12 (a)/(b) 分类器 fixture 自测入 CI——(b) 检出 | ||
| # 灵敏度(INV-4)自此有机器证据,改分类器不连带过测即红。目录内新增 | ||
| # test-*.sh 自动纳入;目录为空=测试面丢失,fail-closed。 | ||
| # #471 修复:逐测试判定——任何单测红都使本步红(原 for 循环退出码= | ||
| # 最后一个测试,中途红被吞掉=CI 假绿) | ||
| shopt -s nullglob | ||
| tests=(governance/tests/test-*.sh) | ||
| shopt -u nullglob | ||
| if [[ ${#tests[@]} -eq 0 ]]; then | ||
| echo "::error::governance/tests 无 test-*.sh——测试面丢失(fail-closed)"; exit 1 | ||
| fi | ||
| for t in "${tests[@]}"; do echo "-- $t"; bash "$t"; done | ||
| reds=() | ||
| for t in "${tests[@]}"; do | ||
| echo "-- $t" | ||
| if ! bash "$t"; then reds+=("$t"); fi | ||
| done | ||
| if [[ ${#reds[@]} -gt 0 ]]; then | ||
| printf '::error::治理自测红(%d 个):%s\n' "${#reds[@]}" "${reds[*]}" | ||
| exit 1 | ||
| fi | ||
| - name: acceptance 收口守卫(#473:验收报告合并时 IR 项下不得有 open bug/P0 issue) | ||
| if: github.event_name == 'pull_request' | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| PR_API: "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}" | ||
| REPO_NAME: ${{ github.repository }} | ||
| run: | | ||
| set -euo pipefail | ||
| # 触发面:PR 触碰 specs/*/acceptance.md(fail-closed:清单拉取失败=红) | ||
| PAGE=1; FILES="" | ||
| while :; do | ||
| RESP=$(gh api "$PR_API/files?per_page=100&page=$PAGE") \ | ||
| || { echo "::error::PR 文件清单拉取失败(fail-closed)"; exit 1; } | ||
| FILES="$FILES"$'\n'"$(echo "$RESP" | jq -r '.[].filename')" | ||
| [[ $(echo "$RESP" | jq 'length') -lt 100 ]] && break | ||
| PAGE=$((PAGE+1)) | ||
| done | ||
| ACCEPTS=$(grep -E '^specs/[^/]+/acceptance\.md$' <<<"$FILES" || true) | ||
| if [[ -z "$ACCEPTS" ]]; then | ||
| echo "OK 本 PR 不触碰 acceptance.md——守卫不适用"; exit 0 | ||
| fi | ||
| # 豁免面:本 PR body 声明将关闭的 issue(Fixes/Closes #n)——修复合流=缺口收口 | ||
| PRBODY=$(gh api "$PR_API" --jq .body 2>/dev/null || echo "") | ||
| FIXED=$(grep -oE '(Fixes|Closes|Resolves) #[0-9]+' <<<"$PRBODY" \ | ||
| | grep -oE '[0-9]+' | sort -un | tr '\n' ' ' || true) | ||
| echo "acceptance 变更:$ACCEPTS;本 PR 关闭豁免:${FIXED:-无}" | ||
| # 逐 acceptance:解析其 IR 引用(- IR: ...#NNN 行)→ 查 IR 项下 open bug/P0 | ||
| for f in $ACCEPTS; do | ||
| IR_NUM=$(grep -oE '#[0-9]+' <<<"$(grep -m1 -E '^- IR: .+#' "$f" || true)" | head -1 | tr -d '#') | ||
| if [[ -z "$IR_NUM" ]]; then | ||
| echo "::error::$f 无法解析 IR 引用(- IR: 行缺 #NNN——fail-closed)"; exit 1 | ||
| fi | ||
| echo "-- $f → IR #$IR_NUM" | ||
| for LBL in bug P0; do | ||
| BADS=$(gh api "search/issues?q=repo:${REPO_NAME}+is:issue+is:open+label:${LBL}+%23${IR_NUM}+in:body" \ | ||
| --jq '.items[].number' 2>/dev/null) || { echo "::error::issue 检索失败(label:$LBL,fail-closed)"; exit 1; } | ||
|
Comment on lines
+237
to
+238
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 6. Issue search misses later pages The acceptance guard invokes search/issues without pagination, so it examines only GitHub's default first 30 matches for each label. An open IR bug/P0 beyond that page is never checked, allowing an acceptance report to pass despite the guard's stated no-open-defects invariant. Agent Prompt
Comment on lines
+237
to
+238
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: set -eu
printf '%s\n' '--- applicable repository convention files ---'
find /tmp/coderabbit-repo-knowledge/cloudbird-software-github-0dfcc7f5 -maxdepth 2 -type f -name '*.md' -print
printf '%s\n' '--- workflow lines 210-255 ---'
sed -n '210,255p' .github/workflows/gate.yml
printf '%s\n' '--- API usage in this workflow ---'
rg -n -C 3 'gh api|search/issues|BADS|IR_NUM|LABEL|paginate|per_page' .github/workflows/gate.ymlRepository: Cloudbird-Software/.github Length of output: 7136 🏁 Script executed: set -eu
printf '%s\n' '--- workflow-specific convention ---'
cat /tmp/coderabbit-repo-knowledge/cloudbird-software-github-0dfcc7f5/conventions/github-workflows.md
printf '%s\n' '--- repository-wide convention sections relevant to checks ---'
rg -n -C 2 'workflow|分页|pagination|issue|API|gate' \
/tmp/coderabbit-repo-knowledge/cloudbird-software-github-0dfcc7f5/conventions/repo-wide.md \
/tmp/coderabbit-repo-knowledge/cloudbird-software-github-0dfcc7f5/conventions/governance.md
printf '%s\n' '--- GitHub CLI availability and API help ---'
if command -v gh >/dev/null 2>&1; then
gh --version | head -1
gh help api | sed -n '1,180p'
else
echo 'gh is unavailable'
fiRepository: Cloudbird-Software/.github Length of output: 9112 🌐 Web query:
💡 Result: The GitHub REST API, including search endpoints, defaults to 30 results per page, with a maximum of 100 results per page [1][2][3]. If a per_page value higher than 100 is specified, the API silently reduces it to 100 without returning an error [3][4]. Pagination is managed via Link headers in the API response [3]. The GitHub CLI (gh api) supports automatic pagination through the --paginate flag [5]. For REST requests, it handles this by automatically following the Link header (rel="next") until no more pages are available [6]. Regarding the output structure and jq processing: - By default, gh api --paginate outputs results as a stream of JSON objects or arrays (JSON Lines format) [5][7]. - The --slurp flag can be used with --paginate to wrap all pages into a single outer JSON array, which is useful for processing the entire set of results at once [5][8]. - Because --paginate emits multiple objects/arrays, using --jq directly on the paginated output can be challenging as the filter is applied to each page individually [7][8][9]. - A common workaround for creating a single, flat array of results from paginated output is to pipe the results to jq --slurp [10][9]. For example: gh api --paginate --jq '.items[]' | jq -s 'add' [10][9] Alternatively, for scenarios where you need to aggregate across all pages, using --slurp and then processing the resulting structure with jq is the standard approach, though it may require specific adjustments based on the API response structure [7][8]. Citations:
[严重级别:低] 分页读取全部匹配的 open issue。
设置 🤖 Prompt for AI Agents |
||
| for n in $BADS; do | ||
| if grep -qw "$n" <<<"$(echo ${FIXED:-} | tr ' ' '\n')" 2>/dev/null; then | ||
| echo " 豁免 #$n(本 PR 关闭)" | ||
| else | ||
| echo "::error::IR #$IR_NUM 项下 open ${LBL} issue #$n 未登记收口——验收报告不得先于缺陷合并(#473)" | ||
| exit 1 | ||
| fi | ||
| done | ||
| done | ||
| done | ||
| echo "OK acceptance 收口守卫通过" | ||
| - name: REPOS.yaml 引用自检(无重名仓) | ||
| run: | | ||
| python3 - <<'EOF' | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| version: 1 | ||
| # 证据账本保留策略声明(IR-0006 BUDGET-02 / AC-3;#468 补件) | ||
| # | ||
| # 声明位=governance/policy/(可 drift 对账、红队可审,ADR-0082);调整走 | ||
| # C1(PR 引 ADR)。执行面脚本=archive 仓 blob-store.sh(sweep/retention 词表 | ||
| # 已过 test-blob-store.sh)——本文件是声明面真源,与执行面分离(声明≠实现, | ||
| # drift-check 对账两者一致性)。 | ||
| tiers: | ||
| adjudication: # 判定层(archive 仓 evidence/ledger.jsonl——hash 链账本) | ||
| tier: hot | ||
| retention: 90d | ||
| storage: archive-jsonl | ||
| note: 统一判定层真源;链验证 verify_evidence.py(archive 仓 scripts/) | ||
| trajectory: # 轨迹层(内网 blob——完整轨迹/大对象) | ||
| tier: warm | ||
| retention: 1y | ||
| storage: self-cloud-blob | ||
| note: blob-store.sh sweep 执行面;词表 30d/90d/180d/1y/3y/forever | ||
| discard: # 丢弃层(GitHub 事件面——runner 销毁即弃,仅 digest 入链) | ||
| tier: digest-only | ||
| retention: 30d | ||
| storage: github-native | ||
| note: 日常 15min 轮影子随 runner 销毁;仅 drill 持久化轮入对应 *-ledger 分支 | ||
| checkpoint: | ||
| cadence: monthly # BEH-02 月度节奏:月末链头 hash+月度汇总提交 git | ||
| cron: "33 7 1 * *" # UTC 每月 1 号 07:33(与 sli-weekly/env-drift 错峰) | ||
| workflow: .github/workflows/evidence-checkpoint.yml | ||
| artifact: evidence/checkpoints/YYYY-MM.json # archive 仓 | ||
| verification: archive 仓 scripts/verify_evidence.py 独立复算(链断=红,fail-closed) | ||
| freshness_window_days: 40 # 最新 checkpoint 超 40 天=红(防月度跑飞,#469 2B) | ||
| enforcement: | ||
| sweep_frequency: daily # blob-store.sh sweep 建议日度(轨迹层过期回收) | ||
| growth_soft_cap_mb: 200 # BUDGET-01 判定层年增长软上限(gzip 后) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: Cloudbird-Software/.github
Length of output: 10568
[严重级别:低] 移除未使用的
GITHUB_TOKEN权限。两个 job 均通过
GOVERNANCE_TOKEN执行 GitHub 操作,未使用${{ github.token }}。请在 job 级设置permissions: {},以符合最小权限要求。🤖 Prompt for AI Agents
Source: Path instructions