【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址https://gitcode.com/gh_mirrors/ge/gsd-core点击查看免费下载导读本文深入解析 gsd-coreGit. Ship. Done - Core在 plan-phase规划阶段中引入的停滞检测Stall Detection机制当规划器planner或计划检查器plan-checker子代理的修订循环停滞——多轮迭代产出相同结果甚至彻底失去返回控制——时系统如何识别该状态、按既定策略升级并退出无限等待。文章以 stall-detection-in-plan-phase.md 为核心骨架结合 plan-phase 工作流、停滞检测辅助实现、回归测试 与 配置文档 逐层展开。读完本文你将掌握该机制的三个配置键planner.stall_detection_enabled/planner.stall_detect_interval_minutes/planner.stall_threshold_minutes、两个核心 Bash 函数gsd_stall_should_recover/gsd_stall_watch的完整行为契约以及如何在项目中启用、调优或安全关闭这一看门狗机制。1. 背景plan-phase 修订循环的停滞问题plan-phase 是 gsd-core 工作流中负责生成与修订规划文档的阶段。它以/gsd-plan-phase命令触发其核心是「规划器修订循环」规划器生成计划 → 计划检查器复核 → 若不合格则重新派发规划器修订最多迭代 3 次。该循环存在一类此前无法恢复的故障对应 bug #2650当 planner / plan-checker 子代理跨多轮迭代产出完全相同的输出甚至Agent()调用本身永不返回控制权时plan-phase 会无限期挂起。更致命的是原有恢复路径plan-phase.md 的 9a/11a「Filesystem Fallback」依赖Agent()已经返回才能触发——当调用根本不返回时该兜底永远无法生效。官方特性文档 stall-detection-in-plan-phase.md 将该机制约束为三条硬性需求需求编号内容REQ-STALL-01修订循环必须检测连续迭代中的相同规划输出以及更广义的「无任何进展」停滞REQ-STALL-02检测到停滞时系统必须在重试前升级策略自动浮出 accept / retry / stop 恢复菜单REQ-STALL-03最大停滞重试次数必须有界封顶于既有的最多 3 次迭代上限这三条需求共同界定了该机制的设计目标识别停滞 → 升级策略 → 有界重试而不是让看门狗本身变成新的无限循环。2. 设计总览有界看门狗而非长驻循环停滞检测的整体设计是「单周期single-cycle看门狗 编排层循环」每个被监管的 spawn 以run_in_backgroundtrue后台派发编排器记录一次TS$(date %s)作为派发时间戳随后反复调用gsd_stall_watch直到它返回waiting/active之外的分类结果。关键在于gsd_stall_watch每次只睡眠一个PLANNER_STALL_INTERVAL_MINUTES就返回绝不自己在内部循环跑满整个阈值。这一设计在 stall-detection-helpers.md 中有明确论证若让单个 Bash 工具调用阻塞threshold interval分钟默认配置下最长 15 分钟宿主工具自身的超时可能在函数打印结果前就杀掉调用——反而静默地废掉了这个修复本身。把循环上移到编排层散文orchestrator prose后每个周期都是一个短小默认 5 分钟、真实、有界的调用能可靠地交还控制权外层阈值由dispatch_ts跨调用累积来实现而非靠单次调用时长。这一模式明确镜像了 execute-phase 中已上线的executor.stall_*修复bug #3212提交e7942c21b但与之有一个关键差异executor 的监控是「纯散文式」prose-only的无法在阻塞式Agent()调用期间运行而 plan-phase 的每个gsd_stall_watch都是真实、有界的 Bash 子进程等待作为独立的工具调用发出无论后台代理自身的完成通知是否抵达都能按自己的节奏把控制权还给编排器。2.1 五种被监管的 spawn测试 plan-phase-stall-detection.test.cjs 明确规定恰好 5 处gsd_stall_watch $TS {outputFile}调用覆盖全部规划器/检查器 spawn 站点跨 plan-phase.md 与其 steps 目录步骤场景输出文件里的完成标记Step 8标准 planner spawn## PLANNING COMPLETE、## PHASE SPLIT RECOMMENDED、## ⚠ Source Audit、## CHECKPOINT REACHED、## PLANNING INCONCLUSIVEStep 8.5.1分块规划大纲chunked outline同上位于 chunked-planning-mode.mdStep 8.5.2分块逐计划chunked per-plan同上位于 chunked-planning-mode.mdStep 10plan-checker spawn## VERIFICATION PASSED、## ISSUES FOUNDStep 12修订循环规划器重派发无标记仅依赖 mtime见下文 4.4 说明测试通过readWorkflowCombined统计gsd_stall_watch $TS {outputFile}的出现次数并断言等于 5同时验证 researcher约第 404 行与 pattern-mapper约第 681 行的 spawn保持普通阻塞调用、不被本次修复波及超出 Agent Brief 范围。3. 三个配置键开关、间隔与阈值停滞检测的全部行为由三个配置键驱动默认值登记在 src/config.cts 的 schema defaults 中正式说明见 docs/CONFIGURATION.md配置键类型默认值语义planner.stall_detection_enabledbooleantrue总开关。truespawn 以run_in_backgroundtrue后台派发并挂上看门狗false改为普通阻塞等待走运行时原生的完成机制。planner.stall_detect_interval_minutesnumber5相邻两次停滞检查的间隔分钟数即gsd_stall_watch单周期睡眠时长也是 mtime 活性判断的窗口。planner.stall_threshold_minutesnumber10从TS起若无完成标记且无磁盘活性达到该分钟数即判定stalled并浮出恢复菜单。测试tests/plan-phase-stall-detection.test.cjs的「config schema」分组断言CJS 的VALID_CONFIG_KEYS与gsd-core/bin/shared/config-schema.manifest.json的validKeys必须同时包含planner.stall_detect_interval_minutes与planner.stall_threshold_minutes文档表中必须写出默认值5与10并通过gsd config-getgsd-core/bin/gsd-tools.cjs验证无配置时返回5/10。3.1 总开关的类型安全只有布尔false能关闭planner.stall_detection_enabled被设计为默认开启的显式退出default-on opt-out其判定规则异常严格只有精确的 JSON 布尔值false能禁用看门狗缺失、畸形、字符串、数字、null、对象等一切其他值全部安全失败为true保持默认开启。这一规则在两层得到验证Config Loader 层src/config-loader.cts中的resolvePlannerStallDetectionEnabled对undefined / null / false / 0 / {}一律返回trueCLI 层测试用 fast-check 属性测试生成大量非布尔字符串断言config-set全部被拒绝且不修改配置同时验证手改配置文件写入字符串false后config-get仍返回true「字符串 false 不得禁用看门狗」。src/config.cts中的键级校验则保证通过 CLI 设置时必须是布尔对planner.stall_detection_enabled传入非布尔值会报错Invalid planner.stall_detection_enabled .... Must be a boolean (true or false).。3.2 配置的生效范围root / project / workstream 层级该开关通过规范的config-get通道解析root / project / workstream 的作用域选择权归于 Config Loader而非工作流本地的 JSON 解析器。测试验证了完整的层级优先级root 配置为falseprojectGSD_PROJECTproduct-a覆盖为true→ 读到trueworkstreamGSD_WORKSTREAMalpha同理可覆盖workstream 配置省略该键 →继承 root 值不会因缺失而默认开启。这意味着你可以全局禁用、在特定项目或工作流下重新开启颗粒度灵活。4. 核心实现两个 Bash 函数的行为契约实现位于 lazily-loaded惰性加载步骤文件 stall-detection-helpers.md。之所以不直接内联进 plan-phase.md是因为 plan-phase.md 被 ADR-857 Phase 6 的PRE_PHASE6门冻结见 phase6-capstone-conformance.test.cjs基线仅剩约 36 字节余量任何新增字节都必须外置。该文件的 bash 围栏以运行时启动器runtime-launcher前奏开头解析gsd-tools.cjs、通过runtime-identity --raw验证包身份必须是opengsd/gsd-core随后用gsd_run query config-get读取三个配置键并对间隔/阈值做^[0-9]$正则校验非纯非负整数则回退默认 5 / 10。4.1gsd_stall_should_recover纯决策函数这是整个机制的「分类器」无 IO、无睡眠、纯同步只根据四个入参输出一个分类结果从不自行 kill 或重试任何东西。gsd_stall_should_recover() { local elapsed_seconds$1 threshold_minutes$2 marker_found$3 artifact_fresh$4 [[ $elapsed_seconds ~ ^[0-9]$ ]] || elapsed_seconds0 [[ $threshold_minutes ~ ^[0-9]$ ]] || threshold_minutes10 local threshold_seconds$(( threshold_minutes * 60 )) if [ $marker_found true ]; then echo marker_received; return 0 fi if [ $artifact_fresh true ]; then echo active; return 0 fi if [ $elapsed_seconds -ge $threshold_seconds ]; then echo stalled; return 0 fi echo waiting; return 0 }决策优先级短路顺序与四个输出值的语义优先级条件输出含义1marker_foundtruemarker_received完成标记已写入输出文件等待满足进入结果处理步骤2artifact_freshtrueactive磁盘上有新鲜的计划产物*-PLAN.mdmtime 在窗口内规划器仍在积极写盘继续等待3elapsed_seconds threshold_minutes * 60stalled超时且无任何活性信号 → 判定停滞浮出 accept / retry / stop 恢复菜单4其余情况waiting未超时继续等待测试对边界覆盖极其严格CLAUDE.md 的 limit-1 / limit / limit1 三件套elapsed599s, threshold10min→waiting阈值前一秒仍等待elapsed600s, threshold10min→stalled恰好到阈值即判定elapsed601s, threshold10min→stalled超过一秒同样判定marker_foundtrue, elapsed0与elapsed99999→ 均marker_received标记短路一切时间判断artifact_freshtrue, elapsed99999→active新鲜产物覆盖超时不误杀正在写盘的规划器属性测试fast-check25 轮无标记、无新鲜产物时stalled当且仅当elapsed_seconds threshold_minutes * 60。4.2gsd_stall_watch单周期真实检查gsd_stall_watch负责执行一个真实周期睡眠 检查 分类gsd_stall_watch() { local dispatch_ts$1 output_file$2 artifact_glob$3; shift 3 local markers($) [[ $dispatch_ts ~ ^[0-9]$ ]] || dispatch_ts$(date %s) sleep $(( PLANNER_STALL_INTERVAL_MINUTES * 60 )) local now elapsed marker_found artifact_fresh now$(date %s) elapsed$(( now - dispatch_ts )) marker_foundfalse if [ -f $output_file ]; then for m in ${markers[]}; do if grep -qF $m $output_file 2/dev/null; then marker_foundtrue; break; fi done fi artifact_freshfalse if [ -n $(find $artifact_glob -mmin -${PLANNER_STALL_INTERVAL_MINUTES} 2/dev/null) ]; then artifact_freshtrue fi gsd_stall_should_recover $elapsed $PLANNER_STALL_THRESHOLD_MINUTES $marker_found $artifact_fresh }实现要点输出文件检查仅当[ -f $output_file ]为真时才对标记数组逐个grep -qF固定字符串匹配避免正则误匹配活性检查用-mmin -N而非-newermt epoch这是从一次真实生产缺陷中提炼的教训——GNU find 的epoch简写不被 macOS 自带的 BSD find(1) 理解实测报Cant parse date/time: epoch配合2/dev/null会静默且永久地把artifact_fresh降级为false导致正在积极写盘的真实规划器被判为停滞。改为-mmin -N“N 分钟内修改过”后GNU findLinux、Git-for-Windows 自带 findutils与 BSD findmacOS行为一致且无需解析日期字符串。注意$artifact_glob刻意不加引号由 shell而非 find完成通配展开。dispatch_ts由调用方记录一次每次重复调用时原样传入外层阈值跨调用累积。4.3 数字校验的可靠性动机不是 RCE是防自杀helpers 文件对interval/threshold的非负整数正则校验有明确注释Bash 算术求值器对$(cmd)形状的操作数会硬报错syntax error: operand expected而非执行命令——安全评审曾一度将其标记为命令注入路径但已在 macOS bash 3.2.57 与 Docker bash:5 上实测证伪。真正的风险是可靠性畸形配置值会让停滞看门狗自身以 Bash 语法错误中止静默废掉整个挂起恢复机制。测试用$(touch marker)载荷验证函数既不报错、也不执行载荷命令且回退到安全默认分类waiting。4.4 修订循环Step 12的「无标记、仅 mtime」特例Step 12修订循环规划器重派发的gsd_stall_watch调用不带任何完成标记参数——修订后的规划器产出仍是*-PLAN.md文件因此只依赖 mtime 活性判断有新鲜文件 →active持续无新鲜文件且超阈值 →stalled此时浮出的恢复菜单是「1) Accept as revised接受修订稿进入步骤 13 / 2) Retry / 3) Stop」。5. 编排层接线{outputFile}绑定契约所有五个 spawn 站点的gsd_stall_watch调用都以{outputFile}作为第二参数例如TS$(date %s) PLANNER_STALL_RESULT$(gsd_stall_watch $TS {outputFile} ${PHASE_DIR}/*-PLAN.md ## PLANNING COMPLETE ## PHASE SPLIT RECOMMENDED ## ⚠ Source Audit ## CHECKPOINT REACHED ## PLANNING INCONCLUSIVE) # 循环直至非 waiting/activemarker_received - step 9stalled - 9a{outputFile}是编排器替换令牌binding token必须由编排器替换为紧邻的前一个run_in_backgroundtrueAgent() 调用返回的async_launched结果中的真实文件路径——与 docs-update.md 第 471 行既有的{outputFile from README agent result}先例一致。它不是Bash 变量helpers 文件明确指出上游没有任何赋值语句若以 Bash 变量引用会永久保持为空导致[ -f $output_file ]恒为假、marker_received永远不可达。plan-phase.md 步骤 7.99## 7.99. Bounded Stall-Detection Helpers (#2650)负责在派发前加载该步骤文件并宣告{outputFile}的绑定契约测试断言了 7.99 必须提及该令牌且 helpers 文档必须解释为何它对 plan-checker 是「承重」的plan-checker 通过 PASS 时不触碰任何*-PLAN.md文件——干净通过时永远没有新鲜 mtime。若{outputFile}未正确绑定一个两分钟即返回## VERIFICATION PASSED的健康检查器会在planner.stall_threshold_minutes到期后被误报为停滞——把已成功的代理报成挂起比原来的无限等待更糟。因此 marker 路径是该 spawn唯一可用的完成信号artifact glob 只是辅助。同时测试断言五个站点全部以gsd_stall_watch $TS {outputFile}形态出现且 plan-phase.md 与 chunked-planning-mode.md 中不得残留未赋值的$PLANNER_OUTPUT_FILE/$CHECKER_OUTPUT_FILE变量名——这是针对原始设计缺陷独立评审发现的阻塞项的回归防护。6. 关闭看门狗false分支的行为与代价当planner.stall_detection_enabled显式为false时每个被监管 spawn 改为发出相同的 Agent() 调用但省略run_in_background通过运行时原生的普通完成机制等待消费真实返回结果完全不调用gsd_stall_watch没有周期性 shell 睡眠。plan-phase.md 明确规定这仍是等待不是 fire-and-forget空、截断或无法识别的返回仍然走既有的 9a / 11a filesystem fallback完成标记契约## PLANNING COMPLETE、## CHECKPOINT REACHED、## VERIFICATION PASSED、## ISSUES FOUND、## PLANNING INCONCLUSIVE完全不变。代价是有界自动恢复的丧失docs 原话若运行时丢失完成交接completion handoff用户可能需要手动中断并使用既有文件系统兜底。这正是 docs/CONFIGURATION.md 中该键警告语的含义。CLI 关闭方式gsd config-set planner.stall_detection_enabled falsesettings-advanced.md高级设置工作流在提供持久化false选项之前必须先展示恢复能力丧失的警告默认true、有界自动恢复、以及config-set命令形态均被测试断言。由于该键是显式退出设计建议绝大多数项目保持默认开启。7. 与 teams-status 守卫的关系AC2 独立性停滞监控独立于researcher spawn 所用的query teams-status/CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS守卫在任何运行时都生效无论 teams 是否激活。测试对此有双重断言plan-phase.md 中query teams-status的调用点必须保持恰好 1 处既有的 researcher 横幅用途新的停滞块不得新增第二个调用点也不得让自己的行为以其结果为条件helpers 文档可以且应当在散文中解释这种独立性AC2 自文档化但不得出现query teams-status调用。8. 有界性与「3 次迭代上限」的衔接REQ-STALL-03 要求停滞重试有界。该边界在两层体现外层plan-phase 修订循环本身封顶 3 次迭代停滞检测不改变这一上限——它只负责在停滞时升级策略浮出 accept / retry / stop 菜单而不是静默重试内层每个gsd_stall_watch是单周期调用由dispatch_ts累积判定stalledstalled即终止等待循环——不会在看门狗层面无限轮询。测试还以「runs the exact shipped bash, not a hand-copied duplicate」为原则与 worktree-cleanup.test.cjs 的extractCwdGuardBash同一抽取模式extractStallHelpersBash()从 helpers 文档中按 bash 围栏边界切出真实发布内容再执行避免「Generative Fix Divergence生成式修复发散」缺陷类别并记录了一次 Windows CI 教训——约 73 行、引号密集的脚本经bash -c的 argv 序列化会在 Windows 上被打乱因此统一改为「写入临时文件、按路径执行」的runBashScript()传输缝。9. 调优建议与观测点收紧成功路径延迟planner.stall_detect_interval_minutes默认 5 意味着首个周期先睡满 5 分钟再检查——几秒内完成的规划器在这一路径上要等一个间隔才能被观察到这是有意披露的权衡见 helpers 文档「Disclosed tradeoff」。对追求更小成功路径延迟的项目可调低该键代价是更频繁的config-get调用。放宽停滞判定planner.stall_threshold_minutes默认 10 分钟。注意gsd_stall_should_recover对阈值入参同样做非负整数校验即使直接调用该函数传入畸形值也会安全回退为 10。观测方法gsd_stall_watch每周期打印marker_received | active | waiting | stalled四者之一编排层据此流转到对应步骤。完整决策逻辑可通过tests/plan-phase-stall-detection.test.cjs中对gsd_stall_should_recover的纯函数调用直接离线推演无需真实等待。运维前提所有配置写入.planning/config.json任意仓库贡献者皆可编辑经gsd config-get读取查看当前生效值用gsd config-get planner.stall_detection_enabled --raw等命令。附关键文件速查用途路径特性说明本文主体文档docs/features/stall-detection-in-plan-phase.md核心 Bash 实现gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md编排接线Step 7.99 / 8 / 10 / 12gsd-core/workflows/plan-phase.md分块规划 spawn 站点gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md回归与属性测试tests/plan-phase-stall-detection.test.cjs配置默认值src/config.cts配置键文档docs/CONFIGURATION.md高级设置入口gsd-core/workflows/settings-advanced.md冻结门测试tests/phase6-capstone-conformance.test.cjs赞分享【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址https://gitcode.com/gh_mirrors/ge/gsd-core点击查看免费下载相关推荐gsd-core 中 Codex discuss-phase 回退契约从“静默取默认值”到“停下、提问、等待”gsd core 中 Codex discuss phase 回退契约从“静默取默认值”到“停下、提问、等待” 本文围绕 gsd core 仓库 .changgsd-core 执行阶段停滞检测与安全恢复契约中断的 execute-phase 如何在重复派发前暴露部分计划漂移gsd core 执行阶段停滞检测与安全恢复契约中断的 execute phase 如何在重复派发前暴露部分计划漂移 本篇文章以变更记录 .changesetOpenChamber 1.6.0消息停滞时聊天自动恢复——SSE 停滞检测与软重同步的源码级解析OpenChamber 1.6.0消息停滞时聊天自动恢复——SSE 停滞检测与软重同步的源码级解析 OpenChamber 1.6.02026 01 29AI Agent人工智能代码智能体交互助手上一篇网易云音乐NCM文件解锁指南3步实现跨平台音乐自由下一篇深度实战使用UABEA高效编辑Unity游戏资源的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考