真正有区分度的技术表达,不是背出 proposal、design、tasks 几个文件名,而是能解释它们为什么分开、怎样约束 Codex、如何与代码和测试互相证明,以及归档后如何保留决策历史。下面的问题都以 OncallAgent 当前仓库为依据,回答时可以先给结论,再根据追问补充案例和边界。
📷 [图片 token=FQdVbBNWto4sOtxDxIccLq2Mnkh(未能下载,见飞书原文)]
先讲清 OpenSpec 的位置
1. 已经有 Git、Issue 和测试,为什么还需要 OpenSpec?
**回答:**它们记录的是不同维度。Git 擅长回答“哪些文件发生了变化”;Issue 更适合讨论、分工和状态跟踪;测试证明部分可执行行为是否成立。它们通常不会完整保存一次变更的动机、非目标、行为契约、技术取舍和实施顺序。
OpenSpec 把这些信息组织成同仓库的 change。proposal 说明为什么做和影响范围,delta spec 定义必须满足的行为,design 记录实现决策,tasks 管理执行与验证。它不是替代 Git、Issue 或测试,而是把三者之间缺少的“变更语义”补齐。
📷 [图片 token=UsUtbpQMHolyyRxatXqcngn5nAc(未能下载,见飞书原文)]
不要说:“用了 OpenSpec 就不需要写测试或提交记录。”更准确的说法是,OpenSpec 负责定义和追踪变化,Git 保存代码历史,测试提供验证证据。
📷 [图片 token=VoLRbk1RZohJWBxsx6EcsqBxn4e(未能下载,见飞书原文)]
2. 哪些改动应该先创建 change?
**回答:**OncallAgent 的仓库规则要求,新增功能、用户可观察行为变化和非平凡缺陷修复,先创建或继续一个聚焦的 OpenSpec change。纯文档、仓库元信息或不改变行为的机械调整可以直接处理,除非任务明确要求走 OpenSpec。
判断重点不是“改了多少行”,而是“是否改变了系统承诺”。一个只改五行代码、却改变权限或 SSE 事件语义的修复,仍然需要规格;一次大规模但完全机械的格式调整,反而未必需要。
📷 [图片 token=If4ibLNRroEKCLxm1YbcqcIXn0b(未能下载,见飞书原文)]
3. Codex、OpenSpec 和开发者分别负责什么?
**回答:**OpenSpec 提供结构化上下文和验收边界;Codex 负责阅读仓库、生成或调整 artifacts、实施代码、运行检查并报告证据;开发者负责目标、范围、取舍和最终批准。AI 可以加快分析和执行,但不能替代方向判断。
在当前本地 Skills 中,Codex 还应读取 OpenSpec CLI 返回的 planningHome、changeRoot、artifactPaths 和 contextFiles,不能凭经验猜测文件位置。这个细节说明 OpenSpec 不只是几份模板,而是一张由 schema 和 artifact graph 驱动的工作图。
📷 [图片 token=HNjibGDIAoG1LfxPf58c2ufPnLc(未能下载,见飞书原文)]
4. OpenSpec 是瀑布开发吗?
**回答:**不是。它要求在编码前先收敛关键不确定性,但 artifacts 可以随着新证据更新。apply 阶段如果发现 design 不成立、Scenario 遗漏或任务粒度不合理,应暂停实现,先修正对应产物,再继续工作。
与瀑布式“文档签字后冻结”不同,OpenSpec 更接近可追踪的增量决策:允许变化,但要求变化留下记录,并重新建立规格、实现和验证之间的一致性。
📷 [图片 token=FLSBbAEvAol32IxyxiOcJiEznhd(未能下载,见飞书原文)]
为什么要拆成多种产物
5. proposal.md 解决什么问题,为什么不能直接写 design.md?
**回答:**proposal 先回答“为什么值得做、准备改变什么、涉及哪些能力、会影响哪些边界”。当前仓库常见结构是 Why、What Changes、Capabilities 和 Impact。它让审查者先判断问题与范围是否正确。
design 回答的是“怎样实现”。如果一开始只讨论类、接口和数据库,很容易在错误的问题上做出漂亮方案。把两者分开,能先控制方向,再优化路径。
📷 [图片 token=A9LbbXbkDoMOa3xcUDAcublbndd(未能下载,见飞书原文)]
6. delta spec 与普通需求描述有什么区别?
**回答:**普通需求常停留在“增加某功能”。delta spec 以 capability 为组织单位,用 Requirement 和 Scenario 写出可观察行为,并明确这次是 ADDED、MODIFIED、REMOVED 还是 RENAMED。
例如,“展示检索排名”还不够验收。更完整的 Scenario 会说明:当引用只在 BM25 一路召回时,向量排名和分数必须为空,前端必须显示“未召回”,不得伪造为第 0 名或 0 分。这样的描述才能稳定映射到契约、实现和测试。
📷 [图片 token=Pf7TbMEFpoK8SJxFiKEcMwUznu5(未能下载,见飞书原文)]
7. design.md 应该写什么?
**回答:**design 记录上下文、目标与非目标、关键决策、替代方案、风险和迁移策略。最有价值的内容不是“修改哪些文件”,而是“为什么由这一层承担责任”。
在 Embedding 批量限制案例中,设计选择把 chunk_size=10 放进 Provider,而不是让文档索引服务理解厂商上限。这样所有调用方共享同一安全行为,业务层仍然提交完整文本列表。这个“责任归属”的解释,比文件清单更能体现工程判断。
📷 [图片 token=Di3NbEKCDo3SJfx2FqFc6DqQngh(未能下载,见飞书原文)]
8. tasks.md 的价值是什么?
**回答:**tasks 把规格和设计转换成有顺序、可执行、可验证的工作单元。合格任务会明确产出或检查,例如“扩展共享引用契约并覆盖旧消息兼容测试”,而不是笼统写“完成后端开发”。
apply 会以未完成 checkbox 作为进度入口,但 checkbox 只是状态声明。任务越多并不代表越专业,关键是每项能够独立判断是否完成,并覆盖代码、测试和验证。
📷 [图片 token=F9lwb7wJZoMDp2xc3nhc5LP5nFH(未能下载,见飞书原文)]
9. .openspec.yaml 与 openspec/config.yaml 有什么区别?
**回答:**change 目录中的 .openspec.yaml 是变更级元数据,当前归档案例通常记录 schema: spec-driven 和创建时间,并随整个 change 一起归档。根目录的 openspec/config.yaml 是仓库级 OpenSpec 配置,声明默认 schema,并可提供项目上下文和 artifact 规则。
两者都不是应用运行配置,也不应该保存 API Key、数据库地址或云凭据。
📷 [图片 token=DnH6bJqqNoLQwkxVMKfckdX5njd(未能下载,见飞书原文)]
规格怎样与代码建立关系
10. delta spec 和 main spec 为什么要分开?
回答:openspec/changes/<change>/specs/ 表达“这次准备改变什么”,openspec/specs/ 表达“系统当前已经承诺什么”。分开后,未完成方案不会提前污染主规格,审查者也能清楚看到本次增量。
实现和验证完成后,sync 将 delta 智能合入 main spec。change 仍可保持 active,直到完成归档。两者可能包含相同 Requirement 的部分内容,但职责和生命周期不同。
📷 [图片 token=Eg0zbOOKqoBZFNxauGucBuWZngh(未能下载,见飞书原文)]
11. Requirement、Scenario 和测试是什么关系?
**回答:**Requirement 定义能力必须满足的行为,Scenario 给出触发条件和预期结果,测试则是证明这些行为成立的可执行证据之一。一条 Scenario 不一定机械对应一个测试函数,一个测试也可能覆盖多个边界。
verify 应从 Scenario 向实现和测试追踪,也应从关键代码反查它兑现了哪条 Requirement。写了 Scenario 不等于已经测试,测试通过也不自动证明所有 Requirement 都有覆盖。
📷 [图片 token=G52hbn5VwoOPwWxA39actC4EnZb(未能下载,见飞书原文)]
12. 如何证明追踪链不是形式主义?
**回答:**要能给出一条可以逐层定位的证据链。例如 Embedding 案例中:proposal 说明单次最多 10 条文本的问题;delta spec 定义小批量、大批量拆分和完整索引场景;design 决定在 Provider 设置批量上限;tasks 安排实现和两层测试;代码定义批量常量;测试验证 11 条输入拆成 10+1 且顺序保持;main spec 保存已生效行为;archive 与 WIKI 保存历史。
只有目录里存在几份 Markdown 文件不算追踪。每一层都需要回答上一层提出的问题,并能在下一层找到证据。
📷 [图片 token=KcsIbPTziokKNKxgRVTcLVl5nqd(未能下载,见飞书原文)]
13. 为什么共享契约变化要先改 packages/api-contracts?
**回答:**这是 OncallAgent 的仓库约束:HTTP envelope、错误码、DTO、OpenAPI 路径和 SSE payload 以 packages/api-contracts 为唯一事实来源。API 或 SSE 变化先更新共享契约,再同步后端序列化、前端消费和双方测试。
OpenSpec 在这里发挥的是边界检查作用。proposal 的 Impact、delta spec 的契约 Scenario、design 的兼容策略和 tasks 的实施顺序共同防止前后端各自复制一套类型。
📷 [图片 token=ZQXYbMX7UozpoExmtOBcZFDdnIy(未能下载,见飞书原文)]
不同工作入口怎样选择
14. new、continue、ff 和 propose 有什么区别?
**回答:**当前本地 Skill 中,new 只建立 change 并给出第一个 artifact 的指引;continue 每次创建一个当前 ready 的 artifact;ff 自己先创建 change,再按依赖顺序快速生成达到 apply-ready 所需的产物;propose 也是一站式创建并生成 apply-ready artifacts,适合边界已经比较清楚的需求。
因此不要把“先 new,再 ff”写成固定流程:本地 ff 和 propose 都包含新建动作。同名 change 已存在时应继续它,而不是重复创建。
📷 [图片 token=UZXIbFjrkoKbVtx7fzocImiEnX7(未能下载,见飞书原文)]
15. Artifact 的顺序可以写死吗?
**回答:**不能跨版本、跨 schema 写死。spec-driven 常见依赖是 proposal 先完成,specs 与 design 随后 ready,tasks 等它们完成后再生成;真正执行时仍应以 openspec status --change ... --json 返回的 done、ready、blocked 和 applyRequires 为准。
其他 schema 可能使用不同 artifact 名称。成熟的做法是读取状态图和 instructions,而不是把某篇教程的目录结构当成所有项目的永恒规则。
📷 [图片 token=PKIObeD3boJVapxx6hscZVgen7g(未能下载,见飞书原文)]
16. apply 只是按 tasks.md 写代码吗?
**回答:**不是。当前 apply Skill 先读取 status,再读取 openspec instructions apply 返回的全部 contextFiles。在 spec-driven change 中通常包括 proposal、delta specs、design 和 tasks,然后才逐项实施并更新 checkbox。
如果实现暴露了设计缺口,apply 可以暂停并回到 artifacts;如果任务需要 API、SSE、迁移或 tenant 边界,还必须遵守仓库对应规则。只读 tasks 而忽略规格与设计,会把执行清单误当成完整需求。
📷 [图片 token=HLe3bgaPyoB26Fx250HcLdX8n8c(未能下载,见飞书原文)]
完成、同步和归档意味着什么
17. tasks 全部勾选,是否等于变更完成?
**回答:**不等于。全部 [x] 只表示任务文件记录为完成,仍需检查 Requirement 是否有实现、Scenario 是否有测试证据、代码是否遵守 design、真实验证命令是否运行成功。
历史归档里的勾选也只能说明当时记录的状态,不能当成本轮环境重新验证的证据。OncallAgent 明确要求只报告实际运行并通过的命令,没有执行的检查必须如实说明。
📷 [图片 token=O8FMbxg4BoyWGaxEjyAcpeo2n8e(未能下载,见飞书原文)]
18. verify 与“把测试跑绿”有什么区别?
**回答:**verify 从完整性、正确性和一致性三个维度审查。完整性关注 artifacts 与 tasks 是否完整;正确性把 Requirement 和 Scenario 映射到实现及测试;一致性检查代码是否兑现 design 并遵守仓库既有模式。
测试是重要证据,但不是全部证据。测试可能遗漏场景,代码也可能虽然通过测试却绕过共享契约或 tenant scope。反过来,verify 中的搜索和推断也不是数学证明,关键结论仍要用真实代码、测试结果和运行证据支持。
📷 [图片 token=P0Wqbg2hFol6LtxllLZcw984n2f(未能下载,见飞书原文)]
19. sync-specs 是复制粘贴吗?
**回答:**不是。当前 Skill 把它定义为 agent-driven 的智能合并:ADDED 新增 Requirement,MODIFIED 只修改目标内容并保留未提及场景,REMOVED 删除已废弃行为,RENAMED 处理名称变化。如果 capability 不存在,才创建新的 main spec。
同步应尽量保持幂等,重复执行不应不断产生相同内容。sync 完成后 change 仍然 active,它只更新当前事实,不负责归档历史。
📷 [图片 token=BO1CbmuEtoZ89sxix4AcUV4An0g(未能下载,见飞书原文)]
20. archive 是删除还是 Git 回滚?
**回答:**都不是。archive 在检查 artifact、tasks 和 delta 同步状态后,把整个 change 移入 openspec/changes/archive/YYYY-MM-DD-<change-name>/,proposal、design、tasks、delta specs 和 .openspec.yaml 都被保留。
归档目录用于审计和追溯,不应继续开发。要修改已经生效的行为,应建立新的 change;要撤销代码,应通过 Git 回滚或反向变更处理,而不是改写历史档案。
📷 [图片 token=QyEcby1tmorQwSxD9T1cLXVon6b(未能下载,见飞书原文)]
21. wiki-sync 是 OpenSpec 自带功能吗?
**回答:**不是。它是 OncallAgent 仓库自定义的 VitePress 同步 Skill,也不是飞书同步。它为 active/archive change 生成 docs/changes/.../index.md,通过 @include 引用 OpenSpec 原文件,并维护总索引与 Sidebar。
脚本还校验符号链接、include 目标、页面集合、导航顺序以及归档 delta 与 main specs 的同步状态。脚本本身不执行 npm run docs:build,归档同步后仍需单独构建。OpenSpec archive 与 wiki-sync 是两个动作。
📷 [图片 token=TOvwbSgfCoEDDwxAZMNcCA8Unab(未能下载,见飞书原文)]
版本差异与最终表达
22. 为什么不能不加判断地照搬网页教程?
**回答:**OncallAgent 当前本地 OpenSpec Skills 标记为 generatedBy: 1.5.0实际协作应以仓库中的 .codex/skills/openspec-*、AGENTS.md、当前 openspec --help 和 status/instructions 输出为准。
📷 [图片 token=BLnCblbRVonankxZLoyc815kn7b(未能下载,见飞书原文)]
23. 面试中怎样用一分钟讲清楚?
我在 OncallAgent 中把 OpenSpec 当作变更控制面,而不是文档生成器。需求明确后先用 proposal 对齐价值和范围,再用 delta spec 把行为写成可验收 Scenario,用 design 记录责任归属和取舍,用 tasks 驱动代码、测试与验证。实施完成后,我会从完整性、正确性和一致性验证规格与代码,随后把 delta 智能合入主规格,将完整 change 按日期归档。仓库自定义的 wiki-sync 再通过符号链接和 VitePress include 生成可浏览历史,因此 Git 记录“改了什么”,OpenSpec 解释“为什么这样改、怎样证明改对了”。
如果继续追问,应立即落到真实案例,不要继续堆概念。小案例可以讲 Qwen Embedding 的 10 条批量上限;跨层案例可以讲三阶段检索排名如何贯穿后端、共享契约、SSE、前端和测试。
📷 [图片 token=HGIBbt8QNoDxUFxACjTcYDzenEf(未能下载,见飞书原文)]
回答时的三个原则
**先讲边界,再讲能力。**明确 OpenSpec 不替代 Git、测试和工程判断,可信度会高于夸大自动化能力。
**用证据链代替术语堆叠。**至少能指出一个 proposal、一个 Scenario、一项 design 决策、一条实现路径和一组测试。
**区分历史记录与当前验证。**归档 tasks 的勾选属于历史;只有本轮真实执行的命令和结果,才能作为当前验证证据。
📷 [图片 token=ZT0tbWuUIoMd3UxQbepcBF2onNe(未能下载,见飞书原文)]