---
name: subagent-execution-governance
description: 把子代理派发从"监督型治理"升级为"契约型治理"：探查/实现/验证角色分离、契约快照（含负空间）、三级有界读取、单写者文件所有权、行动门槛替代轮次门槛、结构化 BLOCKED、token/工具/轮次预算、结构化结果回收与 repair-contract 恢复。用于派发实现型子代理（写代码/写文件类任务）、设计"谁读什么/谁写什么/何时算阻塞"的执行协议、审计子代理空转（反复探查不产出、并行写冲突、上下文膨胀）、或复盘"子代理烧了大量 token 却没产出"的会话。不用于纯问答、单轮小修改、或已有成熟任务流程的普通委派。
version: 0.1.0
triggers:
  - "派发实现型子代理（需要写文件/写代码的任务）"
  - "设计或评审子代理执行协议（读边界/写所有权/退出条件）"
  - "子代理反复探查不产出、空转、静默超时"
  - "多个子代理并行写同一批文件导致冲突"
  - "子代理会话 token 消耗异常膨胀（如输入数十万 token 无产出）"
  - "复盘长会话：审计子代理模型、token 消耗与纪律执行"
not_for:
  - "纯问答或单轮小修改"
  - "已有成熟流程的普通委派（无需重设计）"
  - "只查子代理用了什么模型（直接用会话日志 request/header 事件即可）"
depends_on:
  - task-mode-router
  - execution-discipline
  - minimal-implementation
---

# 子代理执行治理（Subagent Execution Governance）

> 本 skill 由一次真实实现会话审计（session-33b69ec9-1abf-454d-9555-f2d4031a9453）提炼而成。那次会话 3 小时、17 个子代理、执行层消耗 309 万输入 token（主会话 4 倍），`b21b9211` 一个子代理读了 65 个文件后仍被催"直接实现"，主会话被迫 28 次 `send_message` 催促 + 8 次 `interrupt_agent`，最终目标未 complete、会话中途悬死。**根因不是"监督不够凶"，而是子代理的工作边界、信息边界、写权限和退出条件没有工程化。**

## 核心原则

> 不要从"自由探索"直接走向"实现代理完全失去自主权"；正确形态是 **契约驱动 + 有界自治 + 结构化升级**。
>
> 目标不是"主代理不断管子代理"，而是 **系统规则让正常子代理根本不需要被管**。

失败因果链（本 skill 要打断的）：

```text
任务设计错误（探查/实现不分）
  → 执行边界过宽（无限读取授权）
  → 监督不得不频繁介入（催促/中断/重派）
  → send_message / interrupt 注入上下文
  → 上下文继续膨胀 → 缓存命中率恶化 → token 爆炸
```

## 一、角色分离（Discovery → Contract → Implementation → Validation）

禁止让一个子代理同时承担"先研究、再实现、再测试"四种认知模式：

| 角色 | 读 | 写 | 产出 |
|------|----|----|------|
| **Discovery**（探查） | 自由只读 | 禁止 | 契约快照草案（≤15 条 file:line 事实） |
| **Contract Builder**（主会话） | 验证契约 | 写契约文件 | 最终契约快照（含负空间） |
| **Implementation**（实现） | 有界读取（三级） | 只写自有文件 | 代码 + 变更清单 + 状态 |
| **Validation**（验证） | 只读目标文件/测试 | 禁止（或只改测试） | 测试结果 + 差异报告 |

**主会话承担 Contract Builder 与 Orchestrator**：探查结果必须经主会话验证、确认文件无冲突、写入契约快照后，才能派实现子代理。**禁止**探查子代理直接产出实现任务。

## 二、契约快照（Implementation Contract）

每个实现子代理派发前必须有一份契约快照，包含**正空间和负空间**：

```text
GOAL:           一句话目标（可验收）
OWN:            本子代理独占写入的文件（单写者）
MAY READ:       自动允许读取的文件/目录白名单
REFERENCE:      参考实现文件（读但不改）
IN SCOPE:       允许做的事
OUT OF SCOPE:   禁止做的事（防 scope creep）
MUST PRESERVE:  必须保持不变的现有行为/API
MUST NOT:       禁止触碰的模块/文件/顺手重构/兜底路径
EXIT:           完成条件（确定性可判）
BLOCKED:        允许升级阻塞的场景（仅限契约外信息缺失）
```

负空间（OUT OF SCOPE / MUST NOT）是防"模型顺手优化"最有效的手段，**必须有，且比正空间更具体**。

## 三、读取策略：三级有界读取（不是"只写不读"）

实现子代理不自由探索仓库，但保留必要自治——**三级读取权限**：

```text
Level 0 — 自动允许（无预算消耗）
  目标文件、契约快照、指定参考实现、直接测试文件

Level 1 — 有预算的自主读取（最多 N 个额外文件，需带 reason code）
  API_SIGNATURE_UNKNOWN       类型/签名未知
  TEST_EXPECTATION_UNKNOWN    测试预期未知
  TYPE_DEFINITION_REQUIRED    需要类型定义

Level 2 — 需要升级（禁止自主执行）
  跨模块架构探索、repo-wide grep、超过预算、发现契约错误
  → 走结构化 BLOCKED，不自行扩大范围
```

**不要把读的决策权全部收归主会话**（会变成"主代理当 I/O 调度器"的 ping-pong）；也不要完全放开（会回到无限探索）。Level 1 的 reason code 让升级可观测、可审计。

## 四、写入所有权：单写者文件所有权

```text
一个文件在同一时刻只有一个 writer owner。
```

- 共享文件：禁止两个子代理同时写；由主会话（或专门的 integration agent）最后统一修改。
- 派发前主会话必须做**文件级冲突检查**：把每个实现子代理的 OWN 集合求交，交集为空才并行。
- 并行写冲突（"目标文件已被父代理并行更新"）是调度错误，不是模型错误——重派前先改派发拓扑。

## 五、进度状态机（替代"催促"）

实现子代理**只允许**处于以下状态：

```text
IMPLEMENTING    工作中（每阶段报告当前状态与产出物）
MUTATED         已完成首次有效写入（重点信号）
BLOCKED         结构化阻塞（见下）
DONE            完成，附验证证据
FAILED          失败，附原因
```

**没有 `STILL_EXPLORING` 状态。** 正常协议里几乎不存在"催促"——orchestrator 不靠"你做好了吗/赶紧实现/不要再读了"判断执行状态，而是看状态机与资源观测。

空转判定（替代"催两次就判空转"）：连续 K 个有效动作窗口内 `mutation_count = 0` 且 `discovery_budget` 耗尽 → 自动判定 `STALLED`，由 watchdog（观测通道）触发，不是人工催促。

## 六、行动门槛（替代"第 N 轮必须建文件"）

轮次门槛会产生 Goodhart（先 touch 一个空文件满足 KPI 再继续探索）。改用**行动门槛**：

```text
在 exploration budget 耗尽之前，必须发生以下之一：
  A. 第一处有效代码修改（mutation）
  B. 返回 BLOCKED(reason, exact_missing_information)

禁止第三种状态："继续研究。"
```

实现子代理在首次 mutation 前，允许读取目标文件、契约参考文件、最多 K 次 Level-1 额外查询；预算耗尽而未 mutation 即 STALLED。

## 七、结构化 BLOCKED（防止"无限问主代理"）

BLOCKED 必须是结构化异常，不是自然语言求助：

```text
BLOCKED:
  missing_fact:      FooService.create() 的返回类型
  why_required:      当前实现需要决定错误处理分支
  already_checked:   [src/foo.ts, src/types.ts]
  requested_context: [FooService interface 定义]
```

orchestrator 收到后四选一应答：

```text
A. 自己回答（把缺失信息补进契约）
B. 派一个 discovery probe（极窄只读查询）
C. 修改契约（任务设计错误）
D. 判定任务设计错误 → 终止并重设计
```

**禁止**随手回 `send_message("继续看看")`。

## 八、资源预算（必须存在且可观测）

"不要无限探索"必须转成机器可判断的资源约束：

```text
Discovery：    ≤ 12 次 repo read/search，≤ 2 个 agent turns
Implementation：首次写入前 ≤ 6 次工具调用；额外探索 ≤ 4 次
Validation：   只允许 test / lint / targeted read
```

具体数字按任务调整，关键是：**预算必须存在、写入派发 prompt、并在会话纪律体检时可见**。

## 九、上下文回收：结构化结果 only

**工作历史不进入 orchestrator 的活动上下文。** 子代理完成/报告时只回：

```text
Agent A DONE
Changed:    [foo.ts, foo.test.ts]
Validation: 17/17 passed
Deviations: none
Blocker:    none
Details:    artifact://agent-A/report
```

禁止把子代理的完整工作过程（大量探查日志、多轮内部推理）splice 回主上下文。这是缓存保护的**结构性**方案，不是"少发几次消息"。多次大规模 splice 会杀死前缀缓存（实测单步 204,869 输入 token 且 cache=0 与大量 inbox/spliced 高度相关）。

## 十、恢复：checkpoint + repair contract（失败不重派全新 agent）

```text
真实状态 ≠ chat history

任务状态存盘：
  task-state.json          # 当前阶段、状态机、预算消耗
  implementation-contract.md
  completed-work.json      # 已完成文件与验证
  validation-report.json

失败路径：
  FAIL → 创建 repair contract（只处理失败点）→ 派实现
  而不是：重派全新 agent 重新理解仓库
```

主会话即使中断，新的 orchestrator 也能凭存盘状态继续，不从零重放。

## 十一、模型路由（排最后，且不是省钱主杠杆）

模型选择是最后一道配置，不是治理手段：

- Discovery/Validation：便宜高速档（如 `gpt-5.6-luna-max`）
- Implementation：工作马档（如 `gpt-5.6-terra`）
- Orchestrator/决策：前沿档（如 `gpt-5.6-sol-xhigh`）

**经验证据：设计不良的任务，用再便宜的模型也能烧掉 300 万 tokens。** 任务设计正确优先于模型省钱。

## 十二、派发协议清单（每次派实现子代理前过一遍）

1. 契约快照存在？含负空间（OUT OF SCOPE / MUST NOT）？
2. OWN 文件与并行子代理无交集？（单写者）
3. 读取白名单 + 三级权限写清楚了？
4. 预算（工具调用/探索次数/token）数值化并写入 prompt？
5. 行动门槛（mutation 或 BLOCKED，二选一）？
6. 状态机字段（IMPLEMENTING/MUTATED/BLOCKED/DONE/FAILED）要求回报？
7. 结果回收契约（只回结构化摘要，不回工作过程）？

## 与相邻 skill 的分工

- `execution-discipline`：执行层五条铁律（零轮询、不惊群、不肉身读、派发带进度回报）——管"执行纪律"，本 skill 是其**实现型任务的深化协议**。
- `decision-gates`：决策层不跑偏（checkpoint 证据锚、成本比对）——管"决策正确性"。
- 本 skill：**契约型治理**——管"子代理工作边界、信息边界、写权限和退出条件的工程化"。三者可叠加：先按 execution-discipline 自查执行姿势，再按本 skill 出契约，最后按 decision-gates 落 checkpoint。

## 反面案例速查（本 skill 来源）

| 场景 | 当时的错误 | 现在该怎么做 |
|------|-----------|-------------|
| 实现子代理空转 | `b21b9211` 2 轮读 65 文件、grep 30 次、941K tokens 后仍被催"直接实现" | 契约快照 + 三级读取 + 行动门槛：首次写入前 ≤6 次工具调用，无 mutation 即 STALLED |
| 并行写冲突 | 多个子代理并行写同一文件包，`b21b9211` 报告"目标文件已被父代理并行更新" | 派发前文件级冲突检查：OWN 集合求交为空才并行 |
| 催收式管理 | 主会话 28 次 send_message + 8 次 interrupt_agent，双方 token 叠加膨胀 | 状态机 + 结构化 BLOCKED + watchdog 判定，正常路径无催促 |
| 上下文撑爆缓存 | 169 次 inbox/spliced 注入，单步 204,869 输入 token 且 cache=0 | 结构化结果回收：只回 status/changed/validation/blocker/artifact ref |
| 目标中途悬死 | 第三个 goal 未 complete，会话停在 open step | checkpoint 存盘 + repair contract 恢复，不从零重放 |
| 便宜模型不等于省钱 | 执行层 309 万输入 token（luna-max），是主会话 4 倍 | 任务设计正确优先于模型省钱；模型路由排最后 |

## 工具化支撑（P1/P2 落地物）

本 skill 附两套运行时工具，让治理从"靠 prompt 自觉"变成"可观测、可执行"：

### P1 用量观测插件（subagent-usage-observer）

动态 Cordis 插件（Host 端），注册两个工具：

- **`subagent_usage`**：输入子代理 session id → 输出 tokens(input/output/cacheRead)、models、preset、toolCallsCount、mutationsCount、turns、steps。数据源 `sessionQuery.readSession`，只读。
- **`subagent_stalled_check`**：基于预算约束（mutationBudget 默认 1 / toolBudget 默认 6 / turnBudget 默认 3）保守判定：成功的 edit/write 结果达到预算 → `MUTATED`；检测到四字段结构化报告 → `BLOCKED`；工具行动预算或轮次兜底预算任一耗尽且无 mutation/BLOCKED/潜在写入工具 → `STALLED`；命令或委派可能写入但日志无法证明时保持 `IMPLEMENTING`，避免假阳性。

用途：替代"催 2 次判空转"的轮次拍脑袋。watchdog 用工具调用数与 mutation 事实判定，不靠催促。

### P2 splice 摘要化插件（subagent-splice-summarizer）

动态 Cordis 插件（Host 端），监听 `agent/pre-step` waterfall：

- 对进入步骤的 `source.kind === 'subagent-report'` 消息，若文本超 8000 字符，替换为结构化摘要（保留 status/changed/validation/deviations/blocker/artifact 各节前几行，其余截断；无结构字段的走首尾截断）。
- 目的：阻断超长子代理报告全文 splice 回主上下文，保护前缀缓存。非 subagent-report 消息与短报告不动。

注意：**这是体积保护层，不是缓存银弹**。A/B 实验（`scripts/splice-ab-experiment.js`）实测：审计会话 117 条注入共 ~55K tokens（仅占总输入 7%），摘要化仅省 2.6%——真正的大头是每步前缀重复（169 次注入/单步 204K 输入/cache=0）。所以 P2 的价值是"结构性防炸"（极长报告不会一次塞爆），缓存命中率仍需 A/B 重放实测。

### 体检脚本（scripts/）

- `session-discipline-audit.js <session id|zstd 路径>`：完整纪律体检（模型路由、每轮 token、子代理清单、催收成本、cache=0 异常、短轮询、全量读风险）。
- `splice-ab-experiment.js <session id|zstd 路径>`：A/B 体积对比（全量注入 vs 摘要化的字符/token 节省，按轮聚合，top 条目前 5）。

两个脚本均 Node ≥22（`node:zlib` 内置 zstd），支持直接传会话 ID 自动定位。

