sillyspec 3.23.4 → 3.23.5
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/change-list.js +3 -1
- package/src/run.js +19 -6
- package/src/stage-contract.js +15 -2
package/package.json
CHANGED
package/src/change-list.js
CHANGED
|
@@ -70,8 +70,10 @@ export function pathMatches(a, b) {
|
|
|
70
70
|
/**
|
|
71
71
|
* 「文件变更清单」章节标题同义词(与 src/stage-contract.js 的识别集对齐,
|
|
72
72
|
* 避免两个校验器对「有没有清单」给出矛盾结论)。
|
|
73
|
+
* 容忍可选编号前缀(`## 6. 文件变更清单` / `## 6) 文件变更清单`)——
|
|
74
|
+
* brainstorm Step11 模板鼓励 design 章节带编号,编号前缀不应让 plan-postcheck 解析失败。
|
|
73
75
|
*/
|
|
74
|
-
const FILE_LIST_SECTION_RE = /^#{2,3}\s*(文件变更清单|变更文件清单|文件清单|File Changes|Files to Change)/im
|
|
76
|
+
const FILE_LIST_SECTION_RE = /^#{2,3}\s*(?:\d+[.)]\s*)?(文件变更清单|变更文件清单|文件清单|File Changes|Files to Change)/im
|
|
75
77
|
|
|
76
78
|
/**
|
|
77
79
|
* exclude 子标题词集:「不修改/暂缓/保留」类 —— 其下路径从清单移除,不强制 task 覆盖。
|
package/src/run.js
CHANGED
|
@@ -454,8 +454,10 @@ export async function auditQuickCompletion(cwd, guard, options = {}) {
|
|
|
454
454
|
console.log(` - ${r}`)
|
|
455
455
|
}
|
|
456
456
|
}
|
|
457
|
-
console.log(`\n
|
|
458
|
-
console.log(`
|
|
457
|
+
console.log(`\n 如确认接受这些变更,重新运行 --done 时带上对应 flag 即可解锁:`)
|
|
458
|
+
console.log(` sillyspec run quick --done --force-baseline --allow-new --change <id> --output "..."`)
|
|
459
|
+
console.log(` (--force-baseline 覆盖受保护/危险文件如 src/run.js;--allow-new 允许新增文件)`)
|
|
460
|
+
console.log(` 或在首个 sillyspec run quick 启动(step 1)时就声明这些 flag,持久化进 guard。`)
|
|
459
461
|
}
|
|
460
462
|
} catch (e) {
|
|
461
463
|
result.reasons.push(`审计失败: ${e.message}`)
|
|
@@ -1692,7 +1694,7 @@ export async function runCommand(args, cwd, specDir = null) {
|
|
|
1692
1694
|
// --done
|
|
1693
1695
|
if (isDone) {
|
|
1694
1696
|
const doneAnswer = getFlagValue('--answer')
|
|
1695
|
-
return await completeStep(pm, progress, stageName, cwd, outputText, inputText, { confirm: isConfirm, changeName: effectiveChange, nonInteractive: isNonInteractive && !isInteractive, platformOpts, confirmMode, doneAnswer })
|
|
1697
|
+
return await completeStep(pm, progress, stageName, cwd, outputText, inputText, { confirm: isConfirm, changeName: effectiveChange, nonInteractive: isNonInteractive && !isInteractive, platformOpts, confirmMode, doneAnswer, isForceBaseline, isAllowNew })
|
|
1696
1698
|
}
|
|
1697
1699
|
|
|
1698
1700
|
// 默认:输出当前步骤
|
|
@@ -1961,10 +1963,13 @@ async function runStage(pm, progress, stageName, cwd, changeName, skipApproval =
|
|
|
1961
1963
|
try {
|
|
1962
1964
|
const { execSync } = await import('child_process')
|
|
1963
1965
|
const gitStatus = execSync('git status --porcelain', { cwd, encoding: 'utf8', timeout: 10000 })
|
|
1966
|
+
// 记录全部预存脏文件(含 untracked + .sillyspec/ 路径)。quick 会话期间自身写入的元数据
|
|
1967
|
+
// (quicklog/.runtime/modules/_module-map 等)由 auditQuickCompletion 的 isQuickMetadata 精确豁免,
|
|
1968
|
+
// 不需要这里粗放过滤 .sillyspec/——旧过滤致预存 untracked .sillyspec/changes/ 不进 baseline,
|
|
1969
|
+
// 却在 audit 被当「危险(.sillyspec/)+新增」误判永久 blocked(ql-20260713-002-7628 修复)。
|
|
1964
1970
|
const baselineFiles = gitStatus
|
|
1965
1971
|
.trim().split('\n').filter(Boolean)
|
|
1966
1972
|
.map(line => line.slice(3).trim())
|
|
1967
|
-
.filter(f => !f.startsWith('.sillyspec/'))
|
|
1968
1973
|
const allowedFiles = quickOpts?.quickFiles || []
|
|
1969
1974
|
const allowNew = quickOpts?.isAllowNew || false
|
|
1970
1975
|
const forceBaseline = quickOpts?.isForceBaseline || false
|
|
@@ -2614,7 +2619,7 @@ function rollbackStageCompletion(stageData, steps, currentIdx) {
|
|
|
2614
2619
|
}
|
|
2615
2620
|
|
|
2616
2621
|
async function completeStep(pm, progress, stageName, cwd, outputText, inputText = null, options = {}) {
|
|
2617
|
-
const { printNext = true, confirm = false, changeName, platformOpts = {}, nonInteractive = false, confirmMode = null } = options
|
|
2622
|
+
const { printNext = true, confirm = false, changeName, platformOpts = {}, nonInteractive = false, confirmMode = null, isForceBaseline = false, isAllowNew = false } = options
|
|
2618
2623
|
const specBase = platformOpts.specRoot || join(cwd, '.sillyspec')
|
|
2619
2624
|
const stageData = progress.stages[stageName]
|
|
2620
2625
|
const scanProfile = stageData?.scanProfile || null
|
|
@@ -2990,7 +2995,15 @@ async function completeStep(pm, progress, stageName, cwd, outputText, inputText
|
|
|
2990
2995
|
: (existsSync(legacyGuardFile) ? JSON.parse(readFileSync(legacyGuardFile, 'utf8')) : null)
|
|
2991
2996
|
} catch {}
|
|
2992
2997
|
if (guard) {
|
|
2993
|
-
|
|
2998
|
+
// --done 的 --force-baseline/--allow-new 并入 guard(与 step1 持久化值取或)。
|
|
2999
|
+
// 修复 ql-20260713-002-7628:旧代码解析了这两个 flag 但只传 {isConfirm} 给审计,
|
|
3000
|
+
// 致 --done --force-baseline 静默无效、用户被误导「重跑 --confirm」也无法解锁。
|
|
3001
|
+
const mergedGuard = {
|
|
3002
|
+
...guard,
|
|
3003
|
+
forceBaseline: guard.forceBaseline || isForceBaseline,
|
|
3004
|
+
allowNew: guard.allowNew || isAllowNew,
|
|
3005
|
+
}
|
|
3006
|
+
const review = await auditQuickCompletion(cwd, mergedGuard, { isConfirm: confirm })
|
|
2994
3007
|
printQuickAuditReview(review)
|
|
2995
3008
|
if (review.status === 'blocked') {
|
|
2996
3009
|
steps[currentIdx].status = 'pending'
|
package/src/stage-contract.js
CHANGED
|
@@ -258,8 +258,21 @@ function validateBrainstormOutputs(cwd, changeName, context = {}) {
|
|
|
258
258
|
const hasLifecycleKeyword = /\b(session|lease|agent[._-]?run|daemon|lifecycle|state[._-]?transition|claim|heartbeat)\b/i.test(content)
|
|
259
259
|
if (hasLifecycleKeyword) {
|
|
260
260
|
// 显式声明本变更不涉及生命周期契约(覆盖字段名/错误码/否定声明场景):
|
|
261
|
-
// 历史教训:design 提到 daemon_id 字段名或 daemon_not_owned 错误码就触发,被迫加空表(B3a
|
|
262
|
-
|
|
261
|
+
// 历史教训:design 提到 daemon_id 字段名或 daemon_not_owned 错误码就触发,被迫加空表(B3a)。
|
|
262
|
+
//
|
|
263
|
+
// 收紧原则(修:正常 design 不应被误判「已豁免」):
|
|
264
|
+
// - 否定词必须是明确多字短语(不涉及/不适用/未涉及/不包含/没有/n\/a/not applicable/none),
|
|
265
|
+
// 杜绝裸单字「无」与裸「na」在 40 字符宽窗口内任意命中——
|
|
266
|
+
// 「lifecycle 状态无变化」「本变更无需 lifecycle 事件」「lifecycle canal 不涉及」等不再误判;
|
|
267
|
+
// - 否定词必须与「生命周期(契约)/lifecycle(contract)」紧邻(仅允许少量空白/分隔符/「任何」),
|
|
268
|
+
// 不再用 40 字符宽松窗口;工具错误信息本身就指引写「不涉及生命周期契约」这个规范短语。
|
|
269
|
+
const declaresNotApplicable =
|
|
270
|
+
// 否定在前:「不涉及生命周期(契约)」「不适用 lifecycle contract」
|
|
271
|
+
/(?:不涉及|不适用|未涉及|不包含|没有(?:任何)?)\s?(?:任何\s?)?(?:生命周期(?:契约)?|lifecycle(?:[ _=-]?contract)?)/i.test(content) ||
|
|
272
|
+
// 主题在前(表格/列表单元「生命周期契约:不涉及 / N/A / 无」——分隔符强制,杜绝宽窗口)
|
|
273
|
+
/(?:生命周期(?:契约)?|lifecycle(?:[ _=-]?contract)?)\s?[::=]\s?(?:不涉及|不适用|未涉及|无|n\/?a\b|not[ _=-]?applicable|none\b)/i.test(content) ||
|
|
274
|
+
// 英文谓语句:「does not involve / not applicable ... lifecycle」
|
|
275
|
+
/(?:does[ _-]?not[ _-]?involve|not[ _-]?applicable)[^\n]{0,15}lifecycle/i.test(content)
|
|
263
276
|
if (declaresNotApplicable) {
|
|
264
277
|
warnings.push('design.md 显式声明不涉及生命周期契约 — 已豁免「生命周期契约表」要求')
|
|
265
278
|
} else {
|