@ifrankwang/openspec-agents 0.133.1 → 0.134.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -77,7 +77,7 @@ npm install -D @ifrankwang/openspec-agents
77
77
  1. **输入**:项目中的 OpenSpec 变更规范(变更说明、设计、任务清单、验收规格)。
78
78
  2. **分派**:主智能体把任务交给专职智能体——实现者负责写代码,审查者负责检查,质量把关者负责多维验证。
79
79
  3. **门禁**:每一步的结论通过检查后才进入下一步;不通过的结论自动回到实现环节修复。
80
- 4. **收尾**:变更完成后自动合并分支并清理执行环境,全程留痕可审计。
80
+ 4. **收尾**:变更完成后自动合并分支并清理执行环境;change 的全部任务组完成后,主智能体按状态视图指引汇总部署注意事项(规划文档提取 + 开发者实施补充点)向用户汇报。全程留痕可审计。
81
81
 
82
82
  ## 独立审查会话
83
83
 
@@ -6,6 +6,7 @@ permission:
6
6
  read:
7
7
  "*": deny
8
8
  "openspec/states/*": allow
9
+ "openspec/changes/*": allow
9
10
  edit: deny
10
11
  write: deny
11
12
  grep: allow
@@ -29,6 +29,7 @@ capabilities: ["orchestrator"]
29
29
  2. 若未初始化,调用初始化工具(如 `mcp__opx__orch_init`)传入 `change_id` / `task_group_id`;
30
30
  3. 若 worktree 未就绪,调用 `mcp__opx__orch_set_worktree`(或列表中对应工具);
31
31
  4. 再次调用状态查询工具获取权威「下一步」并严格按返回执行。
32
+ - 用户仅给出 change 名(未指定任务组)时的约定语义:表示以简单模式串行实施该 change 的全部任务组。先读取 `openspec/changes/<change 名>/tasks.md` 获取任务组清单与顺序(该文件是编排规划清单,非被编排项目的业务源码,也是初始化前唯一允许读取的项目文件):未初始化时从清单第一个任务组开始,以显式 `mode="simple"` 调用初始化工具;当前任务组收尾完成后初始化顺序中的下一个任务组,直至全部任务组完成后按状态视图指引完成部署注意事项汇报,编排结束。用户明确指定任务组或要求 full 模式时按用户指定执行,不适用本约定。
32
33
  - 子代理工具命名随 harness 不同:
33
34
  - DSH 形态:`openspec_developer` / `openspec_reviewer` 两个物理子代理(`openspec_<role>`,物理 agent 已收敛,9 种逻辑身份经 `_agent` 参数承载),分派时直接调用这些专用工具,不要使用通用 `subagent` 代替;
34
35
  - 其他 harness 按各自原生子代理机制分派(Claude Code / Codex / ZCode 插件已注入对应子代理)。
@@ -51,12 +52,13 @@ capabilities: ["orchestrator"]
51
52
  - 断点续传:子代理因步骤限制中断后重新分派即可继续,无需保存已完成子任务列表
52
53
  - 工具列出多个子代理时并排分派(单条消息中同时发送),不串行等待
53
54
  - 分派前校验分派 prompt:按「禁止事项」中禁止转述的动态内容清单逐项检查,校验通过后再分派
54
- - 先解锁、后 status、再探索;禁止在首次 status 前手动探查项目文件/状态文件
55
+ - 先解锁、后 status、再探索;禁止在首次 status 前手动探查项目文件/状态文件(例外一:用户未指定任务组时,按启动编排约定语义读取 tasks.md 确定任务组清单与顺序;例外二:change 全部任务组完成后的部署要点读取,以状态视图指引为准)
56
+ - change 全部任务组完成后,按状态视图「部署注意事项」区块指引向用户做一次收尾汇报:读主仓库 `openspec/changes/<changeId>/` 规划文档提取部署要点,并原样转述视图列出的实施补充点;不读业务源码、不自行解读或扩写实施补充点,无规划文档时跳过文档提取
55
57
 
56
58
  ## 禁止事项
57
59
 
58
60
  - 禁止修改代码/文档(edit/write 权限已强制禁止)
59
- - **禁止自行阅读/探查被编排项目的业务源码**——收到子代理的 issue 反馈时不得用 read 或其他工具理解 issue 技术内容。issue 的理解与修复是子代理职责,编排者只负责按状态流转分派,不解读 issue 内容;read 仅限状态文件做状态交叉验证
61
+ - **禁止自行阅读/探查被编排项目的业务源码**——收到子代理的 issue 反馈时不得用 read 或其他工具理解 issue 技术内容。issue 的理解与修复是子代理职责,编排者只负责按状态流转分派,不解读 issue 内容;read 仅限状态文件做状态交叉验证,以及 change 全部任务组完成后按状态视图指引读取 `openspec/changes/<changeId>/` 下的规划文档(仅提取部署要点,不读业务源码、不解读实现)
60
62
  - 禁止代子代理提交 step 裁决——step 提交必须由对应子代理独立调用提交工具完成;仅检查点决策可由编排者执行推进
61
63
  - **禁止居中代问/代记 blocker 的用户答复**——需用户拍板的问题由对应子代理直接向用户确认(有人值守)或自行裁决(无人值守),编排者不转述答复、不代记
62
64
  - 禁止使用通用子代理代替专用审查子代理——各子代理角色按工具返回的分派指令确定
@@ -43,6 +43,7 @@ phases:
43
43
  - "提交前完成自检并经 self_check_results 汇总:每项检查须含执行命令与结果摘要(证据可核验,供审查者分级复验对照与核验申报),禁止仅报结论不附证据"
44
44
  - "提交前按已加载的维度规范类 skill(style/architecture/performance/security/maintainability 能力类别)对本次变更文件逐项自检(聚焦本轮变更文件,不扩散全量代码),自检结论与工具检查结果一并汇总进 self_check_results;修复被驳回 issue 后再次提交前同样须按维度规范类 skill 对变更文件自检后再提交"
45
45
  - "自检须覆盖已加载的维度规范类 skill 的全部检查点,self_check_results 按维度逐项列结论(通过/遗留及理由),禁止仅汇总工具检查结果"
46
+ - "实施产出的部署注意点(数据/结构变更脚本执行、消息队列/缓存等中间件配置调整、新增配置项或功能开关、外部接口契约变化)凡属规划文档(design.md/tasks.md/proposal.md/specs/)未记载的,随提交在 self_check_results 中以 `部署注意点:` 开头的独立行逐条申报(每个注意点独占一行);规划文档已记载的不重复申报;修复轮重新提交时保留既有部署注意点行"
46
47
  - "修复完成的 issue 经 fixed_issue_ids 上报后进入待复核(review)状态,终态由报源 reviewer 复核裁定;不可修 issue 经 exempt_issue_ids 申请豁免(申请后进入待裁定(review)状态,由报源 reviewer 在后续审查轮次裁定,被驳回的 issue 回待修复);Info 级 issue 不阻塞提交,无需申请豁免;全部 blocking issue 均已上报修复或申请豁免后才可 passed 提交推进"
47
48
  - "确定性质量工具检查出的问题不得以压制手段(压制注解/注释抑制/规则排除配置/平台状态标注/构建跳过开关等)静默规避:确需压制时,必须在压制处代码注释说明清楚理由(commit message 可补充),并经 exempt_issue_ids 申请豁免由报源 reviewer 裁定;不得以 fixed_issue_ids 冒充修复;被驳回后须移除压制并真实修复"
48
49
  - "遇外部依赖、凭证、真实输入或必须 stub/降级/跳过验收才能继续时,以 verdict=failed 提交 blocker(含 source_role、task_id、category、description、evidence、attempted_actions、options)"
@@ -74,6 +74,7 @@ phases:
74
74
  - "提交前完成自检并经 self_check_results 汇总"
75
75
  - "提交前按已加载的维度规范类 skill(style/architecture/performance/security/maintainability 能力类别)对本次变更文件逐项自检(聚焦本轮变更文件,不扩散全量代码),自检结论与工具检查结果一并汇总进 self_check_results;修复被驳回 issue 后再次提交前同样须按维度规范类 skill 对变更文件自检后再提交"
76
76
  - "自检须覆盖已加载的维度规范类 skill 的全部检查点,self_check_results 按维度逐项列结论(通过/遗留及理由),禁止仅汇总工具检查结果"
77
+ - "实施产出的部署注意点(数据/结构变更脚本执行、消息队列/缓存等中间件配置调整、新增配置项或功能开关、外部接口契约变化)凡属规划文档(design.md/tasks.md/proposal.md/specs/)未记载的,随提交在 self_check_results 中以 `部署注意点:` 开头的独立行逐条申报(每个注意点独占一行);规划文档已记载的不重复申报;修复轮重新提交时保留既有部署注意点行"
77
78
  - "修复完成的 issue 经 fixed_issue_ids 上报后进入待复核(review)状态,终态由报源 reviewer 复核裁定;不可修 issue 经 exempt_issue_ids 申请豁免(申请后进入待裁定(review)状态,由报源 reviewer 在后续审查轮次裁定,被驳回的 issue 回待修复);Info 级 issue 不阻塞提交,无需申请豁免;全部 blocking issue 均已上报修复或申请豁免后才可 passed 提交推进"
78
79
  - "确定性质量工具检查出的问题不得以压制手段(压制注解/注释抑制/规则排除配置/平台状态标注/构建跳过开关等)静默规避:确需压制时,必须在压制处代码注释说明清楚理由(commit message 可补充),并经 exempt_issue_ids 申请豁免由报源 reviewer 裁定;不得以 fixed_issue_ids 冒充修复;被驳回后须移除压制并真实修复"
79
80
  - "遇外部依赖、凭证、真实输入或必须 stub/降级/跳过验收才能继续时,以 verdict=failed 提交 blocker(含 source_role、task_id、category、description、evidence、attempted_actions、options)"
@@ -25,47 +25,6 @@
25
25
  includeDefaultRoots: false
26
26
  customSkillDirs:
27
27
  - !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('./node_modules/@ifrankwang/openspec-agents/assets/skills', baseUrl))"
28
- - id: openspec-subagent-developer
29
- name: '@deepseek-ai/dsh-tool-subagent'
30
- config:
31
- provider: spawn
32
- toolName: openspec_developer
33
- persona: |-
34
- ## DSH 工具接入(必读)
35
-
36
- - 本流程的 opx_* 工具在 DSH 中以 `mcp__opx__*` 形式出现,子代理通常只需要:
37
- - `mcp__opx__status`
38
- - `mcp__opx__agent_submit`
39
- - 如果当前工具列表中没有这些工具,请立即调用 `dev_tool_search` 搜索 `opx`,或一次性解锁:
40
- ```json
41
- {"toolNames": ["mcp__opx__status", "mcp__opx__agent_submit"]}
42
- ```
43
- 如果计划使用 `todo_write` 跟踪任务,也请在同一轮一并解锁。
44
- - 严禁使用 `find` / `which` / `grep` 等 shell 命令搜索 `opx`、`openspec`、MCP 配置或安装路径;工具未出现是因为尚未解锁,不是未安装。
45
- - 解锁后第一件事是调用 `mcp__opx__status` 获取上下文,不要先探查仓库或读取状态文件。
46
-
47
-
48
- OpenSpec 编排流程专用 — 开发者(物理 agent,承载开发者逻辑身份)。遵循 TDD 开发新功能,使用 5-Why 分析修复 Bug。仅在 openspec-agents 工作流内由编排者分派使用,不用于通用开发任务。
49
-
50
- ## 角色(开发者身份)
51
-
52
- 你是后端开发工程师,专注于实现 OpenSpec 任务组中的代码开发任务,同时是质量门与接口测试的执行责任人:确定性检查与接口测试由你执行并经自检申报留痕,自检申报是 reviewer 分级复验的事实输入。
53
-
54
- 提交前按已加载 skill(含质量维度规范类)完成自检,审查兜底与终局裁定仍由对应 reviewer 承担(自检不替代 reviewer 裁定)。
55
-
56
- 自证/验证启动的服务与基础设施一律隔离启动、用后即停,自证完成即清理,不留残留。隔离对象不限于数据库(mock 服务、消息队列/缓存等中间件、扫描分析平台均按隔离标识隔离)。
57
-
58
- 任务执行与提交约束(Task/Issue 清单处理、环境问题处置、blocker 提交、文档读取范围、工具调用边界)以 opx_status 操作指引与约束区块为准,此处不重复描述。
59
-
60
- 修复的 issue 进入待复核,终态由提出者(对应 reviewer)裁定;被驳回后按修复未过次数门槛处理。
61
-
62
- 收尾阶段承担基准分支同步、合并冲突解决、回归复验与隔离环境清理职责;收尾验证中自报 issue 由自己复核收敛(区别于「修复的 issue 进入待复核,终态由提出者裁定」的常规路径)。
63
-
64
- ## 压制处理(开发者身份)
65
-
66
- 确定性质量工具检查出的问题若以压制手段解决(压制注解、注释抑制、规则排除配置、平台状态标注、构建跳过开关等),属豁免申请范畴:必须在压制处代码注释说明清楚理由(commit message 可补充),交由报源审查者判定合理性;不得以压制手段冒充修复。豁免被驳回后,须移除压制并真实修复。
67
- enableRunInBackground: false
68
- backgroundMode: one-shot
69
28
  - id: openspec-subagent-reviewer
70
29
  name: '@deepseek-ai/dsh-tool-subagent'
71
30
  config:
@@ -282,4 +241,45 @@
282
241
  确定性质量工具检查出的问题若以压制手段解决(压制注解、注释抑制、规则排除配置、平台状态标注、构建跳过开关等),一律视为豁免申请,须判定其合理性:可接受情形(无法消除的框架/第三方限制、有证据的误报,且风险可控、理由充分)→ 认可豁免;不可接受情形(真实缺陷被压制规避、仅图省事、无理由或理由不成立)→ 不认可,须要求移除压制并真实修复。判定时核验压制处的理由(代码注释、commit message);不认可时须给出可见的判定理由(写入问题描述/建议或驳回说明)。此判定适用于承担工具检查的审查身份(工具审查者、simple 合并审查者)。
283
242
  enableRunInBackground: false
284
243
  backgroundMode: one-shot
244
+ - id: openspec-subagent-developer
245
+ name: '@deepseek-ai/dsh-tool-subagent'
246
+ config:
247
+ provider: spawn
248
+ toolName: openspec_developer
249
+ persona: |-
250
+ ## DSH 工具接入(必读)
251
+
252
+ - 本流程的 opx_* 工具在 DSH 中以 `mcp__opx__*` 形式出现,子代理通常只需要:
253
+ - `mcp__opx__status`
254
+ - `mcp__opx__agent_submit`
255
+ - 如果当前工具列表中没有这些工具,请立即调用 `dev_tool_search` 搜索 `opx`,或一次性解锁:
256
+ ```json
257
+ {"toolNames": ["mcp__opx__status", "mcp__opx__agent_submit"]}
258
+ ```
259
+ 如果计划使用 `todo_write` 跟踪任务,也请在同一轮一并解锁。
260
+ - 严禁使用 `find` / `which` / `grep` 等 shell 命令搜索 `opx`、`openspec`、MCP 配置或安装路径;工具未出现是因为尚未解锁,不是未安装。
261
+ - 解锁后第一件事是调用 `mcp__opx__status` 获取上下文,不要先探查仓库或读取状态文件。
262
+
263
+
264
+ OpenSpec 编排流程专用 — 开发者(物理 agent,承载开发者逻辑身份)。遵循 TDD 开发新功能,使用 5-Why 分析修复 Bug。仅在 openspec-agents 工作流内由编排者分派使用,不用于通用开发任务。
265
+
266
+ ## 角色(开发者身份)
267
+
268
+ 你是后端开发工程师,专注于实现 OpenSpec 任务组中的代码开发任务,同时是质量门与接口测试的执行责任人:确定性检查与接口测试由你执行并经自检申报留痕,自检申报是 reviewer 分级复验的事实输入。
269
+
270
+ 提交前按已加载 skill(含质量维度规范类)完成自检,审查兜底与终局裁定仍由对应 reviewer 承担(自检不替代 reviewer 裁定)。
271
+
272
+ 自证/验证启动的服务与基础设施一律隔离启动、用后即停,自证完成即清理,不留残留。隔离对象不限于数据库(mock 服务、消息队列/缓存等中间件、扫描分析平台均按隔离标识隔离)。
273
+
274
+ 任务执行与提交约束(Task/Issue 清单处理、环境问题处置、blocker 提交、文档读取范围、工具调用边界)以 opx_status 操作指引与约束区块为准,此处不重复描述。
275
+
276
+ 修复的 issue 进入待复核,终态由提出者(对应 reviewer)裁定;被驳回后按修复未过次数门槛处理。
277
+
278
+ 收尾阶段承担基准分支同步、合并冲突解决、回归复验与隔离环境清理职责;收尾验证中自报 issue 由自己复核收敛(区别于「修复的 issue 进入待复核,终态由提出者裁定」的常规路径)。
279
+
280
+ ## 压制处理(开发者身份)
281
+
282
+ 确定性质量工具检查出的问题若以压制手段解决(压制注解、注释抑制、规则排除配置、平台状态标注、构建跳过开关等),属豁免申请范畴:必须在压制处代码注释说明清楚理由(commit message 可补充),交由报源审查者判定合理性;不得以压制手段冒充修复。豁免被驳回后,须移除压制并真实修复。
283
+ enableRunInBackground: false
284
+ backgroundMode: one-shot
285
285
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ifrankwang/openspec-agents",
3
- "version": "0.133.1",
3
+ "version": "0.134.0",
4
4
  "description": "OpenSpec change orchestration plugin for AI coding agents (Claude Code, Codex, ZCode, DeepSeek Harness, OpenCode)",
5
5
  "author": "ifrankwang",
6
6
  "license": "MIT",
@@ -12,7 +12,7 @@ import type { ParsedTask } from "../tasks-md.ts"
12
12
  import { assertOrchestrator, findTaskGroup } from "../derive.ts"
13
13
  import { assertPathWithin } from "../paths.ts"
14
14
  import { loadWorkflowFile, resolveWorkflowPath, type LoadedWorkflow } from "../workflow/loader.ts"
15
- import { createInitialWorkItem, isBlockingSeverity, isTerminalPhase, recommendForItem, resetInternalRetryCount, adjudicateStep, clearStepTags, REVIEW_FIX_POLICY_KEY } from "../workflow/engine.ts"
15
+ import { createInitialWorkItem, isBlockingSeverity, isTaskGroupSettled, isTerminalPhase, recommendForItem, resetInternalRetryCount, adjudicateStep, clearStepTags, REVIEW_FIX_POLICY_KEY } from "../workflow/engine.ts"
16
16
  import { renderWorkflowStatusView } from "../workflow/status.ts"
17
17
  import { taskChildrenOf } from "../task-children.ts"
18
18
  import type { WorkItem, WorkItemPhase } from "../workflow/types.ts"
@@ -357,21 +357,12 @@ function assertValidResetStepValues(
357
357
 
358
358
  /**
359
359
  * 判断目标组以外的全部任务组(workItems 中 `task:` 前缀、非目标组)是否"终态或从未激活",
360
- * 即 mode 切换窗口(W1 切组 / W2 重制当前组)的共同前置条件:
361
- * - 终态:item.phase === "done" 或 metadata.completed_at 已设置;
362
- * - 从未激活:无执行痕迹——tags 为空且所有 task children 的 phase 均为 todo 或终态(isTerminalPhase)。
360
+ * 即 mode 切换窗口(W1 切组 / W2 重制当前组)的共同前置条件(判定单一事实源:isTaskGroupSettled)。
363
361
  */
364
362
  function otherTaskGroupsSettled(state: OrchestrateState, targetGroupId: string): boolean {
365
363
  return state.workItems
366
364
  .filter((w) => w.id.startsWith("task:") && w.id !== `task:${targetGroupId}`)
367
- .every((item) => {
368
- if (item.phase === "done" || item.metadata["completed_at"] !== undefined) return true
369
- const noTags = Object.keys(item.tags).length === 0
370
- const childrenSettled = item.children
371
- .filter((c) => c.type === "task")
372
- .every((c) => c.phase === "todo" || isTerminalPhase(c.phase))
373
- return noTags && childrenSettled
374
- })
365
+ .every(isTaskGroupSettled)
375
366
  }
376
367
 
377
368
  // ─── 独立审查会话(kind=review,不绑定 OpenSpec change)───
package/src/core/views.ts CHANGED
@@ -199,6 +199,25 @@ export function renderDevSelfCheckDeclaration(metadata: Record<string, unknown>)
199
199
  return lines
200
200
  }
201
201
 
202
+ // ─── 部署注意点提取 ───
203
+
204
+ /** self_check_results 申报中实施补充点的行前缀(与 workflow 配置 implement instructions 的申报格式约定一致)。 */
205
+ const DEPLOYMENT_NOTE_PREFIX = "部署注意点:"
206
+
207
+ /** 从任务组 WorkItem 的 self_check_results 申报中提取「部署注意点:」前缀行(实施补充点,
208
+ * 规划文档未记载、开发者实施时申报);无命中返回空数组。命中行去除行首尾空白后原样保留
209
+ * (含前缀本身,便于转述端直接输出)。 */
210
+ export function extractDeploymentNoteLines(metadata: Record<string, unknown>): string[] {
211
+ const raw = metadata["self_check_results"]
212
+ if (typeof raw !== "string" || raw.trim() === "") return []
213
+ const hits: string[] = []
214
+ for (const line of raw.split("\n")) {
215
+ const trimmed = line.trim()
216
+ if (trimmed.startsWith(DEPLOYMENT_NOTE_PREFIX)) hits.push(trimmed)
217
+ }
218
+ return hits
219
+ }
220
+
202
221
  // ─── 占位符插值层 ───
203
222
 
204
223
  /** 插值白名单:仅这些 key 可被 {{key}} 占位符引用,其余一律保留原文(防配置注入任意动态值)。 */
@@ -32,6 +32,18 @@ export function isTerminalPhase(phase: WorkItemPhase): boolean {
32
32
  return phase === "done" || phase === "cancelled"
33
33
  }
34
34
 
35
+ /** 任务组「终态或从未激活」(mode 切换窗口与终态视图共享的单一判定):
36
+ * done 或 completed_at 已设置即终态;否则须无执行痕迹(tags 为空)且全部 task children
37
+ * 处于 todo 或终态。 */
38
+ export function isTaskGroupSettled(item: WorkItem): boolean {
39
+ if (item.phase === "done" || item.metadata["completed_at"] !== undefined) return true
40
+ const noTags = Object.keys(item.tags).length === 0
41
+ const childrenSettled = item.children
42
+ .filter((c) => c.type === "task")
43
+ .every((c) => c.phase === "todo" || isTerminalPhase(c.phase))
44
+ return noTags && childrenSettled
45
+ }
46
+
35
47
  export function isBlockingSeverity(severity: string | undefined): boolean {
36
48
  return severity !== undefined && (BLOCKING_SEVERITIES as readonly string[]).includes(severity)
37
49
  }
@@ -6,7 +6,7 @@ import type { ExemptionRecord } from "../exemptions.ts"
6
6
  import type { LoadedWorkflow } from "./loader.ts"
7
7
  import type { EngineRecommendation } from "./engine.ts"
8
8
  import type { DetectChangesResult } from "../git.ts"
9
- import { getStepVerdict, isTerminalPhase, isBlockingSeverity, phaseStepMismatch, REVIEW_STEP_TO_LAYER, blockingStepChildren } from "./engine.ts"
9
+ import { getStepVerdict, isTerminalPhase, isBlockingSeverity, isTaskGroupSettled, phaseStepMismatch, REVIEW_STEP_TO_LAYER, blockingStepChildren } from "./engine.ts"
10
10
  import { agentToReviewDimension, agentToReviewLayer, readIssueSource } from "../constants.ts"
11
11
  import { resolveChildIssueFields } from "./reset.ts"
12
12
  import { taskListOf, issueChildrenOf } from "../task-children.ts"
@@ -14,7 +14,9 @@ import {
14
14
  renderSkillSuggestions, renderEfficiencySteps, renderWorktreeSection,
15
15
  renderAgentSummaries, renderDevSelfCheckDeclaration, renderTaskItem, formatFilePath, formatSeverity,
16
16
  isWorktreeReady, renderWorktreeNotReady, interpolateText, renderStateMismatchDiagnostic,
17
+ extractDeploymentNoteLines,
17
18
  } from "../views.ts"
19
+ import { taskGroupFromWorkItem } from "../derive.ts"
18
20
  import { resolveMustDoForCaps, SKIP_REASON_FORMAT } from "../tools/gate.ts"
19
21
 
20
22
  export interface WorkflowStatusViewOptions {
@@ -71,7 +73,7 @@ export function renderWorkflowStatusView(
71
73
  return renderSuspended(item)
72
74
  }
73
75
  if (item.phase === "done" || item.phase === "cancelled") {
74
- return renderTerminalPhase(item, options.state)
76
+ return renderTerminalPhase(item, options.state, caller)
75
77
  }
76
78
  if (rec.status === "blocked") {
77
79
  if (caller.orchestrator) {
@@ -158,8 +160,10 @@ function renderSuspended(item: WorkItem): string {
158
160
  /** 终态渲染:done 区分已完成(completed_at 已写)与待收尾;cancelled 呈现已取消。
159
161
  * 独立审查会话(kind=review)待收口时渲染审查结果报告视图(issue 按严重级别/维度/文件位置归并,
160
162
  * 只审模式的交付物)——fix=none 下 issue 停留 todo 态、step tag=failed、item=done 为合法组合,
161
- * 报告视图不渲染豁免/复核操作提示(该通道在只审模式下是死路,无修复者消费裁定结论)。 */
162
- function renderTerminalPhase(item: WorkItem, state?: OrchestrateState): string {
163
+ * 报告视图不渲染豁免/复核操作提示(该通道在只审模式下是死路,无修复者消费裁定结论)。
164
+ * change 会话两形态在编排视角下追加「部署注意事项」区块(见 renderDeploymentNotesSection),
165
+ * 子代理视角维持既有文案。 */
166
+ function renderTerminalPhase(item: WorkItem, state?: OrchestrateState, caller?: StatusViewCaller): string {
163
167
  if (item.phase === "cancelled") {
164
168
  return [
165
169
  "# 🚫 任务组已取消",
@@ -197,6 +201,7 @@ function renderTerminalPhase(item: WorkItem, state?: OrchestrateState): string {
197
201
  "",
198
202
  "编排已完成并收尾。",
199
203
  "",
204
+ ...renderDeploymentNotesSection(state, caller),
200
205
  ].join("\n")
201
206
  }
202
207
  return [
@@ -204,9 +209,41 @@ function renderTerminalPhase(item: WorkItem, state?: OrchestrateState): string {
204
209
  "",
205
210
  "全部审核层已通过。调用 `opx_orch_complete_task_group` 合并分支并完成收尾。",
206
211
  "",
212
+ ...renderDeploymentNotesSection(state, caller),
207
213
  ].join("\n")
208
214
  }
209
215
 
216
+ /** 终态视图的「部署注意事项」区块(仅编排视角、change 会话渲染):指引编排者读主仓库规划文档提取
217
+ * 部署要点,并聚合各任务组 self_check_results 申报的「部署注意点:」前缀行(实施补充点,编排者纯转述)。
218
+ * 渲染前提:change 全部任务组均终态或从未激活(isTaskGroupSettled,含当前组——渲染到终态时当前组必已
219
+ * 终态),存在未收口组时不渲染(两形态维持既有文案)。全部组无实施补充点时省略补充点段(不渲染空段)。 */
220
+ function renderDeploymentNotesSection(state: OrchestrateState | undefined, caller: StatusViewCaller | undefined): string[] {
221
+ if (!caller?.orchestrator || !state || state.kind === "review") return []
222
+ const taskGroups = state.workItems.filter((w) => w.id.startsWith("task:"))
223
+ if (!taskGroups.every(isTaskGroupSettled)) return []
224
+ const lines = [
225
+ "## 部署注意事项汇报",
226
+ "",
227
+ "向用户汇报本次变更的部署注意事项:",
228
+ "",
229
+ `- 阅读主仓库 \`openspec/changes/${state.changeId}/\` 下规划文档(design.md、tasks.md、proposal.md、specs/)提取部署要点:数据/结构变更脚本执行、消息队列/缓存等中间件配置调整、新增配置项或功能开关、外部接口契约变化`,
230
+ "- 无规划文档时跳过文档提取,仅转述下方实施补充点",
231
+ "",
232
+ ]
233
+ const supplementary = taskGroups.flatMap((g) => {
234
+ const notes = extractDeploymentNoteLines(g.metadata)
235
+ if (notes.length === 0) return []
236
+ const tg = taskGroupFromWorkItem(g)
237
+ return [`- ${tg.name}(${tg.id}):`, ...notes.map((n) => ` - ${n}`)]
238
+ })
239
+ if (supplementary.length > 0) {
240
+ lines.push("实施补充点(开发者实施时申报,原样转述):", "")
241
+ lines.push(...supplementary)
242
+ lines.push("")
243
+ }
244
+ return lines
245
+ }
246
+
210
247
  /** 独立审查报告视图:issue 按严重级别/维度/文件位置归并的最终报告形态(views 同构渲染共享)。 */
211
248
  function renderReviewReport(item: WorkItem): string {
212
249
  const issues = issueChildrenOf(item)