## 背景与决定
用户反馈:用 LLM 开发时 VOBS_C210 非常频繁。讨论后确定三条通道,按可靠性排序:
诊断文本(最高,顺着 agent 一定会看的构建输出)
AGENTS.md(中,常驻上下文)
SKILL.md(中低,支持面窄)
1.8.5 做了第一条(C210/C211 的 fix 改为先教结构)。本次做第二条。
用户的判断我采纳:**不应针对某个特定 LLM** —— 内容工具中立,工具差异只在"从哪个
文件名读"。所以只做两个通道(AGENTS.md + CLAUDE.md 指针),SKILL 后置。
## 关键设计:由框架产出,不由用户手抄
契约随框架演进(1.8.0 Show/ClientOnly、1.8.3 on()/C106、1.8.4 C107、1.8.5 fix 文案)。
手写的话每个项目一份副本**必然漂移**。所以契约正文放在 @vobs/cli 里,
`vobs agent-doc` 生成到项目 —— **与框架版本绑定**:升级 → 重新生成 → 永远同步。
## 三个开关
vobs agent-doc → stdout(先看)
vobs agent-doc --write → 写入 AGENTS.md + CLAUDE.md
vobs agent-doc --check → 与当前框架版本不一致时 exit 1(可进 CI)
--check 是把它从"靠人记得"变成"机器拦"的那一步。
## 不覆盖用户内容(关键)
- 生成内容包在 `<!-- vobs:begin -->` / `<!-- vobs:end -->` 之间,块外是项目自有约定
- **手写的 AGENTS.md(无标记块)不被覆盖** —— 生成块追加在后。
丢掉别人的规则比不生成更糟。
- CLAUDE.md 只写一行 `@AGENTS.md` —— 内容只有一份,不复制。
## 契约正文(一屏内,工具中立)
八节硬约束 + 一句话速记表,每条带诊断码:run-once / 条件渲染(RouterView vs Show vs
JSX 子节点)/ effect 自订阅(on())/ async / 客户端守卫 / 列表换引用 / 数字输入 /
SVG / 提交前自测。全部来自本会话真实踩坑与已落地的诊断。
## 实现中修掉的一个真问题
第一版用 `new URL('../../src/templates/agent-contract.md', import.meta.url)` 读模板,
**全量测试时 8 条全红、单独跑 8 条全过** —— vitest 全量跑会转换模块,
`import.meta.url` 指向虚拟路径,相对路径解析到不存在的文件。
改成把正文内联为 TS 模块 `templates/agent-contract.ts`(脚本从 .md 生成,转义反引号
与 ${),**免疫打包与转换**,也省掉发布时拷模板。全量 1390 测试通过。
## 验证
- agent-doc.test.ts(8 例):生成内容含全部关键约束 · **手写 AGENTS.md 不被覆盖** ·
再次 --write 只替换块且块外新内容保留 · --check 未生成 exit 1 / 生成后 0 ·
块被手改报不一致 · CLAUDE.md 被改成副本也报 · --body 不带标记块 ·
不加开关不写文件
- **真实 CLI 端到端**:①②③ 三个开关实跑,原有内容确认保留、CLAUDE.md 为 `@AGENTS.md`
全量门禁 1390 测试全绿 · typecheck 0 · check:source 0。
## 未做(等用户定)
SKILL.md 手册(建议先写「新增页面」与「发版」两本)·
其它工具的薄指针(.cursor/rules、.clinerules、copilot-instructions——取决于用户用哪些)。