Stop That Shit 深潜:锁与预留机制如何防止 Subagent 预算超订
【免费下载链接】stop-that-shitStop That Shit(别再造史了)|面向 Codex/GPT 场景的多平台 Hook + Skill Guard:拦截 AI coding agent 无需求的哈希、校验和与任务范围膨胀。 A multi-platform Hook + Skill Guard for AI coding agents in Codex/GPT workflows: stop unrequested hashes, checksums, and task-scope creep.项目地址: https://gitcode.com/gh_mirrors/st/stop-that-shit
Stop That Shit(别再造史了)是一个面向 Codex、Claude Code、OpenCode、Pi 等多平台的 AI coding agent 防护插件。它的 delegation-state.cjs 用"预留(reservation)+ 会话锁(session lock)"两套机制,确保你设置的agents=N并发上限不会被重复的 Subagent 生命周期事件"超订"——这正是 Subagent 预算超订防护的完整指南。
什么是"Subagent 预算超订"?先搞懂问题
当 AI agent 把任务委派给 Subagent 时,宿主平台会不断抛来各种生命周期事件:SubagentStart、SubagentStop、tool_result、session.idle……这些事件可能:
- 乱序到达:宿主先报"完成"再报"启动"
- 重复到达:同一个启动事件推送两次
- 迟到到达:上一个任务早已释放,迟到的启动事件又指向旧 agent
如果计数器"来一条 +1,走一条 -1",重复事件就会把预算虚耗掉,新任务被误杀;或者同一批预留被重复记账,上限形同虚设。
Stop That Shit 的答案是:不数事件,只认账本。账本里每个预留有明确的容量上限,重复事件是幂等的,锁保证并发进程改账本时不互相覆盖。
预留(Reservation):先占坑,再干活
预留的核心逻辑在 delegation-state.cjs 的 reserveDelegation()。当你发起一次委派,账本会新增一条预留记录,结构非常直白:
| 字段 | 含义 |
|---|---|
actionId | 宿主给这次调用分配的唯一 ID |
pendingCount | 还没绑定到具体 agent 的"待用名额" |
agentIds | 已经绑定、正在运行的 agent 名单 |
asyncLaunched | 是否以异步方式启动 |
预算判定只关心一件事:活跃总量 = 所有预留的 pendingCount + agentIds 数量之和(见 activeDelegationCount())。决策器在 decision.cjs 中检查:已预留 + 本次申请 > agents=N时,返回AGENT_BUDGET_EXHAUSTED并拒绝——整批拒绝,不消耗任何名额。
三道防重复的关键设计:
- 同一 actionId 只记一次账。
acceptedActions表记住每个 action ID 的已计数量,inspectDelegation() 一旦发现新请求复用了已接受的 ID,就标记duplicateActionId,决策器以DUPLICATE_ACTION_ID拒绝(decision.cjs#L179-L187)。 - 同一 agent 只绑一次。bindSubagent() 检查
agentIdsSeen:重复的启动事件直接返回原状态,什么都不改。 - 已停止的 agent 最多消耗一个名额。"先报停、后报启动"的乱序场景下,迟到的启动只扣减一个
pendingCount,重复事件依旧幂等。
会话锁(Session Lock):并发进程不互相踩
多平台 Hook 的尴尬在于:宿主会为每个事件启动独立的 Hook 进程。两个进程几乎同时发起委派,谁先读账本都可能看到过期数据,最后写入的一方会把对方的预留"覆盖"掉——这就是并发超订。
Stop That Shit 的解法在 state.cjs:
- 锁文件即锁。acquireSessionLock() 用独占模式(
wx标志)创建<session>.lock文件,成功者写入pid:UUID作为令牌;失败者每 10ms 轮询一次,默认 1.5 秒超时。 - 陈旧锁自动清理。持有者崩溃后锁文件会残留,超过 10 秒的陈旧锁会被直接删掉重试,不会永久卡死。
- 只写不读也走锁。所有"合同更新 + 账本写入"都统一走 updateSession():持锁 → 读 → 改 → 写。而纯读操作不加锁,零开销。
- 原子落盘。writeState() 先写临时文件再
rename原子替换,即使写入中途崩溃,账本文件也要么是旧的完整版本、要么是新的完整版本,永远不会半截损坏。
controller.cjs 中有一句直白的注释:"Separate host processes can issue independent agent launches close together. Serialize delegation reservations across Hook processes."(分离的宿主进程可能几乎同时发起独立委派,因此跨 Hook 进程串行化预留。)
💡 这套"锁 + 原子替换 + 校验"的组合拳在 ARCHITECTURE.md 中被概括为:serialize the delegation ledger so concurrent Hook processes cannot oversubscribe the active agent limit(序列化委派账本,使并发 Hook 进程无法超订活跃 agent 上限)。
完整生命周期:从占坑到释放
把账本操作串起来,一次委派的标准流转是这样的:
| 阶段 | 账本动作 | 来源 |
|---|---|---|
| 1️⃣ 发起委派 | 新增预留,占 N 个名额 | applyDelegationFact(accepted) |
| 2️⃣ Subagent 启动 | 绑定 agentId,pendingCount -1 | bindSubagent() |
| 3️⃣ 确认完成 | 解除绑定;预留清空后整条删除 | releaseSubagent()/releaseReservation() |
| 4️⃣ 会话结束 | 清空全部预留 | clearDelegations() |
其中最保守、也最"反直觉"的一条规则是:只有确认的完成证据才能释放名额。工具返回"状态未知"时,预留原样保留(resultUnknown),宁可用完预算也不冒险放行——decision.cjs#L215 的拒绝理由会明确提示"收集到受支持的完成结果之前,请勿复用其预留容量"。
而 decision.cjs#L190-L199 还设了第二道闸:当存在无法证明上界的遗留活动(DELEGATION_STATE_UNPROVEN),在有限预算下会直接暂停新的委派,直到确认完成或开新会话。
幂等与降级:不确定时"只损预算,不损正确性"
这套设计值得新手学习的,是它对"不确定性"的取舍:
- 重复通知零副作用:delegation-state.test.cjs 验证了"agent 只绑定一次,重复启动不再增加活跃数";delegation-lifecycle.test.cjs 更是把 start/stop/after 三种事件的全部 6 种排列顺序各跑一遍,要求每种顺序下名额都恰好释放一次。
- 坏数据进入只读恢复:账本文件损坏时,recoveryState() 会把会话切入"只读恢复"模式,损坏文件原样保留供备份恢复,绝不把畸形计数变成"免费容量"。
- 宁可误拦,不可误放:watch 模式下放行的委派同样占名额(因为宿主确实允许它执行),从 watch 切到 guard 后,之前占的名额继续有效——预算账从不过夜清零。
小结:三招守住 agents=N
| 机制 | 解决的问题 | 关键位置 |
|---|---|---|
| 预留账本 | 事件重复/乱序导致的重复记账 | delegation-state.cjs |
| 会话文件锁 | 并发 Hook 进程互相覆盖 | state.cjs#L254-L295 |
| 完成证据闸门 | 未知状态下的名额泄漏 | decision.cjs#L189-L217 |
对新手来说,Stop That Shit 给出的通用启示其实超越了这个项目本身:给共享资源记账时,用"有上界的预留 + 幂等的状态迁移 + 进程间锁"替代简单的计数器加减,是防止并发超订的教科书式做法。想继续深入,可以从 ARCHITECTURE.md 的 Lifecycle transitions 一节和 test/delegation-state.test.cjs 的边界用例读起。
【免费下载链接】stop-that-shitStop That Shit(别再造史了)|面向 Codex/GPT 场景的多平台 Hook + Skill Guard:拦截 AI coding agent 无需求的哈希、校验和与任务范围膨胀。 A multi-platform Hook + Skill Guard for AI coding agents in Codex/GPT workflows: stop unrequested hashes, checksums, and task-scope creep.项目地址: https://gitcode.com/gh_mirrors/st/stop-that-shit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考