-
Notifications
You must be signed in to change notification settings - Fork 0
fix(governance): drift §15 分支钉点解引用 + §12 adversary 生效锚点(ADR-0083 关联补全) #370
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 |
|---|---|---|
|
|
@@ -519,8 +519,18 @@ fi | |
| # 触发的 required workflow(org-gate.yml 的 on:),其活体证据只能来自 ruleset | ||
| # 生效后的 PR head——push-only / 生效前 PR 的 head 缺失属 (a)「从未接入」 | ||
| # (OK/INFO 计数,非漂移);仅 (b) 生效后 PR head 缺失才报裸奔。 | ||
| # 生效时刻 = org-required-workflows ruleset 创建时刻(API 实测)。 | ||
| ORG_GATE_EFFECTIVE="2026-08-20T07:43:21Z" | ||
| # 生效时刻 = 各 required check 线上生效锚点((a)/(b) 形态判据:晚于锚点的 PR | ||
| # head 才可能携带该 check run,锚点之后的缺失才是"接入后消失"): | ||
| # org-gate:org-required-workflows ruleset 创建时刻(API 实测,2026-08-20) | ||
| # adversary:2026-08-24 W2-C3 落地——main-protection 纳入 required check | ||
| # (ruleset updated_at 00:21:24Z)+ adversary-gate.yml 入 CI-Workflows(首版 | ||
| # 00:30:30Z、merge_group 修正 02:17:02Z);取最晚者,防落地窗口的假 (b) 裸奔 | ||
| # gate:各仓本地 workflow,早于一切 PR 活动(治理基线自 2026-08-18)——无 | ||
| # 锚点,缺失即 (b) | ||
| declare -A CHECK_EFFECTIVE=( | ||
| [org-gate]="2026-08-20T07:43:21Z" | ||
| [adversary]="2026-08-24T02:17:02Z" | ||
| ) | ||
|
|
||
| # @ir0002-s12-classify-begin —— IR-0002(spec PR#152)(a)/(b) 缺失形态分类器。 | ||
| # governance/tests/test-ir0002.sh 按本标记对提取函数体做 fixture 自测((b) 检出 | ||
|
|
@@ -533,12 +543,14 @@ s12_classify() { | |
| if [[ $QUERY_FAIL -eq 1 ]]; then | ||
| drift "repo '$r' check-runs 查询失败,required check '$ctx' 活体无法验证(fail-closed)" | ||
| return 2 | ||
| elif [[ "$ctx" == "org-gate" ]] && ! jq -e --arg eff "$eff" \ | ||
| elif [[ -n "$eff" ]] && ! jq -e --arg eff "$eff" \ | ||
| '[.[] | select((.state == "open" or .merged_at != null) and (.updated_at >= $eff))] | length > 0' \ | ||
| <<<"$PRS_RECENT" >/dev/null 2>&1; then | ||
|
Comment on lines
+546
to
548
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. 2. Pr updated_at误分类 s12_classify 用 PR.updated_at >= eff 判断“生效后有 PR 活动”,会把仅评论/标签等导致的更新时间变化当成活动,从而把本应判为 IR-0002(a) 的仓误报为 (b) 裸奔。此 PR 将该判据泛化到 adversary 后,新增了 adversary 的误报风险。 Agent Prompt
|
||
| # IR-0002 (a) 形态:org-gate ruleset 生效后该仓无 PR 活动——从未接入, | ||
| # 非裸奔(待接入清单;本地 gate 的活体验证不受影响,照常执行) | ||
| echo "OK required-check-live '$r':'$ctx' 待接入(ruleset 生效后无 PR 活动——IR-0002 (a) 形态)" | ||
| # IR-0002 (a) 形态(2026-08-25 泛化:任何带生效锚点的 check 均适用—— | ||
| # org-gate / adversary 同款判据):required 声明生效后该仓无 PR 活动—— | ||
| # 从未接入,非裸奔(待接入清单;本地 gate 的活体验证不受影响,照常执行)。 | ||
| # eff 为空 = 无锚点 check(早于一切 PR 活动)——不适用本分支 | ||
| echo "OK required-check-live '$r':'$ctx' 待接入(required 声明生效后无 PR 活动——IR-0002 (a) 形态)" | ||
| return 0 | ||
| elif [[ "${HAS_PR_ACTIVITY:-1}" -eq 0 ]]; then | ||
| # IR-0002 (a) 泛化:无 PR 仓的 degenerate 采样下任意 required check 缺失= | ||
|
|
@@ -623,7 +635,7 @@ for r in $REPOS; do | |
| done <<<"$CONCL_SHAS" | ||
| if [[ $FOUND -ne 1 ]]; then | ||
| rc=0 | ||
| s12_classify "$r" "$ctx" "$QUERY_FAIL" "${HAS_PR_ACTIVITY:-1}" "$PRS_RECENT" "$ORG_GATE_EFFECTIVE" "$N_CONCL" || rc=$? | ||
| s12_classify "$r" "$ctx" "$QUERY_FAIL" "${HAS_PR_ACTIVITY:-1}" "$PRS_RECENT" "${CHECK_EFFECTIVE[$ctx]:-}" "$N_CONCL" || rc=$? | ||
| case $rc in | ||
| 0) REPO_PENDING=$((REPO_PENDING+1)); S12_PENDING=$((S12_PENDING+1)) ;; | ||
| 1) LIVE_MISS=1 ;; | ||
|
|
@@ -699,13 +711,17 @@ done | |
| # ref 为 tag 时,tag 指针被移动(ruleset 文本不变、内容换了)——钉点的 commit 绑定 | ||
| # 必须与 expected-state.org_required_workflows.ref_commit 一致;钉点 tag 还必须是 | ||
| # CI-Workflows 当前发布不变式(§11:vN==最高 vN.x.y)认可的物。fail-closed。 | ||
| # 2026-08-24 平台约束修订(ADR-0083 关联):ruleset API 对 required workflows 的 | ||
| # ref 只接受分支/标签名(40 位 SHA 恒 422),钉点值=main——分支钉点随合并移动是 | ||
| # 合法常态,commit 绑定不适用;内容完整性改由本节「审判源文件存在性后验」+ | ||
| # §12 required check 活体后验承载(expected-state.json 同款口径)。 | ||
| ORW_CFG=$(jq -c '.org_required_workflows // empty' "$EXPECTED") | ||
| if [[ -n "$ORW_CFG" ]]; then | ||
| WANT_RULESET=$(jq -r '.ruleset' <<<"$ORW_CFG") | ||
| WANT_REPO_ID=$(jq -r '.repository_id' <<<"$ORW_CFG") | ||
| WANT_PATH=$(jq -r '.path' <<<"$ORW_CFG") | ||
| WANT_REF=$(jq -r '.ref' <<<"$ORW_CFG") | ||
| WANT_COMMIT=$(jq -r '.ref_commit' <<<"$ORW_CFG") | ||
| WANT_COMMIT=$(jq -r '.ref_commit // empty' <<<"$ORW_CFG") | ||
| ORW_LIST=$(api "https://api.github.com/orgs/$ORG/rulesets?per_page=100") | ||
| ORW_ID=$(jq -r --arg n "$WANT_RULESET" '.[] | select(.name == $n) | .id' <<<"$ORW_LIST" 2>/dev/null | head -1) | ||
| if [[ -z "$ORW_ID" || "$ORW_ID" == "null" ]]; then | ||
|
|
@@ -724,24 +740,55 @@ if [[ -n "$ORW_CFG" ]]; then | |
| if [[ -n "$BAD" ]]; then | ||
| drift "org-required-workflows 钉点漂移:$BAD(期望 path=$WANT_PATH ref=$WANT_REF repo_id=$WANT_REPO_ID)——审判源被改指(ADR-0046 §15)" | ||
| fi | ||
| # commit 绑定校验(tag 或直接 SHA;ISSUE-263 W1-C5 改为直接钉 commit SHA) | ||
| # 钉点解引用与 commit 绑定(ADR-0083 关联修订)。三类形态: | ||
| # - 40 位 SHA:校验对象在目标仓可达(历史形态,保留兼容) | ||
| # - 分支名:指针随合并移动是合法常态——无 commit 绑定可比(ref_commit 声明 | ||
| # 了也不比,main 每次合并都动),只校验可解引用(分支被删/改名=审判源 | ||
| # 脱钩,fail-closed) | ||
| # - tag 名:指针移动检测(ruleset 文本不变、tag 被移=审判内容被换)—— | ||
| # 与声明 ref_commit 比对 | ||
| if [[ "$WANT_REF" =~ ^[0-9a-f]{40}$ ]]; then | ||
| # 直接钉 commit SHA:校验该对象在目标仓可达 | ||
| REFKIND="sha" | ||
| COMMIT_OBJ=$(api "https://api.github.com/repos/$ORG/CI-Workflows/git/commits/$WANT_REF") | ||
| TAGCOMMIT=$(jq -r '.sha // empty' <<<"$COMMIT_OBJ") | ||
| else | ||
| SHORTREF="${WANT_REF#refs/tags/}" | ||
| TAGROW=$(api "https://api.github.com/repos/$ORG/CI-Workflows/git/ref/tags/$SHORTREF") | ||
| TAGCOMMIT=$(jq -r '.object.sha // empty' <<<"$TAGROW") | ||
| # 先按分支解引用(当前钉点形态:main),失败再按 tag(历史/发布钉点) | ||
| BRANCH_ROW=$(api "https://api.github.com/repos/$ORG/CI-Workflows/git/ref/heads/${WANT_REF#refs/heads/}") | ||
| TAGCOMMIT=$(jq -r '.object.sha // empty' <<<"$BRANCH_ROW") | ||
| if [[ -n "$TAGCOMMIT" ]]; then | ||
| REFKIND="branch" | ||
| else | ||
| SHORTREF="${WANT_REF#refs/tags/}" | ||
| TAGROW=$(api "https://api.github.com/repos/$ORG/CI-Workflows/git/ref/tags/$SHORTREF") | ||
| TAGCOMMIT=$(jq -r '.object.sha // empty' <<<"$TAGROW") | ||
| REFKIND="tag" | ||
| fi | ||
| fi | ||
| if [[ -z "$TAGCOMMIT" ]]; then | ||
| drift "org-required-workflows 钉点 $WANT_REF 解引用/校验失败(fail-closed,ADR-0046 §15)" | ||
| elif [[ "$TAGCOMMIT" != "$WANT_COMMIT" ]]; then | ||
| drift "org-required-workflows 钉点 $WANT_REF 解引用/校验失败(heads/tags 两径均不可达——fail-closed,ADR-0046 §15)" | ||
| elif [[ "$REFKIND" != "branch" && -n "$WANT_COMMIT" && "$TAGCOMMIT" != "$WANT_COMMIT" ]]; then | ||
| drift "org-required-workflows 钉点 $WANT_REF 已移动:${TAGCOMMIT:0:8} ≠ 声明 ${WANT_COMMIT:0:8}——审判内容被换(ADR-0046 §15;还原或更新 expected-state)" | ||
|
Comment on lines
+762
to
770
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: sed -n '730,805p' governance/drift-check.sh
printf '\n-- related declarations and expected-state references --\n'
rg -n -C 3 'WANT_REF|WANT_COMMIT|TAGCOMMIT|SHORTREF|ref_commit|git/ref/tags|git/tags' governance/drift-check.sh governance expected-state.json 2>/dev/null | head -240Repository: Cloudbird-Software/.github Length of output: 17579 🏁 Script executed: printf '%s\n' '-- expected-state files --'
fd -i 'expected-state\.json$' .
printf '%s\n' '-- org_required_workflows declarations --'
fd -i 'expected-state\.json$' . -x sh -c 'echo "FILE: $1"; jq -c ".org_required_workflows // empty" "$1"' sh {}
printf '%s\n' '-- existing tag dereference implementation --'
sed -n '448,470p' governance/drift-check.shRepository: Cloudbird-Software/.github Length of output: 2153 修复 tag fallback 的提交解引用。 当 🤖 Prompt for AI Agents |
||
| elif [[ -n "$BAD" ]]; then | ||
| : # 钉点漂移已上报,不再输出 OK 行(避免同段 OK/DRIFT 并存的误导) | ||
| else | ||
| ok "org-required-workflows 钉点完整($WANT_REF == ${TAGCOMMIT:0:8},path/repository_id 一致)" | ||
| # 内容完整性后验(ADR-0083 关联口径:分支钉点移动是常态,commit 绑定不 | ||
| # 适用——改验「声明的每个审判源文件在钉点 ref 上真实存在」)。文件被删/ | ||
| # 改名 = required workflow 静默失效(PR 不再跑审判),此处即时检出,不等 | ||
| # §12 的 PR head 采样兜底。expected-state.workflows[] 为声明全集。 | ||
| WF_OK_N=0 | ||
| while IFS= read -r wp; do | ||
| [[ -n "$wp" ]] || continue | ||
| WF_META=$(api "https://api.github.com/repos/$ORG/CI-Workflows/contents/${wp}?ref=$WANT_REF") | ||
| if jq -e 'type == "object" and (has("sha") or has("content"))' <<<"$WF_META" >/dev/null 2>&1; then | ||
| WF_OK_N=$((WF_OK_N+1)) | ||
| else | ||
| drift "org-required-workflows 审判源 '$wp' 在 $WANT_REF 上不存在/不可读——required workflow 静默失效(ADR-0046 §15 内容后验,fail-closed)" | ||
| BAD="$BAD missing:$wp" | ||
| fi | ||
| done <<<"$(jq -r '(.workflows // [{path: .path}])[].path' <<<"$ORW_CFG")" | ||
|
Comment on lines
+778
to
+788
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: #!/bin/bash
set -eu
printf '%s\n' '--- governance/drift-check.sh: relevant implementation ---'
sed -n '730,805p' governance/drift-check.sh
printf '%s\n' '--- references to ORW_CFG, WANT_PATH, WF_OK_N, and workflow verdicts ---'
rg -n -C 3 'ORW_CFG|WANT_PATH|WF_OK_N|org-required-workflows|missing:' governance/drift-check.shRepository: Cloudbird-Software/.github Length of output: 8095 🌐 Web query:
💡 Result: When interacting with the GitHub REST Contents API, submodule information is returned as part of the repository content metadata [1][2]. Key behaviors and fields include: Type Field: When you request the specific path of a submodule, the API returns a response where the type field is set to submodule [1][3]. However, when listing the contents of a directory, submodules are historically returned with a type field of file for backwards compatibility reasons [4][5]. Submodule Metadata: - submodule_git_url: This field provides the remote URL of the submodule repository [1][6]. It is used by Git to clone the submodule [1][2]. - sha: For a submodule, the sha field does not refer to the blob SHA of the content (as it would for a regular file); instead, it identifies the specific commit SHA that the submodule is checked out to in the parent repository [1][4]. Behavioral Notes: - Non-GitHub Submodules: If the submodule repository is hosted outside of github.com, related fields such as html_url and _links["html"] will return null values [1][2]. - Accessing the Target SHA: As noted in developer documentation and community discussions, if you are listing a directory and see the submodule listed with a type of file, that file object's sha field successfully represents the target commit SHA of the submodule [4][7]. The submodule_git_url will only be present when accessing the submodule path directly or when the API explicitly identifies the object as a submodule [1][7]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow declarations and schema references ---'
rg -n -C 4 '"workflows"|org_required_workflows|ref_commit' \
governance .github expected-state.json 2>/dev/null || true
printf '%s\n' '--- jq fallback behavior for an empty workflows array ---'
jq -nr '
[
({workflows: []} | (.workflows // [{path: .path}])[].path),
({path: "rules/workflow.yml"} | (.workflows // [{path: .path}])[].path),
({} | (.workflows // [{path: .path}])[].path)
]
'Repository: Cloudbird-Software/.github Length of output: 9927 仅接受普通 workflow 文件,并禁止空清单通过。
当 🤖 Prompt for AI Agents |
||
| if [[ -z "$BAD" ]]; then | ||
| ok "org-required-workflows 钉点完整(${REFKIND} $WANT_REF → ${TAGCOMMIT:0:8},path/repository_id 一致,$WF_OK_N 个审判源文件在册)" | ||
| fi | ||
|
Comment on lines
+788
to
+791
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. 3. 空workflows绕过存在性 §15 的审判源文件存在性后验从 (.workflows // [{path:.path}]) 取路径;当 .workflows 存在但为空数组时,不会回退到
.path,循环将跳过并输出“0 个审判源文件在册”的 OK,导致 required workflow 文件被删/改名也可能漏报。
Agent Prompt
|
||
| fi | ||
| fi | ||
| fi | ||
|
|
||
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.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: Cloudbird-Software/.github
Length of output: 37893
🏁 Script executed:
Repository: Cloudbird-Software/.github
Length of output: 28218
🏁 Script executed:
Repository: Cloudbird-Software/.github
Length of output: 10445
在合并前完成 owner-only 审批。
PR 370 当前仅有机器人评论,未见
@randypanding的批准记录。CI gate 已全部成功。完成 owner-only review 后再合并。🤖 Prompt for AI Agents
Source: Coding guidelines