news 2026/9/28 2:23:48

gsd-core 修复 writer 代理缺失 Edit 工具:用 tools: frontmatter 契约阻止共享文件被静默整文件覆盖

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
gsd-core 修复 writer 代理缺失 Edit 工具:用 tools: frontmatter 契约阻止共享文件被静默整文件覆盖

【免费下载链接】gsd-core

Git. Ship. Done - Core

项目地址:https://gitcode.com/gh_mirrors/ge/gsd-core
点击查看免费下载

本篇技术笔记围绕 gsd-core 仓库中一份归档 changeset(.changeset/archived/wise-hawks-bark.md,type: Fixed,PR #582)展开,剖析一个在 Agent 编排系统中非常典型的缺陷:spawn prompt 里要求"只用 Edit 做局部修改",但代理的tools:frontmatter 没有声明Edit,导致运行时回退到整文件Write,静默覆盖共享文件的兄弟章节。读完本文,你将理解tools:frontmatter 作为"可执行能力契约"的地位、六个 writer 代理的修复内容、同类 bug 家族(#571/#575/#581/#973)的演化,以及 gsd-core 在代码层(Write Guard hook 与 agent 安装校验)对这类事故的兜底机制。

一、这次修复改了什么:一张 changeset 与六个代理

1.1 changeset 原文事实

归档于.changeset/archived/wise-hawks-bark.md的变更记录陈述了以下事实:

  • 影响对象:六个 writer 代理——gsd-eval-planner、gsd-ai-researcher、gsd-domain-researcher、gsd-phase-researcher、gsd-ui-researcher、gsd-debug-session-manager。
  • 修复动作:在它们的tools:frontmatter 中同时携带Edit与Write。
  • 动机:这些代理的 spawn prompt 中声明了"仅 Edit"的写入纪律(Edit-only discipline),但此前 frontmatter 里没有Edit,该纪律"不可执行"(enforceable)。
  • 后果:没有Edit时代理回退到整文件Write,会静默覆盖(silently clobbered)共享文件(如AI-SPEC.md)中其他代理已写好的兄弟章节。
  • 同源问题:与 #571 属于同一 bug 类别,其中gsd-doc-writer已在 #575 修复;本 changeset 关联 issue #581。

1.2 六个代理各自"写什么"

从 agents/ 目录下的代理定义可以确认,这六个代理都是各自阶段/流程中负责产出文档工件的 writer:

代理产出工件共享文件风险点
gsd-domain-researcher.mdAI-SPEC.md的 Section 1b(Domain Context)与 ai-researcher、eval-planner 同写一个文件
gsd-ai-researcher.mdAI-SPEC.md的 Section 3、4、4b同上
gsd-eval-planner.mdAI-SPEC.md的 Section 5(Evaluation Strategy)、6(Guardrails)、7(Production Monitoring)同上
gsd-phase-researcher.mdRESEARCH.md(.planning/phases/XX-name/{phase_num}-RESEARCH.md)与其他 research 阶段产物同处一个规划目录
gsd-ui-researcher.mdUI-SPEC.md($PHASE_DIR/$PADDED_PHASE-UI-SPEC.md)设计契约文档
gsd-debug-session-manager.md调试会话文件(.planning/debug/{slug}.md)及其归档/提交会话状态与证据由多轮子代理共同追加

其中AI-SPEC.md是最典型的"多作者共享文件":gsd-domain-researcher写 Section 1b,gsd-ai-researcher写 Section 3–4b,gsd-eval-planner写 Section 5–7。三者由ai-integration-phase编排器依次 spawn。任何一个代理若用整文件Write覆盖,就会把前面代理写入的章节一并抹掉——这正是本次修复要消灭的静默破坏。

1.3 修复后的 frontmatter 实态

对照仓库现状,六个代理的tools:frontmatter 均已包含Edit与Write(与 compact 变体保持一致):

  • agents/gsd-eval-planner.md第 4 行:tools: Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion
  • agents/gsd-ai-researcher.md第 4 行:tools: Read, Write, Edit, Bash, Grep, Glob, WebFetch, WebSearch, mcp__context7__*, ...
  • agents/gsd-domain-researcher.md第 4 行:tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, ...
  • agents/gsd-phase-researcher.md第 4 行:tools: Read, Write, Edit, Bash, Grep, Glob, Skill, WebSearch, WebFetch, ...
  • agents/gsd-ui-researcher.md第 4 行:tools: Read, Write, Edit, Bash, Grep, Glob, Skill, WebSearch, ...
  • agents/gsd-debug-session-manager.md第 4 行:tools: Read, Write, Edit, Bash, Grep, Glob, Agent, AskUserQuestion

各代理的 compact 变体(如 agents/gsd-eval-planner.compact.md)同步携带相同工具集,安装时两者共同构成代理的完整定义。

二、问题本质:为什么"没有 Edit",Edit-only 纪律就不可执行

2.1 纪律声明在 prompt,能力声明在 frontmatter

以gsd-eval-planner为例,其执行流里对写入方式有非常明确的纪律要求:

  • gsd-domain-researcher.md的 Write contract 写明"ALWAYS use the Write tool to create files— never useBash(cat << 'EOF')or heredoc commands";
  • 同时,这些代理面向共享文件(AI-SPEC.md)的更新动作,语义上是**"更新第 N 节"**,正确姿势应是先Read全文再用Edit精准替换目标章节,而不是把整份文件重写一遍。

问题在于:prompt 里的指令只是"建议",frontmatter 里的tools:才是运行时真正授予的工具集。当代理的 spawn prompt 要求"用 Edit 做局部更新",但tools:里没有Edit时,模型在运行时只会收到Write这一个写工具,于是必然回退到整文件Write——而模型构建整文件 payload 时,往往基于它对文件的部分读取(尤其当文件很长、上下文预算有限时),于是"读了一个窗口、写回整份文件",窗口之外的其他代理的章节就被静默覆盖。

这就是 changeset 所描述的"so the Edit-only discipline in their spawn prompts is enforceable"的含义:修复的本质不是改 prompt 文案,而是给运行时补上执行该纪律所需的工具,让"纪律"从文本约束变成可执行约束。

2.2 共享文件的"兄弟章节"为何必然受害

AI-SPEC.md的写作顺序在 gsd-ai-researcher.md 与 gsd-eval-planner.md 中可见:domain-researcher 先写 Section 1b,ai-researcher 写 Section 3–4b,eval-planner 最后写 Section 5–7。每个代理的<input>都接收ai_spec_path(指向同一个AI-SPEC.md)。

在这种"多写者、单文件、分节协作"的模型下:

  • 任何一个代理在写入时读到的是磁盘上已含他人章节的完整文件;
  • 若它用整文件Write回写,payload 必须重建全文——任何遗漏都会成为"兄弟章节静默丢失";
  • 且这种丢失不报错、不告警,编排器只检查目标节是否存在,很难发现其他节被悄悄删掉。

2.3 同类 bug 家族:#571 → #575 → #581 → #582

changeset 明确指出本修复与 #571 属同一 bug 类别:gsd-doc-writer此前也因缺失Edit而回退整文件 Write,于 #575 修复。本次 #582 将同样的修复推广到六个代理,并关联 #581 作为跟踪。仓库中这类"指令要求 Edit、能力未授予 Edit"的缺陷不是孤例,而是一类系统性问题——这也解释了为何 changeset 会专门标注 "Same bug class as #571"。

更深层的历史教训记录在 hooks/gsd-write-guard.js 的头部注释中:issue #973 记录了一个 planner 读取ROADMAP.md约 16 行窗口后,用整文件Write覆盖了 292 行的完整文件,三个里程碑的已提交历史被摧毁(292 → 16 行,约 94.5% 的内容坍缩)。该事件直接催生了代码层的 Write Guard,也印证了"仅靠 prompt 指令无法阻止覆盖"这一判断——#973 中代理甚至读到了 advisory 提示,却将其判定为"非绑定"并继续执行。

三、代码层的兜底:gsd-write-guard.js 如何拦截灾难性 Write

prompt 修复(给Edit工具)降低了覆盖概率,但 gsd-core 的工程哲学是"由代码而非指令强制执行"(enforced by code rather than by instruction)。为此仓库在 hooks/gsd-write-guard.js 实现了 PreToolUse 写守卫。

3.1 守卫的触发条件与判定逻辑

该 hook 刻意收窄触发面,只拦截"整文件 Write 灾难性收缩 curated.planning/工件":

  • 只针对Write:Edit/MultiEdit天然不在范围内——源码注释写明 "Edit/MultiEdit replace bounded spans and are out of scope by design",即编辑类工具按构造就是有界的,不会整文件覆盖;
  • 只针对已存在的 curated 文件:.planning/ROADMAP.md、.planning/STATE.md、.planning/milestones/*-ROADMAP.md,以及 workstream 与 project 作用域下的对应变体(CURATED_PATTERNS数组共 8 条正则,见 gsd-write-guard.js);
  • 阈值判定:当 pending payload 行数低于磁盘文件行数的SHRINK_RATIO(0.4,即小于当前 40%)时硬阻断(exit 2, decision: 'block');小于FLOOR_LINES(40 行)的 stub 文件豁免;
  • 匹配前先 realpath 解析,防止通过符号链接路径绕过 curated 匹配。

其阻断信息会明确建议:"To fix: use Edit for a scoped change, or Read the full file and include every section in the Write"——与本次 changeset 的修复方向完全一致:能局部改就 Edit,必须重写就先读全文。

3.2 有界的保证与显式逃逸

守卫的头部注释也诚实声明了其边界:它阻止"意外/单次坍缩",但无法防御蓄意绕过(a determined agent)。为此提供了两条显式逃生通道:

  • 环境变量GSD_ALLOW_PLANNING_SHRINK=1:供人类交互式运行;
  • 一次性哨兵文件.planning/.gsd-allow-shrink:供工作流步骤使用——哨兵 15 分钟 TTL、绑定唯一目标文件路径、命中即消费,避免成为长期解锁。

这两条通道都写进了阻断信息与被守卫的绑定测试中,属于"有文档的、路径绑定、单次、可审计"的机制。

3.3 对本 bug 类的防护意义

不难看出,Write Guard 与 #582 的修复形成双层防线:

  1. 工具层(本次 changeset):给六个 writer 代理授予Edit,让"局部更新共享文件"成为运行时可能且自然的动作;
  2. Hook 层(#973 后建立):即使某个代理仍因故发起整文件 Write,只要目标是 curated 规划工件且坍缩超阈值,就会被硬阻断。

对于AI-SPEC.md这类非.planning/路径的共享文件,Write Guard 不直接覆盖(其 curated 匹配是封闭集合),因此工具层修复对AI-SPEC.md的保护更为关键——这正是本次 changeset 的价值所在。

四、tools: frontmatter 是权威契约:安装与校验的源码佐证

tools:frontmatter 不只是"给模型看的提示",它在 gsd-core 中是被程序读取的结构化契约。相关证据集中在 src/agent-install-check.cts:

  • extractToolsValue(来自 src/codex-agent-toml.cts,由 install 发射器与校验器共享)从 agent 文件解析tools:值,支持行内与 YAML 块列表两种形态;
  • deriveCodexSandboxMode(agentName, toolsRaw)根据tools:契约推导 Codex 代理应处的沙箱模式——即 tools 声明直接决定运行时权限边界;
  • checkAgentsInstalled/checkCodexModelPosture/checkCodexSandboxPosture三兄弟构成安装态校验:前者检查六个(乃至更多)agent 文件是否齐全,后两者逐文件比对.toml中的sandbox_mode/model与契约推导值是否漂移。

这意味着:frontmatter 中缺Edit不只是"能力少一个"的问题,而是整个工具契约与实际运行时行为的一致性被破坏。本次修复后,Edit与Write并存于六个代理的契约中,deriveCodexSandboxMode推导出的沙箱权限也随之与 prompt 中的写入纪律对齐。

五、写给 agent 作者的实践清单

从 #571/#575/#581/#582/#973 这一系列事故中,可以提炼出对任何多代理(multi-agent)编排系统的可复用检查项:

  1. 纪律与能力对齐:spawn prompt 中出现的每一个工具名(尤其是写类工具Edit/Write),都必须在tools:frontmatter 中有对应声明;"仅 Edit"类纪律若没有Edit,等于无效约束。
  2. 共享文件只许局部写:凡多代理共写的工件(如AI-SPEC.md、RESEARCH.md、UI-SPEC.md),写入动作应默认为Read全文 +Edit替换目标节;整文件Write仅在新建文件或明确的重写场景使用。
  3. 拒绝 heredoc 创建文件:各代理定义统一要求"ALWAYS use the Write tool",禁止Bash(cat << 'EOF')——避免绕过工具契约与守卫。
  4. 检查 compact 与完整变体同步:本次六个代理的.md与.compact.md变体必须同步携带相同工具集,否则安装后行为不一致。
  5. 验证安装态:通过gsd-tools validate agents一类校验(实现见 src/agent-install-check.cts)确认tools:契约、文件齐全度与沙箱模式三者一致。
  6. 依赖代码级兜底而非仅文案:如 #973 所示,代理可能"读到 advisory 但判定非绑定";对高价值 curated 工件(ROADMAP/STATE/milestone),务必有 Write Guard 这类硬阻断机制。

六、结语

wise-hawks-bark这张归档 changeset 篇幅虽短,却浓缩了 gsd-core 对 Agent 工具契约的一次系统性修正:把"Edit-only 纪律"从 prompt 文本变成可执行能力。它的意义不止于修复六个代理,更在于确立了一条工程原则——在多代理协同写共享文件的场景中,写入能力(tools:)必须与写入纪律(prompt)严格对齐,并在代码层保留对整文件覆盖的最终防线。这条原则连同 Write Guard 的实现(hooks/gsd-write-guard.js)与安装校验(src/agent-install-check.cts),共同构成了当前仓库中可查看、可验证的完整防御体系。

【免费下载链接】gsd-core

Git. Ship. Done - Core

项目地址:https://gitcode.com/gh_mirrors/ge/gsd-core
点击查看免费下载
上一篇:Windows-10-Toast-Notifications 项目常见问题解决方案
下一篇:GetQzonehistory 获取 QQ 空间历史说说卡在扫码登录:zbar 缺失故障复现与 3 条修复路线

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 2:23:05

大麦抢票脚本快速上手:3步配置参数,开售即自动下单

大麦抢票脚本快速上手&#xff1a;3步配置参数&#xff0c;开售即自动下单 【免费下载链接】Automatic_ticket_purchase 大麦网抢票脚本 项目地址: https://gitcode.com/GitHub_Trending/au/Automatic_ticket_purchase 热门演出中午开售&#xff0c;页面瞬间挤满&#x…

作者头像 李华