Merge pull request 'Retry card-close PR creation instead of failing silently' (#86) from fix/close-pr-retry into main
ci / test (push) Failing after 17m9s

This commit was merged in pull request #86.
This commit is contained in:
2026-09-30 16:48:29 +00:00
3 changed files with 47 additions and 5 deletions
@@ -48,3 +48,4 @@
| 同步前验收 | 03dc518eec70eccdc88a1c4f0e525e29bbd2a556 | a552939497cf7f5d8e0b2a9b19a2e9d110000bbd | 2026-09-30T16:03:44Z |
| 同步前验收 | ab3cae07b5854ee71c9dd36b226cabd0f778d48d | c5ed9444a6f6b3ae9be9336c360a1d09204d9491 | 2026-09-30T16:06:01Z |
| 同步前验收 | a612361b98879f4679cab503035e2111c593bf9b | 290b9bbd26077ae4320783d3b24b6cef0c2e6e05 | 2026-09-30T16:38:22Z |
| 同步前验收 | 0b0540e2d251ed6bee77e63fb1ed174dda3ddd41 | 888aa9471c0a6f6917eef1d4801e87b5c411cf6a | 2026-09-30T16:47:54Z |
+26
View File
@@ -0,0 +1,26 @@
# fix/close-pr-retry 任务卡
任务主记录:<PR 链接,task-finish 时填写>;状态:进行中;owner:weichen
## 基线与授权
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 0b0540e2d251ed6bee77e63fb1ed174dda3ddd41
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-close-pr-retry(worktree);分支:fix/close-pr-retry
- 授权与交付终点:用户 2026-09-30 授权修复 task.sh close 建 PR 静默失败问题(此前观察项)。终点:PR 合入 main。未授权:改 task.sh 其它命令、改 post-merge 钩子、改兄弟仓脚本。
## 目标与非目标
- 目标:`cmd_close` 的 PR 创建从单发 curl(`2>/dev/null || true` 静默吞错)改为:退避重试 4 次(间隔 3/6/9s)、每次失败信息可见(stderr)、终局失败时按 head 分支 GET open PR 兜底查回(覆盖 post-merge 钩子与手动 close 并发导致 PR 已存在的场景)、全部落空时保留既有 `[next]` 人工提示。
- 非目标:不改 precheck/start/sync/finish 的行为;不改卡片闭合的合入权归属(仍所有者);不给 close 加自动合入。
## 同步记录
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|---|---|---|---|
| 启动 | 0b0540e2d251ed6bee77e63fb1ed174dda3ddd41 | 0b0540e2d251ed6bee77e63fb1ed174dda3ddd41 | 2026-09-30T16:41:35Z |
## 结果与验证(完成后填写)
- 结果:cmd_close PR 创建重试化:4 次退避重试 + stderr 可见 + 终局按 head 查回已存在 PR + 既有 [next] 提示兜底。post-merge 钩子与 task-finish 自动闭合同路径受益(同一函数)。
- 验证:`bash -n` 通过;行为验证用桩服务器三模式(代码块由 sed 从真实文件逐字提取、在 worktree 分支上下文运行)——①ok:POST 201 → 首发即回收 `http://stub/pr/1`;②exists:POST 恒 500 模拟"PR 已存在"竞态 → 4 次重试后 GET `?state=open` 按 head 查回 `http://stub/pr/9`;③fail:真实 git-panel(TLS 失效主机)→ 4 次重试均可见 `建 PR 第 N 次未成功` → pr_url 空 → 走 [next] 人工提示分支。三轮合计行为与设计一致。
- 影响与剩余项:本次未产生真实卡片可作端到端样本(无已合入未闭合卡),闭环等下次真实 card-close 时由钩子自然验证。剩余项:无。
+20 -5
View File
@@ -231,11 +231,26 @@ EOF
local headbr pr_url=""
headbr=$(git rev-parse --abbrev-ref HEAD)
if [ -n "${GITEA_TOKEN:-}" ]; then
local resp
resp=$(curl -sS -X POST -H "Authorization: token ${GITEA_TOKEN}" -H 'Content-Type: application/json' \
"${web_base}/api/v1/repos/${owner}/${repo}/pulls" \
-d "{\"head\":\"${headbr}\",\"base\":\"${TARGET_BRANCH}\",\"title\":\"Close merged task cards (${headbr})\",\"body\":\"Auto-generated by task.sh close; see commit message for evidence.\"}" 2>/dev/null || true)
pr_url=$(printf '%s' "$resp" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("html_url",""))' 2>/dev/null || true)
# HTTPS 面偶发 broken pipe / 超时(AGENTS.md 访问事实节),单发 curl 会让 PR 静默
# 缺失(闭合分支推了、PR 没建,2026-09-30 三次踩中):退避重试 + 失败可见 + 终局
# 按 head 查回可能已存在的 PR(post-merge 钩子与手动 close 并发时 PR 会重复请求)。
local resp attempt api="${web_base}/api/v1/repos/${owner}/${repo}/pulls"
for attempt in 1 2 3 4; do
resp=$(curl -sS -m 25 -X POST -H "Authorization: token ${GITEA_TOKEN}" -H 'Content-Type: application/json' \
"$api" \
-d "{\"head\":\"${headbr}\",\"base\":\"${TARGET_BRANCH}\",\"title\":\"Close merged task cards (${headbr})\",\"body\":\"Auto-generated by task.sh close; see commit message for evidence.\"}" 2>&1) || resp=""
pr_url=$(printf '%s' "$resp" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("html_url","") if isinstance(d,dict) else "")' 2>/dev/null || true)
[ -n "$pr_url" ] && break
echo "[close] 建 PR 第 ${attempt} 次未成功${resp:+($(printf '%s' "$resp" | head -c 120))},退避重试" >&2
[ "$attempt" = 4 ] || sleep $((attempt * 3))
done
if [ -z "$pr_url" ]; then
resp=$(curl -sS -m 25 -H "Authorization: token ${GITEA_TOKEN}" "$api?state=open" 2>/dev/null || true)
pr_url=$(printf '%s' "$resp" | python3 -c 'import json,sys
for p in json.load(sys.stdin):
if p["head"]["ref"] == sys.argv[1]:
print(p["html_url"]); break' "$headbr" 2>/dev/null || true)
fi
fi
if [ -n "$newbranch" ]; then git checkout -q "${TARGET_BRANCH}"; fi
if [ -n "$pr_url" ]; then