sillyspec 3.28.13 → 3.29.1
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/package.json +1 -1
- package/src/blast-surface.js +168 -0
- package/src/ceremony-tier.js +359 -0
- package/src/change-risk-profile.js +72 -199
- package/src/config-schema.js +22 -1
- package/src/decisions-io.js +32 -0
- package/src/design-facts.js +1 -0
- package/src/doctor-diagnostics.js +119 -1
- package/src/index.js +92 -5
- package/src/knowledge-stats.js +25 -0
- package/src/modules.js +28 -0
- package/src/prefill.js +491 -0
- package/src/probe7-anchor-check.js +8 -6
- package/src/quick-gate-profile.js +8 -4
- package/src/quick-session-owner.js +63 -0
- package/src/redlines.js +195 -0
- package/src/review-dispatch.js +473 -1
- package/src/review-material-pack.js +265 -0
- package/src/review-tier.js +185 -41
- package/src/run/archive-distill.js +6 -0
- package/src/run/command.js +158 -87
- package/src/run/complete-handlers.js +82 -1
- package/src/run/complete.js +90 -3
- package/src/run/gate-snapshot.js +30 -1
- package/src/run/gates.js +301 -19
- package/src/run/prompt.js +423 -21
- package/src/run/quick-audit.js +12 -2
- package/src/run/shared.js +36 -0
- package/src/run/verify-quality-scan.js +15 -7
- package/src/scope-audit.js +8 -1
- package/src/span-risk-surface.js +127 -0
- package/src/stage-contract-spec.js +2 -2
- package/src/stage-contract.js +102 -81
- package/src/stage-review.js +26 -2
- package/src/stages/brainstorm-auto.js +1 -1
- package/src/stages/brainstorm.js +35 -19
- package/src/stages/execute.js +11 -3
- package/src/stages/plan.js +45 -24
- package/src/stages/scan.js +1 -1
- package/src/stages/verify.js +7 -7
- package/src/taskcard.js +3 -0
- package/src/verify-postcheck.js +375 -20
- package/src/verify-probes.js +194 -10
- package/src/worktree-apply.js +1 -1
- package/src/worktree.js +1 -1
- package/templates/prompts/taskcard-rules.md +1 -0
- package/templates/prompts/verify-probes.md +1 -1
|
@@ -4,72 +4,31 @@
|
|
|
4
4
|
* 根据变更涉及的文件类型、关键词、git diff、brainstorm 产物,
|
|
5
5
|
* 判定 5 级风险(doc-only / unit-sufficient / contract-required /
|
|
6
6
|
* integration-critical / deployment-critical),产出门控验证需求。
|
|
7
|
+
*
|
|
8
|
+
* 2026-09-19-ceremony-pricing-five-cuts:判级唯一入口 resolveChangeRisk(危险面=项目
|
|
9
|
+
* _module-map.yaml 顶层 blast 段路径声明 × 变更文件,D-008;explicit frontmatter 只压仪式档
|
|
10
|
+
* 不豁免证据,D-009)。散文词表判级(detectChangeRisk 及词表/否定抑制/枚举继承机器)已整体
|
|
11
|
+
* 退役删除。RISK_TO_TIER 五级词→档位映射表迁入本文件(ceremony-tier re-export 保持既有
|
|
12
|
+
* import 面——依赖方向 ceremony-tier→本文件已有先例)。证据门判据(VERIFICATION_NEEDS /
|
|
13
|
+
* checkIntegrationEvidence / auditRuntimeReceipt)原样保留;span 轴路径模式已迁项目声明面
|
|
14
|
+
* (_module-map.yaml 顶层 span_risk 段,装载 src/span-risk-surface.js——2026-09-19-
|
|
15
|
+
* span-risk-pattern-migration 硬退役,不留 legacy 别名)。
|
|
7
16
|
*/
|
|
8
17
|
import { readFileSync, statSync } from 'fs'
|
|
9
18
|
import { join, isAbsolute } from 'path'
|
|
19
|
+
import { resolveBlastSurfaces } from './blast-surface.js'
|
|
10
20
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
const INTEGRATION_CRITICAL_PATTERNS = [
|
|
14
|
-
/\bdaemon\b/i,
|
|
15
|
-
/\bbackend\b/i,
|
|
16
|
-
/\bclient.*api\b/i,
|
|
17
|
-
/\bgrpc\b/i,
|
|
18
|
-
/\bwebsocket\b/i,
|
|
19
|
-
/\bhttp.*client\b/i,
|
|
20
|
-
/\bsession\b/i,
|
|
21
|
-
/\blease\b/i,
|
|
22
|
-
/\bagent.?run\b/i,
|
|
23
|
-
/\blifecycle\b/i,
|
|
24
|
-
/\bstate.?transition\b/i,
|
|
25
|
-
/\bclaim\b/i,
|
|
26
|
-
/\bheartbeat\b/i,
|
|
27
|
-
/\bcross.?process\b/i,
|
|
28
|
-
/\bipc\b/i,
|
|
29
|
-
/\bmessage.?queue\b/i,
|
|
30
|
-
/\bpub.?sub\b/i,
|
|
31
|
-
/\bcli\.ts\b/i,
|
|
32
|
-
/\bmain\.ts\b/i,
|
|
33
|
-
/\bentrypoint\b/i,
|
|
34
|
-
/\bserver\.(js|ts)\b/i,
|
|
35
|
-
/\bbootstrap\b/i,
|
|
36
|
-
/\bdockerfile\b/i,
|
|
37
|
-
/\bdocker.?compose\b/i,
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
const INTEGRATION_FILE_PATTERNS = [
|
|
41
|
-
/daemon/i,
|
|
42
|
-
/session.?manager/i,
|
|
43
|
-
/agent.?run/i,
|
|
44
|
-
/lifecycle/i,
|
|
45
|
-
/state.?machine/i,
|
|
46
|
-
/lease/i,
|
|
47
|
-
/cli\.(js|ts)$/,
|
|
48
|
-
/main\.(js|ts)$/,
|
|
49
|
-
/server\.(js|ts)$/,
|
|
50
|
-
/bootstrap/i,
|
|
51
|
-
/startup/i,
|
|
52
|
-
]
|
|
21
|
+
// ============(已退役)词表散文判级 ============
|
|
53
22
|
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
// 匹配口径:POSIX 路径、大小写不敏感、段边界锚定——前界 (^|[/_-]) + 后界 (?=[/._-]|$),
|
|
64
|
-
// 防 author/booking/lockfile 类子串假阳(R-03);pattern 为稳定域标识(riskHits 审计输出用)。
|
|
65
|
-
export const QUICK_RISK_PATH_PATTERNS = [
|
|
66
|
-
{ pattern: 'auth', re: /(?:^|[/_-])(?:oauth2?|auth(?:orization|entication|enticator)?)(?=[/._-]|$)/i },
|
|
67
|
-
{ pattern: 'permission', re: /(?:^|[/_-])permissions?(?=[/._-]|$)/i },
|
|
68
|
-
{ pattern: 'billing', re: /(?:^|[/_-])billing(?=[/._-]|$)/i },
|
|
69
|
-
{ pattern: 'migration', re: /(?:^|[/_-])(?:migrations?|migrate)(?=[/._-]|$)/i },
|
|
70
|
-
{ pattern: 'lock', re: /(?:^|[/_-])(?:locks?|mutex(?:es)?)(?=[/._-]|$)/i },
|
|
71
|
-
{ pattern: 'scheduling', re: /(?:^|[/_-])(?:schedul(?:er|ing)|cron|jobs?)(?=[/._-]|$)/i },
|
|
72
|
-
]
|
|
23
|
+
// 2026-09-19-ceremony-pricing-five-cuts task-03 删除收口:INTEGRATION_CRITICAL_PATTERNS /
|
|
24
|
+
// INTEGRATION_FILE_PATTERNS 全宇宙词表与 detectChangeRisk 散文扫描整体退役(D-008:撞词≠危险、
|
|
25
|
+
// 自指陷阱——判级唯一入口是 resolveChangeRisk 声明面口径;知识库 conventions.md「判级/定价/门禁
|
|
26
|
+
// 输入必须项目声明」条目为口径真相源)。
|
|
27
|
+
// 2026-09-19-span-risk-pattern-migration task-03 同款删除收口:quick 出口门禁的六域硬编码
|
|
28
|
+
// 路径模式风险表(auth/permission/billing/migration/lock/scheduling,2026-09-14-quick-exit-
|
|
29
|
+
// tiered-gates task-01 引入)退役——span 模式维与 quick 画像 riskTable 唯一输入源改为项目声明
|
|
30
|
+
// _module-map.yaml 顶层 span_risk 段(装载 src/span-risk-surface.js;匹配口径段边界锚定逐字
|
|
31
|
+
// 继承)。无声明项目 → 空表(维度关闭,禁回退内置表——与 blast 未配置禁回退同款取舍)。
|
|
73
32
|
|
|
74
33
|
/**
|
|
75
34
|
* task 级「端到端/deployment-critical」文本判定(execute 批量完成 autoCheckPlanFromReviews 用)。
|
|
@@ -138,88 +97,27 @@ export const VERIFICATION_NEEDS = {
|
|
|
138
97
|
},
|
|
139
98
|
}
|
|
140
99
|
|
|
141
|
-
/**
|
|
100
|
+
/** 风险分级的判定来源(2026-09-19-ceremony-pricing-five-cuts task-03 改声明面口径) */
|
|
142
101
|
export const RISK_LEVEL_CAUSES = {
|
|
143
102
|
'deployment-critical':
|
|
144
|
-
'
|
|
145
|
-
'
|
|
103
|
+
'变更文件面命中项目声明危险面(_module-map.yaml 顶层 blast 段)且判级为顶档。' +
|
|
104
|
+
'这是按「文件路径 × 项目声明」判定的——若认为该路径不该定此价:改 map 声明(git 可见);危险链路该有真实启动证据。',
|
|
146
105
|
'integration-critical':
|
|
147
|
-
'
|
|
148
|
-
'
|
|
149
|
-
'contract-required': '
|
|
150
|
-
'explicit': 'design.md frontmatter 的 risk_level
|
|
106
|
+
'变更文件面命中项目声明危险面的 evidence:true 路径(需要真实集成证据,D-009)。' +
|
|
107
|
+
'frontmatter risk_level 只压仪式档、不豁免证据要求——证据豁免=改 map 的 evidence 声明(git 可见)。',
|
|
108
|
+
'contract-required': '变更文件面命中项目声明的 S2 档危险面(跨模块契约域)。',
|
|
109
|
+
'explicit': 'design.md frontmatter 的 risk_level 显式声明(压仪式档;不豁免 evidence 要求)。',
|
|
151
110
|
}
|
|
152
111
|
|
|
153
|
-
/** design.md frontmatter 可显式声明的合法 risk_level
|
|
112
|
+
/** design.md frontmatter 可显式声明的合法 risk_level 值(存量五级词兼容集合) */
|
|
154
113
|
const RISK_LEVELS = ['doc-only', 'unit-sufficient', 'contract-required', 'integration-critical', 'deployment-critical']
|
|
155
114
|
|
|
156
|
-
// ============ 同句否定抑制(坑 risk-negation-blindness) ============
|
|
157
|
-
//
|
|
158
|
-
// detectChangeRisk 原为全文 blob 字面命中:design 写「本次不新增 daemon 协议」仍判
|
|
159
|
-
// integration-critical、强制全套集成证据,对无状态后端变更误伤(用户实证 2026-08-24)。
|
|
160
|
-
// 机械抑制规则(不做事语义理解,三段判定):
|
|
161
|
-
// ① 子句切分:按 句号/分号/逗号/换行 切子句,否定只在同子句内生效——「不改动 A。新增 B」
|
|
162
|
-
// 的 B 不继承否定;
|
|
163
|
-
// ② 直接否定:命中位置前方 NEGATION_WINDOW 字符内出现否定提示 → 抑制;
|
|
164
|
-
// ③ 枚举继承:「不改动 daemon / session / lifecycle」中后位关键词与前位被抑制关键词之间
|
|
165
|
-
// 只隔枚举连接符(空格 / 、 , 和 与 及 and or)→ 继承抑制——纯距离窗口盖不住长枚举,
|
|
166
|
-
// 而「不涉及 daemon 的情况下重构 session」的间隔是普通文字,不继承(session 正常命中)。
|
|
167
|
-
// 同一关键词的**全部**命中都被抑制才不参与判级(任一处未抑制命中仍算数)。降级不静默:
|
|
168
|
-
// design/verify 两道 gate 会把被抑制词打进 warning(可审计),design.md frontmatter
|
|
169
|
-
// risk_level 仍是双向权威覆盖通道(声明优先级最高,行为不变)。
|
|
170
|
-
//
|
|
171
|
-
// 否定提示词排除假阳:不同/不断/不久/不时/不仅(「不」的非否定合成词)、无状态/无法
|
|
172
|
-
// (「无状态后端」是常见肯定表述)、非常/非空/非法(「非空校验 session」是在改 session)。
|
|
173
|
-
const NEGATION_CUES = /不(?!同|断|久|时|仅|妨)|未|无(?!状态|法)|非(?!常|空|法)|避免|排除|无需|不再|no\s+new|not\b|don'?t|without|avoid/i
|
|
174
|
-
const NEGATION_WINDOW = 16
|
|
175
|
-
const CLAUSE_SPLIT_RE = /[。;;,,\n]/
|
|
176
|
-
const ENUM_GAP_RE = /^[\s/|、,,]*(?:(?:和|与|及|and|or)[\s/|、,,]*)*$/i
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* 单行内统计各关键词命中(同子句否定窗口 + 枚举继承)。
|
|
180
|
-
* @returns {Map<string, {kept: number, suppressed: number}>} trigger 原文 → 命中统计
|
|
181
|
-
*/
|
|
182
|
-
function collectLineTriggerStats(line, patterns, stats = new Map()) {
|
|
183
|
-
for (const pattern of patterns) {
|
|
184
|
-
const re = new RegExp(pattern.source, pattern.flags.includes('g') ? pattern.flags : pattern.flags + 'g')
|
|
185
|
-
let m
|
|
186
|
-
while ((m = re.exec(line)) !== null) {
|
|
187
|
-
if (m[0].length === 0) { re.lastIndex++; continue }
|
|
188
|
-
// 行内绝对偏移 → 子句内相对偏移(子句内可能有多命中,逐一判定)
|
|
189
|
-
const clauseStart = line.slice(0, m.index).split(CLAUSE_SPLIT_RE).pop().length
|
|
190
|
-
const clauseStartAbs = m.index - clauseStart
|
|
191
|
-
const clause = line.slice(clauseStartAbs).split(CLAUSE_SPLIT_RE)[0]
|
|
192
|
-
const relIndex = m.index - clauseStartAbs
|
|
193
|
-
const prefix = clause.slice(0, relIndex)
|
|
194
|
-
let negated = NEGATION_CUES.test(prefix.slice(-NEGATION_WINDOW))
|
|
195
|
-
if (!negated && prefix.length > 0) {
|
|
196
|
-
// 枚举继承:查同子句内更早的关键词命中,且间隔只有枚举连接符
|
|
197
|
-
for (const other of patterns) {
|
|
198
|
-
const ore = new RegExp(other.source, other.flags.includes('g') ? other.flags : other.flags + 'g')
|
|
199
|
-
let om
|
|
200
|
-
while ((om = ore.exec(clause)) !== null) {
|
|
201
|
-
if (om[0].length === 0) { ore.lastIndex++; continue }
|
|
202
|
-
if (om.index >= relIndex) break
|
|
203
|
-
const gap = clause.slice(om.index + om[0].length, relIndex)
|
|
204
|
-
const prevSuppressed = NEGATION_CUES.test(clause.slice(0, om.index).slice(-NEGATION_WINDOW))
|
|
205
|
-
if (prevSuppressed && ENUM_GAP_RE.test(gap)) { negated = true; break }
|
|
206
|
-
}
|
|
207
|
-
if (negated) break
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
const stat = stats.get(m[0]) || { kept: 0, suppressed: 0 }
|
|
211
|
-
if (negated) stat.suppressed++
|
|
212
|
-
else stat.kept++
|
|
213
|
-
stats.set(m[0], stat)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
return stats
|
|
217
|
-
}
|
|
218
|
-
|
|
219
115
|
/**
|
|
220
116
|
* 从 design.md 顶部 frontmatter 提取显式 risk_level 声明。
|
|
221
117
|
* 只认文档最开头 `---\n...\n---` 块内的 `risk_level: <level>` 单行(与 plan.md 的 plan_level 同款解析),
|
|
222
118
|
* 不扫正文——避免正文里讨论 risk_level 措辞时被误当声明。返回合法 level 或 null。
|
|
119
|
+
* 2026-09-19-ceremony-pricing-five-cuts task-03 删除收口后保留:声明面判级 explicit 通道的
|
|
120
|
+
* 唯一提取器(stage-contract / review-tier / verify-quality-scan / run-gates 消费)。
|
|
223
121
|
*/
|
|
224
122
|
export function extractExplicitRiskLevel(designContent = '') {
|
|
225
123
|
const fm = designContent.match(/^---\r?\n([\s\S]*?)\r?\n---/)
|
|
@@ -230,75 +128,50 @@ export function extractExplicitRiskLevel(designContent = '') {
|
|
|
230
128
|
return RISK_LEVELS.includes(value) ? value : null
|
|
231
129
|
}
|
|
232
130
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
explicit === 'integration-critical' ? ['unit_tests', 'contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'terminal_state_assertion'] :
|
|
247
|
-
explicit === 'contract-required' ? ['unit_tests', 'contract_tests'] :
|
|
248
|
-
explicit === 'doc-only' ? ['static_check'] :
|
|
249
|
-
['unit_tests']
|
|
250
|
-
return { level: explicit, triggers: ['risk_level (explicit)'], suppressedTriggers: [], requiredVerification, explicit: true }
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const combined = [designContent, planContent].join('\n')
|
|
254
|
-
|
|
255
|
-
// 逐行收集命中:同句否定窗口内的命中进 suppressed(全部命中均被抑制才剔除该 trigger)
|
|
256
|
-
const stats = new Map()
|
|
257
|
-
for (const line of combined.split('\n')) {
|
|
258
|
-
collectLineTriggerStats(line, INTEGRATION_CRITICAL_PATTERNS, stats)
|
|
259
|
-
}
|
|
260
|
-
const suppressedTriggers = []
|
|
261
|
-
for (const [t, stat] of stats) {
|
|
262
|
-
if (stat.kept > 0) triggers.push(t)
|
|
263
|
-
else suppressedTriggers.push(t)
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
for (const file of changedFiles) {
|
|
267
|
-
for (const pattern of INTEGRATION_FILE_PATTERNS) {
|
|
268
|
-
if (pattern.test(file)) {
|
|
269
|
-
pattern.lastIndex = 0
|
|
270
|
-
const match = file.match(pattern)
|
|
271
|
-
if (match && !triggers.includes(match[0])) triggers.push(match[0])
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (triggers.length === 0) {
|
|
277
|
-
// 关键词命中全被否定语境抑制(或本就无命中)→ doc-only;抑制词随 suppressedTriggers 上报可审计
|
|
278
|
-
return { level: 'doc-only', triggers: [], suppressedTriggers, requiredVerification: ['static_check'] }
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const deploymentTrigger = triggers.some(t => /cli\.ts|main\.ts|server\.(js|ts)|bootstrap|entrypoint/i.test(t))
|
|
282
|
-
const lifecycleTrigger = triggers.some(t => /session|lease|agent.?run|lifecycle|state.?transition|claim|heartbeat/i.test(t))
|
|
283
|
-
const crossProcessTrigger = triggers.some(t => /daemon|backend|client.*api|grpc|websocket|cross.?process|ipc|message.?queue/i.test(t))
|
|
284
|
-
|
|
285
|
-
let level
|
|
286
|
-
const requiredVerification = ['unit_tests']
|
|
131
|
+
/**
|
|
132
|
+
* 五级风险词 → 仪式档位映射(integration 与 deployment 同归 S3:顶档仪式不分家)。
|
|
133
|
+
* 2026-09-19-ceremony-pricing-five-cuts task-02 自 ceremony-tier.js 迁入(判级域归属本文件,
|
|
134
|
+
* ceremony-tier re-export 保持既有 import 面不变);resolveChangeRisk 的 explicitRiskLevel
|
|
135
|
+
* 兼容通道消费本表(存量 frontmatter 五级词)。
|
|
136
|
+
*/
|
|
137
|
+
export const RISK_TO_TIER = {
|
|
138
|
+
'doc-only': 'S0',
|
|
139
|
+
'unit-sufficient': 'S1',
|
|
140
|
+
'contract-required': 'S2',
|
|
141
|
+
'integration-critical': 'S3',
|
|
142
|
+
'deployment-critical': 'S3',
|
|
143
|
+
}
|
|
287
144
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
} else {
|
|
298
|
-
level = 'unit-sufficient'
|
|
299
|
-
}
|
|
145
|
+
// ============ 声明面判级(2026-09-19-ceremony-pricing-five-cuts task-02 / D-008 / D-009)============
|
|
146
|
+
//
|
|
147
|
+
// 新判级入口:files × 项目声明危险面(loadBlastDeclarations 装载 map blast 段 + local 只升合并)
|
|
148
|
+
// → { tier, evidenceRequired, explicit, hitPrefixes, requiredVerification }。
|
|
149
|
+
// tier:命中声明面最高档(无命中 S1——未配置项目禁回退词表,D-008);explicitRiskLevel
|
|
150
|
+
// (frontmatter 五级词经 RISK_TO_TIER 存量兼容)存在时覆盖 tier(人工通道升/降均尊重),
|
|
151
|
+
// **不豁免 evidenceRequired**(D-009:证据豁免=改 map,git 可见——收掉旧显式短路连证据门
|
|
152
|
+
// 一起免的懒 agent 洞)。
|
|
153
|
+
// evidenceRequired → requiredVerification 取现行 integration-critical 组原样(证据门判据零改动)。
|
|
300
154
|
|
|
301
|
-
|
|
155
|
+
/**
|
|
156
|
+
* 声明面判级(纯函数)。
|
|
157
|
+
* @param {{ files?: string[], blastDeclarations?: Array<{prefixes: string[], tier: string, evidence?: boolean}>, explicitRiskLevel?: string|null }} opts
|
|
158
|
+
* @returns {{ tier: 'S0'|'S1'|'S2'|'S3', level: string, evidenceRequired: boolean, explicit: boolean, hitPrefixes: string[], requiredVerification: string[] }}
|
|
159
|
+
* level=五级词兼容字段(存量消费面零改动):evidenceRequired → 'integration-critical';
|
|
160
|
+
* 否则 tier 反查(S0 doc-only / S1 unit-sufficient / S2 contract-required / S3 integration-critical)。
|
|
161
|
+
*/
|
|
162
|
+
const TIER_TO_COMPAT_LEVEL = { S0: 'doc-only', S1: 'unit-sufficient', S2: 'contract-required', S3: 'integration-critical' }
|
|
163
|
+
|
|
164
|
+
export function resolveChangeRisk({ files = [], blastDeclarations = [], explicitRiskLevel = null } = {}) {
|
|
165
|
+
const surfaces = resolveBlastSurfaces(files, blastDeclarations)
|
|
166
|
+
const level = typeof explicitRiskLevel === 'string' ? explicitRiskLevel.trim().toLowerCase() : ''
|
|
167
|
+
const explicitTier = level ? RISK_TO_TIER[level] : undefined
|
|
168
|
+
const tier = explicitTier ? explicitTier : surfaces.tier
|
|
169
|
+
const evidenceRequired = surfaces.evidence
|
|
170
|
+
const requiredVerification = evidenceRequired
|
|
171
|
+
? ['unit_tests', 'contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'terminal_state_assertion']
|
|
172
|
+
: ['unit_tests']
|
|
173
|
+
const compatLevel = evidenceRequired ? 'integration-critical' : TIER_TO_COMPAT_LEVEL[tier]
|
|
174
|
+
return { tier, level: compatLevel, evidenceRequired, explicit: Boolean(explicitTier), hitPrefixes: surfaces.hitPrefixes, requiredVerification }
|
|
302
175
|
}
|
|
303
176
|
|
|
304
177
|
// ============ 回执来源分类打标(X-10 / D-002 / D-006,2026-09-17-pass-cap-semantics task-02) ============
|
package/src/config-schema.js
CHANGED
|
@@ -156,7 +156,7 @@ export const LOCAL_YAML_SCHEMA = {
|
|
|
156
156
|
{
|
|
157
157
|
id: 'gate_snapshot',
|
|
158
158
|
title: '门禁隔离快照 copy 面',
|
|
159
|
-
note: '门禁快照(gate-snapshot)= HEAD worktree + 会话文件 overlay + 环境目录 junction——gitignored
|
|
159
|
+
note: '门禁快照(gate-snapshot)= HEAD worktree + 会话文件 overlay + 环境目录 junction——gitignored 生成物(类型生成/代码生成产物类)不进 HEAD 也不在会话集,快照内 lint/test 环境性假败(2026-09-16 驾驭小结④,friction5-hardening R4 / D-002@v1)。按本清单在快照构建期从主仓 junction 链接补齐(失败回退复制);快照内 overlay 已覆盖的同名路径跳过(本变更最新态优先)。⚠️ junction 是活链接:快照内再跑生成命令会写穿到主仓该目录。',
|
|
160
160
|
keys: [
|
|
161
161
|
{ path: 'gate_snapshot.copy', type: 'array', optional: true, status: 'live', readers: ['createGateSnapshot (src/run/gate-snapshot.js)'], desc: '门禁隔离快照的生成物/额外路径 copy 面(元素 string,相对仓根):HEAD 快照缺 gitignored 生成物导致 lint/test 环境性假败;声明后快照构建期从主仓 junction 链接(失败回退复制)。⚠️ junction 是活链接,快照内再跑生成命令会写穿到主仓该目录。未配置/空清单 = 全段空转零行为变化。', example: 'gate_snapshot:\n copy:\n - src/generated' },
|
|
162
162
|
{ path: 'gate_snapshot.commands', type: 'array', optional: true, status: 'live', readers: ['parseGateSnapshotCommands + runGateSnapshotCommands (src/run/gate-snapshot.js — createGateSnapshot 环境预检后 copy 面前)'], desc: '门禁隔离快照的命令面(元素 string):快照构建期在环境目录链接后、copy 面前于快照根逐条执行(cwd=快照根、300s/条超时帽、非零退出/超时只 warn 继续——fail-open 不作废快照),产出「本仓应然态」生成物,收口主仓生成物缺失/过期(fresh clone 未跑 postinstall / build-id 类产物)时快照内全量 lint/test 必挂的坑(D-002@v2,2026-09-17-feedback-hardening FR-01)。⚠️ 环境目录(node_modules/venv)是活链接,命令写它们会穿透主仓——只放生成物命令(npm run gen:build-id 类),不放 install/build 全家桶。未配置 = 全段空转零行为。', example: 'npm run gen:build-id' },
|
|
@@ -226,6 +226,16 @@ export const LOCAL_YAML_SCHEMA = {
|
|
|
226
226
|
{ path: 'change-ownership.heartbeat_minutes', type: 'integer', optional: true, status: 'live', readers: ['assertChangeOwnership (src/progress/change-registry.js — 本变更 task-02 接线消费)'], desc: '所有者活跃心跳窗口(分钟):owner 非本会话且 last_active 距今在窗口内 → 拒绝接管类操作(--takeover 显式接管);窗口外 → 放行并自动接管。缺省 15。', example: '15' },
|
|
227
227
|
],
|
|
228
228
|
},
|
|
229
|
+
{
|
|
230
|
+
id: 'ceremony',
|
|
231
|
+
title: '评审仪式档位(ceremony tier)',
|
|
232
|
+
note: '仪式档位(S0~S3)由 CLI 按 blast/span/friction 三轴风险客观定价(blast 轴输入=项目声明危险面 _module-map.yaml 顶层 blast 段,2026-09-19-ceremony-pricing-five-cuts;span 轴路径模式输入=项目声明 _module-map.yaml 顶层 span_risk 段,2026-09-19-span-risk-pattern-migration,无声明项目该维关闭不回退内置表);完成门声明追赶重定价——无摩擦迁移随声明面可升可降、有摩擦迁移地板不退,force_tier 仍只升不降;本段是逃生阀/影子期开关/逐机升档面。读键按「存在则读、不存在用缺省」容错(读取走 readCeremonyLocalConfig)。',
|
|
233
|
+
keys: [
|
|
234
|
+
{ path: 'ceremony.blast_surfaces', type: 'json', optional: true, status: 'live', readers: ['loadBlastDeclarations (src/blast-surface.js — local 逐机升档,与 map 声明逐文件取 max)'], desc: 'blast 危险面逐机覆盖:[{ prefixes: [...], tier: S0~S3 }]——主声明在 _module-map.yaml 顶层 blast 段(进 git、可挂 evidence 位);本键只升不降(与 map 命中逐文件取 max,压低共享声明无效)、不承载 evidence(证据语义属共享 map,D-009)。非法条目静默跳过。', example: '[{ prefixes: [src/my-daemon/], tier: S3 }]' },
|
|
235
|
+
{ path: 'ceremony.force_tier', type: 'enum', values: ['S0', 'S1', 'S2', 'S3'], optional: true, status: 'live', readers: ['readCeremonyLocalConfig (src/run/prompt.js — {REVIEW_TIER} 注入档位菜单/强制轻仪审计痕)', '影子派发前置校验 (src/review-dispatch.js — task-06 影子期框架接线)'], desc: '档位逃生阀:强制仪式档 S0~S3,绕过客观定价(过渡期「就是不信这套」用)。prompt 注入面只升不降——强制档低于客观定价档时不降档(防 prompt 面与 gate 侧判定分裂);非法值 warn 后忽略。', example: 'S3' },
|
|
236
|
+
{ path: 'ceremony.shadow', type: 'boolean', optional: true, status: 'live', readers: ['readCeremonyLocalConfig (src/run/prompt.js — S0/S1 轻档注入影子期注记)', 'runReviewDispatch 影子前置校验 (src/review-dispatch.js — task-06 影子期框架接线)'], desc: '影子期开关,默认 true(on):S0/S1 轻档明面轻仪、后台静默派发重仪式只记账不阻断(影子派发框架随 task-06 落地);置 off = 轻仪转正(转正判据由 doctor 影子对照维度公示,CLI 只出判据不出手)。', example: 'true' },
|
|
237
|
+
],
|
|
238
|
+
},
|
|
229
239
|
],
|
|
230
240
|
};
|
|
231
241
|
|
|
@@ -435,5 +445,16 @@ docs-check:
|
|
|
435
445
|
# owner 非本会话且 last_active 在窗口内 → 拒绝(--takeover 显式接管);窗口外 → 自动接管。
|
|
436
446
|
# change-ownership:
|
|
437
447
|
# heartbeat_minutes: 15 # 所有者活跃心跳窗口(分钟)
|
|
448
|
+
|
|
449
|
+
# ── 评审仪式档位(ceremony tier:S0~S3 由 CLI 按 blast/span/friction 三轴风险客观定价;本段为逃生阀/影子期开关/逐机升档面)──
|
|
450
|
+
# blast 轴输入=项目声明危险面(_module-map.yaml 顶层 blast 段);完成门声明追赶重定价(无摩擦随声明、有摩擦地板不退)。
|
|
451
|
+
# force_tier 强制档位绕过客观定价(过渡逃生阀,prompt 注入面只升不降);shadow 影子期默认 on——
|
|
452
|
+
# S0/S1 轻档明面轻仪、后台静默派发重仪式对照只记账不阻断;置 off = 轻仪转正(转正判据见 doctor 影子对照)。
|
|
453
|
+
# ceremony:
|
|
454
|
+
# force_tier: S3 # 强制全重仪式(逃生阀;S0 | S1 | S2 | S3)
|
|
455
|
+
# shadow: true # 影子期开关(默认 true)
|
|
456
|
+
# blast_surfaces: # blast 逐机升档(只升不降、不承载 evidence;主声明在 _module-map.yaml blast 段)
|
|
457
|
+
# - prefixes: [src/my-daemon/]
|
|
458
|
+
# tier: S3
|
|
438
459
|
`;
|
|
439
460
|
}
|
package/src/decisions-io.js
CHANGED
|
@@ -94,3 +94,35 @@ export function upsertDecision(changeDir, decision) {
|
|
|
94
94
|
writeAtomicSync(filePath, eol === '\n' ? next : next.replace(/\n/g, '\r\n'))
|
|
95
95
|
return { path: filePath, action: 'replaced' }
|
|
96
96
|
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* hasDecisionId —— decisions.md 内决策 ID 的字面存在性校验(机械零语义)。
|
|
100
|
+
* (2026-09-18-preflight-slimming Phase 3,D-003@v2:`--wait --inherit-from D-xxx@vN` 的
|
|
101
|
+
* fail-closed 锚点校验——防伪造决策锚点完成 wait 记录态,R-04。)
|
|
102
|
+
*
|
|
103
|
+
* 语义边界(机械,本模块无现成标题解析器可复用——upsertDecision 是写入者,读取侧解析归
|
|
104
|
+
* decision-distill;此处只做字面存在性,对齐 upsertDecision 块定位的双形态匹配):
|
|
105
|
+
* - 只认 canonical 标题行 `## D-xxx@vN`(upsertDecision 的唯一产出格式),匹配形态与其
|
|
106
|
+
* 块定位逐字同源:行前缀 `## <id> `(后随标题)或整行恰为 `## <id>`。
|
|
107
|
+
* - 不做版本归一(D-1@v1 不匹配 D-001@v1)、不做别名展开、不做标题级漂移容收(###/####
|
|
108
|
+
* 形态归 distill parseDecisions 的知识库提炼语义,锚点校验不放宽——fail-closed 保守)。
|
|
109
|
+
* - 文件不存在 / 不可读 / 参数形态非法 → 一律 false 不抛错(调用方 exit 2 即可)。
|
|
110
|
+
*
|
|
111
|
+
* @param {string} changeDir 变更目录(其下 decisions.md)
|
|
112
|
+
* @param {string} id 决策 ID 字面(如 `D-001@v2`)
|
|
113
|
+
* @returns {boolean} 标题行字面存在与否
|
|
114
|
+
*/
|
|
115
|
+
export function hasDecisionId(changeDir, id) {
|
|
116
|
+
if (typeof changeDir !== 'string' || !changeDir) return false
|
|
117
|
+
if (typeof id !== 'string' || id === '') return false
|
|
118
|
+
try {
|
|
119
|
+
const filePath = join(changeDir, 'decisions.md')
|
|
120
|
+
if (!existsSync(filePath)) return false
|
|
121
|
+
const raw = readFileSync(filePath, 'utf8')
|
|
122
|
+
// CRLF 归一(Windows 换行防御,与 upsertDecision 同款口径)
|
|
123
|
+
const lines = raw.replace(/\r\n/g, '\n').split('\n')
|
|
124
|
+
return lines.some((l) => l.startsWith(`## ${id} `) || l.trim() === `## ${id}`)
|
|
125
|
+
} catch {
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
}
|
package/src/design-facts.js
CHANGED
|
@@ -301,6 +301,7 @@ export function generateDesignSkeleton({ changeName, decisionsText, author, now
|
|
|
301
301
|
lines.push(`# 设计文档(Design)— ${change}`)
|
|
302
302
|
lines.push('')
|
|
303
303
|
lines.push(`<!-- 由 sillyspec design-init 生成的骨架(${change})——逐节填散文后删除本注释;存量手写路径不受影响 -->`)
|
|
304
|
+
lines.push('<!-- 引用规范:全文源码位置写仓根相对全路径+行号(src/foo.js:123)——裸文件名在 docs-check 层1 靠 basename 全仓扫描找候选,找不到候选或关键词窗口不匹配即失效,到 pre-push 才拦(2026-09-19 实证 64 处返工) -->')
|
|
304
305
|
lines.push('')
|
|
305
306
|
|
|
306
307
|
lines.push('## 背景')
|
|
@@ -36,6 +36,10 @@ import { readFrictionLedger } from './friction-ledger.js';
|
|
|
36
36
|
// FR 索引消费(2026-09-18-fr-index-l1 L1,D-007):D14 第四检查——epoch 后归档的索引在场+取代完整。
|
|
37
37
|
// fr-index 是纯 fs 叶子链(仅 decision-distill 底座),doctor 静态引入无环。
|
|
38
38
|
import { FR_INDEX_EPOCH, scanFrIndex, parseChangeRequirements } from './fr-index.js';
|
|
39
|
+
// 影子对照账消费(2026-09-18-ceremony-risk-pricing task-06 / FR-04 / R-04 / R-06):读
|
|
40
|
+
// stage-reviews-shadow/shadow-ledger.json 累计账。review-dispatch 依赖链(fs-atomic /
|
|
41
|
+
// stage-review / quicklog / ceremony-tier)均不回指 doctor,静态引入无环。
|
|
42
|
+
import { readShadowLedger, SHADOW_NAMESPACE_DIR, SHADOW_PROMOTION_SAMPLE_TARGET } from './review-dispatch.js';
|
|
39
43
|
|
|
40
44
|
// db 角色标签
|
|
41
45
|
const DB_ROLE = {
|
|
@@ -1292,6 +1296,117 @@ function detectArchiveIntegrity(cwd, authoritySpecDir) {
|
|
|
1292
1296
|
};
|
|
1293
1297
|
}
|
|
1294
1298
|
|
|
1299
|
+
// ── 影子对照维度(2026-09-18-ceremony-risk-pricing task-06 / FR-04 / R-04 / R-06 / D-007@v1)──
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* 轻仪影子期对照(ceremony_shadow):读 stage-reviews-shadow/ 对照账,输出轻/重 catch 差异
|
|
1303
|
+
* 报告(影子重仪式抓到、主线轻仪没抓到的问题清单)+ 转正判据计数——N/10 轻档对照样本且
|
|
1304
|
+
* (漏检=0 ∨ 漏检均 advisory 级(checklist result=gap;result=fail 为非 advisory))→ 达标。
|
|
1305
|
+
*
|
|
1306
|
+
* 语义(降级先例对齐 detectLifecycleDocStaleness):
|
|
1307
|
+
* - 无影子数据(账不存在 / 零条目零 skip)→ pass + 跳过注记,不误报;
|
|
1308
|
+
* - local.yaml ceremony.shadow: off → 已转正,pass + 终态统计(转正后不再报缺口);
|
|
1309
|
+
* - 影子期 on:达标 → pass +「可转正」提示;未达标 → pass:false + WARNING(advisory 公示
|
|
1310
|
+
* 缺口——影子链路只记账不阻断,本维度同样不进任何 gate / verify / archive 判定面)。
|
|
1311
|
+
* R-04 公示:findings 明示影子期 token 不降反升属「花钱买标定」+ local.yaml 开关位置 + 判据。
|
|
1312
|
+
* 转正动作本身是人工置 ceremony.shadow: off——CLI 只出判据不出手(safe_actions 只描述)。
|
|
1313
|
+
*/
|
|
1314
|
+
function detectCeremonyShadowComparison(cwd, authoritySpecDir) {
|
|
1315
|
+
const base = { name: 'ceremony_shadow', label: '影子对照', safe_actions: [] };
|
|
1316
|
+
try {
|
|
1317
|
+
const pointer = resolvePointer(cwd);
|
|
1318
|
+
const runtimeRoot = resolveRuntimeRoot(
|
|
1319
|
+
{ runtimeRoot: pointer.present && pointer.runtimeRoot && existsSync(pointer.runtimeRoot) ? pointer.runtimeRoot : null },
|
|
1320
|
+
authoritySpecDir || join(cwd, '.sillyspec'),
|
|
1321
|
+
);
|
|
1322
|
+
const ledger = readShadowLedger(runtimeRoot);
|
|
1323
|
+
const entries = Array.isArray(ledger.entries) ? ledger.entries : [];
|
|
1324
|
+
const skips = Array.isArray(ledger.skips) ? ledger.skips : [];
|
|
1325
|
+
if (entries.length === 0 && skips.length === 0) {
|
|
1326
|
+
return { ...base, pass: true, severity: null, findings: [`无影子对照数据(${SHADOW_NAMESPACE_DIR}/ 未开账——影子期未跑或已转正),跳过`] };
|
|
1327
|
+
}
|
|
1328
|
+
// 开关面(R-04 公示位置;读取容错对齐 readCeremonyLocalConfig 口径——本维度同步,内联读)
|
|
1329
|
+
let shadowOn = true;
|
|
1330
|
+
try {
|
|
1331
|
+
const lp = join(cwd, '.sillyspec', 'local.yaml');
|
|
1332
|
+
if (existsSync(lp)) {
|
|
1333
|
+
const doc = jsYaml.load(readFileSync(lp, 'utf8'));
|
|
1334
|
+
if (doc && typeof doc === 'object' && doc.ceremony && typeof doc.ceremony === 'object' && typeof doc.ceremony.shadow === 'boolean') {
|
|
1335
|
+
shadowOn = doc.ceremony.shadow;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
} catch { /* 开关读取失败按缺省 on */ }
|
|
1339
|
+
|
|
1340
|
+
const completed = entries.filter((e) => e && e.state === 'completed');
|
|
1341
|
+
const inFlight = entries.filter((e) => e && e.state === 'in-flight');
|
|
1342
|
+
const dead = entries.filter((e) => e && (e.state === 'failed' || e.state === 'abandoned' || e.state === 'bad-review'));
|
|
1343
|
+
|
|
1344
|
+
// catch 差异聚合:漏检 = 影子抓到而主线轻仪没抓到(result=fail → 非 advisory 级;gap → advisory 级)
|
|
1345
|
+
const missed = [];
|
|
1346
|
+
for (const e of completed) {
|
|
1347
|
+
for (const m of (e.diff && Array.isArray(e.diff.missedByLight) ? e.diff.missedByLight : [])) {
|
|
1348
|
+
missed.push({ change: e.change, stage: e.stage, item: m.item, result: m.result, note: m.note });
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
const nonAdvisory = missed.filter((m) => m.result === 'fail');
|
|
1352
|
+
const verdictDelta = completed.filter((e) => e.diff && e.diff.verdictDelta);
|
|
1353
|
+
|
|
1354
|
+
const findings = [];
|
|
1355
|
+
// ① catch 差异报告(影子抓到主线没抓到的问题列表)
|
|
1356
|
+
if (missed.length > 0) {
|
|
1357
|
+
for (const m of missed.slice(0, 5)) {
|
|
1358
|
+
findings.push(`影子抓到主线漏检(${m.change}/${m.stage}):[${m.result}] ${String(m.item).slice(0, 100)}${m.note ? `——${String(m.note).slice(0, 60)}` : ''}`);
|
|
1359
|
+
}
|
|
1360
|
+
if (missed.length > 5) findings.push(`…还有 ${missed.length - 5} 项漏检(明细见 ${SHADOW_NAMESPACE_DIR}/shadow-ledger.json)`);
|
|
1361
|
+
} else if (completed.length > 0) {
|
|
1362
|
+
findings.push(`已完成 ${completed.length} 例轻/重对照,零漏检——轻仪 catch 面与重仪式一致`);
|
|
1363
|
+
}
|
|
1364
|
+
if (verdictDelta.length > 0) findings.push(`verdict 对照:${verdictDelta.length}/${completed.length} 例主线轻仪通过而影子重仪式判 fail`);
|
|
1365
|
+
if (inFlight.length > 0) findings.push(`在途影子派发 ${inFlight.length} 条(回收走 dispatchShadowReview mode='status'——影子结论不进任何主线判定)`);
|
|
1366
|
+
if (dead.length > 0) findings.push(`失败/未回收影子派发 ${dead.length} 条(fire-and-forget 记账,不补派不阻断)`);
|
|
1367
|
+
if (skips.length > 0) {
|
|
1368
|
+
const lastSkip = skips[skips.length - 1];
|
|
1369
|
+
findings.push(`跳过留痕 ${skips.length} 条(档位非轻档/开关 off/平台通道不可用——最近一条:${lastSkip && lastSkip.reason ? String(lastSkip.reason).slice(0, 80) : '未知原因'})`);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
const N = completed.length;
|
|
1373
|
+
const target = SHADOW_PROMOTION_SAMPLE_TARGET;
|
|
1374
|
+
// ② 转正判据(计数逻辑非文案——acceptance:N≥10 且漏检=0 ∨ 漏检均 advisory 级)
|
|
1375
|
+
const promotable = N >= target && nonAdvisory.length === 0;
|
|
1376
|
+
|
|
1377
|
+
if (!shadowOn) {
|
|
1378
|
+
findings.push(`ceremony.shadow: off——轻仪已转正(影子对照账保留供审计:N=${N},累计漏检 ${missed.length} 项)`);
|
|
1379
|
+
return { ...base, pass: true, severity: CHECK_SEVERITY.PASSED, findings };
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
findings.push(`转正进度:${N}/${target} 轻档对照样本,漏检 ${missed.length} 项(非 advisory 级 ${nonAdvisory.length} 项)——判据:N≥${target} 且漏检=0 或漏检均 advisory 级(checklist result=gap)`);
|
|
1383
|
+
// ③ R-04 token 面注记(影子期开销公示,防困惑)
|
|
1384
|
+
findings.push('影子期 token 不降反升属「花钱买标定」(重仪式后台并行跑攒对照证据)——开关 local.yaml ceremony.shadow(当前 on),转正由人工置 off,CLI 只出判据不出手');
|
|
1385
|
+
|
|
1386
|
+
if (N === 0) {
|
|
1387
|
+
findings.push('尚无完成对照——转正判据未开始累计(在途/失败条目不计入 N)');
|
|
1388
|
+
return { ...base, pass: false, severity: CHECK_SEVERITY.WARNING, findings };
|
|
1389
|
+
}
|
|
1390
|
+
if (promotable) {
|
|
1391
|
+
findings.push(`影子期达标可转正:N=${N}≥${target} 且漏检均为 advisory 级(或零漏检)——可人工置 local.yaml ceremony.shadow: off`);
|
|
1392
|
+
return {
|
|
1393
|
+
...base, pass: true, severity: CHECK_SEVERITY.PASSED, findings,
|
|
1394
|
+
safe_actions: [{ dimension: 'ceremony_shadow', action: 'promote_light_ceremony', risk: 'manual_edit', rationale: '影子期达标,轻仪可转正', next_step: '人工编辑 .sillyspec/local.yaml 置 ceremony.shadow: off(CLI 只出判据不出手)' }],
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
const gaps = [];
|
|
1398
|
+
if (N < target) gaps.push(`还差 ${target - N} 个轻档对照样本`);
|
|
1399
|
+
if (nonAdvisory.length > 0) gaps.push(`${nonAdvisory.length} 项非 advisory 级(fail)漏检待补救核对`);
|
|
1400
|
+
findings.push(`转正判据未达标:${gaps.join(';')}`);
|
|
1401
|
+
return {
|
|
1402
|
+
...base, pass: false, severity: CHECK_SEVERITY.WARNING, findings,
|
|
1403
|
+
safe_actions: [{ dimension: 'ceremony_shadow', action: 'review_missed_findings', risk: 'advisory', rationale: '影子抓到主线漏检项——影子结论不阻断,人工裁决是否主线补救', next_step: `核对 ${SHADOW_NAMESPACE_DIR}/shadow-ledger.json 漏检明细(非 advisory 级项优先)` }],
|
|
1404
|
+
};
|
|
1405
|
+
} catch (e) {
|
|
1406
|
+
return { ...base, findings: [`探测降级(${e?.message || e})——skipped`], pass: true, severity: null, skipped: true };
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1295
1410
|
/**
|
|
1296
1411
|
* renderDoctorSummary(FR-01,全新输出契约——2026-09-09-doctor-noai):逐维
|
|
1297
1412
|
* ✅/⚠️/❌ + label + findings 首行 + safe_actions 提示行。顶层非 --json 命令与
|
|
@@ -1346,7 +1461,10 @@ export async function runDoctorDiagnostics({ cwd }) {
|
|
|
1346
1461
|
const selfMaintenanceTax = detectSelfMaintenanceTax(cwd, authoritySpecDir)
|
|
1347
1462
|
// D14(2026-09-17 archive-rescan,OpenSpec validate --archived 对标):归档完整性事后重扫(只读 WARNING)
|
|
1348
1463
|
const archiveIntegrity = detectArchiveIntegrity(cwd, authoritySpecDir)
|
|
1349
|
-
|
|
1464
|
+
// task-06(2026-09-18-ceremony-risk-pricing / FR-04 / R-04):影子对照——轻/重 catch 差异报告 +
|
|
1465
|
+
// 转正判据计数(纯增量挂载,只读不阻断;无影子数据跳过不误报)
|
|
1466
|
+
const ceremonyShadow = detectCeremonyShadowComparison(cwd, authoritySpecDir)
|
|
1467
|
+
const dimensions = [multiDb, pointerHealth, changesSplit, changeDb, executeMismatch, docBloat, repoNativeChain, lifecycleDoc, worktreeHealth, buildEnv, mcpEndpoints, applyManifestDrift, selfMaintenanceTax, archiveIntegrity, ceremonyShadow];
|
|
1350
1468
|
|
|
1351
1469
|
return {
|
|
1352
1470
|
dimensions,
|