sillyspec 3.28.4 → 3.28.5

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.
Files changed (49) hide show
  1. package/AGENTS.md +1 -0
  2. package/package.json +1 -1
  3. package/results-review-tier.md +18 -0
  4. package/src/agent-session-log.js +4 -3
  5. package/src/archive-delta.js +2 -2
  6. package/src/change-list.js +11 -1
  7. package/src/commit-suggest.js +52 -1
  8. package/src/config-cat.js +6 -1
  9. package/src/decision-distill.js +45 -5
  10. package/src/decisions-io.js +96 -0
  11. package/src/design-facts.js +3 -0
  12. package/src/dispatch/probe.js +31 -8
  13. package/src/docs-check.js +68 -3
  14. package/src/index.js +426 -17
  15. package/src/mcp-server.js +144 -0
  16. package/src/module-impact.js +8 -1
  17. package/src/modules.js +46 -34
  18. package/src/progress/change-registry.js +18 -0
  19. package/src/progress.js +13 -1
  20. package/src/quicklog.js +60 -6
  21. package/src/review-dispatch.js +377 -0
  22. package/src/run/archive-distill.js +48 -0
  23. package/src/run/command.js +19 -5
  24. package/src/run/complete-handlers.js +63 -3
  25. package/src/run/complete.js +139 -2
  26. package/src/run/gate-snapshot.js +88 -0
  27. package/src/run/gates.js +115 -55
  28. package/src/run/prompt.js +54 -9
  29. package/src/run/quick-audit.js +38 -13
  30. package/src/run/shared.js +109 -6
  31. package/src/run/stage.js +17 -2
  32. package/src/run/verify-quality-scan.js +336 -0
  33. package/src/scope-audit.js +735 -0
  34. package/src/sillyhub-mcp/client.js +58 -3
  35. package/src/stage-review-checklist.js +44 -0
  36. package/src/stage-review.js +122 -4
  37. package/src/stages/archive.js +57 -164
  38. package/src/stages/brainstorm.js +5 -9
  39. package/src/stages/execute.js +13 -10
  40. package/src/stages/plan.js +3 -10
  41. package/src/stages/verify.js +14 -33
  42. package/src/sync.js +51 -10
  43. package/src/task-review.js +108 -16
  44. package/src/taskcard.js +46 -3
  45. package/src/validate-artifacts.js +163 -0
  46. package/src/verify-postcheck.js +90 -10
  47. package/src/verify-probes.js +134 -12
  48. package/src/worktree-apply.js +119 -14
  49. package/templates/agents-instruction.md +1 -0
@@ -54,8 +54,11 @@ export class SillyHubMcpClient {
54
54
  : DEFAULT_TIMEOUT_MS;
55
55
  // 缺 url 或 token → 视为未配置,所有方法降级不发网络
56
56
  this._configured = Boolean(this._url && this._token);
57
- // 端点必须带尾斜杠
58
- this._endpoint = this._url ? `${this._url}/mcp/` : '';
57
+ // 端点必须带尾斜杠。双形态兼容(2026-09-10 成对签发批次,活体实证 /mcp/mcp/ 404):
58
+ // ① origin 形态(历史 local.yaml / §7.4 兜底写入,如 https://host)——本客户端拼 /mcp/;
59
+ // ② 完整端点形态(平台 mcp-tokens 签发响应的 gateway_url 如 https://host/mcp/,及平台侧
60
+ // daemon local-yaml-writer 写的 origin+'/mcp')——路径已含 /mcp 不再叠加,叠加必 404。
61
+ this._endpoint = !this._url ? '' : (/\/mcp$/.test(this._url) ? `${this._url}/` : `${this._url}/mcp/`);
59
62
  this._rpcId = 0;
60
63
  // MCP streamable HTTP session 状态:惰性 initialize 后才有(见 _ensureSession)
61
64
  this._sessionId = null;
@@ -541,8 +544,11 @@ export class SillyHubMcpClient {
541
544
  const result = await this._callTool('dispatch_worker', args);
542
545
  if (result === null) return { workerId: null, status: 'unavailable' };
543
546
  const value = this._parseToolReturnValue(result);
547
+ // id 兜底(2026-09-10 spike + 平台侧 22cdf89d1 回归实证):dispatch_worker 实返
548
+ // {id, role, objective, status, agent_type…}——旧解析只认 worker_id/workerId,派发
549
+ // 成功时 workerId=null,后续轮询/get_worker_result 全断(平台侧交付遗留①)。
544
550
  const workerId = value && typeof value === 'object'
545
- ? (value.worker_id ?? value.workerId ?? null)
551
+ ? (value.worker_id ?? value.workerId ?? value.id ?? null)
546
552
  : null;
547
553
  const status = value && typeof value === 'object'
548
554
  ? (value.status ?? 'unknown')
@@ -550,6 +556,24 @@ export class SillyHubMcpClient {
550
556
  return { workerId, status };
551
557
  }
552
558
 
559
+ /**
560
+ * daemon 在线状态(调 get_daemon_status tool——平台侧 2026-09-10 P1-3 新增,read scope、
561
+ * 无副作用,比 DB status 强:后者有 45s 假在线窗口,本工具吃 ws_hub 实时连接)。
562
+ * fail-open 三态:online=true/false 为明确判定;null = 未知(旧 backend 无此 tool /
563
+ * token 缺 read scope / 调用异常)——调用方不得据此判不可用。
564
+ * @returns {Promise<{online: boolean|null, raw: object|null}>}
565
+ */
566
+ async getDaemonStatus() {
567
+ if (!this._configured) return { online: null, raw: null };
568
+ const result = await this._callTool('get_daemon_status', {}, { quiet: true });
569
+ if (result === null || result.isError) return { online: null, raw: null };
570
+ const value = this._parseToolReturnValue(result);
571
+ const online = value && typeof value === 'object' && typeof value.daemon_online === 'boolean'
572
+ ? value.daemon_online
573
+ : null;
574
+ return { online, raw: value ?? null };
575
+ }
576
+
553
577
  /**
554
578
  * 列出 mission 下的 worker(调 list_workers tool)。
555
579
  * @param {string} missionId
@@ -565,6 +589,37 @@ export class SillyHubMcpClient {
565
589
  return [];
566
590
  }
567
591
 
592
+ /**
593
+ * 取 worker 结果产物(调 get_worker_result tool,FR-04 终态回收链——worker completed 后
594
+ * 经此取 artifacts,此前该 tool 无 CLI 封装)。
595
+ *
596
+ * artifacts 原样透传不裁剪:平台实返形态([{kind, content_ref, id}…] 或演进形态)整组
597
+ * 透传——kind 匹配 / JSON 提取等语义解析归消费方(review-dispatch 的
598
+ * extractReviewFromArtifacts),本封装保持传输层纯度(task-02 constraints)。
599
+ * @param {object} p
600
+ * @param {string} p.missionId
601
+ * @param {string} p.workerId
602
+ * @returns {Promise<{workerId: string|null, status: string, artifacts: Array}>}
603
+ * 正常 → {workerId, status, artifacts} 原样解析(status 缺省 'unknown',artifacts 缺省 []);
604
+ * 未配置 / 调用失败(null) / tool isError → { workerId: null, status: 'unavailable', artifacts: [] }
605
+ * (与 dispatchWorker/getDaemonStatus 同族 best-effort,绝不向上抛)
606
+ */
607
+ async getWorkerResult({ missionId, workerId } = {}) {
608
+ if (!this._configured) return { workerId: null, status: 'unavailable', artifacts: [] };
609
+ const result = await this._callTool('get_worker_result', { mission_id: missionId, worker_id: workerId });
610
+ if (result === null || result.isError) return { workerId: null, status: 'unavailable', artifacts: [] };
611
+ const value = this._parseToolReturnValue(result);
612
+ // workerId 兜底链与 dispatchWorker 同款(2026-09-10 平台实返 {id,…} 形态实证)
613
+ const wid = value && typeof value === 'object'
614
+ ? (value.worker_id ?? value.workerId ?? value.id ?? null)
615
+ : null;
616
+ const status = value && typeof value === 'object'
617
+ ? (value.status ?? 'unknown')
618
+ : 'unknown';
619
+ const artifacts = value && Array.isArray(value.artifacts) ? value.artifacts : [];
620
+ return { workerId: wid, status, artifacts };
621
+ }
622
+
568
623
  /**
569
624
  * 终止 worker lease 防双写(UB-6 超时 fallback 用)。
570
625
  *
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 三 stage 审查清单单一来源(2026-09-10-review-dispatch task-01 / FR-05 / D-001@1)
3
+ *
4
+ * 条目从 stages/{brainstorm,plan,execute}.js 的 prompt 文本逐字迁移(2026-09-10)——
5
+ * 文字/标点/全半角/markdown 记号零改动:下游 plan-postcheck、review-dispatch
6
+ * worker_prompt(task-03)等可能字面引用,改写即漂移。
7
+ * 一致性由 test/stage-review-checklist.test.mjs 钉死(内嵌迁移前快照逐条比对)。
8
+ *
9
+ * 条目为纯文本,不含序号/checkbox/缩进等结构性前缀(渲染层负责前缀形态):
10
+ * - brainstorm:前 3 条 =「交叉审查模型」三层检查(prompt 内渲染为「N. 」序号行),
11
+ * 后 6 条 =「交叉点抽取」(渲染为「- 」列表行);
12
+ * - plan:渲染为「- [ ] 」checkbox 行(审查清单段);
13
+ * - execute:渲染为 2 空格缩进的「N. 」序号行(QA 段「以下三项始终必查」)。
14
+ */
15
+ export const REVIEW_CHECKLISTS = {
16
+ brainstorm: [
17
+ '**定义层**:模糊概念是否有可测试定义。例如"高可用""异常数据""本地缓存""重试"。',
18
+ '**一致性层**:跨章节/跨产物是否打架。例如数据流 vs 容错策略、schema vs 输入格式、非目标 vs tasks。',
19
+ '**可行性层**:关键假设是否有来源。例如 P99 延迟、上游 SLA、缓存 TTL、数据量、权限模型、兼容旧配置。',
20
+ '模块 A 依赖模块 B 的实体/状态/接口',
21
+ 'requirements.md 的 FR 与 design.md 的数据模型/API/状态机',
22
+ 'design.md 的容错策略与数据流、缓存、重试、回滚',
23
+ 'tasks.md 的执行范围与 design.md 的非目标',
24
+ 'decisions.md 的 D-xxx@vN 与 design.md 当前说法',
25
+ 'scan/module docs 或源码中的真实约束与 design.md 假设',
26
+ ],
27
+ plan: [
28
+ 'task 编号与 Wave checkbox 格式正确,execute 依赖此格式解析任务',
29
+ 'plan_level 档位与实际复杂度匹配(none/light/full 没选错)',
30
+ '跨任务契约:task-A 的产出(接口/DTO/响应)被 task-B 消费时,consumer 是否在 TaskCard expects_from 声明所需字段、provider 是否在 provides 承诺、两边字段一致?(plan-postcheck 会硬校验,此处是独立视角复查)',
31
+ '文件覆盖:design.md 文件变更清单中的每个源码文件,是否都被至少一个 task 的 allowed_paths 覆盖?(漏覆盖 = execute 必然漏改。跨仓变更对账口径:design 清单按「## <repo-key> 仓变更」分段、路径相对各仓根,task 卡 repo: + 同口径相对路径匹配——allowed_paths 带仓库名前缀或绝对路径 = 永不命中、对账不上)',
32
+ '不存在 P0/P1 unresolved blocker 残留',
33
+ '没有实现细节泄漏到 plan.md(接口签名/代码示例应在 tasks/task-NN.md)',
34
+ '关键路径与 Wave 依赖合理(无循环依赖、无遗漏前置)',
35
+ 'Wave 分组说明:依赖方向违规(depends_on 同 Wave / 后置 Wave)在 --done 时会按拓扑**自动修复**(提案经一致性+文件面验证后落盘);共享文件的手工分 Wave 串行是合法安全模式,不会被自动改写',
36
+ '连带测试归属:本批改动是否会导致既有测试断言失效(改共享/被多 task 依赖源文件、改被测试精确匹配的值如 UI 文案/按钮文本/错误信息/常量/枚举字面量、改函数签名或返回结构等单文件场景)?此类 task 是否在 related_tests 声明了失效测试、且路径在 allowed_paths 内(或由独立测试 task 覆盖)?(漏声明 = execute 阶段测试债、主代理事后兜底)',
37
+ 'acceptance 字段对照实际 schema/类型源文件核验存在性与形态,不凭 design.md 文字臆断(plan-postcheck best-effort grep 会给 allowed_paths 源文件未命中的 snake_case/camelCase 标识符提 warning,此处是语义层复查;臆断 = execute 阶段返工)',
38
+ ],
39
+ execute: [
40
+ '跨 task 交界(A 产出的接口/数据结构与 B 的消费是否对得上)',
41
+ 'design.md 整体对照(最终实现拼起来是否仍符合设计意图,而非仅各 task 局部合规)',
42
+ '组装行为(全量测试/构建/启动通过——单 task 测试全绿 ≠ 组装正确)',
43
+ ],
44
+ }
@@ -16,6 +16,7 @@
16
16
  import { existsSync, readFileSync, mkdirSync, readdirSync, writeFileSync } from 'fs'
17
17
  import { join } from 'path'
18
18
  import { createHash } from 'crypto'
19
+ import jsYaml from 'js-yaml'
19
20
  import { VALID_VERDICTS, REVIEW_SCHEMA_VERSION } from './task-review.js'
20
21
  import { detectSpecDirTypo } from './spec-dir-typo.js'
21
22
  import { resolveRuntimeRoot } from './run/shared.js'
@@ -31,6 +32,65 @@ const STAGE_MAIN_DOC = { brainstorm: 'design.md', plan: 'plan.md', execute: 'des
31
32
  // stage → reviewType(对齐 gates.js Stage Review Gate 的映射)
32
33
  const STAGE_REVIEW_TYPE = { brainstorm: 'design', plan: 'plan', execute: 'acceptance' }
33
34
 
35
+ // ── 独立审查通道优先序(2026-09-10 用户裁决:通道顺序是主观权衡,归用户配置不归 CLI 拍死)──
36
+ // 可选通道(tier=independent 时按序尝试首个可用):
37
+ // agent-tool 宿主 Agent tool 派子代理(现状默认首位)
38
+ // platform CLI 直发平台 worker(sillyspec review-dispatch,P2 落地——P1 指引标注「暂跳过」)
39
+ // host-mcp 宿主自有 MCP 派活(能派独立执行者的 MCP tool)
40
+ // self 降级自审(兜底;reviewerNotes 首行「降级:」+ reviewer.channel="self",gate ⚠️ 留痕)
41
+ const REVIEW_CHANNELS = ['agent-tool', 'platform', 'host-mcp', 'self']
42
+ /** 缺省序 = 现状行为(agent-tool 打头),未配置用户零行为变化。 */
43
+ export const DEFAULT_REVIEW_CHANNEL_PRIORITY = ['agent-tool', 'platform', 'host-mcp', 'self']
44
+
45
+ /**
46
+ * 读 local.yaml `review_dispatch.channel_priority`(best-effort 绝不抛)。
47
+ * 语义规则:未知通道名过滤(warn 一次);`self` 永远隐式垫底(防「全配置但全不可用 → 无路可走」);
48
+ * 空/缺段/解析失败 → 缺省序。配置只在**可用通道之间**起偏好作用——宿主没 Agent tool 时该通道
49
+ * 自然跳过(PI 上实际生效序退化为 platform → self),配的是偏好不是能力。
50
+ * @param {string} cwd 项目根(local.yaml 定位)
51
+ * @returns {string[]} 通道优先序(恒非空,末位恒为 'self')
52
+ */
53
+ export function readReviewChannelPriority(cwd) {
54
+ let raw = null
55
+ try {
56
+ const p = join(cwd || process.cwd(), '.sillyspec', 'local.yaml')
57
+ if (existsSync(p)) raw = jsYaml.load(readFileSync(p, 'utf8'))
58
+ } catch { raw = null }
59
+ const rd = raw && typeof raw === 'object' && raw.review_dispatch && typeof raw.review_dispatch === 'object'
60
+ ? raw.review_dispatch
61
+ : null
62
+ const arr = rd && Array.isArray(rd.channel_priority) ? rd.channel_priority : null
63
+ if (!arr || arr.length === 0) return DEFAULT_REVIEW_CHANNEL_PRIORITY.slice()
64
+ const picked = []
65
+ for (const v of arr) {
66
+ if (typeof v !== 'string') continue
67
+ const name = v.trim()
68
+ if (!REVIEW_CHANNELS.includes(name)) {
69
+ if (name) console.warn(`[stage-review] review_dispatch.channel_priority 未知通道「${name}」已忽略(可选:${REVIEW_CHANNELS.join(' / ')})`)
70
+ continue
71
+ }
72
+ if (name !== 'self' && !picked.includes(name)) picked.push(name)
73
+ }
74
+ if (picked.length === 0) return DEFAULT_REVIEW_CHANNEL_PRIORITY.slice()
75
+ return [...picked, 'self']
76
+ }
77
+
78
+ /**
79
+ * reviewer.channel 识别(gate 审计分支依据)。优先级:结构化 reviewer.channel(合法值)>
80
+ * reviewerNotes 首行「降级:」约定(2026-09-10 负面①降级条款的落款形态,向后兼容)>
81
+ * 'unspecified'(既无标记的存量 review——历史产物默认宿主子代理口径,gate 不特殊处理)。
82
+ * @param {object|null} review 解析后的 review.json
83
+ * @returns {string} 'agent-tool'|'platform'|'host-mcp'|'self'|'unspecified'
84
+ */
85
+ export function classifyReviewerChannel(review) {
86
+ const ch = review && typeof review === 'object' && review.reviewer
87
+ && typeof review.reviewer === 'object' && typeof review.reviewer.channel === 'string'
88
+ ? review.reviewer.channel.trim()
89
+ : ''
90
+ if (REVIEW_CHANNELS.includes(ch)) return ch
91
+ return isDegradedSelfReview(review) ? 'self' : 'unspecified'
92
+ }
93
+
34
94
  /**
35
95
  * 渲染 review.json 产物契约(markdown)给 review 子代理事前看 —— schema 表 + 完整 JSON 示例 +
36
96
  * docHash 算法 + 重算提示 + 位置。复用 validateStageReviewSchema 的同源常量,事前给的 == 事后查的。
@@ -38,10 +98,13 @@ const STAGE_REVIEW_TYPE = { brainstorm: 'design', plan: 'plan', execute: 'accept
38
98
  * 历史教训:review 子代理靠读 stage-review.js 源码 + 翻模板才搞清 schema,常错(漏 schemaVersion /
39
99
  * checklist 按层嵌套对象 / docHash 用主文档改版前的旧 sha256)。本函数把契约前置进 prompt。
40
100
  *
41
- * @param {{ stage?: string, changeDir?: string, reviewRunId?: string, tier?: string }} opts
101
+ * @param {{ stage?: string, changeDir?: string, reviewRunId?: string, tier?: string, channelPriority?: string[] }} opts
102
+ * channelPriority:通道优先序(readReviewChannelPriority 产物)。tier=independent 时渲染
103
+ * 「审查执行通道」段(按用户配置序,缺省现状序);缺省参数则函数自读(cwd 取 process.cwd,
104
+ * prompt.js/gates.js 等有精确 cwd 的调用方显式传入)。
42
105
  * @returns {string} markdown 段;tier=self 返回简短提示(无需 review.json)
43
106
  */
44
- export function renderReviewJsonContract({ stage, changeDir, reviewRunId, tier } = {}) {
107
+ export function renderReviewJsonContract({ stage, changeDir, reviewRunId, tier, channelPriority } = {}) {
45
108
  if (tier === 'self') {
46
109
  return '> review.json 契约:tier=self(此刻 design.md 变更文件数 ≤3,当前 agent 自审即可)。⚠️ 注意:本判定基于此刻 design.md 快照——Stage Review Gate 会以 --done 时刻的 design.md 重新判定,若 design 后续扩大到 >3 文件,tier 将升级为 independent 并硬要求独立审查子代理产出 review.json。以 gate 实际校验结果为准(FAILED 即 independent 需补 review.json),勿据本提示提前断言无需 review.json。'
47
110
  }
@@ -50,7 +113,25 @@ export function renderReviewJsonContract({ stage, changeDir, reviewRunId, tier }
50
113
  const mainDocRel = changeDir ? 'changes/<change>/' + mainDoc : mainDoc
51
114
  const stageLbl = stage || '<stage>'
52
115
  const runIdLbl = reviewRunId || '<reviewRunId>'
116
+ // 通道指引(2026-09-10 用户裁决:顺序归配置)。platform 通道 P2(review-dispatch 命令)落地前
117
+ // 标注暂跳过——指引不引用不存在的命令(stage prompt 铁律「不编造 CLI 子命令」)。
118
+ const priority = Array.isArray(channelPriority) && channelPriority.length > 0
119
+ ? channelPriority
120
+ : readReviewChannelPriority(process.cwd())
121
+ const CHANNEL_DESC = {
122
+ 'agent-tool': '宿主 Agent tool 派独立子代理(subagent_type: general,独立上下文)',
123
+ 'platform': 'CLI 直发平台独立 worker(`sillyspec review-dispatch --change <变更名> --stage <stage>` 派发,`--status` 轮询回收——独立进程/会话,宿主无 Agent/MCP 亦可用)',
124
+ 'host-mcp': '宿主自有 MCP 派独立执行者(能派活的 MCP tool,如平台 MCP 的 dispatch 类工具)',
125
+ 'self': '降级自审:主代理切换审查者角色,reviewerNotes **首行**记「降级:环境无子代理可用」+ reviewer.channel="self"(gate 放行但留 ⚠️ 审计行),逐条结论附源码锚点补偿独立性',
126
+ }
127
+ const channelLines = priority.map((c, i) => `${i + 1}. **${c}** —— ${CHANNEL_DESC[c] || c}`).join('\n')
53
128
  const L = [
129
+ '## 审查执行通道(tier=independent,按 local.yaml `review_dispatch.channel_priority` 优先序,**按序尝试首个可用通道**;顺序是用户配置的偏好,不是能力判定——某通道在当前宿主不可用就跳到下一个)',
130
+ '',
131
+ channelLines,
132
+ '',
133
+ '无论走哪个通道,review.json 落款 `reviewer: { "channel": "<通道名>", "missionId": null, "model": null }`(channel 用上方通道名;platform 通道填 missionId,可查模型时填 model)。这是审计面:gate 按 channel 留痕,self 触发 ⚠️——如实声明,勿伪装通道。',
134
+ '',
54
135
  '## review.json 产物契约(CLI Stage Review Gate 将硬校验,以下为精确 schema —— 事前给的 == 事后查的)',
55
136
  '',
56
137
  '> 路径:`{SPEC_ROOT}/.runtime/stage-reviews/' + stageLbl + '-' + runIdLbl + '/review.json`(本次 reviewType=' + reviewType + ',主审查文档=' + mainDocRel + ')',
@@ -63,7 +144,8 @@ export function renderReviewJsonContract({ stage, changeDir, reviewRunId, tier }
63
144
  '- `docHash`: reviewedFiles[0] 文件内容的 sha256(hex,见下方算法)',
64
145
  '- `requiredEvidence`: 非空数组(specVerdict 或 qualityVerdict = cannot_verify 时必填,反逃逸)',
65
146
  '- `checklist`(可选): 扁平数组 —— 每项 { item: string, result: ∈ { ' + CHECKLIST_RESULTS.join(' / ') + ' }, note?: string }。注意是**扁平数组**,不是按层(定义/一致/可行性)嵌套对象',
66
- '- `reviewerNotes`: 说明(verdict=fail 时写明阻断项)',
147
+ '- `reviewer`(可选,审计字段): { "channel": "agent-tool|platform|host-mcp|self", "missionId"?: string, "model"?: string } —— 审查执行通道落款(见上方「审查执行通道」段)',
148
+ '- `reviewerNotes`: 说明(verdict=fail 时写明阻断项;宿主环境无 Agent tool 降级自审时**首行**记「降级:环境无子代理可用」——gate 放行但留 ⚠️ 审计行,勿伪装子代理审查)',
67
149
  '',
68
150
  '### docHash(一键代填,勿手算)',
69
151
  '- **推荐**:docHash 先占位(如 `"TODO"`),review.json 写完后跑',
@@ -89,6 +171,7 @@ export function renderReviewJsonContract({ stage, changeDir, reviewRunId, tier }
89
171
  ' "checklist": [',
90
172
  ' { "item": "<审查项>", "result": "pass", "note": "<证据/说明>" }',
91
173
  ' ],',
174
+ ' "reviewer": { "channel": "<agent-tool|platform|host-mcp|self>", "missionId": null, "model": null },',
92
175
  ' "reviewerNotes": "<总结>"',
93
176
  '}',
94
177
  '```',
@@ -323,6 +406,21 @@ export function getLatestStageReviewRunId(runtimeRoot, stage, changeName) {
323
406
  }
324
407
  }
325
408
 
409
+ /**
410
+ * 降级自审检测(2026-09-10 用户反馈①:PI agent 等宿主环境无 Agent tool,tier=independent
411
+ * 硬要求子代理时只能降级自审——stage prompts 降级条款约定 reviewerNotes **首行**记
412
+ * 「降级:环境无子代理可用」)。gate 侧配套:检测到该标记 → 放行但留 ⚠️ 审计行——独立性折损
413
+ * 可见可追溯,与「宁可见的折损,不可静默的伪装」口径一致(静默放行会把降级 review 与真子代理
414
+ * review 混同,事后审计无法区分)。best-effort:无标记视为未降级(向后兼容,不构成新阻断)。
415
+ * @param {object|null} review - 解析后的 review.json(validateStageReview 的返回值 .review)
416
+ * @returns {boolean}
417
+ */
418
+ export function isDegradedSelfReview(review) {
419
+ const notes = review && typeof review.reviewerNotes === 'string' ? review.reviewerNotes : ''
420
+ const firstLine = notes.split(/\r?\n/, 1)[0].trim()
421
+ return /^降级[::]/.test(firstLine)
422
+ }
423
+
326
424
  /**
327
425
  * stage-level review 总校验(brainstorm/plan/execute-acceptance 的 done gate)
328
426
  *
@@ -356,7 +454,7 @@ export function validateStageReview(opts) {
356
454
  const reviewPath = join(reviewDir, 'review.json')
357
455
 
358
456
  if (!reviewRunId || !existsSync(reviewPath)) {
359
- const errs = [`缺少 ${stage} 阶段的 stage review.json — tier=independent 要求独立审查子代理产出(期望路径:${reviewPath})`]
457
+ const errs = [`缺少 ${stage} 阶段的 stage review.json — tier=independent 要求独立审查子代理产出(期望路径:${reviewPath});宿主环境无 Agent tool(如 PI agent)时按 prompt 降级条款由当前 agent 自审产出,reviewerNotes 首行记「降级:环境无子代理可用」`]
360
458
  const typo = detectSpecDirTypo(runtimeRoot)
361
459
  if (typo) errs.push(`💡 路径疑似拼错:发现 ${typo.typoDir} 目录(应为 ${typo.canonical}),review.json 可能误存于此——检查是否把 .sillyspec 拼成了变体`)
362
460
  return {
@@ -497,6 +595,25 @@ export function printStageReviewResult(result, context = {}) {
497
595
  console.error(` ${reviewDir}/review.json`)
498
596
  }
499
597
  console.error(`\n 提示:tier=independent 要求独立审查子代理产出 review.json,补全后重新 --done`)
598
+ console.error(` 宿主环境无 Agent tool(如 PI agent)→ 降级条款:当前 agent 切审查者角色自审产出,reviewerNotes 首行记「降级:环境无子代理可用」(gate 放行但留 ⚠️ 审计行),勿伪装子代理审查`)
599
+ // 在途区分(2026-09-10-review-dispatch FR-06):review-dispatch 已派发平台审查但 worker 未终态时,
600
+ // 提示区分「在途」而非只有笼统缺件——agent 看得懂该等(--status 回收)还是该处置(--kill 后降级)。
601
+ // 内联读在途记录(同步 readFileSync,不经 import——review-dispatch.js 静态 import 本模块,反向
602
+ // import 成环;路径契约 = review-dispatch.js dispatchRecordPath:runtimeRoot/review-dispatch-<change>.json)。
603
+ // fail-open:无 changeName / 无记录 / 读失败 → 零输出(旧口径)。
604
+ if (changeName && runtimeRoot) {
605
+ try {
606
+ const recPath = join(runtimeRoot, `review-dispatch-${changeName}.json`)
607
+ if (existsSync(recPath)) {
608
+ const rec = JSON.parse(readFileSync(recPath, 'utf8'))
609
+ if (rec && typeof rec === 'object' && (rec.state === 'in-flight' || rec.state === 'dispatching')) {
610
+ console.error(`\n 🛰️ 平台审查在途:mission ${rec.missionId}(state ${rec.lastState || rec.state},最近进展 ${rec.lastStateAt ? new Date(rec.lastStateAt).toLocaleString('zh-CN') : '未知'})`)
611
+ console.error(` → 先 sillyspec review-dispatch --status --change ${changeName} 轮询回收(worker completed 后自动落盘 review.json);`)
612
+ console.error(` → 或 sillyspec review-dispatch --kill --change ${changeName} 显式放弃后走通道降级。`)
613
+ }
614
+ }
615
+ } catch { /* fail-open */ }
616
+ }
500
617
  console.error(` 可用 sillyspec register-stage-review --change <名> --stage ${stage} [--from <已有review.json>] 一步生成 run 目录 + review.json 骨架(docHash 自动算)+ 写 marker + 自检,省掉手动建目录/写 marker`)
501
618
  // docHash 失配精确指路(2026-08-21 agent-手工产出审计项②):改版后忘重算是最高频失败,
502
619
  // 一键重算命令带上真实 change 名,agent 可直接照抄执行(--refresh-hash 保 verdict)。
@@ -623,6 +740,7 @@ export function registerStageReview({ changeName, stage, fromFile, cwd, platform
623
740
  reviewedFiles,
624
741
  docHash,
625
742
  requiredEvidence: [`待独立审查子代理对照 ${mainDoc} 逐节核验(骨架由 register-stage-review 生成)`],
743
+ reviewer: { channel: null, missionId: null, model: null },
626
744
  reviewerNotes: '骨架由 register-stage-review 生成,verdict 待独立审查子代理填写',
627
745
  }
628
746
  mode = 'skeleton'
@@ -4,163 +4,65 @@ export const definition = {
4
4
  description: '规范沉淀,可追溯',
5
5
  steps: [
6
6
  {
7
- name: '任务完成度检查',
8
- prompt: `检查任务完成度,决定是否可归档。
9
-
10
- ### 客观完成度报告(CLI 已注入,勿自行数 checkbox)
11
- {TASK_COMPLETION_REPORT}
12
-
13
- ### 判定规则
14
- - **真相源 = review.json verdict**:上方报告由 CLI 从 .runtime/execute-runs/<runId>/tasks/task-NN/review.json 的 specVerdict + qualityVerdict 算出。
15
- - tasks.md 的 - [x] checkbox **仅作显示态参考**:它依赖自动回填,runId marker / review 缺失时会停在未勾态,与客观 verdict 不一致时**以本报告为准**,不要被未勾 checkbox 误导。
16
- - 报告「未通过 / 缺失」= 0 且 source 为 review.json → 完成度合格,进入下一步。
17
- - 报告含「cannot_verify 草稿(未真正复核)」计数 > 0 → 这些 task 只有 cannot_verify 草稿兜底、未被真正复核:先确认 verify 阶段已兑现其 requiredEvidence(verify-result.md 有对应证据),缺证据的派独立子代理对照 task brief + git diff 补真实复核(升级为 pass/fail),勿静默放行。
18
- - 报告 source 为 plan-checkbox-fallback(客观源不可用)→ 完成度无法客观确认,**必须暂停**让用户交叉核对,不要直接放行。
19
- - 存在未完成任务 → **必须暂停等待用户决定**,不要自行判断"可以归档"。
20
- - 调用:sillyspec run archive --wait --reason "存在未完成任务,是否继续归档" --options "继续归档,回到execute完成剩余任务" --output "未完成任务列表"
21
-
22
- ### 输出
23
- 完成度报告(已通过/总数 + 数据源 + 未完成任务列表)`,
24
- outputHint: '完成度报告',
25
- optional: false
26
- },
27
- {
28
- name: 'extract-module-impact',
29
- prompt: `最终确认 changes/<change>/module-impact.md(本次变更的模块影响分析)。该文档由 plan review_plan 步生成首版、execute 各 Wave 更新、verify 核对——本步是归档前最后一次确认(module-impact 已非 archive 生成,改活文档终审)。
30
-
31
- ### 操作
32
- 1. 读取 changes/<change>/module-impact.md(应已存在;large 变更 plan 阶段产出首版)
33
- 2. **机械核对已由 CLI 代算(2026-09-09 起)**:下方「三重核对报告」区块由 CLI 注入(module-impact 矩阵文件 × 真实 diff 三源文件 × module-map 归属的机器比对结果)——不要重跑 git diff 手工比对,直接消费报告:
34
- {ARCHIVE_IMPACT_AUDIT}
35
- 3. 报告标「不一致」的项才需要你裁决:漏标受影响模块 / 影响类型错误 / 实际未触碰的模块被误标 → 直接修正 module-impact.md,使其与实际变更一致(以 git diff 为准——真实 > 记录);报告「一致 ✓」则跳过修正
36
- 4. module-impact.md 第一行标题必须用中文:# 模块影响分析(Module Impact)— <变更简述>
37
-
38
- ### 降级(module-impact.md 不存在)
39
- 若缺失(如旧变更未经新 plan 流程,或 small 变更从未生成):先跑 \`sillyspec module-impact --change <change-name>\`——CLI 按 _module-map.yaml paths 前缀匹配预填「模块影响矩阵」骨架(文件×模块归属 + 未匹配文件清单,机械部分全代算),你只需逐行填「影响类型」(逻辑变更/数据结构变更/接口变更/调用关系变更/配置变更/新增)与 needs_review 标记,补首行中文标题 \`# 模块影响分析(Module Impact)— <变更简述>\`。骨架无从生成(无 module-map / 无 diff)时才按 archive-impact.yaml 的 impact-analyzer 角色规则全手写。
40
-
41
- 确认完成后,下一步 sync-module-docs 会读 module-impact.md 更新模块卡片文档。`,
42
- outputHint: 'module-impact.md 终审结果 + 修正摘要',
7
+ name: 'decision-distill 决策提炼',
8
+ // noAI 化(P0-4 安全变体,noai-ir-roadmap §3):旧 prompt 本身就是「调用 CLI 纯函数
9
+ // distillIntoKnowledge 并转述返回」——全流程最纯的中继步,LLM 零判断增量。提炼本体
10
+ // 改由 _cliAction(run/archive-distill.js)机械执行:written 逐条打印 / skipped 零输出
11
+ // 注记 / needsWait(rejected 缺字段)打印裁决指引——人工裁决从本步 conditionalWait
12
+ // 三段式收敛到「确认归档 --confirm」的用户确认点(归档用户裁决本就归那里)。全路径
13
+ // 不抛(裁决是确认步输入,非本步阻断条件);异常降级跳过不阻断归档(旧步第 4 点
14
+ // best-effort 语义)。步骤名不变(存量进度库按名匹配零漂移)。
15
+ noAI: true,
16
+ _cliAction: 'archiveDistill',
17
+ prompt: '',
18
+ outputHint: '决策提炼结果(CLI 机械执行输出)',
43
19
  optional: false
44
20
  },
45
21
  {
46
- name: 'sync-module-docs',
47
- // conditionalWait(坑 archive-subconfirm-redundant,2026-08-23 实证:requiresWait 硬门强制
48
- // 三段式 --wait → --continue --answer → --done,与 verify 文档同步阻断门、归档移动前死信
49
- // 校验、下一步「确认归档 --confirm」四层确认重复,交互碎。改 conditionalWait(brainstorm-auto
50
- // 同款先例):常规同步直接写入 + --done(agent 写入动作在 --done 前完成,坑 verify-archive-
51
- // flow-pitfalls 坑4 的「无机会写入」不回归);仅异常(needs_review/未映射/标记缺失)才 --wait
52
- // 请用户裁决。用户确认收敛到「确认归档 --confirm」一处。
22
+ name: 'extract-module-impact 与归档语义收尾',
23
+ // P0-4 全量并步(noai-ir-roadmap §3,含 §2 砍步取证):吸收旧「任务完成度检查 /
24
+ // extract-module-impact / sync-module-docs / 更新路线图和提交」四步——取证结论:
25
+ // 三处注视点(完成度暂停判定 / 模块文档同步异常裁决 / 确认归档 diff 注视)全部保留,
26
+ // 合并只砍仪式性 --done 往返(6 步 6 次 → 3 步 2 次人工 --done + 1 次 noAI)。
27
+ // 步骤名保留「extract-module-impact」子串:complete-handlers 的 workflow post_check
28
+ // 按名内含匹配本步,零改动。conditionalWait 合并 sync-module-docs 先例(坑
29
+ // archive-subconfirm-redundant):常规直接写入 + --done,异常才 --wait。
30
+ migratedFrom: ['任务完成度检查', 'extract-module-impact', 'sync-module-docs', '更新路线图和提交'],
53
31
  conditionalWait: true,
54
32
  repeatableWait: true,
55
33
  maxWaitRounds: 3,
56
- waitReason: '等待用户裁决模块文档同步异常',
57
- waitOptions: ['确认写入', '跳过同步'],
58
- prompt: `根据 module-impact.md 同步更新模块索引和卡片文档。
59
-
60
- ### ⚠️ 核心原则:结构化事实改 _module-map.yaml,语义解释改模块卡片
61
- - \`_module-map.yaml\` 是唯一的结构化索引源(paths/tags/entrypoints/depends_on/used_by/status/needs_review)
62
- - 模块卡片只负责语义说明(定位/契约摘要/关键逻辑/注意事项/人工备注)
63
- - 一个信息只维护一次,不要两边重复
64
-
65
- ### 操作
66
- 1. 读取 \`.sillyspec/changes/<change-name>/module-impact.md\`
67
- 2. 如果没有受影响模块(只有 unmapped)→ 提示用户,跳过同步
68
- 3. 对每个受影响模块,按影响类型分别更新:
69
-
70
- #### 更新 _module-map.yaml 的规则
71
- - **路径变化** → 更新对应模块的 paths
72
- - **依赖变化** → 更新 depends_on / used_by(同时更新反向模块的 used_by / depends_on)
73
- - **导出符号变化** → 更新 entrypoints / main_symbols
74
- - **新增模块** → 添加完整条目
75
- - **模块废弃** → status: deprecated
76
- - **不确定的影响** → needs_review: true, review_reasons 追加原因
77
- - 如果 _module-map.yaml 的 generated_at 已过时,更新为当前时间
78
-
79
- #### 更新模块卡片(modules/<module-id>.md)的规则
80
- - **契约语义变化**(新增/删除对外能力) → 更新"契约摘要"
81
- - **关键逻辑变化** → 更新"关键逻辑"
82
- - **边界变化**(模块职责扩大/缩小) → 更新"定位"
83
- - **注意事项变化** → 更新"注意事项"
84
- - **内部实现变化**(不影响对外接口) → 通常不更新卡片
85
- - **人工备注** → 永远保护,不覆盖
86
-
87
- #### 人工备注保护
88
- 1. 用正则提取 \`<!-- MANUAL_NOTES_START -->\` 到 \`<!-- MANUAL_NOTES_END -->\` 之间的内容
89
- 2. 生成新卡片后,原样回填到人工备注区域
90
- 3. 如果标记缺失或重复 → 在 _module-map.yaml 中标记 needs_review: true
91
-
92
- #### 新建模块卡片模板
93
- 一级标题格式 \`# <中文名>(<module-id>)\`——中文名从模块职责提炼 2-8 字,括号内保留 module-id 原样(sillyhub 平台解析识别用,与 scan 生成约定一致)。frontmatter 已带 author/created_at 真值(<git-user>/<now-datetime> 占位符由 CLI 每步替换),照抄即过元数据校验、勿删。
94
- \`\`\`markdown
95
- ---
96
- schema_version: 1
97
- doc_type: module-card
98
- module_id: <module-id>
99
- author: <git-user>
100
- created_at: <now-datetime>
101
- ---
102
-
103
- # <中文名>(<module-id>)
104
-
105
- ## 定位
106
-
107
- ## 契约摘要
108
-
109
- ## 关键逻辑
34
+ waitReason: '等待用户裁决归档语义收尾异常(模块文档同步/完成度存疑)',
35
+ waitOptions: ['确认继续', '跳过该项'],
36
+ prompt: `归档语义收尾:完成度判定 → module-impact 终审 → 模块文档同步 → 路线图更新,一次完成。
110
37
 
111
- ## 注意事项
112
-
113
- ## 人工备注
114
-
115
- <!-- MANUAL_NOTES_START -->
116
-
117
- <!-- MANUAL_NOTES_END -->
118
- \`\`\`
38
+ ### ① 任务完成度判定(真相源 = review.json)
39
+ #### 客观完成度报告(CLI 已注入,勿自行数 checkbox)
40
+ {TASK_COMPLETION_REPORT}
41
+ - tasks.md 的 - [x] checkbox 仅作显示态参考(自动回填可能停在未勾态,与客观 verdict 不一致时以报告为准)。
42
+ - 报告「未通过 / 缺失」= 0 且 source 为 review.json → 完成度合格,继续 ②。
43
+ - 「cannot_verify 草稿(未真正复核)」计数 > 0 → 先确认 verify 阶段已兑现其 requiredEvidence;缺证据的派独立子代理对照 task brief + git diff 补真实复核(升级 pass/fail),勿静默放行。
44
+ - source 为 plan-checkbox-fallback(客观源不可用)或存在未完成任务 → --wait 请用户裁决,勿自行放行。
119
45
 
120
- 4. 展示所有更新内容(diff 摘要),按同步状态分流:
121
- - **常规**(无 needs_review 影响、无 unmapped 模块、人工备注标记齐全)→ **直接写入** _module-map.yaml 和受影响的模块卡片,diff 摘要写入 --output 后 --done(本步不单独弹确认——用户确认收敛到下一步「确认归档 --confirm」,该步展示同一 diff 摘要)
122
- - **异常**(含 needs_review 影响项 / unmapped 模块 / 人工备注标记缺失或重复 / 覆盖会丢失手动维护字段)→ 暂停请用户裁决:
123
- \`sillyspec run archive --wait --reason "等待用户裁决模块文档同步异常" --options "确认写入,跳过同步" --output "diff 摘要 + 异常说明"\`
124
- 用户 --continue --answer "确认写入" 后写入;"跳过同步" 则不写入,提示 "module-impact.md 已保留,可稍后手动同步"
125
- 5. 回填 module-impact.md 的"更新结果"表格,区分目标:
126
- - 目标列写 "\`_module-map.yaml: <module-id>\`" 或 "\`modules/<module-id>.md\`"
127
- 6. **同步完成后**,如需刷新 \`_module-map.yaml\` 索引:\`sillyspec modules rebuild --force\`(默认 dry-run 只预览不写;\`--force\` 才覆盖,但会清空 tags/entrypoints/main_symbols/depends_on/used_by 等手动维护字段——与 archive-impact 的「人工备注保护」约束冲突,仅当手动字段已并入骨架或可接受覆盖时用;优先手动更新 dependencies.md / 模块卡片)
46
+ ### ② module-impact 终审(机械核对已由 CLI 代算——不要重跑 git diff 手工比对,直接消费报告)
47
+ {ARCHIVE_IMPACT_AUDIT}
48
+ - 报告标「不一致」的项才需裁决修正 module-impact.md(以 git diff 为准——真实 > 记录);「一致 ✓」跳过。
49
+ - module-impact.md 缺失时先跑 \`sillyspec module-impact --change <change-name>\` 生成骨架(含「## 模块影响矩阵」「## 未匹配文件」「## 更新结果」三章节——workflow contains_sections 契约同源,勿删章节;归属列 CLI 预填),逐行填「影响类型」与 review 标记,未命中文件归「未匹配文件」章节;首行标题用中文 \`# 模块影响分析(Module Impact)— <变更简述>\`。
128
50
 
129
- ### 输出
130
- 已更新的文件路径列表 + diff 摘要(异常时附裁决结果)`,
131
- outputHint: '模块文档更新结果',
132
- optional: false
133
- },
134
- {
135
- name: 'decision-distill 决策提炼',
136
- // conditionalWait(同 sync-module-docs 先例):常规提炼直接写入 + --done;仅异常(rejected
137
- // 条目缺否决理由/复潮条件 → distillIntoKnowledge 返回 needsWait 非空)才 --wait 请用户裁决
138
- // (补录后重跑幂等 / 跳过该条)。勿引入 requiresWait 硬门——与下一步「确认归档 --confirm」
139
- // 重复确认(坑 archive-subconfirm-redundant)。
140
- conditionalWait: true,
141
- repeatableWait: true,
142
- maxWaitRounds: 3,
143
- waitReason: '等待用户裁决 rejected 决策缺字段',
144
- waitOptions: ['补录后继续', '跳过该条'],
145
- prompt: `读取变更 decisions.md,把有实现影响的决策提炼进决策知识库(提炼/幂等/INDEX 路由行本体在 CLI 纯函数 src/decision-distill.js,本步只接线,勿手工改写条目格式)。
51
+ ### ③ 模块文档同步(结构化事实改 _module-map.yaml,语义解释改模块卡片,一个信息只维护一次)
52
+ - 按 module-impact 影响类型更新 _module-map.yaml(paths/depends_on/used_by/entrypoints/status/needs_review)与受影响模块卡片(契约摘要/关键逻辑/定位/注意事项;内部实现变化通常不更新卡片)。
53
+ - 人工备注(MANUAL_NOTES 标记区间)永远保护回填;标记缺失/重复 → needs_review: true。
54
+ - 新建卡片用骨架模板(frontmatter \`author: <git-user>\` / \`created_at: <now-datetime>\` 占位符由 CLI 每步替换真值,照抄即过元数据校验);一级标题 \`# <中文名>(<module-id>)\`。
55
+ - 常规(无 needs_review 影响/无 unmapped/人工备注标记齐全)→ 直接写入 + --done;异常(needs_review/unmapped/备注标记缺失/覆盖丢手动字段)→ --wait 请用户裁决。
56
+ - 回填 module-impact.md「更新结果」表(目标列 \`_module-map.yaml: <module-id>\` 或 \`modules/<module-id>.md\`,区分 done/skipped)——verify 已硬校验无 pending 死信,此处保持清零。
57
+ - 如需 rebuild 索引:\`sillyspec modules rebuild --force\` 会清空手动维护字段,仅当可接受时用(优先手动更新)。
146
58
 
147
- ### 操作
148
- 1. 定位参数:changeDir = \`.sillyspec/changes/<change-name>\`(decisions.md 所在——本步在「确认归档」移动目录之前,仍在原位);knowledgeRoot = \`.sillyspec/knowledge\`;headHash = \`git rev-parse --short HEAD\`(落条目「最近确认」)
149
- 2. 调用纯函数 \`distillIntoKnowledge(changeDir, knowledgeRoot, headHash)\`,按返回的 \`{ written, skipped, needsWait }\` 分流(见第 3 点):
150
- - 能解析到 sillyspec 源码/安装位置时(如 sillyspec 仓内 dogfood)直接 import 调用:\`node --input-type=module -e "import { distillIntoKnowledge } from './src/decision-distill.js'; console.log(JSON.stringify(distillIntoKnowledge(changeDir, knowledgeRoot, headHash), null, 2))"\`
151
- - 解析不到(consumer 仓库无源码,node import 会 ERR_MODULE_NOT_FOUND)→ 按其语义执行:解析 D-xxx@vN 条目;入选规则 = 任意 type 的 status=rejected 留痕 + 五类 type(architecture/compatibility/boundary/definition/process)且 status∈{confirmed,accepted} 提炼 implemented,type=scope 不入选;条目幂等落 \`.sillyspec/knowledge/decisions/<模块域>.md\`(同 ID 同版本重写不重复追加、@vN+1 整段替换旧版并注 supersedes;「模块域」缺失按 impacts 路径与 _module-map.yaml paths 前缀匹配兜底,仍未中归 unmapped);implemented 条目写「状态:implemented + 锚点:<src 路径:行号,未记录则填"未记录"> + 最近确认:<headHash> + 理由:<一句话>」,rejected 条目写「状态:rejected + 否决理由: + 复潮条件:」;\`knowledge/INDEX.md\` 的 decisions 路由行(\`- <域>|decision|决策 → [decisions/<域>.md](decisions/<域>.md)\`)幂等增删
152
- 3. 分流规则:
153
- - **常规**(needsWait 为空且 written 非空)→ 写入已由函数完成,written 摘要(decisions/<域>.md × D-xxx@vN × append/update/supersede)写入 --output 后直接 --done(本步不单独弹确认——用户确认收敛到下一步「确认归档 --confirm」)
154
- - **needsWait 非空**(rejected 条目缺否决理由/复潮条件,该条目未写盘、其余条目照常提炼)→ 暂停请用户裁决:
155
- \`sillyspec run archive --wait --reason "等待用户裁决 rejected 决策缺字段" --options "补录后继续,跳过该条" --output "needsWait 描述 + 已提炼摘要"\`
156
- - 「补录后继续」→ 按 needsWait 描述在 changes/<change-name>/decisions.md 补齐缺失的否决理由/复潮条件,重跑第 2 步(幂等,重跑安全)
157
- - 「跳过该条」→ 该条目不入库,--output 注记跳过原因后 --done
158
- - **零输出**(skipped 非空 = 无 decisions.md / 无入选条目)→ 不创建任何文件、不动 INDEX,--output 注一句 skipped 原因后直接 --done(同 docs-debt 无债零输出原则)
159
- 4. 降级:调用或解析异常(模块加载失败 / decisions.md 结构异常抛错)→ warn 记录异常摘要后跳过本步 --done(best-effort,同 agent-session-log 先例),不阻断归档——决策提炼失败不挡归档移动
59
+ ### ④ 路线图更新
60
+ - \`.sillyspec/ROADMAP.md\` 存在 → 标记对应 Phase 为已完成。
61
+ - DB 注销由「确认归档」--confirm 时 CLI 完成(unregisterChange),无需手动。
160
62
 
161
63
  ### 输出
162
- 已提炼条目清单(decisions/<域>.md × D-xxx@vN × action)或零输出/跳过/降级原因`,
163
- outputHint: '决策提炼结果',
64
+ 完成度判定结论 + module-impact 修正摘要 + 已同步文件列表 + 路线图更新(异常时附裁决结果)`,
65
+ outputHint: '归档语义收尾结果',
164
66
  optional: false
165
67
  },
166
68
  {
@@ -169,10 +71,17 @@ created_at: <now-datetime>
169
71
 
170
72
  ### 操作
171
73
  1. 展示:变更目录名、包含的文件列表(含 module-impact.md)、生成总结
172
- 2. 确保任务清单(tasks.md)所有 task checkbox 都已勾选
173
- 3. 让用户确认后,用 \`--confirm\` 完成本步骤:
74
+ 2. 展示变更范围对账表(计划改动 × 实际改动三态全表 + 行数——CLI 机械注入如下,勿手算勿手改;⚠️ 计划外文件补 design.md 声明或 --output 注明原因,计划未动文件确认是否遗漏;advisory 对账不阻断归档,注入异常时该区置换单行降级指引,回退手跑 \`sillyspec scope-audit --change <变更名>\`):
75
+
76
+ {SCOPE_AUDIT_TABLE}
77
+
78
+ 3. 确保任务清单(tasks.md)所有 task checkbox 都已勾选;对照下方完成度快照做归档前最后注视(未完成/存疑 → 停下勿确认):
79
+ {TASK_COMPLETION_REPORT}
80
+ 4. 若上一步机械决策提炼输出「⚠️ rejected 决策缺否决理由/复潮条件(needsWait)」:先按其指引处理——补录 decisions.md 缺失字段后重跑提炼命令(幂等),或经用户裁决跳过该条;处理结果写进本步 --output
81
+ 5. **git 暂存已由 CLI 自动完成**(归档完成时自动 add:\`.sillyspec/changes/archive/\`、本次同步的模块文档、\`.sillyspec/knowledge/decisions/\`——未提交,由用户通过统一提交工具处理)——你不需要也不应该手动 git add
82
+ 6. 让用户确认后,用 \`--confirm\` 完成本步骤:
174
83
  \`sillyspec run archive --done --confirm --output "确认归档"\`
175
- 4. CLI 会创建 \`.sillyspec/changes/archive/\`,并将变更目录移动到 \`.sillyspec/changes/archive/<原变更名>/\`
84
+ 7. CLI 会创建 \`.sillyspec/changes/archive/\`,并将变更目录移动到 \`.sillyspec/changes/archive/<原变更名>/\`
176
85
 
177
86
  ### 输出
178
87
  归档完成 + archive 目录路径`,
@@ -184,21 +93,5 @@ created_at: <now-datetime>
184
93
  requiresConfirm: true,
185
94
  optional: false
186
95
  },
187
- {
188
- name: '更新路线图和提交',
189
- prompt: `更新路线图并确认归档。
190
-
191
- ### 操作
192
- 1. 如果 \`.sillyspec/ROADMAP.md\` 存在,标记对应 Phase 为已完成
193
- 2. 确认 sillyspec.db 中该变更已不再 active(确认归档步骤由 CLI 调用 unregisterChange)
194
-
195
- ### 说明
196
- - **git 暂存已由 CLI 自动完成**(归档完成时自动 add:\`.sillyspec/changes/archive/\`、本次同步的模块文档、\`.sillyspec/knowledge/decisions/\`——未提交,由用户通过统一提交工具处理)——你不需要也不应该手动 git add
197
-
198
- ### 输出
199
- 归档完成确认 + 累积规范统计`,
200
- outputHint: '归档完成',
201
- optional: false
202
- }
203
96
  ]
204
97
  }