尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Gas Town Convoy 生命周期设计:事件驱动的完成收敛与自动派发机制

Gas Town Convoy 生命周期设计:事件驱动的完成收敛与自动派发机制 Gas Town Convoy 生命周期设计事件驱动的完成收敛与自动派发机制【免费下载链接】gastownGas Town - multi-agent workspace manager项目地址: https://gitcode.com/GitHub_Trending/ga/gastown导读本文围绕 Gas Towngastown多 Agent 工作区管理器中Convoy车队的生命周期设计展开讲解其如何从被动追踪器进化为主动收敛的完成驱动单元daemon 内置的ConvoyManager通过 5 秒事件轮询与 30 秒 stranded 扫描双 goroutine 检测完成、喂养就绪任务并自动关闭车队同时深入剖析gt sling自动创建 convoy 的单批/批量语义、rig 自动解析、冲突处理与close --force/land手动覆盖机制。读完本文你将掌握 Convoy 从创建、派发、完成检测到落地通知的完整状态机以及对应源码文件与测试的定位方法。一、问题背景Convoy 为什么需要主动收敛Convoy 是 Gas Town 中跨 rig 追踪批量工作的持久化单元ID 形如hq-cv-*。它把多个 issue 组织在一个跟踪单元内让这批工作何时落地、包含哪些内容一目了然。相关概念与快速上手可参考 Convoy 概念文档。但原始设计中Convoy 是被动的追踪器它把工作分组却不会驱动工作推进。完成闭环存在结构性缺口Create → Assign → Execute → Issues close → ??? → Convoy closes这个???是Deacon patrol 运行gt convoy check——一种基于轮询的单点故障。当 Deacon 不可用时Convoy 不会关闭工作完成了但闭环永远无法落地land。由此引出本文要解决的核心设计目标——让 Convoys主动收敛于完成事件驱动由 issue 关闭触发完成检查而不是靠轮询集中管理由唯一观察者daemon负责避免散落的副作用钩子可人工覆盖人类可以强制关闭force-close。二、生命周期总览三条创建路径汇入同一闭环下面的流程图完整描述了 Convoy 从创建、派发、完成检测到落地的全过程以及 daemon 观察者与手动覆盖的位置三条创建路径最终都汇入同一个生命周期路径命令说明手动创建gt convoy create Feature X gt-abc gt-def --notify overseer显式创建 convoy 并跟踪指定 issue自动创建gt sling bead rig每次 sling 派发自动创建除非--no-convoy公式创建gt formula runconvoy 类型通过 convoy 类型公式执行见internal/cmd/formula.go的executeConvoyFormula完成检测由 daemon 的ConvoyManager驱动它并行运行两个 goroutine事件轮询每 5 秒轮询所有 rig 的 beads store hq通过GetAllEventsSince检测 close 事件并调用convoy.CheckConvoysForIssue——该函数既检查完成状态又喂养下一个就绪 issue 给 polecatStranded 扫描每 30 秒运行gt convoy stranded --json兜底捕获事件驱动路径遗漏的 convoy例如崩溃/重启后喂养就绪工作或自动关闭空 convoy。手动覆盖close --force、land完全绕过检查。历史沿革Witness 与 Refinery 观察者最初被设计为冗余观察者但后来被移除规格 S-04、S-05。daemon 的多 rig 事件轮询 stranded 扫描已提供充分覆盖。三、核心实现daemon 的ConvoyManager双 goroutineConvoyManager是 Convoy 的唯一观察者代码位于 internal/daemon/convoy_manager.go。它运行两个相互独立、均可通过 context 取消的 goroutine循环触发方式行为事件轮询GetAllEventsSince每 5 秒所有 rig store hq检测 close 事件调用CheckConvoysForIssueStranded 扫描gt convoy stranded --json每 30 秒通过gt sling喂养第一个就绪 issue自动关闭空 convoy3.1 事件轮询的工程细节从源码看convoy_manager.go事件轮询有几个关键工程决策首次轮询是预热seed第一轮只推进各 store 的高水位标记high-water mark而不处理事件避免 daemon 重启时重放整个历史事件pollStoresSnapshot中m.seeded标志按 store 独立高水位lastEventIDs sync.Map以 store 名为键保存时间戳每个 store 独立推进1 秒回看窗口由于 Dolt 中CURRENT_TIMESTAMP是秒级精度轮询时在上一高水位基础上回看 1 秒防止同秒内发生的事件在下个周期被漏掉eventPollLookback幂等与去重processedCloses与processedLifecycleEvents两个sync.Map防止同一 close 事件被多 store 复制或跨周期重复处理见 GH #1798issue 重新打开reopen时会清除去重标记使后续再次 close 能被重新处理指数退避轮询出错时间隔翻倍上限 60 秒GH #2686成功恢复后重置回 5 秒同时置位recoveryMode让 stranded 扫描在恢复期缩短到 5 秒重试convoy_manager.go中eventPollMaxBackoffparked rig 跳过isRigParked(name)为 true 的 rig 在轮询中被跳过hq store 始终用于 convoy 查找因为 convoy 是hq-*前缀Inf/NaN 数据兜底当事件表出现损坏行如 Go 零值time.Time写入 double 列导致 SQL 报错时isInfNaNError识别该错误并将高水位推进到当前时间以跳过坏行漏掉的事件由 stranded 扫描兜底捕获并发安全scanMu序列化scan()调用避免启动清扫startup sweep、常规扫描与恢复回调并发产生重复检查started atomic.Bool通过CompareAndSwap防止Start()被重复调用双重调用是 no-op 并记录警告。3.2 Stranded 扫描的分流逻辑scan()遍历gt convoy stranded --json的解析结果按状态分流convoy_manager.go中scan函数条件动作ReadyCount 0调用feedFirstReady喂养第一个就绪 issueTrackedCount 0空 convoy调用closeEmptyConvoy自动关闭但创建不足 5 分钟convoyGracePeriod见 GH #2303的空 convoy 会跳过避免 sling 的bd dep add尚未在 Dolt 中可见时误关有 tracked issue 但无就绪运行gt convoy check id检查完成度——若全部 issue 已关闭则自动关闭否则为 no-opfeedFirstReady按ReadyIssues顺序逐个尝试解析前缀beads.ExtractPrefix→ 解析 rigbeads.GetRigNameForPrefix→ 检查 rig 是否 parked → 执行gt sling id rig --no-boot若 convoy 有base_branch字段则追加--base-branch成功派发一个即返回任何一步失败都记录日志并继续尝试下一个 issueTestFeedFirstReady_UnknownPrefix_Skips、_UnknownRig_Skips等测试覆盖这些路径。另外Start()还会启动一个一次性启动清扫runStartupSweep等待 10 秒让 Dolt 稳定后执行一次scan()捕获 daemon 停机期间或 Dolt 不可用期间完成的 convoy。3.3 共享观察函数CheckConvoysForIssue事件轮询与 stranded 扫描共享的核心函数是 internal/convoy/operations.go 中的CheckConvoysForIssue其工作流通过 SDKGetDependentsWithMetadata在 hq store 查找追踪该 issue 的 convoy过滤tracks类型依赖排除blocks跳过已关闭isConvoyClosed与已 stagedisConvoyStaged状态以staged_开头、尚未 launch的 convoy对每个开放 convoy 运行gt convoy check id幂等对已关闭 convoy 是无害 no-op若检查后 convoy 仍开放调用feedNextReadyIssue反应式喂养下一个就绪 issue——使喂养事件驱动化而非依赖轮询式 patrol 周期。feedNextReadyIssue的就绪判定规则源码中feedNextReadyIssue与相关辅助函数状态必须为open且无 assignee类型必须可 slingIsSlingableTypetask、bug、feature、chore空类型默认视为 taskepic 等容器类型和 decision 等非工作类型排除未被阻塞isIssueBlocked检查blocks、conditional-blocks、waits-for、merge-blocks类型的未关闭依赖merge-blocks还要求 CloseReason 以Merged in 开头确认代码已合并见 #1893parent-child明确不视为阻塞按优先级数值小者优先排序、同优先级按 ID 字典序稳定排序每次调用只喂养一个 issue下一次喂养由下一次 close 事件触发防止批次溢出。此外CheckConvoysForIssue全部使用exec.CommandContext 进程组util.SetProcessGroupdaemon 关闭时通过 context 取消可杀死挂起的gt子进程而不阻塞关闭S-09测试TestConvoyManager_ShutdownKillsHangingSubprocess。二进制路径在 daemon 启动时解析并传入m.gtPath避免 PATH 依赖的行为漂移S-10。四、gt sling自动创建 Convoy 的完整行为gt sling为每次派发自动创建 convoy除非传--no-convoy但单 bead 与多 bead批量sling 的行为差异显著。相关源码位于 internal/cmd/sling_convoy.go 与 internal/cmd/sling_batch.go。4.1 单 bead slinggt sling sh-task-1 gastown执行流程检查sh-task-1是否已被某个开放 convoy 跟踪isTrackedByConvoy先通过原始 dep 查询向上找tracks类型追踪者再按描述模式Auto-created convoy tracking beadID兜底搜索开放 convoy若未被跟踪创建一个自动 convoyWork: issue-title跟踪该单个 beadcreateAutoConvoyID 形如hq-cv-5位随机小写字符生成一个 polecat、挂接 bead、开始工作。结果1 个 bead、1 个 convoy、1 个 polecat。即使只有一只 swarm也获得 convoy 面板可见性。createAutoConvoy还会把 convoy 的合并策略merge strategydirect/mr/local空默认 mr与base_branch通过beads.SetConvoyFields写入 convoy 描述并防御性地拒绝 flag 风格的标题传播进 convoy 名称IsFlagLikeTitle见 gt-e0kx5。4.2 批量 sling3 参数rig 自动解析gt sling gt-task-1 gt-task-2 gt-task-3rig 从 beads 前缀自动解析resolveRigFromBeadIDs遍历每个 bead 提取前缀并在routes.jsonl中查 rig所有 bead 必须解析到同一 rig。显式传 rig 参数仍然可用但会打印弃用警告gt sling gt-task-1 gt-task-2 gt-task-3 gastown # Deprecation: gt sling now auto-resolves the rig from bead prefixes. # You no longer need to explicitly specify gastown.批量 sling 创建一个 convoy 跟踪全部 beads。生成任何 polecat 之前runBatchSling调用createBatchConvoyinternal/cmd/sling_convoy.go创建标题为Batch: N beads to rig的单个 convoy并为所有 beads 添加tracks依赖。结果3 个 beads、1 个 convoy、3 个 polecats——全部并行派发spawn 之间间隔 2 秒防止 Dolt 锁竞争。convoy ID 与合并策略通过beadFieldUpdates存储在每个 bead 上getConvoyInfoFromIssue从 issue 的 attachment 字段直接读取convoy_id/merge_strategy/convoy_owned这样gt done可以通过快速路径找到 convoy避免不可靠的跨 rig dep 解析gt-7b6wf fix。没有上限gt sling 10 beads会生成 10 个 polecat 共享 1 个 convoy。唯一的节流是--max-concurrent默认 0 不限制它只是控制 spawn 速率不限制并发总数。4.3 Rig 解析错误显式 rig 时跨 rig 守卫逐一检查每个 bead 的前缀与目标 rig 是否匹配不匹配时报错并给出批量专属建议移除该 bead、单独 sling、或--force覆盖见 gt-myecw。自动解析时resolveRigFromBeadIDs在以下情况报错bead 无有效前缀建议显式指定 rig 或检查 bead ID前缀未在routes.jsonl中映射包括 town 级path.的hq-*前缀建议检查路由或从目标 rig 目录创建 beadbeads 解析到不同 rig逐一列出每个 bead 的 rig建议分别 sling。4.4 已被跟踪的 bead 冲突如果批量中任一 bead 已被其他 convoy 跟踪批量 sling 在生成任何 polecat 之前直接报错printConvoyConflict并打印冲突的 bead 及其所属 convoy现有 convoy 中所有 bead 及其状态冲突项高亮标记← conflict4 条推荐操作从本批次移除该 beadgt sling other-beads... rig将 bead 移入新批次bd dep remove convoy bead --typetracks后重新 sling关闭旧 convoy 后一起重新 slinggt convoy close convoy --reason re-batching改为把其他 beads 加入现有 convoygt convoy add convoy other-beads...。4.5 初始派发 vs daemon 喂养依赖处理差异初始派发是并行的所有 beads 顺序获得 polecatspawn 间隔 2 秒但在同一次批量 sling 调用中全部派发无论依赖如何。即使gt-task-2对gt-task-1有blocks依赖两者都会被 sling。isIssueBlocked检查只适用于 daemon 驱动的 convoy 喂养close 事件之后不适用于初始批量派发后续喂养尊重依赖任务关闭后daemon 的事件驱动喂养器在派发共享 convoy 的下一个就绪 issue 前会检查IsSlingableType与isIssueBlocked。4.6 无 convoy 模式批量 sling 加--no-convoy完全跳过 convoy 创建gt sling gt-task-1 gt-task-2 gt-task-3 gastown --no-convoy五、Issue 到 Rig 的解析Convoy 是 rig 无关的Convoy 是 rig 无关的。像hq-cv-6vjz2这样的 convoy 活在 hq store 中追踪sh-pb6sa这类 issue ID——但它不存储该 issue 属于哪个 rig。rig 关联在派发时通过两次查找解析提取前缀sh-pb6sa→sh-字符串解析beads.ExtractPrefix解析 rig在~/gt/.beads/routes.jsonl中查sh-→ 得到{prefix:sh-,path:gastown/.beads}→ rig 名称为gastown。这发生在feedFirstReadystranded 扫描路径和feedNextReadyIssue事件轮询路径调用gt sling之前。前缀未出现在routes.jsonl中的 issue或映射到path.的hq-*等 town 级前缀会被跳过——见isSlingableBead()。前缀解析与路由查找实现在 internal/beads/routes.go 的ExtractPrefix与GetRigNameForPrefix。两条路径在解析出 rig 名称后都会检查isRigParked目标是 parked rig 的 issue 会被记录日志并跳过而非派发。跨 rig 场景下feedNextReadyIssue通过StoreResolver直接查询各 rig store 获取最新状态GH #2624无 resolver 时回退到bd show --json子进程fetchCrossRigBeadStatus按前缀分组后逐 rig 查询。事件轮询还会在 issue 关闭后调用FireCrossRigDepNotifications通过gt nudge rig/witness通知受影响的跨 rig 依赖方解除阻塞。六、手动关闭与落地绕过检查的覆盖机制6.1gt convoy closegt convoy close已完整实现包括针对废弃 convoy 的--force。从 internal/cmd/convoy.go 的帮助文本可见# Close a completed convoy gt convoy close hq-cv-abc # Force-close an abandoned convoy gt convoy close hq-cv-xyz --reasonwork done differently # Close with explicit notification gt convoy close hq-cv-abc --notify mayor/行为要点默认验证跟踪的 issue 是否已全部完成--force即使跟踪的 issue 仍开放也强制关闭设置close_reason字段向 owner 与订阅者发送通知幂等——关闭已关闭的 convoy 是 no-op。典型使用场景废弃的 convoy 不再相关、工作在跟踪路径之外完成、强制关闭卡住的 convoy。6.2gt convoy landowned convoygt convoy land针对--owned标记的 convoy带gt:ownedlabel调用方管理生命周期支持--force即使有开放 issue 也落地、--keep-worktrees跳过 worktree 清理与--dry-run。6.3gt convoy check的增强# Check all convoys (current behavior) gt convoy check # Check specific convoy (new) gt convoy check hq-cv-abc # Dry-run mode gt convoy check --dry-run6.4 Owner 与通知目标gt convoy create Feature X gt-abc --owner mayor/ --notify overseer字段用途owner谁发起的请求收到完成通知notify额外订阅者若owner未指定默认取创建者来自created_by。七、Convoy 状态机OPEN ──(all issues close)──► CLOSED │ │ │ ▼ │ (add issues) │ │ └─────────────────────────────┘ (auto-reopens)向已关闭的 convoy 添加 issue 会自动重新打开auto-reopen。废弃状态新增OPEN ──► CLOSED (completed) │ └────► ABANDONED (force-closed without completion)超时/SLA未来规划可选的due_at字段用于 convoy 截止时间gt convoy create Sprint work gt-abc --due2026-01-15逾期的 convoy 会出现在gt convoy stranded --overdue中。八、命令速查当前已实现命令说明gt convoy create Name issue... [--owner] [--notify] [--owned] [--merge] [--molecule] [--from-epic epic]创建 convoy 并跟踪 issuegt convoy add convoy-id issue...向 convoy 添加 issuegt convoy status [id]显示进度与活跃 workersgt convoy list [--all] [--statusclosed] [--json]面板式列表gt convoy check [id] [--dry-run]检查并自动关闭已完成的 convoygt convoy stranded [--json]查找未分配工作的 convoygt convoy close id [--force] [--reason] [--notify]手动关闭gt convoy land id [--force] [--keep-worktrees] [--dry-run]落地 owned convoy未来规划gt convoy reopen convoy-id为清晰起见提供显式 reopen当前通过 add 隐式实现。九、实现状态、测试与关键文件核心 convoy manager 已完整实现并通过测试。规格 docs/design/convoy/spec.md 中的 S-01 至 S-18 全部 DONE涵盖事件轮询S-01、stranded 扫描S-02、共享观察函数S-03、daemon 生命周期集成S-06、MR beads 中的 convoy 字段S-07、生命周期安全S-08、子进程 context 取消S-09、解析后二进制路径S-10、测试缺口补齐S-11/S-12/S-13、测试基础设施改进S-14、文档更新S-15、纠正性跟进S-16、refinery 观察者根路径验证与修复S-17/S-18。关键不变量I-1~I-12与失败模式、恢复策略也均有文档与测试对应。剩余未来工作P2Owner 字段——定向通知打磨P3Timeout/SLA——截止时间跟踪。关键文件组件文件Convoy 命令internal/cmd/convoy.go自动 convoyslinginternal/cmd/sling_convoy.go批量 sling 与 rig 解析internal/cmd/sling_batch.goConvoy 操作CheckConvoysForIssue、feedNextReadyIssueinternal/convoy/operations.goDaemon 管理器事件轮询 stranded 扫描internal/daemon/convoy_manager.go前缀/rig 解析internal/beads/routes.goMR beads 中的 convoy 字段internal/beads/fields.goFormula convoyinternal/cmd/formula.goexecuteConvoyFormula规格与故事docs/design/convoy/spec.md测试资产internal/daemon/convoy_manager_test.go——22 个单元测试覆盖事件检测、stranded 分流、生命周期边界、错误路径与负向断言如TestEventPoll_SkipsNonCloseEvents_NegativeAssertion验证非 close 事件零副作用internal/daemon/convoy_manager_integration_test.go——//go:build integration集成测试验证挂起子进程不阻塞 daemon 关闭internal/convoy/operations_test.go 与 internal/convoy/store_test.go——观察函数边界条件与 store 辅助函数internal/daemon/daemon_test.go——daemon 级 manager 生命周期。相关文档Convoy 概念与使用通知投递协议十、总结从被动追踪到主动收敛Convoy 生命周期设计的核心转变是把完成检测从Deacon 轮询单点迁移到daemon 驻留的ConvoyManager5 秒事件轮询提供事件驱动的主路径30 秒 stranded 扫描作为崩溃/重启后的安全网共享的CheckConvoysForIssue幂等函数同时完成检查完成 喂养下一个就绪 issue手动close --force/land保留人类覆盖能力。gt sling的自动 convoy 创建单批 1 convoy、批量 1 convoy 多 polecat、rig 前缀自动解析则确保连 swarm of one 也有面板可见性。这套机制让 Gas Town 中的批量工作始终有一条可观测、可收敛、可人工介入的落地路径。【免费下载链接】gastownGas Town - multi-agent workspace manager项目地址: https://gitcode.com/GitHub_Trending/ga/gastown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表