十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Gas Town PR Sheriff 审查实录:stale-heartbeat 会话活性安全补丁的 replacement/fixup 全流程

Gas Town PR Sheriff 审查实录:stale-heartbeat 会话活性安全补丁的 replacement/fixup 全流程 Gas Town PR Sheriff 审查实录stale-heartbeat 会话活性安全补丁的 replacement/fixup 全流程【免费下载链接】gastownGas Town - multi-agent workspace manager项目地址: https://gitcode.com/GitHub_Trending/ga/gastown导读本文以 Gas Town 仓库中pr-sheriff-evidence/gt-4468fx-pr4468-stale-heartbeat/目录下的完整 PR Sheriff 审查证据链report.md、evidence.json 与 merge-gate-check.txt为骨架还原针对原 PR #4468fix(polecat): require agent-process death before reconcile-killing stale-heartbeat sessions的一次完整replacement_fixup merge_decision审查。读者将从中掌握 Gas Town 的 PR Sheriff 证据驱动的审查协议15 路研究 55 评审 清理优先 归属保留 阻断扫描 合并门禁并深入理解isSessionProcessDead判定逻辑与 tmux checked-livenessfail-closed语义的源码级实现。一、案件背景PR #4468 要解决什么问题Gas Town 的 polecat 会话由gtCLI 管理会话通过gt prime、gt hook、bd show等命令周期性 touch 心跳文件以表明自身存活。原 PR #4468 的修复意图是在 reconcile 清理阶段不能仅凭心跳过期就判定会话死亡并执行 kill必须先确认 agent 进程确实退出再回收 stale-heartbeat 会话避免误杀仍然在跑的活跃 agent。从 manager.go 的源码注释可以确认该问题的原始背景polecat 会话通过gt命令 touch 心跳而长时间运行的工具调用可能让活跃 agent 在数分钟内不触发任何 gt/bd 命令因此 stale 心跳只是可疑而非确凿死亡证据早期gt-kncti还出现过权限不足导致的误判permission denied false positives。这正是 PR #4468 需要被认真对待的原因。但 PR Sheriff 在审查中发现原 PR 的意图正确实现却存在一个致命语义缺陷——它仍然把IsAgentAlive返回false或查询错误一律当作死亡处理。于是案件被归类为needs_replacement_or_fixup_analysis最终采用replacement_pr模式执行。二、PR Sheriff 审查流水线总览本次审查run_idgt-4468fx-pr4468-stale-heartbeatpolicy 版本pr-sheriff-policy/v1.1的顶层配置如下字段值Subject原 PR #4468 fix(polecat): require agent-process death before reconcile-killing stale-heartbeat sessionsModereplacement_fixupmerge_decisionAuthormarvincris / Marvin Cris Andradeknown 等级Base/Headupstream/main62fc77c7→d3858c3c替换分支polecat/mirelurk/gt-4468fx-pr4468-stale-heartbeatLabelsstatusmerge-ready、priorityp1、kindbug替换目标原 PR 保持status/needs-review、priority/p1、kind/bugTriage categoryneeds_replacement_or_fixup_analysisAction modereplacement_pr研究/评审规模Research legs 15/15Pre-implementation reviews 5/5Post-implementation reviews 5/5Cleanup-firstacceptable_minimalHuman approvalsnot_requiredkind/bug 实现Gate summary12 pass, 2 not_applicable, 0 waived, 0 failBlocking gates无针对 replacement merge gateFinal verdictmerge_replacementmerge path allowed: true值得注意的标签策略替换目标是statusmerge-ready而原 PR #4468 保持status/needs-review且没有任何 review 批准——原 PR 不是合并对象合并对象是替换分支。这种标签快照机制label-snapshot-replacement确保审查结论不会被原 PR 的陈旧状态污染。三、15 路独立研究问题被拆解成哪些维度PR Sheriff 协议要求对案件从 15 个独立研究视角research legs取证每路研究拥有独立的independence_key与actor避免单一视角盲区。本次 15 路研究覆盖了问题有效性R01stale 心跳本身不是死亡证据但 PR #4468 原样实现仍把IsAgentAlivefalse/query-error 当死——意图成立、实现有缺陷测试覆盖缺口R02现有测试缺少stale 心跳 活跃 agent / 不活跃 agent / 查询错误三类回归用例tmux 活性错误语义R03tmux.IsAgentAlive只返回bool把 tmux/pane/process 查询失败全部折叠成false——破坏性清理需要 checked 语义current-main 漂移R04原 PR 的 basef8e6d072落后于当前 main最终替换分支需 rebase 到62fc77c7清理优先R05最小修复应收紧既有谓词与 checked liveness而非新增 timeout/retry 层归属保留R06carry forward 时必须保留 marvincris/mayor 与原始 Claude co-author 归属标签门禁R07原 PRstatus/needs-review且无批准阻塞 merge-as-isCI 与本地验证R08原 PR CI 陈旧且缺回归测试最终需聚焦测试 CGO_ENABLED0本地验证阻断扫描R09批准缺失、base 陈旧、测试缺失、false-on-query-error 语义是原 PR 四大未解决问题可测试性R10应引入窄的包级 liveness seam checked tmux helper而非宽泛的 Manager 抽象PID 回退R11无心跳会话保留 legacy PID 回退stale 心跳 查询不确定应返回 not-dead运维安全R12误杀活跃会话的爆炸半径远大于保留不确定的 stale 会话历史背景R13GH#3342 与 daemon idle-reap 保护历史支持保留活跃 agent、fail-closed 处理不确定性证据 schemaR14最终证据要求 1555 记录、替换流程、归属、验证、阻断扫描与 checker 输出最终处置R15建议 current-main sheriff replacement/fixup 而非 merge-as-is。从 evidence.json 可以看到每路研究都记录了lens研究视角与结论摘要这正是证据可追溯性的关键设计。四、源码级剖析isSessionProcessDead 的三层判定逻辑本次 replacement 的核心改动位于 manager.go 的isSessionProcessDead。该函数采用三层判定且每一层都遵循只有能确认死亡才返回 true的 fail-closed 原则第一层心跳判定主路径gt-qjtq ZFC fixif townRoot ! { stale, exists : IsSessionHeartbeatStale(townRoot, sessionName) if exists { if !stale { return false // 心跳新鲜确定存活 } if t nil { return false // stale 但无 tmux无法取证不判死 } alive, err : sessionAgentAlive(t, sessionName) if err ! nil { return false // 查询出错fail closed不判死 } return !alive // 只有明确 not-alive 才判死 } }心跳本身的判定在 heartbeat.goIsSessionHeartbeatStale读取townRoot/.runtime/heartbeats/session.json若心跳时间戳距今超过SessionHeartbeatStaleThreshold可通过operational.polecat.heartbeat_stale_threshold在settings/config.json配置则判 stale心跳文件不存在时返回existsfalse避免 rollout 期间未启用心跳的会话被误杀交由上层回退。第二层活性查询 seam本次 replacement 的关键引入点var sessionAgentAlive func(t *tmux.Tmux, sessionName string) (bool, error) { return t.IsAgentAliveChecked(sessionName) }sessionAgentAlive是一个包级可替换变量——这正是 R10 要求的窄的包级 liveness seam测试可以注入假实现来覆盖 live/not-live/query-error 三种分支而无需引入宽泛的 Manager 抽象。第三层PID 信号探测回退legacy无心跳会话当townRoot为空或心跳文件不存在时回退到GetPanePIDos.FindProcessp.Signal(syscall.Signal(0))探测进程存活。这一层同样谨慎tmux 查询失败、PID 非数字都返回 false不判死仅当 PID 为空或信号探测失败进程确认不存在时才返回 true。五、源码级剖析tmux checked-liveness 的 fail-closed 语义PR #4468 原实现的缺陷根源在 tmux.go 的 API 设计。原IsAgentAlive是 bool-only 的// IsAgentAlive checks if an agent is running in the session using agent-agnostic detection. func (t *Tmux) IsAgentAlive(session string) bool { alive, _ : t.IsAgentAliveChecked(session) return alive } // IsAgentAliveChecked is like IsAgentAlive, but preserves liveness-query errors // so destructive cleanup callers can fail closed instead of // treating unknown liveness as confirmed absence. func (t *Tmux) IsAgentAliveChecked(session string) (bool, error) { // ... 底层调用 IsRuntimeRunningChecked(session, processNames) }replacement 的 API 兼容策略P04/POST04 评审确认是保留 bool 版IsAgentAlive不动兼容既有调用方如 tmux.go 的IsAgentRunning检查等新增 checked 版IsAgentAliveChecked把错误上抛供破坏性清理路径如isSessionProcessDeadfail closed。checked 语义的核心实现在 IsRuntimeRunningChecked优先读取会话环境变量GT_PANE_IDZFC gt-qmsx只检查声明的那个 pane减少误报面若getEnvironmentOptional失败则直接返回错误fail closed命中GT_PANE_ID后通过display-message获取 pane 当前命令与 PID与processNames匹配未命中时回退到list-panes -s全 pane 扫描任何一步查询失败都返回错误Windowspsmux下%N形式的 pane ID 可能不被display-message支持此时回退 legacy 路径而非直接判 false。换言之false只表示确认不运行错误则表示不知道——这两者在破坏性清理场景下必须被严格区分。这正是 PR #4468 语义缺陷的技术根源也是 replacement 的落点。六、测试回归live / not-live / query-error / no-tmux 全覆盖replacement 在 heartbeat_test.go 补齐了原 PR 缺失的聚焦回归。以TestIsSessionProcessDead_HeartbeatStaleUsesAgentLivenessheartbeat_test.go为核心通过表驱动测试注入sessionAgentAliveseam场景alivealiveErr期望 deadlive_agenttruenilfalse活跃 agent 存活禁止 killnot_live_agentfalseniltrue明确确认进程退出才允许 killquery_errorfalseerrors.New(tmux query failed)false查询不确定fail closed此外还有TestIsSessionProcessDead_HeartbeatFreshheartbeat_test.go新鲜心跳 → 无论 tmux 如何都判存活TestIsSessionProcessDead_HeartbeatStaleWithoutTmuxFailsClosedheartbeat_test.gostale 心跳但 tmux 为 nil → 无活性证据 → 不判死TestIsSessionProcessDead_EmptyTownRootheartbeat_test.go空 townRoot 跳过心跳路径验证向后兼容不 panic心跳基础用例TestIsSessionHeartbeatStale_NoFile/Fresh/Old无文件不判 stale、新鲜不 stale、超时判 stale与 V1/V2 心跳文件向后兼容读取。tmux 侧聚焦测试覆盖IsRuntimeRunningChecked的错误传播、IsAgentAlivebool 包装兼容、GT_PANE_ID声明 pane 检测、shellnode 子进程运行时识别、以及不存在的会话返回错误。七、清理优先cleanup-first评审拒绝创可贴式修补审查协议要求对修复方案做cleanup_first评估本次结论为acceptable_minimalreplacement只收紧既有的 stale-heartbeat 死会话谓词与既有的 tmux 运行时匹配器增加 checked 错误语义没有引入任何 retry 循环、更长 timeout 或第二层 liveness 机制。evidence.json 逐一核验了五类禁止模式均不存在bandaid创可贴、workaround_layer绕行层、compatibility_shim兼容垫片、patch_on_patch补丁叠补丁、scope_creep范围蔓延。5 路 pre-implementation 评审P01 清理优先 / P02 正确性 / P03 测试计划 / P04 API 兼容 / P05 政策路径全部 approve且 5 路 post-implementation 评审POST01-POST05针对最终 headd3858c3c同样全部 approve覆盖清理优先、正确性、验证充分性、API 兼容与政策/归属。八、验证矩阵与基线豁免replacement 的验证全部在最终 headd3858c3c上进行且包含一条重要的本地基线豁免逻辑git diff --check upstream/main..HEADpass无空白/冲突标记问题polecat 聚焦 stale-heartbeat 回归passfresh / stalelive / stalenot-live / stalequery-error / staleno-tmux fail-closed 全部通过tmux 聚焦 checked-liveness 回归passchecked 活性、bool 包装兼容、运行时检测CGO_ENABLED0 go test -count1 ./internal/polecatpass本地缺 ICU 头文件故禁用 CGOCGO_ENABLED0 go build ./cmd/gtpass基线证明tmux 全量测试中的两个本地失败sleep 被报告为 coreutils 相关在干净的upstream/main62fc77c7上可复现证明是本地基线行为、与本次改动无关——这是broad internal/tmux sleep/coreutils failures reproduce on upstream/main baseline的由来因此baseline_red_waiver判定为not_applicable。九、归属保留与替换流程不吞没原作者贡献replacement 流程的核心约束是attribution.requiredtrueevidence.json替换分支 carry forward 了 marvincris 提出的 stale-heartbeat live-agent safety 意图因此最终 commitd3858c3c通过Co-authored-by 方式保留归属——commit trailers 引用原 PR #4468、原始 commit4fabb623、mayor marvincrisoutlook.com及 Claude Fable 5 co-author。证据复用策略也很清晰evidence_reuse.reused_artifact_refs复用原 PR 快照与 R01/R06 研究成果fresh_artifact_refs则要求最终 checked 语义、测试与 current-main 验证全部是新生成的。superseded_closure状态为deferred_until_replacement_merge——在原 PR #4468 的替换版本带着归属证据落地之前不得将其标记为 superseded 关闭这也是 final verdict 中唯一的 required action。十、阻断扫描与合并门禁12 pass / 2 n/a / 0 failblocker_scan扫描了 labels、PR 评论、review 评论、CI 检查、beads 笔记、本地评审与验证共 7 类来源结论unresolved_blockers_foundfalse。原 PR #4468 的 merge-as-is 阻断项status/needs-review、无批准、原 head 陈旧、缺测试、bool query-error 语义由替换 headd3858c3c全部解除。最终门禁由gt-pr-sheriff-check执行merge-gate-check.txtcommand: gt-pr-sheriff-check --evidence pr-sheriff-evidence/gt-4468fx-pr4468-stale-heartbeat/evidence.json --merge-gate exit_code: 0 PR Sheriff: PASS merge_path_allowed: true verdict: merge_replacement gates: 12 pass, 2 not_applicable, 0 waived, 0 fail十一、最终裁决与可复用的审查范式final块的裁决为merge_replacementdecision_valid: true、merge_path_allowed: true理由完整覆盖原 PR 不可按原样合并current-main 替换分支通过了 15 路研究、5 路实施前评审、5 路最终 head 实施后评审、清理优先、归属保留、阻断扫描、聚焦验证与构建门禁且无未解决的阻断项。从本案例可以沉淀出一套可复用的审查范式适用于任何意图正确但实现有缺陷的补丁先验证据 vs 确凿证据破坏性操作kill/reap只允许依据确凿证据执行false确认不存在与查询失败不确定必须区分——checked error 是 fail-closed 的最小实现手段窄 seam 优于宽抽象用包级可替换函数 表驱动测试覆盖 live/not-live/error 三分支比引入 Manager 级抽象更可测、更易评审cleanup-first 否决膨胀修复只动既有点位禁止 retry/timeout/新活性层等创可贴归属与证据链不可分割carry forward 必须 Co-authored-by 保留原作者且替换的语义、测试、验证必须 fresh 而非复用基线失败不等于改动失败先用干净 baseline 复现证明无关性再决定是否豁免。如需深入复核可继续查看 report.md结论速览、evidence.json完整证据记录与 merge-gate-check.txt门禁输出并对照 manager.go、heartbeat.go、tmux.go 与 heartbeat_test.go 逐行验证。【免费下载链接】gastownGas Town - multi-agent workspace manager项目地址: https://gitcode.com/GitHub_Trending/ga/gastown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表