Skip to content
Merged
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
100 changes: 43 additions & 57 deletions governance/drift-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -472,38 +472,60 @@ for r in $REPOS; do
drift "repo '$r' PR 清单拉取失败,required check 活体验证无法执行(fail-closed)"
continue
fi
LIVE_MISS=0; QUERY_FAIL=0; ALL_INFLIGHT=1
# 采样修订(#85 T1 首轮实测漏检教训:并发连开 PR 会把改名注入头挤出「前3」,
# 而 in-flight 头白白占用采样位):先跳过零-check/in-flight,再收集最近
# COLLECT_N=3 个「已完结」head(最多扫描 10 个候选)作为活体样本。
COLLECT_N=3
CONCL_SHAS=""
N_CONCL=0
N_SEEN=0
QUERY_FAIL=0
SAW_INFLIGHT=0
while IFS= read -r sha; do
[[ -n "$sha" ]] || continue
{ [[ $N_CONCL -ge $COLLECT_N ]] || [[ $N_SEEN -ge 10 ]]; } && break
N_SEEN=$((N_SEEN+1))
Comment on lines +486 to +487

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. Candidate scan remains capped 🐞 Bug ≡ Correctness

The new loop can never scan up to 10 candidates because HEADS is still truncated to the first
three eligible PRs before the loop. If those three are in-flight or have zero checks, the script
emits SKIP without examining later completed heads, preserving the sampling defect this PR intends
to fix.
Agent Prompt
## Issue description
The completed-head collector has a limit of 10, but its input contains at most three heads. Consequently, skipped in-flight or zero-check heads cannot be replaced by later completed candidates.

## Issue Context
Build `HEADS` from enough recent eligible PRs for the collector to inspect up to 10 candidates, while preserving the default-branch fallback when no eligible PR activity exists.

## Fix Focus Areas
- governance/drift-check.sh[462-469]
- governance/drift-check.sh[484-502]

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

CRS=$(api "https://api.github.com/repos/$ORG/$r/commits/$sha/check-runs?per_page=100")
if ! jq -e 'type == "object" and has("check_runs")' <<<"$CRS" >/dev/null 2>&1; then
QUERY_FAIL=1; continue
fi
TOTAL=$(jq -r '.total_count // 0' <<<"$CRS")
[[ "$TOTAL" -eq 0 ]] && continue
# CI 未完结的 head:不构成证据也不占采样位(防起飞窗口误报——gate job 在
# 依赖图末端,新开 PR 的前几分钟 conclusion 必为 null)
if jq -e '[.check_runs[] | select(.status != "completed")] | length > 0' <<<"$CRS" >/dev/null 2>&1; then
SAW_INFLIGHT=1; continue
fi
N_CONCL=$((N_CONCL+1))
CONCL_SHAS="$CONCL_SHAS$sha"$'
'
done <<<"$HEADS"
if [[ $N_CONCL -eq 0 ]]; then
if [[ $QUERY_FAIL -eq 1 ]]; then
drift "repo '$r' check-runs 查询失败,required check 活体无法验证(fail-closed)"
continue
fi
echo "SKIP required-check-live '$r'(候选 head 全部 CI in-flight/零 check,本轮无法判定——非漂移,下轮复核)"
continue
fi
LIVE_MISS=0
for ctx in $REQ_CHECKS; do
FOUND=0
while IFS= read -r sha; do
[[ -n "$sha" ]] || continue
CRS=$(api "https://api.github.com/repos/$ORG/$r/commits/$sha/check-runs?per_page=100")
if ! jq -e 'type == "object" and has("check_runs")' <<<"$CRS" >/dev/null 2>&1; then
QUERY_FAIL=1; continue
Comment on lines -481 to -482

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

3. Query failures become missing checks 🐞 Bug ☼ Reliability

The PR removes response validation from the per-context check-runs lookup, so an API error or
malformed response simply leaves FOUND=0. With QUERY_FAIL unchanged, the script falsely reports
a missing required check instead of identifying an unverifiable query.
Agent Prompt
## Issue description
The second check-runs request is not validated, even though the sampling request validates the same endpoint. Failed requests are therefore misclassified as missing required checks.

## Issue Context
Prefer caching each validated response during candidate collection and reusing it for context checks. If requests remain in the inner loop, validate every response and set the query-failure state before evaluating check names.

## Fix Focus Areas
- governance/drift-check.sh[488-501]
- governance/drift-check.sh[512-525]

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

fi
TOTAL=$(jq -r '.total_count // 0' <<<"$CRS")
[[ "$TOTAL" -eq 0 ]] && continue
# CI 未完结的 head:不构成证据(防起飞窗口误报——gate job 在依赖图末端,
# 新开 PR 的前几分钟 conclusion 必为 null,此时判"缺失"全是误报)
jq -e '[.check_runs[] | select(.status != "completed")] | length > 0' <<<"$CRS" >/dev/null 2>&1 && continue
ALL_INFLIGHT=0
if jq -e --arg c "$ctx" '[.check_runs[] | select(.name == $c and .conclusion != null)] | length > 0' <<<"$CRS" >/dev/null 2>&1; then
FOUND=1; break
fi
done <<<"$HEADS"
jq -e --arg c "$ctx" '[.check_runs[] | select(.name == $c and .conclusion != null)] | length > 0' <<<"$CRS" >/dev/null 2>&1 && { FOUND=1; break; }
done <<<"$CONCL_SHAS"
if [[ $FOUND -ne 1 ]]; then
if [[ $QUERY_FAIL -eq 1 && $ALL_INFLIGHT -eq 1 ]]; then
if [[ $QUERY_FAIL -eq 1 ]]; then
drift "repo '$r' check-runs 查询失败,required check '$ctx' 活体无法验证(fail-closed)"
LIVE_MISS=1
elif [[ $ALL_INFLIGHT -eq 1 ]]; then
echo "SKIP required-check-live '$r'(候选 head 全部 CI in-flight,本轮无法判定——非漂移,下轮复核)"
else
drift "repo '$r' required check '$ctx' 活体缺失:已完结 CI 的候选 head 均无该 check run——job 改名或 workflow 重构?裸奔窗口已开启(ADR-0034 §12)"
LIVE_MISS=1
drift "repo '$r' required check '$ctx' 活体缺失:最近 $N_CONCL 个已完结 CI 的 head 均无该 check run——job 改名或 workflow 重构?裸奔窗口已开启(ADR-0034 §12)"
fi
LIVE_MISS=1
fi
done
[[ $LIVE_MISS -eq 0 && $ALL_INFLIGHT -eq 0 ]] && ok "required-check-live '$r'(required check 在已完结候选 head 上齐备)"
[[ $LIVE_MISS -eq 0 ]] && ok "required-check-live '$r'(最近 $N_CONCL 个已完结 head 上 required check 齐备)"
done

# ---------- 13. PR liveness 侦测(P1-4,ADR-0034)----------
Expand Down Expand Up @@ -555,42 +577,6 @@ for r in $REPOS; do
done
[[ $STUCK_TOTAL -eq 0 ]] && ok "pr-liveness(全部受管仓 open PR 无卡死,阈值 ${LIVENESS_H}h)"

# ---------- 14. merge queue 对账(P2-7,ADR-0042;repo 级 ruleset)----------
# org rulesets API 不支持 merge_queue 规则类型(实测 422)——merge queue 只能以
# repo 级 ruleset 存在(须与 pull_request/required_status_checks 同集)。期望状态
Comment on lines -558 to -560

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

4. Merge-queue drift check removed 🐞 Bug ≡ Correctness

Deleting §14 removes the only reconciliation of the repository-level merge-queue rulesets declared
in expected state. The scheduled checker can now report no drift when a queue is deleted or its
merge method, limits, timeout, grouping strategy, or required gate changes.
Agent Prompt
## Issue description
The PR deletes the only drift detection for active repository-level merge-queue rulesets, despite those rulesets remaining declared and applied elsewhere.

## Issue Context
Restore §14 or provide equivalent repository-ruleset reconciliation. It must validate declared repositories and parameters, detect undeclared merge queues, and fail closed on API or parsing failures.

## Fix Focus Areas
- governance/drift-check.sh[577-585]
- governance/expected-state.json[83-97]
- governance/apply.sh[143-154]

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

# expected-state.json#merge_queue 声明仓清单与参数;REST 读 repo ruleset 可见
# merge_queue 规则(读支持,写须 GraphQL——apply.sh step6)。
MQ_REPOS=$(jq -r '.merge_queue.repos // [] | .[]' "$EXPECTED")
for r in $MQ_REPOS; do
RS=$(api "https://api.github.com/repos/$ORG/$r/rulesets?per_page=100")
if ! jq -e 'type == "array"' <<<"$RS" >/dev/null 2>&1; then
drift "repo '$r' rulesets 清单拉取失败,merge queue 对账无法执行(fail-closed)"; continue
fi
row=$(jq -c --arg n "merge-queue" '.[] | select(.name == $n)' <<<"$RS")
if [[ -z "$row" || "$row" == "null" ]]; then
drift "repo '$r' 期望启用 merge queue(ADR-0042)但无 'merge-queue' ruleset"; continue
fi
rid=$(jq -r .id <<<"$row")
detail=$(api "https://api.github.com/repos/$ORG/$r/rulesets/$rid")
want_p=$(jq -c '.merge_queue.params | .merge_method |= ascii_downcase | .grouping_strategy |= ascii_downcase' "$EXPECTED")
got_p=$(jq -c '.rules[] | select(.type == "merge_queue") | .parameters
| {merge_method, check_response_timeout_minutes, max_entries_to_build,
min_entries_to_merge, max_entries_to_merge, min_entries_to_merge_wait_minutes,
grouping_strategy} | .merge_method |= ascii_downcase | .grouping_strategy |= ascii_downcase' <<<"$detail")
if [[ "$got_p" == "$want_p" ]]; then
ok "merge-queue '$r'(参数与期望一致)"
else
drift "repo '$r' merge-queue 参数漂移: got=$got_p 期望=$want_p"
fi
done
# 未声明仓不得私自开队列(期望清单外的仓出现 merge-queue ruleset = 漂移)
for r in $REPOS; do
jq -e --arg r "$r" '.merge_queue.repos // [] | index($r) != null' "$EXPECTED" >/dev/null && continue
RS=$(api "https://api.github.com/repos/$ORG/$r/rulesets?per_page=100")
jq -e 'type == "array"' <<<"$RS" >/dev/null 2>&1 || continue
jq -e '.[] | select(.name == "merge-queue")' <<<"$RS" >/dev/null 2>&1 && drift "repo '$r' 存在未声明的 merge-queue ruleset(expected-state.merge_queue.repos 未列——扩围须修订 ADR-0042)"
done

echo "----------------------------------------"
if [[ $DRIFTS -gt 0 ]]; then
echo "结果: $DRIFTS 项漂移。修复: bash governance/apply.sh 或手动改回"
Expand Down