sillyspec 3.28.4 → 3.28.6
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/AGENTS.md +1 -0
- package/package.json +1 -1
- package/results-review-tier.md +18 -0
- package/sa-rename-probe/src/new.js +4 -0
- package/src/agent-session-log.js +4 -3
- package/src/archive-delta.js +2 -2
- package/src/change-list.js +11 -1
- package/src/commit-suggest.js +52 -1
- package/src/config-cat.js +6 -1
- package/src/config-schema.js +13 -0
- package/src/decision-distill.js +53 -7
- package/src/decisions-io.js +96 -0
- package/src/design-facts.js +3 -0
- package/src/dispatch/probe.js +31 -8
- package/src/docs-check.js +77 -8
- package/src/friction-tally.js +221 -0
- package/src/index.js +430 -18
- package/src/mcp-server.js +144 -0
- package/src/module-impact.js +8 -1
- package/src/modules.js +46 -34
- package/src/progress/change-registry.js +20 -0
- package/src/progress.js +16 -1
- package/src/quicklog.js +60 -6
- package/src/review-dispatch.js +377 -0
- package/src/run/archive-distill.js +48 -0
- package/src/run/command.js +35 -5
- package/src/run/complete-handlers.js +136 -3
- package/src/run/complete.js +170 -2
- package/src/run/gate-snapshot.js +159 -0
- package/src/run/gates.js +166 -69
- package/src/run/prompt.js +54 -9
- package/src/run/quick-audit.js +78 -16
- package/src/run/shared.js +138 -7
- package/src/run/stage.js +31 -2
- package/src/run/verify-quality-scan.js +383 -0
- package/src/scope-audit.js +941 -0
- package/src/sillyhub-mcp/client.js +58 -3
- package/src/stage-review-checklist.js +44 -0
- package/src/stage-review.js +122 -4
- package/src/stages/archive.js +57 -164
- package/src/stages/brainstorm.js +8 -12
- package/src/stages/execute.js +13 -10
- package/src/stages/plan.js +3 -10
- package/src/stages/verify.js +14 -33
- package/src/sync.js +51 -10
- package/src/task-review.js +108 -16
- package/src/taskcard.js +46 -3
- package/src/validate-artifacts.js +163 -0
- package/src/verify-postcheck.js +90 -10
- package/src/verify-probes.js +134 -12
- package/src/worktree-apply.js +119 -14
- 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
|
-
|
|
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
|
+
}
|
package/src/stage-review.js
CHANGED
|
@@ -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
|
-
'- `
|
|
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'
|
package/src/stages/archive.js
CHANGED
|
@@ -4,163 +4,65 @@ export const definition = {
|
|
|
4
4
|
description: '规范沉淀,可追溯',
|
|
5
5
|
steps: [
|
|
6
6
|
{
|
|
7
|
-
name: '
|
|
8
|
-
prompt
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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: '
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
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:
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
149
|
-
|
|
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
|
-
|
|
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.
|
|
173
|
-
|
|
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
|
-
|
|
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
|
}
|