sillyspec 3.28.13 → 3.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/package.json +1 -1
  2. package/src/blast-surface.js +168 -0
  3. package/src/ceremony-tier.js +359 -0
  4. package/src/change-risk-profile.js +72 -199
  5. package/src/config-schema.js +21 -0
  6. package/src/decisions-io.js +32 -0
  7. package/src/design-facts.js +1 -0
  8. package/src/doctor-diagnostics.js +119 -1
  9. package/src/index.js +92 -5
  10. package/src/knowledge-stats.js +25 -0
  11. package/src/modules.js +28 -0
  12. package/src/prefill.js +491 -0
  13. package/src/probe7-anchor-check.js +8 -6
  14. package/src/quick-gate-profile.js +8 -4
  15. package/src/quick-session-owner.js +63 -0
  16. package/src/review-dispatch.js +473 -1
  17. package/src/review-material-pack.js +265 -0
  18. package/src/review-tier.js +185 -41
  19. package/src/run/archive-distill.js +6 -0
  20. package/src/run/command.js +158 -87
  21. package/src/run/complete-handlers.js +82 -1
  22. package/src/run/complete.js +37 -1
  23. package/src/run/gates.js +301 -19
  24. package/src/run/prompt.js +423 -21
  25. package/src/run/shared.js +36 -0
  26. package/src/run/verify-quality-scan.js +14 -6
  27. package/src/scope-audit.js +8 -1
  28. package/src/span-risk-surface.js +127 -0
  29. package/src/stage-contract-spec.js +2 -2
  30. package/src/stage-contract.js +102 -81
  31. package/src/stage-review.js +26 -2
  32. package/src/stages/brainstorm.js +28 -15
  33. package/src/stages/execute.js +10 -2
  34. package/src/stages/plan.js +29 -12
  35. package/src/stages/verify.js +7 -7
  36. package/src/taskcard.js +3 -0
  37. package/src/verify-postcheck.js +375 -20
  38. package/src/verify-probes.js +117 -10
  39. package/src/worktree.js +1 -1
  40. package/templates/prompts/taskcard-rules.md +1 -0
  41. package/templates/prompts/verify-probes.md +1 -1
@@ -445,7 +445,7 @@ export function getLatestStageReviewRunId(runtimeRoot, stage, changeName) {
445
445
  * @param {string} changeName - 变更名(跨变更过滤锚)
446
446
  * @returns {{ priorRunId: string, verdicts: string, openFindings: string[], passItems: string[], notesPreview: string }|null}
447
447
  */
448
- export function collectSameStagePriorReview(runtimeRoot, stage, changeName) {
448
+ export async function collectSameStagePriorReview(runtimeRoot, stage, changeName) {
449
449
  if (!runtimeRoot || !stage || !changeName) return null
450
450
  const dir = join(runtimeRoot, 'stage-reviews')
451
451
  if (!existsSync(dir)) return null
@@ -490,11 +490,27 @@ export function collectSameStagePriorReview(runtimeRoot, stage, changeName) {
490
490
  openFindings.push(`[verdict-fail] verdict=fail 但 checklist 无明细——以 reviewerNotes 为准:${notes || '(无 notes)'}`)
491
491
  }
492
492
  if (openFindings.length === 0 && passItems.length === 0) return null
493
+ // fixDiff 腿(QA Gap 2 修正,design Wave 2.4 兑现):复审唯一基准面的另一半——上一轮之后
494
+ // 主文档的修复 diff 摘要(git diff --stat 半边,主文档相对 HEAD 的未提交改动;纯函数侧
495
+ // 不再追多提交窗口——复审场景主文档几乎总在工作树)。fail 时留空串不阻断渲染。
496
+ let fixDiff = ''
497
+ try {
498
+ const metaPath = join(runtimeRoot, '..', 'changes', changeName)
499
+ const docCandidates = ['design.md', 'plan.md', 'proposal.md']
500
+ for (const doc of docCandidates) {
501
+ const dp = join(metaPath, doc)
502
+ if (!existsSync(dp)) continue
503
+ const { safeGit } = await import('./git-helper.js')
504
+ fixDiff = String(safeGit(join(runtimeRoot, '..', '..'), ['diff', '--stat', 'HEAD', '--', dp], { trim: false }) || '')
505
+ if (fixDiff.trim()) break
506
+ }
507
+ } catch { /* fixDiff best-effort:无 git/无 diff → 空串(渲染体仍成立) */ }
493
508
  return {
494
509
  priorRunId: latest.runId,
495
510
  verdicts: `spec=${latest.review.specVerdict}, quality=${latest.review.qualityVerdict}`,
496
511
  openFindings,
497
512
  passItems,
513
+ fixDiff: fixDiff.trim().slice(0, 2000),
498
514
  notesPreview: String(latest.review.reviewerNotes || '').replace(/\s+/g, ' ').slice(0, 160),
499
515
  }
500
516
  }
@@ -513,17 +529,25 @@ export function collectSameStagePriorReview(runtimeRoot, stage, changeName) {
513
529
  export function renderPriorRoundFindingsMd(collected, cap = 15) {
514
530
  if (!collected) return ''
515
531
  const L = []
516
- L.push(`**同阶段上一轮审查结论(run ${collected.priorRunId},verdict ${collected.verdicts})——本次为复审,以增量为主,不重演全量审查**:`)
532
+ // 2026-09-19-review-material-pack D-002/Grill 交叉点 1:建议语→排他语——本块是再审的
533
+ // **唯一基准面**(fixDiff 由采集端并入 collected.openFindings 的证据行,渲染体扩展、
534
+ // 占位符机制零改动)。子代理服从必读清单而非回灌块的教训:排他语必须明写「唯一」。
535
+ L.push(`**同阶段上一轮审查结论(run ${collected.priorRunId},verdict ${collected.verdicts})——本材料是本轮复审的唯一基准面:只核验下列未决项的修复状态与本次改版新增面,不重演全量审查(全量重读=重做已付的评审)**:`)
517
536
  if (collected.openFindings.length > 0) {
518
537
  const shown = collected.openFindings.slice(0, cap)
519
538
  L.push(`- **上一轮未决问题(逐项核验修复状态:已修给证据、未解决必须如实再次 fail——漏放行=假通过)**(${shown.length}/${collected.openFindings.length} 条):`)
520
539
  for (const f of shown) L.push(` - ${f}`)
521
540
  }
541
+ if (collected.fixDiff && String(collected.fixDiff).trim()) {
542
+ L.push('- **上一轮后的修复 diff 摘要(唯一基准面的证据半边——核验修复是否真实落地)**:')
543
+ L.push('```diff\n' + String(collected.fixDiff) + '\n```')
544
+ }
522
545
  if (collected.passItems.length > 0) {
523
546
  const shown = collected.passItems.slice(0, cap)
524
547
  L.push(`- **上一轮已实证 pass 面(勿重复报告/重验——除非本次修复改动明确触及该面才定向复查)**(${shown.length}/${collected.passItems.length} 条):`)
525
548
  for (const p of shown) L.push(` - ${p}`)
526
549
  }
550
+ L.push(`- **包外查证口径**:本基准面之外的文件可定向查证但须在 reviewerNotes 列明查证清单;禁全量扫读仓。材料不足以核验某未决项→该项 cannot_verify+列缺件,不得靠全量重读自救。`)
527
551
  return `\n${L.join('\n')}\n`
528
552
  }
529
553
 
@@ -172,7 +172,7 @@ export const definition = {
172
172
  - answer: <用户确认或代码查证结果>
173
173
  - normalized_requirement: <可测试的约束>
174
174
  - impacts: [FR-?, task-?, verify-?]
175
- - evidence: <文件路径/代码位置/用户回答轮次>
175
+ - evidence: <文件路径/代码位置/用户回答轮次>(源码引用写仓根相对全路径+行号,如 src/stage-contract.js:789——裸文件名在 docs-check 层1 靠 basename 全仓扫描找候选,找不到候选或关键词窗口不匹配即失效,到 pre-push 才拦,2026-09-19 实证 64 处返工)
176
176
  - 故障面: <本决策引入的新失败模式>(可选,type=architecture 时建议填写)
177
177
  - 退役判据: <出现什么信号时简化/删除本机制>(可选,type=architecture 时建议填写)
178
178
  \`\`\`
@@ -271,6 +271,11 @@ export const definition = {
271
271
  },
272
272
  {
273
273
  name: '写设计文档并自审',
274
+ // 前置失败清单声明(2026-09-18-preflight-slimming task-04,D-001@v1):本步产出 design.md,
275
+ // prompt 渲染时 renderPreflightFailures 按 run/prompt.js PREFLIGHT_VALIDATORS 映射表只读快跑
276
+ // 声明的 validator 子集(design-file-list=design.md 文件清单幻觉路径快查;
277
+ // four-piece-rules=brainstorm 四件套规则轻查),无失败/异常恒空串零注入。
278
+ preflightValidators: ['design-file-list', 'four-piece-rules'],
274
279
  prompt: `撰写 design 文档并进行 AI 自审。
275
280
 
276
281
  ### 文件标题规则(sillyhub 平台解析识别用)
@@ -381,11 +386,11 @@ design.md 文件路径 + 自审结果
381
386
  ### 定位
382
387
  这是设计完成后的质量门,不是需求探索。目标不是继续发散,而是找出 design.md 内部、四件套之间、文档与外部约束之间的结构性矛盾。
383
388
 
384
- ### 审查执行方式(CLI 按变更规模判定,占位符由 run.js 注入)
389
+ ### 审查执行方式(CLI 按 ceremony_tier 风险定价,占位符由 run.js 注入)
385
390
  tier: {REVIEW_TIER}({REVIEW_TIER_REASON})
386
- - tier=self:当前 agent 直接执行下方交叉审查(小变更)
387
- - tier=independent:必须用 Agent tool 启动一个独立的设计审查子代理(独立上下文,不共享你的分析与倾向),子代理按下方"交叉审查模型"审查 design.md 并输出 review.json。review.json 产物契约(CLI Stage Review Gate 将硬校验,schema + 完整示例 + docHash 算法如下,照抄改值):
388
- 宿主环境无 Agent tool 可用(调用报 Unknown agent / Available agents: none)→ 不卡死:主代理切换为审查者角色自审替代,reviewerNotes 首行记录「降级:环境无子代理可用」,逐条结论附源码锚点(file:line 或 grep/read 证据)补偿独立性。
391
+ - tier=self(ceremony 档 S0/S1):当前 agent 按「仪式档位菜单」执行对应档轻仪交叉审查(S0=CLI 清单核验 / S1=CLI 清单核验+定向探针抽查)——轻仪是风险定价的正常形态而非降级,清单机械项一条不省
392
+ - tier=independent(ceremony 档 S2/S3):必须用 Agent tool 启动一个独立的设计审查子代理(独立上下文,不共享你的分析与倾向),子代理按下方"交叉审查模型"审查 design.md 并输出 review.json——S2=独立评审×1;S3=两轮独立评审(两轮独立子代理交叉,第二轮聚焦首轮未决项与新增面)。review.json 产物契约(CLI Stage Review Gate 将硬校验,schema + 完整示例 + docHash 算法如下,照抄改值):
393
+ 宿主环境无 Agent tool 可用(调用报 Unknown agent / Available agents: none)→ 不卡死(降级兜底,仅评审通道全不可用时):主代理切换为审查者角色自审替代,reviewerNotes 首行记录「降级:环境无子代理可用」,逐条结论附源码锚点(file:line 或 grep/read 证据)补偿独立性。
389
394
  {PRIOR_REVIEW_FACTS}
390
395
  {REVIEW_JSON_CONTRACT}
391
396
  子代理只产出 review + Unresolved Blockers,**是否调用 sillyspec run brainstorm --wait 仍由你(主 agent)根据其 verdict 决定**(子代理不直接操作 CLI 状态机)。
@@ -393,23 +398,27 @@ tier: {REVIEW_TIER}({REVIEW_TIER_REASON})
393
398
  - 时间盒收敛:审查子代理的调研是收敛动作不是发散——必读材料读完即逐条出结论并落盘 review.json;仅个别结论存疑时定向补证,禁止循环扩大核验面(连续读文件 10+ 次仍零结论 = 立即停止扩展、基于已读材料收敛)。
394
399
  - 写通道降级:子代理写操作持续被平台拒绝(session not in running turn 类故障)→ 重试 ≤3 次即停,把完整审查结论(含 review.json 全文)作为最终文本回传主代理,由主代理代为落盘并在 reviewerNotes 首行留痕「代落盘:子代理写通道故障」;禁止长时间空转重试。
395
400
 
401
+ ### 仪式档位菜单(ceremony_tier 按 risk 客观定价——注入的 tier 行已标注当前档)
402
+ - S0:CLI 清单核验——按下方交叉审查模型/交叉点清单机械逐项核对(零 token 发散),结论逐条附证据锚点,无需独立子代理
403
+ - S1:CLI 清单核验 + 定向探针抽查——机械项全查之外,对最高风险的 1-2 个交叉点做定向源码探针(读相关实现验证设计假设),无需独立子代理
404
+ - S2:独立评审×1——一个独立审查子代理完整执行下方交叉审查模型并产出 review.json
405
+ - S3:两轮独立评审 + Grill 深查——两个独立审查子代理分别审查(视角互补:一个查结构性矛盾/一致性,一个查可行性与外部约束),首轮未决项进第二轮复核;对 P0 歧义逐一压力测试(Grill 深查)
406
+ 当前档以注入的 ceremony_tier 行为准(run.js 注入 {REVIEW_TIER} 时随附菜单并标注);档位由 blast/span/friction 三轴客观计算,agent 自报只可升不可降——按当前档如实执行,不自行升仪或降仪。S2/S3 评审通道全不可用时按上方降级兜底执行并留 degraded 痕(reviewerNotes「降级:环境无子代理可用」);S0/S1 的 self 是档位定价的正常形态,不是降级。
407
+
396
408
  ### 默认行为
397
409
  1. 默认必须执行一次交叉审查;不要让用户凭主观判断决定"要不要 Grill"。
398
410
  2. 只有以下情况可以轻量跳过,并必须记录原因:
399
411
  - 用户明确要求 no-grill / 显式跳过
400
412
  - 文档是一页以内、单模块、无状态流转、无 schema/API/兼容策略变更
401
- - plan_level 明确为 none,且只改 1-2 个文件
413
+ - ceremony 档为 S0(客观定价已是最低仪式档)且只改 1-2 个文件——S0 的 CLI 清单核验仍要执行,跳过的只是发散深查
402
414
  3. 即使跳过,也要输出"Design Grill skipped"和原因,不能静默跳过。
403
415
 
404
- ### 输入材料
405
- 1. 必须读取完整 \`{SPEC_ROOT}/changes/<change-name>/design.md\`
406
- 2. 读取 proposal.md、requirements.md、tasks.md、decisions.md(如存在)
407
- 3. 读取 scan/module docs:
408
- - \`{SPEC_ROOT}/docs/<project>/scan/ARCHITECTURE.md\`
409
- - \`{SPEC_ROOT}/docs/<project>/scan/CONVENTIONS.md\`
410
- - \`{SPEC_ROOT}/docs/<project>/modules/_module-map.yaml\`
411
- - 命中的模块文档
412
- 4. 按 design.md 文件变更清单读取相关源码、测试、配置、schema 或样例数据;矛盾经常藏在设计与外部约束交叉处,素材宁可多读,不要只读摘要。
416
+ ### 输入材料(材料包基准面——CLI 注入素材半边+主代理补点名半边,2026-09-19-review-material-cli-wiring)
417
+ 1. **评审材料包(基准面——checklist 逐条只对本包作答)**:CLI 已机械组装注入下方(src/review-material-pack.js assembleStageReviewMaterials('grill-first'):design 要点 digest+文件清单);「五个交叉点」节留位——派发前由你补齐五点(每点 title+file:line 锚,如需源码片段按同文件 extractSnippets 口径附),留空则评审者按 cannot_verify 列缺件。tier=independent 时把补完的整包贴进子代理派发 prompt:
418
+ {REVIEW_MATERIALS}
419
+ 2. 包外文件可按需定向查证(列明查过的文件),**禁止全量扫读仓**——矛盾经常藏在交叉处,定向读那一处,不读整个仓。
420
+ 3. **自检首项(先于一切审查项)**:材料包是否足以逐条作答下方审查清单;不足以作答→cannot_verify+在 requiredEvidence 列缺件(缺什么列什么——这是包质量问题不是你的问题),不要靠全量扫读自救。
421
+ 4. decisions.md 全文可读(决策条目是审查对象本体,不属于扫读面)。
413
422
 
414
423
  ### 交叉审查模型
415
424
  按三层检查并输出 cross-check matrix:
@@ -479,6 +488,10 @@ status: passed | needs-user-input | blocked | skipped
479
488
  name: '生成规范文件',
480
489
  // 重命名自「用户确认并生成规范文件」(去确认门控:设计已在「分段展示设计」步确认过,末步再问纯冗余),老进度 completed 由迁移逻辑承接
481
490
  migratedFrom: ['用户确认并生成规范文件'],
491
+ // 前置失败清单声明(2026-09-18-preflight-slimming task-04,D-001@v1):本步 --done 消费
492
+ // brainstorm.design.* 门禁(含 design-file-list 文件清单核验与四件套规则)——事前清单与
493
+ // 事后门禁同源(validator 子集映射见 run/prompt.js PREFLIGHT_VALIDATORS 注释)。
494
+ preflightValidators: ['design-file-list', 'four-piece-rules'],
482
495
  prompt: `按变更规模生成规范文件并给出实现路径建议(设计已在前置步骤确认过,本步不再暂停等确认,生成后展示摘要即可)。
483
496
 
484
497
  ### 规模评估(展示前先做)
@@ -440,8 +440,9 @@ ${REVIEW_CHECKLISTS.execute.map((item, index) => ' ' + (index + 1) + '. ' + ite
440
440
  **执行纪律(坑 review-subagent-stall,2026-09-15 wp EHS 会话实证)**:brainstorm/plan 阶段已实证的结论(既往 stage-review checklist pass 项、file:line 锚点)直接引用勿重验;必读材料读完即逐条出结论落盘,仅结论存疑时定向补证,禁止循环扩大核验面(连续读文件 10+ 次仍零结论 = 基于已读材料立即收敛)。QA 子代理写操作持续被平台拒绝(session not in running turn 类)→ 重试 ≤3 次即停,完整结论(含 review.json 全文)回传主代理代落盘,reviewerNotes 首行留痕「代落盘:子代理写通道故障」;禁止长时间空转重试。
441
441
  **gate 重试修复**:review.json 落盘后若 design.md 又有改版,gate 会**自动机械重算 docHash 放行**(verdict/checklist 保留,结论是否仍适用于新文档需人工确认)——**不要重做审查**(重做=同一材料第三遍);主文档路径错/缺失不会被自动修复,按 gate 报错修正 reviewedFiles[0],或跑 \`sillyspec register-stage-review --change <变更名> --stage execute --refresh-hash\`。
442
442
 
443
- ### 操作
444
- 1. 读取 design.md(技术方案)——按章节精准读(design-check 对照表可借 tasks 卡 §锚点定位),避免反复整读全文
443
+ ### 操作(材料包口径——2026-09-19-review-material-cli-wiring)
444
+ 1. **评审材料包(基准面)**:CLI 已机械组装注入下方(src/review-material-pack.js assembleStageReviewMaterials('execute-qa'):diff 摘要+design 热区+验收清单)——checklist 逐条只对包作答;包外文件可定向查证但须列明(禁全量扫读);包不足以作答→cannot_verify+requiredEvidence 列缺件。task review 双 pass 抽查、未双 pass 全量重审的分级照旧(上方「审查范围分级」);tier=independent 时把整包贴进 QA 子代理派发 prompt:
445
+ {REVIEW_MATERIALS}
445
446
  2. 逐一对照 design.md 中的设计要点与实际代码实现
446
447
  3. 检查接口签名、数据结构、模块划分是否一致
447
448
  4. 记录偏差项(偏差 ≠ 错误,可能是合理的实现调整)
@@ -1234,6 +1235,8 @@ ${workdirLines}
1234
1235
  蓝图文件(tasks.md / design.md / proposal.md / requirements.md)在主工作区 {SPEC_ROOT}/changes/<change>/ 下(CLI 已替换为主仓绝对路径),它们可能不在 worktree 中。读取蓝图时使用主工作区路径,不要拼接到 worktree 路径下。
1235
1236
 
1236
1237
  ⚠️ **铁律:spec 流程产物只写主仓 {SPEC_ROOT},绝不写进 worktree 副本**——包括 module-impact.md / knowledge 条目 / 模块卡与 \`<module>.changelog.md\` sidecar。在 worktree 内发现 \`.sillyspec/\` 目录是 checkout 副本,写进去的任何内容都会随 worktree cleanup 整目录蒸发(2026-09-10 实证:模块文档写副本、归档被迫 checkout 补救)。子代理 prompt 中涉及此类产物时,必须原样带上主仓绝对路径。
1238
+
1239
+ ⚠️ **Python 后端导入链陷阱(venv editable install,2026-09-18 实证 gen:types 坑)**:worktree 内跑任何 \`import app\` / dump_openapi / gen:types 类命令时,若用的是主仓 venv(junction 链接或裸调主仓 python),其 editable install(\`_editable_impl_*.pth\`)指向**主仓**绝对路径——\`import app\` 会静默解析到主仓旧代码,产出旧 schema/旧行为且零报错(看似「后端没生效」)。**跑生成链前设 \`PYTHONPATH=<worktree>/backend\`**(PYTHONPATH 优先于 .pth),或用 worktree 自建 venv;\`sillyspec worktree doctor\` 的 editable-install-escape 检查可提前暴露。
1237
1240
  `
1238
1241
  : ''
1239
1242
  }
@@ -1316,6 +1319,7 @@ ${taskSummary}
1316
1319
  4. ${moduleDocPoint}
1317
1320
  5. 任务含测试代码时,把下方「测试用例设计」整段复制进子代理 prompt,要求子代理按此设计测试用例
1318
1321
  6. **增量落盘与中断接手指引**:每完成一个可见产出(代码/测试/文档),立即写盘并执行一次最小验证(如语法检查、单跑相关测试)。工作过程中如被 429/API 配额/会话中断,应在最终回复里输出「已完成清单」(含文件路径、测试命令、当前卡点),不要只输出结论——主代理会依据磁盘产物和该清单判断哪些部分已完成,哪些需接手补做,避免重做已落盘的工作
1322
+ **中间验证定向优先:node --test <本任务测试文件>;全量 npm test 留 task 收口与 verify --done**(测选路引导,2026-09-18-preflight-slimming task-04:中间验证只跑本任务相关测试文件,全量套件留给 task 收口与 verify --done,防每步全量测试拖慢执行;与 taskcard-rules.md verify 段同款文案)
1319
1323
  7. **任务边界铁律**:严格只实现本 task 的 \`allowed_paths\` 内文件;若 design.md/plan.md 明确指定了接口/回调/钩子接入位置,必须逐字遵守;不允许顺手实现其他 task 的内容(如 task-01 不要把 task-02 的接入也做了)。如发现必须改其他 task 文件才能继续,先回到主代理由主代理决定是否重分 Wave 或调整 plan,禁止子代理私自越界
1320
1324
  8. **batch 子代理协议**(仅当按「执行方式」节条件合并 batch 时附加进该子代理 prompt):按 batch 内 task 顺序逐个完成实现闭环——读取 tasks/task-N.md → 实现 → 跑该 task 的 verify 命令 → 记录该 task 报告(改动文件清单 / verify 结果 / 卡点)→ 才开始下一个 task;最终回复输出逐 task 报告清单。禁止写 review.json、禁止勾选 tasks.md checkbox——task 审查与勾选归主 agent,在子代理返回后逐 task 进行。越权即停:发现必须改 batch 内其他 task 或任何 batch 外 task 的 allowed_paths 文件 → 立即停止本 task 及后续,报告冲突文件与卡点,回主 agent 裁决(重分 Wave / 调整 plan / 回退独立子代理)。第 7 条任务边界铁律在 batch 语境下的「本 task」= 当前正在实现的 task
1321
1325
  ${globalConstraintsBlock}
@@ -1463,6 +1467,10 @@ export function buildExecuteSteps(planFilePath = null, options = {}) {
1463
1467
  const waveSteps = waves.map((wave, i) => ({
1464
1468
  name: `Wave ${i + 1} 执行`,
1465
1469
  mode: 'implementation',
1470
+ // 前置失败清单声明(2026-09-18-preflight-slimming task-04,D-001@v1):任务步预留键位——
1471
+ // allowed_paths 越界速查(allowed-paths-scan)留 v2 扩展点(run/prompt.js PREFLIGHT_VALIDATORS
1472
+ // 未收录键安全跳过),本期显式空声明锚定 shape,渲染行为与未声明同态(恒空清单零注入)。
1473
+ preflightValidators: [],
1466
1474
  prompt: buildWavePrompt(wave, i + 1, changeDir, worktreePath, { dispatchMode: options.dispatchMode, branch: options.branch, ctx, _execCache: execCache }),
1467
1475
  outputHint: `Wave ${i + 1} 执行结果`,
1468
1476
  optional: false
@@ -99,6 +99,10 @@ const stepClassify = {
99
99
  - 需要人工审查设计方向
100
100
  - 涉及 worktree / baseline / sandbox 等基础设施
101
101
 
102
+ ### plan_level 语义(编排标签,不定价评审仪式)
103
+ plan_level 只决定**工作量轴**:wave 拆分、并行子代理调度、plan 模板厚度(none 占位 / light 四段 / full 全蓝图)。
104
+ 评审仪式档位(ceremony_tier,S0~S3)由 CLI 按 blast/span/friction 三轴风险**客观定价**(风险轴),与 plan_level 无关——「计划写得完整」不会、也不应触发更重的评审仪式;反之,agent 报 full 但档位为 S0/S1 时,CLI 强制轻仪执行并在审查步注入文案留审计痕。plan_level 按编排需要如实判定即可,勿用它博取或回避评审强度。
105
+
102
106
  ### 输出格式
103
107
  在输出开头,以如下格式输出分类结果:
104
108
 
@@ -130,7 +134,12 @@ needs_human_review: true | false
130
134
  const stepGeneratePlan = {
131
135
  id: 'generate_plan',
132
136
  name: '生成分级计划',
133
- prompt: `根据 plan.md frontmatter 的 plan_level 结果,按对应级别生成计划。
137
+ // 前置失败清单声明(2026-09-18-preflight-slimming task-04,D-001@v1):产出 plan.md 前置快查
138
+ // brainstorm 四件套规则轻子集(与生成分级计划 --done 消费的门禁同源;postcheck 轻子集
139
+ // postcheck-lite 为 v2 扩展点——PREFLIGHT_VALIDATORS 未收录,勿用)。声明键位随步骤定义
140
+ // 分发(键名消费见 run/prompt.js renderPreflightFailures)。
141
+ preflightValidators: ['four-piece-rules'],
142
+ prompt: `根据 plan.md frontmatter 的 plan_level 结果,按对应级别生成计划。plan_level 只决定编排(wave 拆分 / 并行子代理 / 模板厚度——工作量轴);评审仪式档位一律由 ceremony_tier 按 risk 客观定价(风险轴),与本步所选级别无关。
134
143
 
135
144
  ### 操作
136
145
  1. 读取 plan.md frontmatter 的 \`plan_level:\` 字段(上一步已落盘为持久锚点;文件不存在或无该字段时回退读上一步输出的分类结果)
@@ -148,7 +157,7 @@ const stepGeneratePlan = {
148
157
  ---
149
158
 
150
159
  #### plan_level = none
151
- 生成最小 plan.md(占位文件,保持流程兼容),不生成完整蓝图。格式:
160
+ 生成最小 plan.md(占位文件,保持流程兼容),不生成完整蓝图(plan_level=none 只收缩编排与模板厚度,不减免评审仪式——仪式档位由 ceremony_tier 定价)。格式:
152
161
  \`\`\`markdown
153
162
  ---
154
163
  plan_level: none
@@ -173,7 +182,7 @@ plan_level: none
173
182
  ---
174
183
 
175
184
  #### plan_level = light
176
- 生成轻量 plan.md,保存到变更目录。只包含以下四部分:
185
+ 生成轻量 plan.md,保存到变更目录(plan_level=light 只决定轻量模板与简化编排,不升降评审仪式——仪式档位由 ceremony_tier 定价)。只包含以下四部分:
177
186
 
178
187
  \`\`\`markdown
179
188
  ---
@@ -212,7 +221,7 @@ light 计划的约束:
212
221
  ---
213
222
 
214
223
  #### plan_level = full
215
- 生成完整 plan.md,保存到变更目录。格式如下:
224
+ 生成完整 plan.md,保存到变更目录(plan_level=full 只展开完整蓝图与 wave/并行编排,不加重评审仪式——仪式档位由 ceremony_tier 定价;agent 报 full 而档位 S0/S1 时 CLI 强制轻仪并留审计痕)。格式如下:
216
225
 
217
226
  \`\`\`markdown
218
227
  ---
@@ -315,13 +324,18 @@ plan_level + 计划内容(审查在下一步独立进行)`,
315
324
 
316
325
  /**
317
326
  * 审查计划 step —— 把"审查"从生成 step 拆出(原 stepGeneratePlan 生成+自检同次输出是最严重的 self-review)。
318
- * 按规模分级:tier=self 当前 agent 自审;tier=independent 强制独立子代理 + review.json。
319
- * 占位符 {REVIEW_TIER} / {REVIEW_TIER_REASON} / {STAGE_REVIEW_RUN_ID} 由 run.js 注入。
327
+ * 按档分级(2026-09-18-ceremony-risk-pricing task-05):tier 由 ceremony_tier 风险定价映射——
328
+ * S0/S1→self(CLI 清单核验[+定向探针])、S2/S3→independent(单轮/两轮);plan_level 降级为编排标签,
329
+ * 不再驱动评审仪式档。占位符 {REVIEW_TIER} / {REVIEW_TIER_REASON} / {STAGE_REVIEW_RUN_ID} 由 run.js 注入
330
+ * ({REVIEW_TIER} 注入值随附 ceremony 档位菜单,见 run/prompt.js renderCeremonyTierInjection)。
320
331
  */
321
332
  const stepReviewPlan = {
322
333
  id: 'review_plan',
323
334
  name: '审查计划',
324
335
  conditionalWait: true,
336
+ // 前置失败清单声明(2026-09-18-preflight-slimming task-04):审查步不产出规范文件、无
337
+ // --done 门禁面要前置快查——显式空声明锚定键位(renderPreflightFailures 空/未声明同态返 '')。
338
+ preflightValidators: [],
325
339
  waitReason: '等待用户确认计划后进入执行',
326
340
  waitOptions: ['确认,进入执行', '需要调整'],
327
341
  prompt: `对上一步生成的 plan.md 做审查。生成与审查分离——不在生成 plan 的同一上下文里自审,避免确认偏差。
@@ -332,19 +346,22 @@ plan.md 审查通过后、进入 execute 前,若 plan_level=full(跨模块/
332
346
  - 调用:\`sillyspec run plan --wait --reason "等待用户确认计划" --options "确认,进入执行,需要调整" --output "计划摘要"\`
333
347
  - 用户确认后再 --done;plan_level=none/light(小变更)无需等待,正常完成即可
334
348
 
335
- ### 当前审查分级(CLI 按变更规模判定,占位符由 run.js 注入)
349
+ ### 当前审查分级(CLI 按 ceremony_tier 风险定价,占位符由 run.js 注入)
336
350
  tier: {REVIEW_TIER}({REVIEW_TIER_REASON})
337
- - tier=self:当前 agent 直接执行下方审查清单(小变更,独立审查仪式成本 > 收益)
338
- - tier=independent:必须用 Agent tool 启动一个独立的计划审查子代理(独立上下文,不共享你生成 plan 时的分析与倾向),由子代理执行下方审查清单并输出 review.json
339
- 宿主环境无 Agent tool 可用(调用报 Unknown agent / Available agents: none)→ 不卡死:主代理切换为审查者角色自审替代,reviewerNotes 首行记录「降级:环境无子代理可用」,逐条结论附源码锚点(file:line 或 grep/read 证据)补偿独立性。
351
+ - tier=self(ceremony 档 S0/S1):当前 agent 直接执行下方审查清单——S0=CLI 清单核验;S1=CLI 清单核验+定向探针抽查(对最高风险条目定向读源码验证)。轻仪是风险定价的正常形态而非偷懒豁免,清单机械项一条不省
352
+ - tier=independent(ceremony 档 S2/S3):必须用 Agent tool 启动独立的计划审查子代理(独立上下文,不共享你生成 plan 时的分析与倾向),由子代理执行下方审查清单并输出 review.json——S2=单轮;S3=两轮独立评审(视角互补,第二轮聚焦首轮未决项)
353
+ 宿主环境无 Agent tool 可用(调用报 Unknown agent / Available agents: none)→ 不卡死(降级兜底,仅评审通道全不可用时):主代理切换为审查者角色自审替代,reviewerNotes 首行记录「降级:环境无子代理可用」,逐条结论附源码锚点(file:line 或 grep/read 证据)补偿独立性。
354
+ - 仪式按 risk 计价,plan_level 仅编排:agent 自报 plan_level=full 而 CLI 判档 S0/S1 时,CLI 强制轻仪执行并在注入文案留审计痕(强制轻仪说明行随 tier 注入)——不因「计划写得完整」进入 independent×2,勿自行升仪对抗定价
340
355
 
341
356
  ### 审查清单(读取 plan.md 的 plan_level,逐条核对)
342
357
  ${REVIEW_CHECKLISTS.plan.map((item) => '- [ ] ' + item).join('\n')}
343
358
 
344
359
  ### tier=independent 时:启动 plan-review 子代理
345
360
  {PRIOR_REVIEW_FACTS}
346
- 用 Agent tool 启动子代理(subagent_type: general),prompt 要点:
347
- 1. 独立读取 {SPEC_ROOT}/changes/<change>/plan.md + design.md + tasks/*.md(不要让生成者喂结论给你,自己读原始文件)
361
+ 用 Agent tool 启动子代理(subagent_type: general),**材料包口径(2026-09-19-review-material-cli-wiring)**——CLI 已机械组装注入下方(src/review-material-pack.js assembleStageReviewMaterials('plan-review'):design 硬约束,缺「全局硬约束」节时 decisions.md P0/P1 accepted 决策兜底);「plan 差量」节留位——派发前由你逐约束判定补齐(一致/偏离/未覆盖+注),留空则评审者按 cannot_verify 列缺件。把补完的整包贴进派发 prompt:
362
+ {REVIEW_MATERIALS}
363
+ prompt 要点:
364
+ 1. **材料包是评审基准面**:checklist 逐条只对包作答;包外文件可定向查证但须列明(禁全量扫读);包不足以作答→cannot_verify+requiredEvidence 列缺件。plan.md 本体可读(审查对象),design.md 只读包内 digest+点名锚(不是全文)。
348
365
  2. 执行上方审查清单,每条给 pass/gap/fail + 证据
349
366
  3. 输出 review.json(CLI Stage Review Gate 将硬校验,契约如下 —— schema + 完整示例 + docHash 算法,照抄改值):
350
367
  {REVIEW_JSON_CONTRACT}
@@ -162,7 +162,7 @@ export const definition = {
162
162
  2. 如果存在:
163
163
  - 逐个读取 tasks/task-NN.md,对照 frontmatter 的 \`acceptance:\` 列表逐条核验(TaskCard 协议的验收标准在 frontmatter YAML,正文无 checkbox)
164
164
  - 每条 acceptance 对照实际实现/测试结果判定满足与否,未满足的项列为不通过
165
- - 覆盖对账走探针 7 矩阵:verify-result.md「探针 7:验收×测试覆盖矩阵」段已由 CLI 预填归属测试文件与关键词提示(骨架未生成时先跑 \`sillyspec verify-probes --change <change-name> --init\`,幂等不覆盖已有正文);逐行填判定(covered/partial/uncovered/non-testable 四选一)与证据——covered/partial 附测试锚点(file:line / \`.test.\` 测试文件名 / 反引号包裹的路径或测试名,行号可省),non-testable 写一句理由;判定/证据未填会被 verify \`--done\` 门禁阻断(fail-closed)
165
+ - 覆盖对账走探针 7 矩阵:verify-result.md「探针 7:验收×测试覆盖矩阵」段已由 CLI 预填归属测试文件与关键词提示(骨架未生成时先跑 \`sillyspec verify-probes --change <change-name> --init\`,幂等不覆盖已有正文);逐行填判定(covered/covered-service/partial/uncovered/non-testable 五选一——covered-service 适用:端点行为由 service 层等非端点层测试锁定,证据附测试锚点)与证据——covered/covered-service/partial 附测试锚点(file:line / \`.test.\` 测试文件名 / 反引号包裹的路径或测试名,行号可省),non-testable 写一句理由;判定/证据未填会被 verify \`--done\` 门禁阻断(fail-closed)
166
166
  3. 如果不存在:跳过此步骤
167
167
 
168
168
  ### 输出
@@ -191,11 +191,11 @@ export const definition = {
191
191
 
192
192
  ### 操作
193
193
  1. 汇总以上所有检查结果
194
- 2. **变更风险等级(change_risk_profile)由 CLI 自动判定与门控**:你无需自己扫描关键词。本步骤 --done 时,CLI 会用 detectChangeRisk 扫描 design.md / plan.md 自动判定等级(doc-only / unit-sufficient / contract-required / integration-critical / deployment-critical)并强制门控:integration-critical / deployment-critical 变更若结论为 PASS / PASS WITH NOTES 但缺少真实集成证据,CLI 直接阻断 verify 完成——谎报结论无效。
195
- - **判级是机械字面匹配 + 同句否定抑制**:关键词命中位置前方同句有否定提示(不/未/无/避免…,如「本次不新增 daemon 协议」)时该次命中被抑制、不参与判级;「daemon 不稳定」「不同模块的 daemon」这类否定词不在关键词前方的仍正常命中。
196
- - **误判时的诚实出路(豁免级)**:在 design.md 顶部 frontmatter 加一行 \`risk_level: <真实等级>\`(doc-only / unit-sufficient / contract-required / integration-critical / deployment-critical),CLI 会以声明为准覆盖关键词判级。声明后若是 unit-sufficient 等豁免级,PASS WITH NOTES 不再被强制拦;但结论为 PASS 仍需对应证据。
197
- - **留痕要求(防逃逸)**:用了显式声明,必须在本报告「变更风险等级」section 写明「risk_level 由 design frontmatter 显式声明 = <等级>(覆盖关键词判级)」+ 一句话理由,让豁免可审计;若有命中被否定语境抑制,同样写明被抑制关键词与理由(抑制可审计,不许用来静默降级)。
198
- 你只需:在 verify-result.md 的「变更风险等级」section 如实记录变更性质;若变更涉及 daemon/backend 跨进程、session/lease/lifecycle 状态机、或部署启动路径,在「Runtime Evidence」section 提供真实集成证据(启动命令、daemon↔backend 调用与日志关键片段、终态断言)。
194
+ 2. **变更风险等级(change_risk_profile)由 CLI 自动判定与门控**:你无需自己扫描。本步骤 --done 时,CLI 按项目声明危险面(\`_module-map.yaml\` 顶层 blast 段的路径前缀声明)× 变更文件面自动判定(2026-09-19-ceremony-pricing-five-cuts 起的声明面口径——文档措辞与文件内容不参与判级),并强制门控:命中 evidence:true 声明路径的变更若结论为 PASS / PASS WITH NOTES 但缺少真实集成证据,CLI 直接阻断 verify 完成——谎报结论无效。
195
+ - **判级输入=文件路径 × 项目声明**(不是关键词):变更文件(design 文件变更清单 / 实际 diff)命中声明前缀即取该声明档位;未配置项目/未命中路径一律 S1 起步。「撞词」时代结束:文档里写 daemon/session 等措辞不再影响判级。
196
+ - **frontmatter risk_level 的作用边界**:在 design.md 顶部 frontmatter 加 \`risk_level: <等级>\`(doc-only / unit-sufficient / contract-required / integration-critical / deployment-critical)可**压低/抬高仪式档**(评审轮次定价),但**不豁免 evidence:true 命中的集成证据要求**——证据豁免的唯一出路是改 \`_module-map.yaml\` blast 段的 evidence 声明(进 git、评审可见),不得靠声明措辞绕证据门。
197
+ - **留痕要求(防逃逸)**:用了显式声明,必须在「变更风险等级」section 写明「risk_level 由 design frontmatter 显式声明 = <等级>(压仪式档;evidence 要求不受豁免)」+ 一句话理由,让声明可审计。
198
+ 你只需:在 verify-result.md 的「变更风险等级」section 如实记录变更性质;若变更文件命中 evidence:true 声明路径(CLI 会在 design/verify 门禁 warnings 点名命中前缀),在「Runtime Evidence」section 提供真实集成证据(启动命令、跨进程调用与日志关键片段、终态断言)。
199
199
  - **集成回执须真实执行——commands.smoke 配置后由 CLI 亲跑并机器落盘(source: cli-noai-smoke 标记,亲跑段谎报无效)**,其余形态仍须 agent 实跑后据实填写:CLI 对其余形态只校验**字面存在**(是否含关键词),**不会替你启动 daemon、打真实请求或跑迁移**——它是否名副其实取决于你是否实跑过,不得凭堆关键词通过门控。(测试套件对账另算:commands.test 由 CLI 真实执行,那块谎报无效。)
200
200
 
201
201
  3. **生成 verify-result.md 骨架(勿从零手写)**:先跑 \`sillyspec verify-probes --change <change-name> --init\`——一条命令生成十章节骨架(已存在不覆盖),其中**探针结果章节已机械预填**(探针 1 的 TODO/FIXME 命中清单、探针 3 的测试覆盖、探针 5 的 API 契约对账表、探针 6 的删除对账三态判定),文件落 \`{SPEC_ROOT}/changes/<change-name>/verify-result.md\`(CLI 替换出的**主仓绝对路径**;若当前 cwd 在 worktree 内也绝不落 worktree 副本——CLI 校验读主仓,副本随 worktree 清理蒸发)。你只需把各 \`<!--TODO-->\` 占位替换为语义结论;半语义探针(2 关键词覆盖 / 4 决策追踪)与断言抽查、集成盲区标注由你补在对应 TODO 处
@@ -204,7 +204,7 @@ export const definition = {
204
204
  6. 给出结论:PASS / PASS WITH NOTES / FAIL(受风险门控约束)——**结论只认骨架「结论枚举:」槽行(行首锚定,把 <待填:三选一> 整体替换为枚举值);槽留待填会被 gate 判不过,正文其他位置的 PASS/FAIL 字样不参与判定**
205
205
  - **结论想写 PASS 前自查四事实条件(PASS 封顶语义,--done 门禁按 verify-facts.json 实拦)**:①集成实测未跑 ②「## 移交项(结构化)」含 blocking 级行 ③db/**/*.sql 未声明执行(verify-result.md 无「已对目标库执行」声明、回执 command 亦不含该文件)④验收×测试覆盖矩阵含 partial/uncovered 行且移交项零有效行——任一成立即不得写 PASS:改写 PASS WITH NOTES 并在「## 移交项(结构化)」表格如实分行(类型枚举 env-blocked/manual-acceptance/db-script/other)。severity 口径:db-script/env-blocked 恒 blocking;manual-acceptance/other 默认 advisory、须显式标 blocking 才计入封顶;确需把 blocking 降为 advisory 必须附「(降级:<理由>,依据 <file:line 或 D-xxx>)」
206
206
  7. **核对 module-impact.md**(若 \`{SPEC_ROOT}/changes/<change>/module-impact.md\` 存在):对照本次实际代码变更(git diff)与 module-impact.md 的模块影响矩阵,发现不一致(漏标受影响模块 / 影响类型错误 / 实际未触碰的模块被误标)则在 verify-result.md 标注。module-impact 由 plan 首版生成、execute 各 Wave 更新,verify 是最后一次核对机会(archive 仅终审不再生成)。这是 advisory 核对(不阻断 verify 完成),但 module-impact 与实际严重背离应记为风险。
207
- 8. **smoke 纪律(local.yaml 配置 commands.smoke 或本变更判级 integration/deployment-critical 时执行以下纪律;本步 prompt 为静态导出,命中条件以文本内嵌说明注入——未命中时本段不适用)**:
207
+ 8. **smoke 纪律(local.yaml 配置 commands.smoke 或本变更命中 evidence:true 声明危险面时执行以下纪律;本步 prompt 为静态导出,命中条件以文本内嵌说明注入——未命中时本段不适用)**:
208
208
  ${REVIEW_CHECKLISTS.verify.map((item) => ' - ' + item).join('\n')}
209
209
 
210
210
  ### verify-result.md 章节结构(骨架已含,占位替换即可)
package/src/taskcard.js CHANGED
@@ -128,6 +128,9 @@ constraints:
128
128
  精确文件路径(仓根相对、正斜杠),当前不存在、将由本 task 新建的文件加
129
129
  NEW: 前缀(如 NEW:src/foo.js);禁 glob(src/**)、禁目录前缀(src/dir/)、
130
130
  禁绝对路径;无明确文件级意图时保留 [] 占位行不动。
131
+ implementation/acceptance 里的源码位置同样写仓根相对全路径+行号(src/foo.js:123)——
132
+ 裸文件名在 docs-check 层1 靠 basename 全仓扫描找候选,找不到候选或关键词
133
+ 窗口不匹配即失效,到 pre-push 才拦(2026-09-19 实证 64 处返工)。
131
134
  可选字段按需插进上方 frontmatter(规则见 taskcard-rules):
132
135
  repo: 仅跨仓 task 填(local.yaml repos: 注册的仓 key;缺省=main。allowed_paths 相对该仓根写,
133
136
  禁止带仓库名前缀/绝对路径——review 对账按仓根相对路径匹配,带前缀永不命中)