Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion governance/REPOS.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ repos:
visibility: public
status: active
role: 试卷层——封存验收场景+golden+泄漏诱饵(宪法 §1/§4B,IR-0003 W1-C4 .github#167,
ADR-0056);owner 直管;App 不挂载(DECISION-02 隔离不变量,drift-check §18 断言)
ADR-0056/ADR-0080);owner 直管;cloudbrid-agent 严禁挂载,验证者 APP 可挂载并
仅用于测试/验证路径写权(DECISION-02 隔离不变量,drift-check §18 断言)
key_paths: [entries/, schema/, canary/]

# 上游依赖(不属于本组织;不 fork、不 submodule)
Expand Down
91 changes: 62 additions & 29 deletions governance/drift-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -832,50 +832,65 @@ for r in $PROTO_REPOS; do
done
[[ $PROTO_OK -eq 0 ]] && ok "统一入口协议块一致(真源 template-service × $PROTO_N 个 entry_protocol 仓,逐字节比对)"

# ---------- 18. holdout 隔离断言(DECISION-02:App 安装差异隔离,W1-C4/ADR-0056)----------
# 试卷层 holdout 的读隔离不靠保密(公开仓,ADR-0056/DECISION-02),靠两条:
# cloudbrid-agent App 不安装到该仓(agent 的组织级凭据通道物理不可达)+ 泄漏诱饵
# 周检(.github 仓 holdout-canary-sweep,宪法 §6/§11)。本节断言第一条:
# App installation 的仓清单不得包含 holdout——在清单 = P0(隔离失效,agent 可持
# 组织凭据读考卷)。正向对照防检测器失明假绿(§4 同思想):installation 清单必须
# 非空且包含 .github——连 .github 都不在清单 = 端点读法错/权限变了 → 按漂移报错,
# ---------- 18. holdout 隔离断言(DECISION-02:App 安装差异隔离,W1-C4/ADR-0056,ADR-0080 修订)----------
# 试卷层 holdout 的读隔离不靠保密(公开仓,ADR-0056/DECISION-02),靠三条:
# - cloudbrid-agent App 不安装到该仓(agent 的组织级凭据通道物理不可达);
# - 验证者 APP 可安装,但仅作为测试/验证路径写入主体(ADR-0080);
# - 泄漏诱饵周检(.github 仓 holdout-canary-sweep,宪法 §6/§11)。
# 本节断言 App 挂载差异:
# - cloudbrid-agent installation 的仓清单含 holdout = P0 漂移;
# - 验证者 APP installation 的仓清单含 holdout = OK(合法);
# - 验证者 APP 尚未安装 → INFO/SKIP(实施阶段再断言)。
# 正向对照防检测器失明假绿(§4 同思想):cloudbrid-agent 的 installation 清单必须
# 非空且含 .github——连 .github 都不在清单 = 端点读法错/权限变了 → 按漂移报错,
# 绝不把"看不见"当"没有"。API 任何失败 = fail-closed 报漂移。
# §17 编号预留给并行卡 W1-C3(多代理并行修改本文件,编号互不占段)。
# 端点注(2026-08-21 实测):installation id 走 /orgs/<org>/installations(admin
# 端点,new-repo-init.sh §3 同款)——/user/installations 要求 GitHub App 令牌,
# 经典 PAT 一律 403;仓清单走 /user/installations/<id>/repositories(经典 PAT 可读)。
HOLDOUT_REPO="holdout"

# 拉取一次 org 安装列表,供 cloudbrid-agent 与验证者 APP 共用。
HOLDOUT_INST=$(api "https://api.github.com/orgs/$ORG/installations?per_page=100")
if ! jq -e 'type == "object" and (.installations | type == "array")' <<<"$HOLDOUT_INST" >/dev/null 2>&1; then
drift "holdout 隔离断言失败:org installations 拉取失败(fail-closed——检测器失明不得伪装通过,ADR-0056 §18): $(jq -r '.message // "非 JSON 响应"' <<<"$HOLDOUT_INST" 2>/dev/null || echo 传输失败)"
else
# app 名取 expected-state 单一真源(与 §6 一致),不重复硬编码
# 辅助函数:拉取指定 installation 下的全部仓名;stdout 为排序去重后的仓名列表;
# 失败时返回非 0(stdout 为空,不应使用)。
holdout_install_repos() {
local iid="$1" tmp fail page n chunk
tmp=$(mktemp)
fail=0
page=1
while :; do
chunk=$(api "https://api.github.com/user/installations/$iid/repositories?per_page=100&page=$page")
if ! jq -e 'type == "object" and (.repositories | type == "array")' <<<"$chunk" >/dev/null 2>&1; then
fail=1; break
fi
n=$(jq '.repositories | length' <<<"$chunk")
[[ "$n" -eq 0 ]] && break
jq -r '.repositories[].name' <<<"$chunk" >>"$tmp"
[[ "$n" -lt 100 ]] && break
page=$((page+1))
done
if [[ $fail -ne 0 ]]; then
rm -f "$tmp"
return 1
fi
sort -u "$tmp"
rm -f "$tmp"
}

# cloudbrid-agent:老 DECISION-02 主体,必须不挂载 holdout。
HOLDOUT_APP=$(jq -r .github_app.name "$EXPECTED")
INST_ID=$(jq -r --arg s "$HOLDOUT_APP" '.installations[]? | select(.app_slug == $s) | .id' <<<"$HOLDOUT_INST" | head -1)
if [[ -z "$INST_ID" || "$INST_ID" == "null" ]]; then
drift "app '$HOLDOUT_APP' 的 installation 未找到——holdout 隔离断言无法执行(fail-closed,ADR-0056 §18)"
else
# 全分页列出该 installation 下的仓(>100 仓单页漏检——§1/§4 同教训)
INST_TMP=$(mktemp)
INST_FAIL=0
INST_PAGE=1
while :; do
CHUNK=$(api "https://api.github.com/user/installations/$INST_ID/repositories?per_page=100&page=$INST_PAGE")
if ! jq -e 'type == "object" and (.repositories | type == "array")' <<<"$CHUNK" >/dev/null 2>&1; then
INST_FAIL=1; break
fi
INST_N=$(jq '.repositories | length' <<<"$CHUNK")
[[ "$INST_N" -eq 0 ]] && break
jq -r '.repositories[].name' <<<"$CHUNK" >>"$INST_TMP"
[[ "$INST_N" -lt 100 ]] && break
INST_PAGE=$((INST_PAGE+1))
done
if [[ $INST_FAIL -ne 0 ]]; then
INST_REPOS=$(holdout_install_repos "$INST_ID")
if [[ $? -ne 0 ]]; then
drift "installation#$INST_ID 仓清单拉取失败,holdout 隔离断言无法执行(fail-closed,ADR-0056 §18)"
rm -f "$INST_TMP"
else
INST_REPOS=$(sort -u "$INST_TMP")
rm -f "$INST_TMP"
HOLDOUT_HIT=0
grep -qx "$HOLDOUT_REPO" <<<"$INST_REPOS" && HOLDOUT_HIT=1
# 正向对照(防失明):清单非空且含 .github,否则断言结论不可信
Expand All @@ -887,10 +902,28 @@ else
HOLDOUT_HIT=-1
fi
if [[ $HOLDOUT_HIT -eq 1 ]]; then
drift "holdout 出现在 app '$HOLDOUT_APP' installation#$INST_ID 仓清单——P0:App 挂上 holdout = 试卷层隔离失效(DECISION-02/ADR-0056 §18;立即在 App 设置页移除该仓访问并追查何时挂载)"
drift "holdout 出现在 app '$HOLDOUT_APP' installation#$INST_ID 仓清单——P0:cloudbrid-agent 挂上 holdout = 试卷层隔离失效(DECISION-02/ADR-0056 §18;立即在 App 设置页移除该仓访问并追查何时挂载)"
elif [[ $HOLDOUT_HIT -eq 0 ]]; then
ok "holdout 隔离成立(installation#$INST_ID 共 $(grep -c . <<<"$INST_REPOS") 仓不含 holdout;正向对照 .github 在清单)"
ok "holdout 隔离成立(cloudbrid-agent installation#$INST_ID 共 $(grep -c . <<<"$INST_REPOS") 仓不含 holdout;正向对照 .github 在清单)"
fi
fi
fi

# 验证者 APP(ADR-0080):允许挂载 holdout,作为测试/验证路径写入主体。
VERIFIER_APP=$(jq -r '.verifier_app.name // empty' "$EXPECTED")
if [[ -n "$VERIFIER_APP" && "$VERIFIER_APP" != "null" ]]; then
VERIFIER_INST_ID=$(jq -r --arg s "$VERIFIER_APP" '.installations[]? | select(.app_slug == $s) | .id' <<<"$HOLDOUT_INST" | head -1)
if [[ -n "$VERIFIER_INST_ID" && "$VERIFIER_INST_ID" != "null" ]]; then
VERIFIER_REPOS=$(holdout_install_repos "$VERIFIER_INST_ID")
if [[ $? -ne 0 ]]; then
drift "验证者 APP '$VERIFIER_APP' installation#$VERIFIER_INST_ID 仓清单拉取失败,无法校验 holdout 挂载合法性(fail-closed,ADR-0080 §18)"
elif grep -qx "$HOLDOUT_REPO" <<<"$VERIFIER_REPOS"; then
ok "holdout 由验证者 APP '$VERIFIER_APP' 合法挂载(installation#$VERIFIER_INST_ID,ADR-0080)"
else
ok "验证者 APP '$VERIFIER_APP' 已安装但未挂载 holdout(ADR-0080 允许,非漂移)"
fi
else
echo "INFO 验证者 APP '$VERIFIER_APP' 尚未安装——holdout 挂载合法性断言跳过(实施阶段再校验,ADR-0080 §18)"
fi
fi
fi
Expand Down
5 changes: 5 additions & 0 deletions governance/expected-state.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@
"administration"
]
},
"verifier_app": {
"comment": "ISSUE-263 / ADR-0080:验证者 APP,仅测试/验证路径写权;holdout 挂载需经 drift-check §18 允许;id 在实施阶段填入",
"name": "verifier-app",
"id": null
},
"direct_push_exemptions": {
"comment": "§8 直推检测豁免——两类,均须 ADR 背书,逐完整 SHA 登记(新直推不可能搭便车):(a) 破玻璃直推回填:GM-2 破玻璃的机器可读回填形式(ADR-0016 附录),事件定性见 ADR-0017(.github 两条:净变更仅为 Trae IDE 分享 zip 与误入 gitlink,彼时 PR 流程已运转);(b) 建仓 bootstrap 初始 commit(ADR-0021):仓库创建时序上分支/PR 尚不存在、不可能走 PR——Shorts_Director/agent-tools/Script_Writer 的 Initial commit 类(含 GitHub UI 建仓附带的 README 编辑 commit),登记为豁免而非追认破玻璃;arbiter 建仓 bootstrap 同理(W1-C2 .github#165/ADR-0054)。archive(W1-C1 .github#164/ADR-0053):bootstrap README commit 同属 (b) 类——注意 ADR-0046 后 org-required-workflows ruleset 无 bypass,空仓首推被 required workflow 拦截,豁免操作=对 org ruleset 的 archive 仓库做秒级临时 exclude 后经 contents API 建 commit 并即刻还原(全程约 5 秒,已留痕于 #164);ruleset 最终态与落盘定义一致(drift-check §1 不受影响)。",
".github": [
Expand Down