sillyspec 3.26.7 → 3.26.8

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.
@@ -64,32 +64,6 @@ export function resolveWaitingStepWithAnswer(steps, doneAnswer, nowStr) {
64
64
  return waitIdx
65
65
  }
66
66
 
67
- /**
68
- * wait 选项单选校验(坑 wait-choice-enforcement):定义了 waitOptions 的 requiresWait/
69
- * repeatableWait 步骤,--answer 必须命中预设选项之一——防止 agent 一句话代答绕过人工
70
- * 选择(方案选择类 wait 的 answer 本就该是选项本身)。开放回答型步骤(澄清追问,answer
71
- * 为自由文本)在 step 定义显式声明 waitFreeAnswer: true 豁免。
72
- * 校验失败打印选项清单 + exit 1(fail-loud,与 requiresWait 门同风格)。
73
- *
74
- * waitOptions 优先取 stepDef(运行时构建的步骤定义,与 brainstorm/plan 静态定义同源),
75
- * 回退 DB step entry(--continue 路径 waiting 步骤的 waitOptions 已落盘)。
76
- */
77
- function enforceWaitChoice(stepDef, stepEntry, answer, stageName, changeName) {
78
- if (answer == null) return
79
- const opts = Array.isArray(stepDef?.waitOptions) ? stepDef.waitOptions
80
- : (Array.isArray(stepEntry?.waitOptions) ? stepEntry.waitOptions : null)
81
- if (!opts || opts.length === 0) return
82
- if (stepDef?.waitFreeAnswer === true) return
83
- if (opts.includes(answer)) return
84
- console.error(`❌ Step "${stepEntry?.name || '当前步骤'}" 是封闭单选 wait,--answer 必须命中预设选项之一(防 AI 代答绕过人工选择)。`)
85
- console.error(` 你的 --answer: ${JSON.stringify(answer)}`)
86
- console.error(` 可选值(复制其一): ${opts.join(' / ')}`)
87
- console.error(` 自由文本回答的场景,该步骤应声明 waitFreeAnswer(当前未声明)。`)
88
- if (changeName) console.error(` 重试:sillyspec run ${stageName} --done --answer "<选项>" --change ${changeName}`)
89
- else console.error(` 重试:sillyspec run ${stageName} --done --answer "<选项>"`)
90
- process.exit(1)
91
- }
92
-
93
67
  export async function completeStep(pm, progress, stageName, cwd, outputText, inputText = null, options = {}) {
94
68
  const { printNext = true, confirm = false, changeName, platformOpts = {}, nonInteractive = false, isForceBaseline = false, isAllowNew = false, isAllowDelete = false } = options
95
69
  const specBase = platformOpts.specRoot || join(cwd, '.sillyspec')
@@ -148,10 +122,6 @@ export async function completeStep(pm, progress, stageName, cwd, outputText, inp
148
122
  const defStepsForCurrent = await getStageSteps(stageName, cwd, progress, platformOpts?.specRoot || null)
149
123
  const currentStepDef = defStepsForCurrent?.[currentIdx] || {}
150
124
  const currentStep = steps[currentIdx]
151
- // --done --answer 解 waiting 的路径同样过单选校验(与下方 requiresWait doneAnswer 分支同规则)
152
- if (_resolvedWaitIdx !== -1 && _doneAnswer) {
153
- enforceWaitChoice(currentStepDef, currentStep, _doneAnswer, stageName, changeName)
154
- }
155
125
  if (currentStepDef.requiresWait === true && !currentStep.waitAnswer) {
156
126
  // 检查 --done 是否带了 --answer:如果是,自动补全 waitAnswer 状态,一步完成
157
127
  const doneAnswer = typeof options !== 'undefined' && options.doneAnswer ? options.doneAnswer : null
@@ -168,7 +138,6 @@ export async function completeStep(pm, progress, stageName, cwd, outputText, inp
168
138
  currentStep.waitReason = currentStepDef.waitReason || '等待用户输入'
169
139
  console.log(`⚠️ Step "${currentStep.name}" 的确认(${currentStepDef.waitReason})已在前置步骤完成,自动跳过。`)
170
140
  } else if (doneAnswer) {
171
- enforceWaitChoice(currentStepDef, currentStep, doneAnswer, stageName, changeName)
172
141
  currentStep.status = 'waiting'
173
142
  currentStep.waitAnswer = doneAnswer
174
143
  currentStep.waitReason = currentStepDef.waitReason || '等待用户输入'
@@ -750,7 +719,6 @@ export async function continueStep(pm, progress, stageName, cwd, answer, options
750
719
  const prevOutput = currentStep.output || ''
751
720
  const waitRound = (currentStep.waitRound || 0) + 1
752
721
  currentStep.waitRound = waitRound
753
- enforceWaitChoice(currentStepDef, currentStep, answer, stageName, changeName)
754
722
  currentStep.waitAnswer = answer
755
723
  currentStep.waitAnswers = Array.isArray(currentStep.waitAnswers) ? currentStep.waitAnswers : []
756
724
  currentStep.waitAnswers.push({
package/src/run/gates.js CHANGED
@@ -248,6 +248,24 @@ export async function runStageCompletionGates({ stageName, cwd, changeName, plat
248
248
  const { runVerifyRequiredEvidenceCheck, printVerifyRequiredEvidenceCheck } = await import('../verify-postcheck.js')
249
249
  const evidenceCheck = runVerifyRequiredEvidenceCheck({ cwd, specBase, changeName })
250
250
  printVerifyRequiredEvidenceCheck(evidenceCheck)
251
+ // ── module-impact 死信探针(blocking,债单 D-1/D-5):更新结果表 pending/待办行 → 阻断 verify ──
252
+ // 与 archive 移动前校验(extractPendingDocSyncRows)同一口径,把死信号从 archive 提前到 verify:
253
+ // agent 在 verify 阶段就须完成文档同步并回填 done/skipped,而非拖到归档被拦(修复 perf-remediation
254
+ // 类「verify PASS → archive 才发现 pending」的时序漏洞)。
255
+ const verifyChangeDir = resolveChangeDir(cwd, progress, platformOpts?.specRoot)
256
+ if (verifyChangeDir) {
257
+ const impactPath = join(verifyChangeDir, 'module-impact.md')
258
+ if (existsSync(impactPath)) {
259
+ const { extractPendingDocSyncRows } = await import('./complete-handlers.js')
260
+ const pendingRows = extractPendingDocSyncRows(readFileSync(impactPath, 'utf8'))
261
+ if (pendingRows.length > 0) {
262
+ console.error(`\n❌ verify 阶段被阻断:module-impact.md「更新结果」表存在 ${pendingRows.length} 个未清 pending/待办项(死信)`)
263
+ for (const row of pendingRows) console.error(` - ${row}`)
264
+ console.error(' 文档同步是 verify 的收尾义务:请完成模块文档同步并回填状态为 done/skipped(说明原因),再重新完成 verify。')
265
+ return rollbackCompletionAndReturn(pm, progress, stageData, steps, currentIdx, cwd, changeName, platformOpts)
266
+ }
267
+ }
268
+ }
251
269
  console.log('\n✅ 验证通过,下一步:sillyspec run archive')
252
270
  }
253
271