sillyspec 3.29.4 → 3.30.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 (148) hide show
  1. package/.claude/skills/sillyspec-auto/SKILL.md +54 -54
  2. package/.claude/skills/sillyspec-brainstorm/SKILL.md +2 -2
  3. package/.claude/skills/sillyspec-commit/SKILL.md +1 -1
  4. package/.claude/skills/sillyspec-flow/SKILL.md +57 -0
  5. package/.claude/skills/sillyspec-quick/SKILL.md +18 -116
  6. package/AGENTS.md +38 -20
  7. package/SKILL.md +9 -3
  8. package/assets/command-cards/flow.md +32 -0
  9. package/assets/command-cards/run-archive.md +26 -0
  10. package/assets/command-cards/run-brainstorm.md +33 -0
  11. package/assets/command-cards/run-execute.md +29 -0
  12. package/assets/command-cards/run-plan.md +27 -0
  13. package/assets/command-cards/run-quick.md +17 -0
  14. package/assets/command-cards/run-verify.md +29 -0
  15. package/assets/command-cards/status.md +27 -0
  16. package/package.json +2 -1
  17. package/packages/dashboard/server/executor.js +2 -1
  18. package/packages/dashboard/server/parser.js +7 -7
  19. package/r7-test-env/README-R7-TEST.md +93 -0
  20. package/r7-test-env/demo-project/lib/calc.js +6 -0
  21. package/r7-test-env/demo-project/package.json +1 -0
  22. package/r7-test-env/demo-project/test.js +10 -0
  23. package/r7-test-env/prompt-R7-SMOKE.md +41 -0
  24. package/r7-test-env/sillyspec-r7.cmd +3 -0
  25. package/round4/account.mjs +98 -0
  26. package/round4/b1-analyze-task08.mjs +40 -0
  27. package/round4/b1-b1-target-validation.md +32 -0
  28. package/round4/b3a-prompt-audit-result.md +66 -0
  29. package/round4/b3a-prompt-audit.mjs +89 -0
  30. package/round4/brief-autocompact.md +34 -0
  31. package/round4/brief-session-replay.md +50 -0
  32. package/round4/deep-read-synthesis.md +38 -0
  33. package/round4/extract-full.mjs +38 -0
  34. package/round4/extract-timeline.mjs +43 -0
  35. package/round4/find-sessions.mjs +13 -0
  36. package/round4/full-r4-l.md +4707 -0
  37. package/round4/full-r4-o-l.md +4300 -0
  38. package/round4/full-r4-os.md +1681 -0
  39. package/round4/full-r4-sf.md +4255 -0
  40. package/round4/full-r4-sq.md +1734 -0
  41. package/round4/gate-audit-relay-receipt.md +30 -0
  42. package/round4/gate-value-audit.md +52 -0
  43. package/round4/optimization-plan.md +165 -0
  44. package/round4/prereg.md +67 -0
  45. package/round4/prompt-R4-L.md +63 -0
  46. package/round4/prompt-R4-O-L.md +59 -0
  47. package/round4/prompt-R4-O-S.md +56 -0
  48. package/round4/prompt-R4-S-F.md +60 -0
  49. package/round4/prompt-R4-S-Q.md +66 -0
  50. package/round4/r4-final-report.html +347 -0
  51. package/round4/r4-os-timeline.txt +297 -0
  52. package/round4/r4-sf-timeline.txt +738 -0
  53. package/round4/r4-sq-timeline.txt +322 -0
  54. package/round4/r4l-timeline.txt +793 -0
  55. package/round4/r4ol-timeline.txt +729 -0
  56. package/round4/r4sf-timeline.txt +852 -0
  57. package/round4/r5-scenario-matrix.md +46 -0
  58. package/round4/result-S.md +171 -0
  59. package/round4/result-final.md +46 -0
  60. package/round4/result-quality.md +63 -0
  61. package/src/change-list.js +9 -0
  62. package/src/classify-change.js +6 -0
  63. package/src/command-cards.js +74 -0
  64. package/src/commit-guard.js +2 -0
  65. package/src/config-schema.js +64 -12
  66. package/src/contract-matrix.js +26 -5
  67. package/src/db.js +35 -4
  68. package/src/decision-distill.js +9 -3
  69. package/src/diagnostic-codes.js +2 -1
  70. package/src/docs-check.js +1 -1
  71. package/src/doctor-diagnostics.js +44 -2
  72. package/src/flow-draft.js +748 -0
  73. package/src/flow-parity.js +236 -0
  74. package/src/flow-review.js +184 -0
  75. package/src/flow.js +1492 -0
  76. package/src/foreign-declared.js +17 -1
  77. package/src/fr-index.js +396 -22
  78. package/src/fs-atomic.js +5 -1
  79. package/src/git-helper.js +2 -2
  80. package/src/greenfield-bootstrap.js +98 -0
  81. package/src/handoff.js +120 -7
  82. package/src/hunk-attribution.js +191 -0
  83. package/src/index.js +602 -21
  84. package/src/init.js +25 -8
  85. package/src/knowledge-classify.js +47 -1
  86. package/src/knowledge-digest.js +162 -0
  87. package/src/knowledge-quicklog.js +246 -0
  88. package/src/machine-draft.js +128 -0
  89. package/src/machine-interface.js +194 -7
  90. package/src/mcp-server.js +1 -0
  91. package/src/module-impact.js +51 -22
  92. package/src/module-resolve.js +3 -1
  93. package/src/preview-progress.js +160 -0
  94. package/src/progress/change-registry.js +11 -3
  95. package/src/progress/shared.js +3 -2
  96. package/src/progress.js +56 -13
  97. package/src/quicklog.js +44 -6
  98. package/src/redomain.js +147 -0
  99. package/src/review-material-pack.js +204 -1
  100. package/src/run/archive-distill.js +4 -2
  101. package/src/run/bg-sync.js +67 -0
  102. package/src/run/command.js +127 -10
  103. package/src/run/complete-handlers.js +363 -73
  104. package/src/run/complete.js +356 -74
  105. package/src/run/gate-snapshot-ledger.js +213 -0
  106. package/src/run/gate-snapshot.js +260 -38
  107. package/src/run/gates.js +482 -268
  108. package/src/run/green-cache.js +123 -0
  109. package/src/run/junction-rm.js +70 -0
  110. package/src/run/next.js +14 -0
  111. package/src/run/prompt.js +185 -55
  112. package/src/run/quick-audit.js +213 -14
  113. package/src/run/shared.js +123 -4
  114. package/src/run/stage.js +182 -216
  115. package/src/run/test-ledger.js +264 -0
  116. package/src/run/verify-quality-scan.js +84 -7
  117. package/src/scope-audit.js +6 -1
  118. package/src/sentinel-assertions.js +114 -0
  119. package/src/setup.js +3 -3
  120. package/src/spec-sync.js +41 -1
  121. package/src/stage-contract.js +26 -8
  122. package/src/stage-review-checklist.js +2 -0
  123. package/src/stage-review.js +7 -2
  124. package/src/stage-templates.js +157 -0
  125. package/src/stages/archive.js +2 -0
  126. package/src/stages/brainstorm.js +24 -106
  127. package/src/stages/execute.js +222 -85
  128. package/src/stages/knowledge.js +74 -1
  129. package/src/stages/plan-postcheck.js +385 -20
  130. package/src/stages/plan.js +23 -1
  131. package/src/stages/quick.js +10 -2
  132. package/src/stages/verify.js +4 -2
  133. package/src/sync.js +81 -23
  134. package/src/task-done.js +181 -0
  135. package/src/task-review.js +8 -55
  136. package/src/taskcard-frontmatter.js +57 -0
  137. package/src/test-bindings.js +549 -0
  138. package/src/timeline.js +218 -0
  139. package/src/ui-visual.js +175 -0
  140. package/src/verify-draft.js +214 -0
  141. package/src/verify-postcheck.js +919 -159
  142. package/src/verify-probes.js +103 -25
  143. package/src/watcher.js +1031 -0
  144. package/src/worktree-apply.js +44 -4
  145. package/src/worktree-deps.js +22 -8
  146. package/src/worktree-policy.js +42 -0
  147. package/src/worktree.js +102 -15
  148. package/templates/agents-instruction.md +37 -18
package/src/spec-sync.js CHANGED
@@ -617,10 +617,48 @@ export async function syncSpecTree(specRoot, platform, changeName, opts = {}) {
617
617
  // 服务器、不进冲突文件;服务器「冲突 op 跳过、其余照常 apply」语义下本会话真改动
618
618
  // 已落——follower 清空即视为本轮同步成功(写基线,下次起 pre-POST 即拦)。
619
619
  const serverVersions = body.server_versions || {};
620
+ // 墓碑拒收通道(runbook spec-push-conflict-recovery 待修①,2026-09-25 实证):平台
621
+ // apply_ops 对带 platform_deleted 墓碑的路径**无条件拒收**(与版本无关,update 的
622
+ // base=version 完全匹配仍拒)。此前 CLI 只消费 server_versions → 墓碑拒收被译成
623
+ // 「又有更新/版本冲突」,resolve --keep-local 重推永远无效(转圈)。墓碑路径从版本
624
+ // 冲突叙事中分离:恢复走平台 manifest-heal 通道(2026-09-26-manifest-heal-endpoint)。
625
+ const tombstoned = Array.isArray(body.platform_deleted)
626
+ ? body.platform_deleted.filter((p) => typeof p === 'string')
627
+ : [];
628
+ const tombSet = new Set(tombstoned);
620
629
  const { followers, real } = opts.forcePush
621
630
  ? { followers: [], real: serverVersions }
622
631
  : partitionConflictPaths(serverVersions, localFiles, baseHashes, lastSyncTs);
623
- const realPaths = Object.keys(real);
632
+ const realPaths = Object.keys(real).filter((p) => !tombSet.has(p));
633
+
634
+ // 纯墓碑形态:版本冲突面为空、拒收全是墓碑 → 不写/不清通用冲突标记(clear 会把
635
+ // 状态洗成「已消解」误导 status),单独落冲突文件 + 正确恢复指引。
636
+ if (realPaths.length === 0 && tombstoned.length > 0) {
637
+ let tombConflictPath = null;
638
+ try {
639
+ const runtimeDir = join(specRoot, '.runtime');
640
+ mkdirSync(runtimeDir, { recursive: true });
641
+ tombConflictPath = join(runtimeDir, `spec-sync-conflict-${changeName}.json`);
642
+ writeFileSync(tombConflictPath, JSON.stringify({
643
+ change: changeName,
644
+ kind: 'spec-tree',
645
+ created_at: new Date().toISOString(),
646
+ server_versions: {},
647
+ conflicting_paths: [],
648
+ platform_deleted: tombstoned,
649
+ note: '路径被平台删除墓碑拒收(非版本冲突,resolve 重推无效)。恢复通道:平台 manifest-heal 清墓碑后重推,或联系管理员确认删除是否有意。',
650
+ }, null, 2) + '\n', 'utf8');
651
+ } catch (e) {
652
+ console.warn(`[spec-sync] 墓碑冲突文件写入失败(信息仅打印): ${e.message}`);
653
+ }
654
+ console.warn('');
655
+ console.warn(`⚠️ [spec-sync] ${tombstoned.length} 个路径被平台删除墓碑拒收(非版本冲突,resolve --keep-local 重推无效): ${tombstoned.slice(0, 5).join(', ')}${tombstoned.length > 5 ? ' 等' : ''}`);
656
+ console.warn(`⚠️ 恢复通道:平台 manifest-heal(清墓碑后重推即闭环);详情: ${tombConflictPath || '(写入失败)'}`);
657
+ return { synced: 0, conflict: true, serverVersions: {}, tombstoned, conflictPath: tombConflictPath };
658
+ }
659
+ const tombNote = tombstoned.length > 0
660
+ ? `\n⚠️ 另有 ${tombstoned.length} 个路径被平台删除墓碑拒收(非版本冲突,resolve 无效;恢复走平台 manifest-heal): ${tombstoned.slice(0, 5).join(', ')}${tombstoned.length > 5 ? ' 等' : ''}`
661
+ : '';
624
662
 
625
663
  // 冲突横幅降噪(坑 spec-sync-conflict-banner-spam,与 sync() 进度侧 2026-08-23 修法同族):
626
664
  // 未决 spec 树冲突存在期间,每步自动同步都会对同一批真冲突路径再撞一次——此前每轮
@@ -658,6 +696,7 @@ export async function syncSpecTree(specRoot, platform, changeName, opts = {}) {
658
696
  created_at: new Date().toISOString(),
659
697
  server_versions: real,
660
698
  conflicting_paths: realPaths,
699
+ ...(tombstoned.length > 0 ? { platform_deleted: tombstoned } : {}),
661
700
  auto_followed: followers,
662
701
  note: 'spec 树文件冲突(仅列本地真改动文件;本地未改动文件已自动跟随服务器)。resolve --keep-local 以本地为准重推;--take-platform 拉平台 bundle 覆盖冲突文件(接受服务器版本,2026-09-03 起支持)',
663
702
  }, null, 2) + '\n', 'utf8');
@@ -669,6 +708,7 @@ export async function syncSpecTree(specRoot, platform, changeName, opts = {}) {
669
708
  const shownPaths = realPaths.slice(0, 5).join(', ') + (realPaths.length > 5 ? ' 等' : '');
670
709
  console.warn('');
671
710
  console.warn(`⚠️ [spec-sync] 检测到 spec 树冲突(${realPaths.length} 个文件: ${shownPaths};逐文件服务器版本见冲突详情文件)`);
711
+ if (tombNote) console.warn(`⚠️ [spec-sync]${tombNote.trim()}`);
672
712
  console.warn(`⚠️ 处置:sillyspec platform resolve ${changeName} --keep-local | --take-platform | --abort(冲突详情: ${conflictPath || '(写入失败)'})`);
673
713
  return { synced: 0, conflict: true, serverVersions: real, autoResolved: followers.length, conflictPath };
674
714
  }
@@ -21,7 +21,7 @@ import { backfillFrontmatter } from './scan-postcheck.js'
21
21
  import { parseAllowedPaths } from './stages/plan-postcheck.js'
22
22
  // FR 索引消费(2026-09-18-fr-index-l1 L1):重复嫌疑软门(advisory)——fr-index 是纯 fs+decision-distill
23
23
  // 叶子链,静态引入无环;knowledge-hits 同为叶子。
24
- import { parseChangeRequirements, resolveTouchedDomains, readActiveFrDigest, frTitleOverlap } from './fr-index.js'
24
+ import { parseChangeRequirements, resolveTouchedDomains, readActiveFrDigest, frTitleOverlap, FR_TITLE_OVERLAP_THRESHOLD } from './fr-index.js'
25
25
  import { discoverModuleIndex } from './decision-distill.js'
26
26
  import { appendKnowledgeHit } from './knowledge-hits.js'
27
27
  // design.md 文件清单解析(2026-09-17-pass-cap-semantics task-02 事实③):change-list 是纯 fs
@@ -360,7 +360,7 @@ function validateBrainstormOutputs(cwd, changeName, context = {}) {
360
360
  const o = frTitleOverlap(fr.title, a.title)
361
361
  if (!hit || o > hit.o) hit = { a, o }
362
362
  }
363
- if (hit && hit.o >= 0.6) {
363
+ if (hit && hit.o >= FR_TITLE_OVERLAP_THRESHOLD) {
364
364
  warnings.push(`疑似重复 FR:新「${fr.local} ${fr.title}」与现行 ${hit.a.id}「${hit.a.title}」标题重叠度 ${(hit.o * 100).toFixed(0)}%——若为改写/取代请在该 FR 块加承接行 \`承接: ${hit.a.id}\`,若为不同需求请改标题区分(advisory,L1 观察指标)`)
365
365
  try {
366
366
  appendKnowledgeHit(join(specRoot || join(cwd, '.sillyspec'), '.runtime'), {
@@ -1201,12 +1201,21 @@ export function judgeApiCoverageMatrix(args) {
1201
1201
  const key = `${m[1]} ${m[2]}`
1202
1202
  if (!parseSet.has(key)) anchorViolations.push(`${rowLabel(r)}:design接口表#${key} 未命中 design 接口段解析面(空指)`)
1203
1203
  }
1204
- if (!apiEvidenceHasAnchorForm(e)) anchorViolations.push(`${rowLabel(r)}:证据缺用例依据锚点(五形态之一)`)
1204
+ if (!apiEvidenceHasAnchorForm(e)) {
1205
+ // 分诊(2026-09-23 执行会话实证:design接口表# 字样在场但 # 后非 METHOD /path 形态时,
1206
+ // 泛化「缺五形态之一」让人无法区分「形态没写对」与「写了没算数」——三轮试错后 agent 被逼
1207
+ // 换更弱的 DDL@ 形态过门,fail-closed 反而促成了更泛的锚点。定向报出该形态的命中条件。)
1208
+ API_ANCHOR_DESIGN_API_RE.lastIndex = 0
1209
+ const bareDesignAnchor = /design接口表#/.test(e) && !API_ANCHOR_DESIGN_API_RE.test(e)
1210
+ anchorViolations.push(bareDesignAnchor
1211
+ ? `${rowLabel(r)}:证据含 design接口表# 但 # 后未提取到 METHOD /path——该形态仅 \`design接口表#POST /api/xx\` 形态计命中(仅写表名/行号/散文描述不计)`
1212
+ : `${rowLabel(r)}:证据缺用例依据锚点(五形态之一)`)
1213
+ }
1205
1214
  }
1206
1215
  if (anchorViolations.length > 0) {
1207
1216
  errors.push(
1208
1217
  `接口验证覆盖矩阵有 ${anchorViolations.length} 项证据锚点缺失/空指` +
1209
- `(covered/partial 证据须含锚点五形态之一:design接口表#METHOD /path(须命中 design 接口段解析面)/ 权限矩阵[角色×动作] / 契约表@行标识 / DDL@列名 / 载荷@构造点路径;covered-service 证据须含测试锚点三形态之一:\`.test.\` 文件名 / file:line / 反引号包裹的测试名):${anchorViolations.join(';')}。` +
1218
+ `(covered/partial 证据须含锚点五形态之一,可复制样例:design接口表#POST /api/xx(# 后必须 METHOD /path 且命中 design 接口段解析面,仅表名/行号不计)/ 权限矩阵[admin×读] / 契约表@任务卡字段清单 / DDL@users.id / 载荷@e2e_body.json;covered-service 证据须含测试锚点三形态之一:\`.test.\` 文件名 / file:line / 反引号包裹的测试名):${anchorViolations.join(';')}。` +
1210
1219
  `修复:在证据列补真实锚点(design接口表# 锚点须与 design.md 接口段端点一致,防编造端点)。`
1211
1220
  )
1212
1221
  }
@@ -1945,6 +1954,14 @@ export function checkTransition(fromStage, toStage, options = {}) {
1945
1954
  return { allowed: false, reason: `未知阶段: ${toStage}` }
1946
1955
  }
1947
1956
 
1957
+ // 出生未入门态(2026-09-27-change-birth-stage-brainstorm):出生阶段自 'scan'(auxiliary)改
1958
+ // 'brainstorm' 后,「辅助→主流程任意放行」不再天然覆盖出生态——但 thin/quick、backfill、
1959
+ // complete-step 等大量路径从出生态直接 run 目标阶段(旧 scan 出生全都放行)。等价语义:
1960
+ // brainstorm 仍 pending/无 stages 行(从未真正进入主流程)视同辅助态;真在 brainstorm 中
1961
+ // (in-progress/completed)仍受主流程守卫约束。
1962
+ const fromBirthUntouched = fromStage === 'brainstorm'
1963
+ && (!fromStageData || !fromStageData.status || fromStageData.status === 'pending')
1964
+
1948
1965
  // 辅助阶段随时可执行(archive 除外:从主流程进入 archive 需要校验)
1949
1966
  if (AUXILIARY_STAGES.includes(toStage) && toStage !== 'archive') {
1950
1967
  return { allowed: true }
@@ -1971,15 +1988,16 @@ export function checkTransition(fromStage, toStage, options = {}) {
1971
1988
  if (fromStage === 'verify') {
1972
1989
  return { allowed: true }
1973
1990
  }
1974
- // 独立运行 archive(无前置)也允许
1975
- if (!fromStage || AUXILIARY_STAGES.includes(fromStage)) {
1991
+ // 独立运行 archive(无前置)也允许;出生未入门态同放行(旧 scan 出生态等价——
1992
+ // thin/quick 从未进主流程的变更直接归档走此路)
1993
+ if (!fromStage || AUXILIARY_STAGES.includes(fromStage) || fromBirthUntouched) {
1976
1994
  return { allowed: true }
1977
1995
  }
1978
1996
  return { allowed: false, reason: 'archive 的前置阶段是 verify,不能从 ' + fromStage + ' 跳转' }
1979
1997
  }
1980
1998
 
1981
- // 从辅助阶段进入主流程:允许
1982
- if (AUXILIARY_STAGES.includes(fromStage)) {
1999
+ // 从辅助阶段进入主流程:允许(出生未入门态同——见函数头注释)
2000
+ if (AUXILIARY_STAGES.includes(fromStage) || fromBirthUntouched) {
1983
2001
  return { allowed: true }
1984
2002
  }
1985
2003
 
@@ -29,6 +29,8 @@ export const REVIEW_CHECKLISTS = {
29
29
  'scan/module docs 或源码中的真实约束与 design.md 假设',
30
30
  '涉及角色/权限/字典类数据时,实证不能只看静态定义:须以生产查询口径可解析到目标结果——用真实租户/库下可执行的查询(如按 enname+tenant 查询)确认返回非空,仅 enname 存在不算通过',
31
31
  '涉及新页面/前端路由时,用户入口 × 菜单/注册 DML 对账:入口路由与菜单表、角色-权限注册 DML 逐一对账——能点到的入口必有对应菜单与授权注册行,菜单/注册 SQL 必须出现在交付清单(design.md 文件变更清单或 db/ 脚本)',
32
+ '**承诺可兑现性**:design 的每个可测承诺(如 at-least-once / 不重不漏 / 恒定窗口)逐条过最坏输入四维——乱序到达、并发写、会话或变更切换、多工作区作用域——实现能否兑现?不能兑现则降级承诺文案或改取简方案(如全量重拉结构性消灭整类时序缺陷),禁止立兑现不了的承诺(R14/R15 对撞 5 个 P1 全落此维,pytest 覆盖不到)',
33
+ '**三方对齐**:spec 文本、design、实现三者的取舍矛盾必须二选一对齐——不许"实现跟 design、spec 跟 design"各说各话(增量 vs 全量、有上限 vs 无上限是归档审计翻出的主型);design 拒绝备选方案的理由不得同样适用于自选方案(自我论证矛盾)',
32
34
  ],
33
35
  plan: [
34
36
  'task 编号与 Wave checkbox 格式正确,execute 依赖此格式解析任务',
@@ -20,7 +20,7 @@ import { createHash } from 'crypto'
20
20
  import jsYaml from 'js-yaml'
21
21
  import { VALID_VERDICTS, REVIEW_SCHEMA_VERSION } from './task-review.js'
22
22
  import { detectSpecDirTypo } from './spec-dir-typo.js'
23
- import { resolveRuntimeRoot } from './run/shared.js'
23
+ import { resolveRuntimeRoot, resolveSpecDir } from './run/shared.js'
24
24
 
25
25
  // 文档型 stage review 的合法 reviewType
26
26
  const STAGE_REVIEW_TYPES = ['design', 'plan', 'proposal', 'code', 'acceptance']
@@ -825,7 +825,12 @@ export function registerStageReview({ changeName, stage, fromFile, cwd, platform
825
825
  throw new Error(`register-stage-review: stage 无效 "${stage}"(应为 brainstorm|plan|execute)`)
826
826
  }
827
827
 
828
- const specBase = platformOpts.specRoot || join(cwd, '.sillyspec')
828
+ // specBase 回退走 resolveSpecDir 向上找根(坑 register-stage-review-cwd-pollution-no-hint,
829
+ // 2026-09-25 实证:bash cd 持久化停在 changes/<变更>/tasks/ 时 join(cwd,'.sillyspec') 拼出
830
+ // cwd 相对的事务性错误路径,报「主审查文档不存在」且无 cwd 提示)。与 run/gate 同源
831
+ // (run/shared.js resolveSpecDir 单一真相源,含 home/tmpdir 拒绝守卫),平台模式 specRoot
832
+ // 仍优先——与 shared.js:2123 规范写法 platformOpts?.specRoot || resolveSpecDir(cwd) 一致。
833
+ const specBase = platformOpts.specRoot || resolveSpecDir(cwd)
829
834
  const runtimeRoot = resolveRuntimeRoot(platformOpts, specBase)
830
835
  const changeDir = join(specBase, 'changes', changeName)
831
836
  const reviewType = STAGE_REVIEW_TYPE[stage]
@@ -0,0 +1,157 @@
1
+ /**
2
+ * stage-templates.js — 阶段产物模板出上下文(R16 减负批次,2026-09-24)
3
+ *
4
+ * 背景:brainstorm「生成规范文件」步内嵌 proposal/requirements/tasks/decisions 四份完整落盘
5
+ * 模板(~100 行)——说明书每轮全量回放(R15 实测 burst 单次载荷 1581 行/45K token,59 轮
6
+ * brainstorm 等于把说明书交了 59 遍钱),而模板只在真写产物的轮才需要。M1 指纹缓存只省
7
+ * 「复入重发」不省「每轮回放」,模板出上下文是唯一能把回放面砍掉的手段。
8
+ *
9
+ * 机制:模板单一源在本文件 → materializeStageTemplates 在 outputStep 渲染期幂等落盘
10
+ * `<specBase>/.runtime/templates/<name>.md` → 步骤 prompt 只留要点 + 文件路径 +「写该产物前
11
+ * Read 一次」。产物格式漂移由 --done 门禁兜底(brainstorm.design.* / four-piece 规则逐条
12
+ * 点名——模板没读会撞门禁,重读即修复,闭环在既有机制上)。
13
+ *
14
+ * 只收纯格式骨架;行为纪律(NEW: 前缀铁律/数据流标注/FR 承接纪律等)留在 prompt——规则
15
+ * 是判断依据须常驻,模板是抄写目标按需读。
16
+ */
17
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
18
+ import { join } from 'path'
19
+
20
+ /** brainstorm 四件套产物格式模板(自 stages/brainstorm.js「生成规范文件」步逐字迁移——
21
+ * 与 prompt 指针版本同源;decisions 九字段与可选字段说明一并迁入)。 */
22
+ export const BRAINSTORM_ARTIFACT_TEMPLATES_MD = `# brainstorm 规范文件产物模板(proposal / requirements / tasks / decisions)
23
+
24
+ > 写对应产物前读本文件一次,严格按模板结构与 frontmatter 产出;缺章节/缺 frontmatter 会被
25
+ > 「生成规范文件」步 --done 门禁逐条点名(brainstorm.design.* / four-piece 规则)。
26
+
27
+ ## proposal.md 格式要求
28
+ \`\`\`markdown
29
+ ---
30
+ author: <git-user>
31
+ created_at: <now-datetime>
32
+ ---
33
+ # 提案书(Proposal)
34
+
35
+ ## 动机
36
+ 为什么做、解决什么核心问题
37
+
38
+ ## 关键问题
39
+ 为什么现有方案不够(展开 2-3 个具体痛点)
40
+
41
+ ## 变更范围
42
+ 本次做什么
43
+
44
+ ## 不在范围内(显式清单)
45
+ - 不做 X
46
+ - 不做 Y
47
+
48
+ ## 成功标准(可验证)
49
+ - 旧配置默认行为不变
50
+ - 新功能在配置后可用
51
+ - ...
52
+ \`\`\`
53
+
54
+ ## requirements.md 格式要求
55
+ \`\`\`markdown
56
+ ---
57
+ author: <git-user>
58
+ created_at: <now-datetime>
59
+ ---
60
+ # 需求规格(Requirements)
61
+
62
+ ## 角色
63
+ | 角色 | 说明 |
64
+ |---|---|
65
+ | 开发者 | ... |
66
+
67
+ ## 功能需求
68
+
69
+ ### FR-01: 需求名称
70
+ 覆盖决策:D-001@v1, D-002@v1(如适用)
71
+ 承接: FR-<域>-NNN(如适用——改写/取代注入清单中的已有行为时必填,全新行为省略本行)
72
+ Given 前提条件
73
+ When 触发动作
74
+ Then 期望结果
75
+
76
+ (每个边界条件独立 GWT 块;场景名行 \`#### 场景:X\` 会被归档索引用作摘要,建议保留)
77
+ (约束强度标注——RFC 2119 约定:FR 正文与非功能需求里的约束必须用固定词标硬度:MUST/必须=硬性要求(违反即缺陷);MUST NOT/禁止=红线(绝对不允许,如「平台 MUST NOT 基于 provisional 事件内容做流程判定」);SHOULD/应当=强烈建议(偏离须注明理由);MAY/可以=可选。禁止裸写无强度词的约束句(「不做 X」读不出是描述还是禁令)。fr/ 归档索引逐字透传,强度词随知识注入带给后续变更——这是规范语言的全链路载体)
78
+
79
+ ## 非功能需求
80
+ - 兼容性:...
81
+ - 可回退:...
82
+ - 可测试:...
83
+
84
+ ## 决策覆盖矩阵(如存在 decisions.md)
85
+ | 决策 ID | 覆盖的 FR | 说明 |
86
+ |---|---|---|
87
+ | D-001@v1 | FR-01 | ... |
88
+ \`\`\`
89
+
90
+ ## tasks.md 格式要求(scale=large 时生成骨架;任务清单唯一真相——plan 阶段展开细节并写回本文件)
91
+ \`\`\`markdown
92
+ ---
93
+ author: <git-user>
94
+ created_at: <now-datetime>
95
+ ---
96
+ # 任务清单(Tasks)
97
+
98
+ - [ ] task-01: <任务名>
99
+ - [ ] task-02: <任务名>
100
+ \`\`\`
101
+ > 骨架只列任务名。plan 阶段会把展开后的清单**写回本文件**(checkbox 行带一句话名,可附 [model:xxx]/(depends_on: …) 标注;保留 frontmatter/标题/ql-xxx 等非 task-XX 行);execute 勾选与 verify 对照都在本文件。
102
+
103
+ ## decisions.md 格式要求(前序步骤已增量创建时对账即可;无决策的变更不生成)
104
+ \`\`\`markdown
105
+ ---
106
+ author: <git-user>
107
+ created_at: <now-datetime>
108
+ ---
109
+
110
+ # 决策记录(Decisions)
111
+
112
+ ## D-001@v1: 决策短标题
113
+ - type: definition | consistency | feasibility | term | boundary | premise | architecture | compatibility | risk
114
+ - priority: P0 | P1 | P2
115
+ - status: accepted | unresolved | rejected | superseded
116
+ - supersedes:
117
+ - source: user | code | docs
118
+ - question: 被解决的问题
119
+ - answer: 用户确认或代码查证结果
120
+ - normalized_requirement: 可测试的约束
121
+ - impacts: [FR-01, task-01, verify-01]
122
+ - evidence: 用户回答轮次或代码/文档路径
123
+ \`\`\`
124
+
125
+ > 可选字段(按需另加,旧格式决策缺这些字段不受影响):**锚点**(决策落点主文件,\`<src 路径>:<行号或符号>\`;status=confirmed 时必填);**模块域**(决策涉及的模块 ID,可多个逗号分隔——合法 id 只取**当前变更所属项目**的 \`{SPEC_ROOT}/docs/<project>/modules/_module-map.yaml\`,多项目仓勿读其他子项目的 map(核验不认,报错会点名归属);规划中的新模块用 \`NEW:<名>\` 前缀声明,冒号后不加空格:\`NEW:foo\` 合法、\`NEW: foo\` 属书写错误。「生成规范文件」步 --done 的模块域核验对不认识的 id 直接阻断);**否决理由**(status=rejected 时必填);**复潮条件**(status=rejected 时必填);**故障面**(本决策引入的新失败模式——新机制落地时留痕它引入什么失败模式;type=architecture 时建议填写,可选);**退役判据**(出现什么信号时简化或删除本机制——信号出现就该简化它;type=architecture 时建议填写,可选)。
126
+ `
127
+
128
+ /** 模板清单(name → 内容)。新模板入册即自动被 materialize 落盘。 */
129
+ const STAGE_TEMPLATE_FILES = {
130
+ 'brainstorm-artifact-templates.md': BRAINSTORM_ARTIFACT_TEMPLATES_MD,
131
+ }
132
+
133
+ /**
134
+ * 幂等落盘全部阶段模板到 `<specBase>/.runtime/templates/`(内容有变才写,原子性靠短文件
135
+ * best-effort——模板只读消费,半写窗口下一次渲染会补齐)。
136
+ * @returns {string|null} 模板目录绝对路径(异常 → null,调用方 fail-soft)
137
+ */
138
+ export function materializeStageTemplates(specBase) {
139
+ try {
140
+ if (!specBase) return null
141
+ // 只在已存在的 specBase 内落盘(坑:凭空新建 <cwd>/.sillyspec 会污染 spec-dir 向上
142
+ // 搜索语义——渲染测试曾在 test/ 目录下落出 test/.sillyspec,spec-dir-home-guard 向上
143
+ // 命中即断言失败)。specBase 不存在 = 本目录不是变更根,零动作。
144
+ if (!existsSync(specBase)) return null
145
+ const dir = join(specBase, '.runtime', 'templates')
146
+ mkdirSync(dir, { recursive: true })
147
+ for (const [name, content] of Object.entries(STAGE_TEMPLATE_FILES)) {
148
+ const p = join(dir, name)
149
+ let cur = null
150
+ try { cur = readFileSync(p, 'utf8') } catch { /* 首次 */ }
151
+ if (cur !== content) writeFileSync(p, content, 'utf8')
152
+ }
153
+ return dir
154
+ } catch {
155
+ return null
156
+ }
157
+ }
@@ -87,6 +87,8 @@ export const definition = {
87
87
  \`sillyspec run archive --done --confirm --output "确认归档"\`
88
88
  8. CLI 会创建 \`.sillyspec/changes/archive/\`,并将变更目录移动到 \`.sillyspec/changes/archive/<原变更名>/\`
89
89
 
90
+ 9. **收口输出转达义务**(知识可见性契约,2026-09-23):--confirm 收口输出里若出现「📚/⚠️ 知识收件箱」横幅(uncategorized 待审知识标题清单),必须在最终回复中**原文转达用户**——CLI 输出用户不可见,你的转述是用户看到它的唯一通道;未出现则不提及(不得编造)。
91
+
90
92
  ### 输出
91
93
  归档完成 + archive 目录路径`,
92
94
  outputHint: '归档确认',
@@ -53,10 +53,10 @@ export const definition = {
53
53
  - 多项目且用户已指定:直接确认,不需要等待
54
54
  - 多项目且用户未指定:列出项目列表,需要用户确认本次需求属于哪个子项目
55
55
 
56
- ### 早期规模筛查(判断是否该走 quick)
56
+ ### 早期规模筛查(判断是否该走轻量变更)
57
57
  加载上下文后,用需求描述 + 模块上下文**粗判**本次变更规模:
58
- - **明显 small**(满足:预计改动 ≤ 2 个文件、单模块、无 schema/API/状态机/权限变更;或属于改文案/修 bug/样式调整/配置微调等纯执行类)→ 输出:「此变更规模较小,建议直接走 quick 流程」+ 一句依据,给出建议命令 \`sillyspec run quick "<需求>"\`。用户同意则本阶段可在此收尾(\`--done\` 并提示转 quick),不必继续走完整设计流程。
59
- - **拿不准或明显 large**(涉及多模块、schema、状态流转、新架构等)→ 不要提示 quick,继续进入下一步「对话式探索与需求澄清」。
58
+ - **明显 small**(满足:预计改动 ≤ 2 个文件、单模块、无 schema/API/状态机/权限变更;或属于改文案/修 bug/样式调整/配置微调等纯执行类)→ 输出:「此变更规模较小,建议直接走轻量变更」+ 一句依据,给出建议命令 \`sillyspec flow start --change <名> --input "<需求>"\`(2 调用协议收口)。用户同意则本阶段可在此收尾(\`--done\` 并提示转轻量变更),不必继续走完整设计流程。
59
+ - **拿不准或明显 large**(涉及多模块、schema、状态流转、新架构等)→ 不要提示轻量变更,继续进入下一步「对话式探索与需求澄清」。
60
60
  - 这是**粗判**,只为让明显的小变更免走完整设计流程;不确定就继续,后续「生成规范文件」步骤会基于 design.md 文件清单做精判兜底。
61
61
 
62
62
  ### 输出
@@ -76,8 +76,8 @@ export const definition = {
76
76
  maxWaitRounds: 8,
77
77
  waitReason: '等待用户回答需求问题或澄清',
78
78
  waitOptions: ['回答见--answer', '信息够了,进入方案讨论'],
79
- prompt: `### ⚡ 薄跑判定(P0-3,2026-09-20 对撞实验驱动——需求已收敛来源走全仪探索是纯税)
80
- **需求来源已收敛**(满足任一:--input 显式声明承接/已澄清/方案已定;fork 或关联会话已多轮澄清过本需求,你已能不问自答全部 P0 歧义;任务书/评审结论直接给定方案与边界)→ 本步可**薄跑**:A-D 节按已收敛事实直书结论(歧义零清单、范围直裁、决策若已有则引用源会话不重问),一次性完成不再多轮追问。薄跑不降质量底线:范围结论与决策落盘照常,仅省「明知故问」的交互轮。Grill(Step 7)与规范文件(Step 8)**不适用薄跑、永不省**。
79
+ prompt: `### ⚡ 轻量跑判定(P0-3,2026-09-20 对撞实验驱动——需求已收敛来源走全仪探索是纯税)
80
+ **需求来源已收敛**(满足任一:--input 显式声明承接/已澄清/方案已定;fork 或关联会话已多轮澄清过本需求,你已能不问自答全部 P0 歧义;任务书/评审结论直接给定方案与边界)→ 本步可**轻量跑**:A-D 节按已收敛事实直书结论(歧义零清单、范围直裁、决策若已有则引用源会话不重问),一次性完成不再多轮追问。轻量跑不降质量底线:范围结论与决策落盘照常,仅省「明知故问」的交互轮。Grill(Step 7)与规范文件(Step 8)**不适用轻量跑、永不省**。
81
81
 
82
82
  通过对话探索需求细节、分析原型(如有)、判断拆分/批量、并对歧义点做需求澄清 Grill。本步骤合并了原有的对话探索、原型分析、需求范围评估、需求澄清 Grill 四个环节——按下方 A→D 顺序按需执行。提问遵守「一次只问一个」(见 D 节追问策略):必须问的问题逐个问,能查代码/文档解决的不问;不要把多个歧义打包成一屏清单一次抛给用户(用户记不住、答不全,反而漏掉关键约束);也不要为凑数把单个问题拖成多轮。
83
83
 
@@ -345,10 +345,21 @@ design.md 第一行标题必须用中文:# 设计文档(Design)— <变更
345
345
  |---|---|---|---|
346
346
  | R-01 | ... | P0/P1/P2 | ... |
347
347
 
348
+ 10b. **非功能生命周期**(进程/资源/清理面——2026-09-22 R7 dogfood 复盘新增,41 孤儿 watcher
349
+ 泄漏实证:设计审查惯于覆盖 API/通道/语义面,「死亡面」无人审):
350
+ - 本变更是否引入长驻进程/后台任务/持有外部资源(文件锁/监听句柄/子进程)?
351
+ - 引入 → 逐项写自灭条件(谁负责杀、何时死、父环境消失后靠什么死——不得只依赖 spawn 侧
352
+ 环境变量;参考三闸模式:首拍终态即退/外部资源蒸发连续 N 拍退/硬寿命帽)
353
+ - 不涉及 → 风险登记表加一行「无长驻进程/外部资源,生命周期面不适用」显式留痕
354
+
348
355
  11. **决策追踪**(如存在 Grill/重大决策):
349
356
  - 列出当前版本 D-xxx@vN 决策 ID
350
357
  - 说明每个 D-xxx@vN 被哪些 FR-xxx / 设计章节覆盖
351
358
  - 标注仍未解决的 D-xxx@vN 或剩余风险
359
+ - **多裁定组合推演**(2026-09-22 R7 dogfood 复盘新增:升厚×断点续两裁定各自正确、组合
360
+ 出归档死锁的实证):≥2 条相互约束的裁定(状态升降/回退路径/失败语义类)时,自审必须
361
+ 做组合状态表推演——各裁定的状态叉乘 × 关键子步(如归档/重入)逐格过一遍,死锁格写
362
+ 进风险登记与调和方案;无组合面(裁定互不作用)显式记「无组合约束」一行
352
363
  12. **自审**(AI 对自身设计的校验)
353
364
 
354
365
  ### 操作
@@ -392,8 +403,8 @@ design.md 文件路径 + 自审结果
392
403
  ### 审查执行方式(CLI 按 ceremony_tier 风险定价,占位符由 run.js 注入)
393
404
  tier: {REVIEW_TIER}({REVIEW_TIER_REASON})
394
405
  - tier=self(ceremony 档 S0/S1):当前 agent 按「仪式档位菜单」执行对应档轻仪交叉审查(S0=CLI 清单核验 / S1=CLI 清单核验+定向探针抽查)——轻仪是风险定价的正常形态而非降级,清单机械项一条不省
406
+ > 🚪 **评审豁免(2026-09-26-review-unsupervised-exit)**:若本会话环境**无嵌套派发能力**(无 Agent/Task 类子代理工具)——不产 review.json、**不做自审表演**;改为在变更目录写一行声明文件 review-unsupervised.md(内容含 unsupervised 字样+时间+一句环境说明),Stage Review 门见声明即放行并留痕(声明随归档;Task Review 层已退役 2026-09-26-task-review-retire)。有派发能力时本豁免不适用(真独立评审仍优待)。
395
407
  - tier=independent(ceremony 档 S2/S3):必须用 Agent tool 启动一个独立的设计审查子代理(独立上下文,不共享你的分析与倾向),子代理按下方"交叉审查模型"审查 design.md 并输出 review.json——S2=独立评审×1;S3=两轮独立评审(两轮独立子代理交叉,第二轮聚焦首轮未决项与新增面)。review.json 产物契约(CLI Stage Review Gate 将硬校验,schema + 完整示例 + docHash 算法如下,照抄改值):
396
- 宿主环境无 Agent tool 可用(调用报 Unknown agent / Available agents: none)→ 不卡死(降级兜底,仅评审通道全不可用时):主代理切换为审查者角色自审替代,reviewerNotes 首行记录「降级:环境无子代理可用」,逐条结论附源码锚点(file:line 或 grep/read 证据)补偿独立性。
397
408
  {PRIOR_REVIEW_FACTS}
398
409
  {REVIEW_JSON_CONTRACT}
399
410
  子代理只产出 review + Unresolved Blockers,**是否调用 sillyspec run brainstorm --wait 仍由你(主 agent)根据其 verdict 决定**(子代理不直接操作 CLI 状态机)。
@@ -412,7 +423,7 @@ tier: {REVIEW_TIER}({REVIEW_TIER_REASON})
412
423
  - S1:CLI 清单核验 + 定向探针抽查——机械项全查之外,对最高风险的 1-2 个交叉点做定向源码探针(读相关实现验证设计假设),无需独立子代理
413
424
  - S2:独立评审×1——一个独立审查子代理完整执行下方交叉审查模型并产出 review.json
414
425
  - S3:两轮独立评审 + Grill 深查——两个独立审查子代理分别审查(视角互补:一个查结构性矛盾/一致性,一个查可行性与外部约束),首轮未决项进第二轮复核;对 P0 歧义逐一压力测试(Grill 深查)
415
- 当前档以注入的 ceremony_tier 行为准(run.js 注入 {REVIEW_TIER} 时随附菜单并标注);档位由 blast/span/friction 三轴客观计算,agent 自报只可升不可降——按当前档如实执行,不自行升仪或降仪。S2/S3 评审通道全不可用时按上方降级兜底执行并留 degraded 痕(reviewerNotes「降级:环境无子代理可用」);S0/S1 的 self 是档位定价的正常形态,不是降级。
426
+ 当前档以注入的 ceremony_tier 行为准(run.js 注入 {REVIEW_TIER} 时随附菜单并标注);档位由 blast/span/friction 三轴客观计算,agent 自报只可升不可降——按当前档如实执行,不自行升仪或降仪。S2/S3 评审通道全不可用时按上方评审豁免处理(写 review-unsupervised.md 声明,不自审表演);S0/S1 的 self 是档位定价的正常形态,不是降级。
416
427
 
417
428
  ### 默认行为
418
429
  1. 默认必须执行一次交叉审查;不要让用户凭主观判断决定"要不要 Grill"。
@@ -513,7 +524,7 @@ status: passed | needs-user-input | blocked | skipped
513
524
  ### 操作
514
525
  1. **按规模生成规范文件**:
515
526
  - **scale=large**:在 \`{SPEC_ROOT}/changes/<change-name>/\` 下生成完整四件套(design.md / decisions.md 可选 / proposal.md / requirements.md / tasks.md),实现路径 → \`sillyspec run plan --change <变更名>\`
516
- - **scale=small**:只生成/补全 design.md(proposal/requirements/tasks 对 quick 无用,不生成),实现路径 → \`sillyspec run quick --linked-changes <变更名>\`
527
+ - **scale=small**:只生成/补全 design.md(proposal/requirements/tasks 由轻量变更机器稿补齐),实现路径 → \`sillyspec flow start --change <变更名>\`(轻量变更收编头脑风暴产物,2 调用收口)
517
528
  - 两种规模都执行 \`git add {SPEC_ROOT}/changes/<change-name>/\` — 暂存本变更的规范文件(精确到变更目录,勿用 .sillyspec/ 整目录——会裹挟其他活跃变更;不要 commit,由用户通过统一提交工具处理)。**平台模式跳过 git add**(specRoot 不在 sourceRoot 的 git repo 内)
518
529
 
519
530
  2. 生成完成后展示 design.md 摘要 + **规模评估结果(small/large + 一句依据)** + 实现路径建议,告知用户“如有异议直接说,可修改文件、改 scale 或 --reopen 回退”(不暂停,展示完直接 --done)
@@ -526,109 +537,16 @@ created_at: <now-datetime>
526
537
  ---
527
538
  \`\`\`
528
539
 
529
- ### proposal.md 格式要求
530
- \`\`\`markdown
531
- ---
532
- author: <git-user>
533
- created_at: <now-datetime>
534
- ---
535
- # 提案书(Proposal)
536
-
537
- ## 动机
538
- 为什么做、解决什么核心问题
539
-
540
- ## 关键问题
541
- 为什么现有方案不够(展开 2-3 个具体痛点)
542
-
543
- ## 变更范围
544
- 本次做什么
545
-
546
- ## 不在范围内(显式清单)
547
- - 不做 X
548
- - 不做 Y
549
-
550
- ## 成功标准(可验证)
551
- - 旧配置默认行为不变
552
- - 新功能在配置后可用
553
- - ...
554
- \`\`\`
540
+ ### 产物格式模板(出上下文——R16 减负批次 2026-09-24)
541
+ proposal.md / requirements.md / tasks.md / decisions.md 四份产物的完整格式模板**不再内嵌本说明书**(每轮全量回放是 brainstorm 段 token 膨胀主源),已由 CLI 落盘:
542
+ \`{SPEC_ROOT}/.runtime/templates/brainstorm-artifact-templates.md\`
543
+ **生成任一规范文件前先 Read 该模板文件一次**,严格按模板结构与 frontmatter 产出——缺章节/缺 frontmatter 会被本步 --done 门禁逐条点名(brainstorm.design.* / four-piece 规则),届时重读模板修复即可。
544
+ 模板要点速览(细则以模板文件为准):proposal=动机/关键问题/变更范围/不在范围内/成功标准;requirements=角色表 + FR-XX GWT 块(含覆盖决策行与承接行)+ 非功能需求 + 决策覆盖矩阵;tasks=task-NN checkbox 骨架(只列任务名,plan 阶段展开写回);decisions=D-xxx@vN 九字段 + 可选字段(锚点/模块域/否决理由/复潮条件/故障面/退役判据)。
555
545
 
556
546
  ### 触达域现行 FR(FR 索引注入,2026-09-18-fr-index-l1)
557
547
  {FR_INDEX_DIGEST}
558
548
  写作纪律(防重复 FR——L1 索引实验的承接数据源):改写/取代上列已有行为 → 对应新 FR 块**必须加承接行**引用全局 id(如 \`承接: FR-core-engine-003\`,多个逗号分隔);全新行为 → 新 FR 块不加承接。承接行在归档时由 CLI 翻旧条目 superseded 并建取代链。
559
549
 
560
- ### requirements.md 格式要求
561
- \`\`\`markdown
562
- ---
563
- author: <git-user>
564
- created_at: <now-datetime>
565
- ---
566
- # 需求规格(Requirements)
567
-
568
- ## 角色
569
- | 角色 | 说明 |
570
- |---|---|
571
- | 开发者 | ... |
572
-
573
- ## 功能需求
574
-
575
- ### FR-01: 需求名称
576
- 覆盖决策:D-001@v1, D-002@v1(如适用)
577
- 承接: FR-<域>-NNN(如适用——改写/取代上列注入清单中的已有行为时必填,全新行为省略本行)
578
- Given 前提条件
579
- When 触发动作
580
- Then 期望结果
581
-
582
- (每个边界条件独立 GWT 块;场景名行 \`#### 场景:X\` 会被归档索引用作摘要,建议保留)
583
-
584
- ## 非功能需求
585
- - 兼容性:...
586
- - 可回退:...
587
- - 可测试:...
588
-
589
- ## 决策覆盖矩阵(如存在 decisions.md)
590
- | 决策 ID | 覆盖的 FR | 说明 |
591
- |---|---|---|
592
- | D-001@v1 | FR-01 | ... |
593
- \`\`\`
594
-
595
- ### tasks.md 格式要求(scale=large 时生成骨架;任务清单唯一真相——plan 阶段展开细节并写回本文件)
596
- \`\`\`markdown
597
- ---
598
- author: <git-user>
599
- created_at: <now-datetime>
600
- ---
601
- # 任务清单(Tasks)
602
-
603
- - [ ] task-01: <任务名>
604
- - [ ] task-02: <任务名>
605
- \`\`\`
606
- > 骨架只列任务名。plan 阶段会把展开后的清单**写回本文件**(checkbox 行带一句话名,可附 [model:xxx]/(depends_on: …) 标注;保留 frontmatter/标题/ql-xxx 等非 task-XX 行);execute 勾选与 verify 对照都在本文件。
607
-
608
- ### decisions.md 格式要求(前序步骤已增量创建时对账即可;无决策的变更不生成)
609
- \`\`\`markdown
610
- ---
611
- author: <git-user>
612
- created_at: <now-datetime>
613
- ---
614
-
615
- # 决策记录(Decisions)
616
-
617
- ## D-001@v1: 决策短标题
618
- - type: definition | consistency | feasibility | term | boundary | premise | architecture | compatibility | risk
619
- - priority: P0 | P1 | P2
620
- - status: accepted | unresolved | rejected | superseded
621
- - supersedes:
622
- - source: user | code | docs
623
- - question: 被解决的问题
624
- - answer: 用户确认或代码查证结果
625
- - normalized_requirement: 可测试的约束
626
- - impacts: [FR-01, task-01, verify-01]
627
- - evidence: 用户回答轮次或代码/文档路径
628
- \`\`\`
629
-
630
- > 可选字段(按需另加,旧格式决策缺这些字段不受影响):**锚点**(决策落点主文件,\`<src 路径>:<行号或符号>\`;status=confirmed 时必填);**模块域**(决策涉及的模块 ID,可多个逗号分隔——合法 id 只取**当前变更所属项目**的 \`{SPEC_ROOT}/docs/<project>/modules/_module-map.yaml\`,多项目仓勿读其他子项目的 map(核验不认,报错会点名归属);规划中的新模块用 \`NEW:<名>\` 前缀声明,冒号后不加空格:\`NEW:foo\` 合法、\`NEW: foo\` 属书写错误。本步 --done 的模块域核验对不认识的 id 直接阻断);**否决理由**(status=rejected 时必填);**复潮条件**(status=rejected 时必填);**故障面**(本决策引入的新失败模式——新机制落地时留痕它引入什么失败模式;type=architecture 时建议填写,可选);**退役判据**(出现什么信号时简化或删除本机制——信号出现就该简化它;type=architecture 时建议填写,可选)。
631
-
632
550
  ### 后续变更包处理
633
551
  如果 MASTER.md 中规划了后续变更包(拆分后的子阶段),**必须同时为每个后续包创建独立变更目录**:
634
552
  1. 读取 MASTER.md 中的变更包列表(包名 + 边界描述)