工程+规则(CI回执等待异步化·用户令): ci_watch.sh新件(认领判据=回执commit为合并提交或后代+回执时刻晚于合并,半截JSON重试,900s超时防挂死,经git公共目录恒读主区回执)+process-conventions§1.3回执等待纪律+AGENTS.md运行与验证一句+iter技能总收口步换代——合回落地先呈报总结,回执等待一律run_in_background后台任务,禁前台sleep/轮询;两会话实证(合并→呈报阻塞20.5分钟/全天前台sleep等闸)

This commit is contained in:
DKChan committed 2026-11-02 00:10:25 +08:00
1 parent de5e31ed55
commit e340f14e87
3 files changed
+59 -2

No files matched your search

+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
# 回执等待件——合回后等 CI 回执的后台轮询器(2026-11-02 用户裁;规则面=process-conventions §1 第 3 条「回执等待纪律」)。
# 用法: tools/ci_watch.sh <合并提交hash> [超时秒,默认 900]
# 调用面: 合回落地先向用户呈报本批总结,随即以 run_in_background 后台任务运行本件;命令结束时
# harness 唤醒会话——绿→删区收口;红→呈报红闸名单走修复批;超时→呈报「回执未落」(CI 可能崩溃)。
# 「本次回执已落」认领判据(三条全满足,防认错轮):
# ① 回执可整读——CI 写回执非原子,半截/读空静默重试;
# ② 回执 commit 为传入提交或其后代——另一会话合回推进尖端触发补跑覆盖时按后代认领(coalesce 语义见 ci_runner.sh);
# ③ 回执 timestamp 晚于合并提交时刻——排除合并前盘上的旧回执(旧尖端是合并提交的祖先,只查②会误领旧回执)。
# 退出码: 0=绿 1=红 2=超时/回执未落 3=参数错误
set -u
# 主工作区定位:CI 只在主工作区跑(post-merge 钩子限主区),从任务 worktree 内调用也要读主区回执——
# 经 git 公共目录(.git)的上级定位主仓根,主区调用与 worktree 调用同一读数
COMMON="$(git -C "$(dirname "$0")" rev-parse --path-format=absolute --git-common-dir 2>/dev/null)" \
|| { echo "不在 git 仓库内"; exit 3; }
REPO="$(dirname "$COMMON")"
STATUS="$REPO/game/tests/.gates/ci-status.json"
BASE="${1:-}"
TIMEOUT="${2:-900}"
INTERVAL=30
[ -n "$BASE" ] || { echo "用法: ci_watch.sh <合并提交hash> [超时秒]"; exit 3; }
BASE="$(git -C "$REPO" rev-parse --verify "${BASE}^{commit}")" || { echo "提交不存在: $BASE"; exit 3; }
BASE_CT="$(git -C "$REPO" log -1 --format=%ct "$BASE")"
_deadline=$(( $(date +%s) + TIMEOUT ))
while :; do
_read="$(python -I -c "
import json, sys, datetime
try:
d = json.load(open(sys.argv[1], encoding='utf-8'))
ts = int(datetime.datetime.fromisoformat(d['timestamp']).timestamp())
print(d.get('commit', ''), d.get('verdict', ''), ts)
except Exception:
pass
" "$STATUS")"
_com=""; _verdict=""; _ts=""
if [ -n "$_read" ]; then
set -- $_read
[ $# -eq 3 ] && { _com="$1"; _verdict="$2"; _ts="$3"; }
fi
if [ -n "$_com" ] && [ -n "$_verdict" ] && [ -n "$_ts" ] \
&& [ "$_ts" -gt "$BASE_CT" ] \
&& git -C "$REPO" merge-base --is-ancestor "$BASE" "$_com" 2>/dev/null; then
python -I -c "
import json, sys
d = json.load(open(sys.argv[1], encoding='utf-8'))
print('CI回执:', d.get('verdict'), '| commit', d.get('commit', '')[:8], '| 耗时', d.get('duration_s'), 's')
if d.get('note'): print('note:', d['note'])
for g in d.get('red_gates', []): print('红闸:', g)
for l in d.get('key_lines', []): print('key:', l)
" "$STATUS"
[ "$_verdict" = "green" ] && exit 0
exit 1
fi
_now=$(date +%s); _left=$((_deadline - _now))
[ "$_left" -le 0 ] && { echo "超时(${TIMEOUT}s)回执未落——CI 可能崩溃或未触发,查 game/tests/.gates/ 的 ci.lock 与 ci.log"; exit 2; }
sleep $(( _left < INTERVAL ? _left : INTERVAL ))
done