sillyspec 3.27.2 → 3.27.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/.claude/skills/sillyspec-execute/SKILL.md +8 -0
- package/.claude/skills/sillyspec-quick/SKILL.md +2 -1
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-archive/SKILL.md +87 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-auto/SKILL.md +93 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-commit/SKILL.md +67 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-continue/SKILL.md +47 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-execute/SKILL.md +214 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-explore/SKILL.md +102 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-export/SKILL.md +57 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-init/SKILL.md +177 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-plan/SKILL.md +156 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-quick/SKILL.md +116 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-resume/SKILL.md +70 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-scan/SKILL.md +79 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-state/SKILL.md +64 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-status/SKILL.md +54 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-verify/SKILL.md +87 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
- package/.claude/worktrees/sillyspec-data-model-research/.gitattributes +2 -0
- package/.claude/worktrees/sillyspec-data-model-research/.husky/pre-push +20 -0
- package/.claude/worktrees/sillyspec-data-model-research/README.md +203 -0
- package/.claude/worktrees/sillyspec-data-model-research/SKILL.md +91 -0
- package/.claude/worktrees/sillyspec-data-model-research/bin/sillyspec.js +2 -0
- package/.claude/worktrees/sillyspec-data-model-research/logo.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/package-lock.json +977 -0
- package/.claude/worktrees/sillyspec-data-model-research/package.json +39 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/favicon.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/index.html +16 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/logo.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-dashboard.html +836 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-overview.html +256 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/package.json +25 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/executor.js +95 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/index.js +679 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/parser.js +526 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/watcher.js +357 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/agent-session-log.js +803 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/change-list.js +290 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/change-risk-profile.js +270 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/check-primitives.js +65 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/classify-change.js +109 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/commit-suggest.js +202 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/config-cat.js +88 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/config-schema.js +328 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/constants.js +97 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/contract-matrix.js +480 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/datetime.js +20 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/db-engine.js +55 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/db.js +381 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/decision-distill.js +517 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/local-agent.js +155 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/sillyhub-mcp.js +245 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/probe.js +246 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/strategy.js +121 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-check.js +825 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-debt.js +393 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-gate.js +107 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/doctor-diagnostics.js +1099 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/endpoint-extractor.js +420 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/foreign-declared.js +78 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/fs-atomic.js +73 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/git-helper.js +150 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/hooks/claude-pre-tool-use.cjs +130 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/hooks/worktree-guard.js +892 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/index.js +2798 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/init.js +626 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/knowledge-match.js +231 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/local-detect.js +128 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/local-register.js +104 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/machine-interface.js +543 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/migrate.js +127 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/module-impact.js +166 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/module-resolve.js +222 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/modules.js +580 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/change-registry.js +336 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/consistency-doctor.js +355 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/shared.js +36 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/stage-machine.js +594 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/step-store.js +265 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress.js +1304 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/quick-recommend.js +115 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/quicklog.js +852 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/review-tier.js +77 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/command.js +1595 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/complete-handlers.js +1552 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/complete.js +1259 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/concurrent-detect.js +156 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/gates.js +1084 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/multi-repo-context.js +208 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/next.js +128 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/prompt.js +843 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/quick-audit.js +186 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/scan-profile.js +269 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/shared.js +1562 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/stage.js +584 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run.js +23 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-diff.js +388 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-postcheck.js +512 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-staleness.js +95 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/setup.js +414 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/client.js +597 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/config.js +62 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/spec-dir-typo.js +50 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/spec-sync.js +358 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-engine.js +171 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-spec.js +550 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract.js +889 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-review.js +597 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/archive.js +199 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm-auto.js +236 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm.js +617 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/cmd-existence.js +145 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/doctor.js +540 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/execute.js +1275 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/explore.js +43 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/index.js +29 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/knowledge.js +514 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan-postcheck.js +1344 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan.js +650 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/quick.js +167 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/scan.js +608 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/status.js +64 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/verify.js +246 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/sync.js +1470 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/task-review.js +1498 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/taskcard.js +210 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/verify-postcheck.js +1718 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/verify-probes.js +277 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/version.js +19 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/workflow.js +600 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/workspace.js +125 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree-apply.js +1446 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree-deps.js +576 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree.js +1715 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/claude-instruction.md +23 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/taskcard-rules.md +28 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/testcase-design.md +12 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/verify-probes.md +32 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/archive-impact.yaml +79 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/scan-docs.yaml +132 -0
- package/eval/README.md +86 -0
- package/eval/eval.config.json +28 -0
- package/eval/lib/adapters/cli.mjs +63 -0
- package/eval/lib/adapters/dryrun.mjs +27 -0
- package/eval/lib/config.mjs +82 -0
- package/eval/lib/db.mjs +76 -0
- package/eval/lib/report.mjs +120 -0
- package/eval/lib/runner.mjs +157 -0
- package/eval/lib/tasks.mjs +73 -0
- package/eval/lib/util.mjs +107 -0
- package/eval/lib/workdir.mjs +30 -0
- package/eval/run.mjs +83 -0
- package/eval/tasks/001-slugify/files/package.json +6 -0
- package/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
- package/eval/tasks/001-slugify/task.json +6 -0
- package/eval/tasks/001-slugify/verify.mjs +28 -0
- package/eval/tasks/002-each-day/files/package.json +6 -0
- package/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
- package/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
- package/eval/tasks/002-each-day/task.json +6 -0
- package/eval/tasks/002-each-day/verify.mjs +31 -0
- package/eval/tasks/003-word-report/files/data/notes.txt +5 -0
- package/eval/tasks/003-word-report/files/package.json +6 -0
- package/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
- package/eval/tasks/003-word-report/task.json +6 -0
- package/eval/tasks/003-word-report/verify.mjs +38 -0
- package/package.json +4 -2
- package/src/agent-session-log.js +45 -5
- package/src/change-risk-profile.js +83 -14
- package/src/config-schema.js +18 -3
- package/src/contract-matrix.js +90 -29
- package/src/datetime.js +20 -0
- package/src/decision-distill.js +517 -0
- package/src/dispatch/backends/local-agent.js +4 -4
- package/src/docs-check.js +227 -0
- package/src/docs-debt.js +187 -24
- package/src/doctor-diagnostics.js +102 -2
- package/src/foreign-declared.js +78 -0
- package/src/index.js +89 -9
- package/src/init.js +9 -7
- package/src/knowledge-match.js +107 -6
- package/src/local-register.js +7 -1
- package/src/module-resolve.js +12 -1
- package/src/modules.js +2 -1
- package/src/plan-adopt-waves.js +126 -0
- package/src/quicklog.js +50 -4
- package/src/run/command.js +122 -43
- package/src/run/complete-handlers.js +48 -19
- package/src/run/complete.js +5 -2
- package/src/run/gates.js +87 -22
- package/src/run/prompt.js +84 -42
- package/src/run/quick-audit.js +11 -0
- package/src/run/shared.js +152 -10
- package/src/run.js +2 -2
- package/src/scan-postcheck.js +30 -17
- package/src/stage-contract-spec.js +2 -1
- package/src/stage-contract.js +68 -4
- package/src/stages/archive.js +53 -12
- package/src/stages/brainstorm-auto.js +7 -0
- package/src/stages/brainstorm.js +11 -0
- package/src/stages/doctor.js +157 -1
- package/src/stages/execute.js +32 -6
- package/src/stages/plan-postcheck.js +76 -17
- package/src/stages/plan.js +1 -0
- package/src/stages/quick.js +6 -1
- package/src/stages/verify.js +31 -14
- package/src/sync.js +41 -3
- package/src/task-review.js +2 -2
- package/src/taskcard-placeholders.js +21 -0
- package/src/taskcard.js +10 -1
- package/src/verify-postcheck.js +315 -13
- package/src/verify-probes.js +46 -4
- package/src/worktree-apply.js +184 -7
- package/src/worktree-deps.js +57 -0
- package/src/worktree.js +201 -58
- package/templates/prompts/taskcard-rules.md +1 -0
package/src/stage-contract.js
CHANGED
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
* CLI 不再相信 prompt 完成,completeStep 后必须过 validator。
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { existsSync, readdirSync, readFileSync } from 'fs'
|
|
8
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'fs'
|
|
9
9
|
import { join, basename } from 'path'
|
|
10
10
|
import { safeGit } from './git-helper.js'
|
|
11
|
+
import { nowWallClock } from './datetime.js'
|
|
11
12
|
import { detectChangeRisk, checkIntegrationEvidence, VERIFICATION_NEEDS, RISK_LEVEL_CAUSES } from './change-risk-profile.js'
|
|
12
13
|
import { SCAN_REQUIRED_DOCS, AUXILIARY_STAGES } from './constants.js'
|
|
13
14
|
import { evaluateRules } from './stage-contract-engine.js'
|
|
14
15
|
import { getRule } from './stage-contract-spec.js'
|
|
16
|
+
import { backfillFrontmatter } from './scan-postcheck.js'
|
|
15
17
|
import { parseAllowedPaths } from './stages/plan-postcheck.js'
|
|
16
18
|
|
|
17
19
|
/**
|
|
@@ -163,6 +165,31 @@ function parseDecisionRecords(content) {
|
|
|
163
165
|
return records
|
|
164
166
|
}
|
|
165
167
|
|
|
168
|
+
/**
|
|
169
|
+
* decisions.md 缺 author/created_at header 时自动补齐(坑 decisions-header-late-warning,
|
|
170
|
+
* 2026-08-24 用户反馈二期:brainstorm step8 模板曾自带无 frontmatter 的 decisions 样例——
|
|
171
|
+
* agent 照抄生成必缺 header,拖到后续环节才提示。模板已修;本函数兜存量:gate 前幂等补
|
|
172
|
+
* 机械字段(author=git user / created_at=now),与 scan-fix-headers 同一 backfillFrontmatter 实现。
|
|
173
|
+
* @param {string} changeDir - changes/<name> 目录
|
|
174
|
+
* @returns {boolean} true = 本次补齐过(false = 无 decisions.md / header 已齐 / 写失败)
|
|
175
|
+
*/
|
|
176
|
+
export function ensureDecisionDocHeader(changeDir) {
|
|
177
|
+
try {
|
|
178
|
+
const decisionPath = join(changeDir, 'decisions.md')
|
|
179
|
+
if (!existsSync(decisionPath)) return false
|
|
180
|
+
const content = readFileSync(decisionPath, 'utf8')
|
|
181
|
+
let author = 'unknown'
|
|
182
|
+
const gitUser = safeGit(changeDir, ['config', 'user.name'])
|
|
183
|
+
if (gitUser && gitUser.value) author = String(gitUser.value).trim() || 'unknown'
|
|
184
|
+
const r = backfillFrontmatter(content, { author, createdAt: nowWallClock() })
|
|
185
|
+
if (!r.changed) return false
|
|
186
|
+
writeFileSync(decisionPath, r.content)
|
|
187
|
+
console.log(`ℹ️ 已自动补齐 decisions.md 缺失的 author/created_at header: ${decisionPath}`)
|
|
188
|
+
return true
|
|
189
|
+
} catch { /* 补齐失败不阻断 gate——decisions 内容校验照常进行 */ }
|
|
190
|
+
return false
|
|
191
|
+
}
|
|
192
|
+
|
|
166
193
|
function extractCurrentDecisionIds(content) {
|
|
167
194
|
const records = parseDecisionRecords(content)
|
|
168
195
|
if (records.length === 0) return extractIds(content, 'D')
|
|
@@ -286,6 +313,27 @@ function validateBrainstormOutputs(cwd, changeName, context = {}) {
|
|
|
286
313
|
}
|
|
287
314
|
}
|
|
288
315
|
}
|
|
316
|
+
|
|
317
|
+
// ── 风险判级提前提示(坑 risk-first-use-opaque,2026-08-24 用户实证)──
|
|
318
|
+
// verify --done 的 risk gate 既往只在流程末段暴露,首次使用者撞墙后才知道 frontmatter
|
|
319
|
+
// risk_level 覆盖通道。design --done 即按同款 detectChangeRisk 预判一次(此时 plan.md 尚未
|
|
320
|
+
// 产出,verify 时会连 plan 一起重判、级别可能上浮——本提示 advisory 不作为定论):
|
|
321
|
+
// 高危关键词命中 / 存在否定抑制 → 透出判级结果 + 覆盖指引,把机制可见性提前到 design 阶段。
|
|
322
|
+
{
|
|
323
|
+
const profile = detectChangeRisk({ designContent: content })
|
|
324
|
+
if (['integration-critical', 'deployment-critical'].includes(profile.level) && !profile.explicit) {
|
|
325
|
+
warnings.push(
|
|
326
|
+
`[risk] 本次 design 判级 ${profile.level}(命中关键词:${profile.triggers.join('、')})——verify --done 将强制真实集成证据门控。` +
|
|
327
|
+
`若属关键词误伤(实际未触碰 daemon/session/启动入口),现在就在 design.md frontmatter 加 risk_level: <真实等级>(如 unit-sufficient)显式覆盖,免得到 verify 阶段才发现而返工。`
|
|
328
|
+
)
|
|
329
|
+
}
|
|
330
|
+
if ((profile.suppressedTriggers || []).length > 0) {
|
|
331
|
+
warnings.push(
|
|
332
|
+
`[risk] 关键词命中 ${profile.suppressedTriggers.join('、')} 已被同句否定语境抑制,当前判级 ${profile.level}。` +
|
|
333
|
+
`若实际涉及对应改动,请在 design.md frontmatter 声明高危级(抑制可审计,防静默降级逃证据门控)。`
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
289
337
|
}
|
|
290
338
|
|
|
291
339
|
const decisionsFile = join(changeDir, 'decisions.md')
|
|
@@ -461,11 +509,22 @@ function validateVerifyOutputs(cwd, changeName, context = {}) {
|
|
|
461
509
|
planContent: readIfExists(join(changeDir, 'plan.md')),
|
|
462
510
|
})
|
|
463
511
|
const conclusion = extractVerifyConclusion(verify)
|
|
512
|
+
// ── 否定抑制审计(坑 risk-negation-blindness)──
|
|
513
|
+
// 关键词命中被同句否定语境抑制而不参与判级时必须透出:抑制是把双刃剑(「本次不新增
|
|
514
|
+
// daemon」真豁免 vs 靠堆否定措辞静默降级逃证据门控),这里让每次抑制可见可核对;
|
|
515
|
+
// frontmatter risk_level 仍是双向权威覆盖通道。
|
|
516
|
+
if ((changeRiskProfile.suppressedTriggers || []).length > 0) {
|
|
517
|
+
warnings.push(
|
|
518
|
+
`[${changeRiskProfile.level}] 风险判级:关键词命中 ${changeRiskProfile.suppressedTriggers.join(', ')} 已被同句否定语境(不/未/无/避免…)抑制,未参与判级。` +
|
|
519
|
+
`若本变更实际涉及跨进程/状态机/启动入口改动,请在 design.md frontmatter 加 risk_level: integration-critical(或 deployment-critical)显式声明或提供真实集成证据——抑制可审计,不许用来静默降级。`
|
|
520
|
+
)
|
|
521
|
+
}
|
|
464
522
|
if (['integration-critical', 'deployment-critical'].includes(changeRiskProfile.level)) {
|
|
465
523
|
// 关键词误伤早期引导(坑2,FR-02):命中高危关键词且无 frontmatter risk_level 时,
|
|
466
524
|
// 无条件透出 frontmatter 覆盖指引(不依赖 conclusion / evidence),让 agent 早期即可
|
|
467
|
-
// 判断是否属误判并显式覆盖,而非撞到 evidence gate
|
|
468
|
-
//
|
|
525
|
+
// 判断是否属误判并显式覆盖,而非撞到 evidence gate 末尾才发现出路③。含否定语境的
|
|
526
|
+
// 命中已在 detectChangeRisk 内被同句否定抑制(见 change-risk-profile.js NEGATION_CUES),
|
|
527
|
+
// 此处命中的都是无否定语境的真命中;仍误伤的走 frontmatter 显式覆盖通道。
|
|
469
528
|
if (!changeRiskProfile.explicit) {
|
|
470
529
|
warnings.push(
|
|
471
530
|
`[${changeRiskProfile.level}] 本次变更被关键词判级(命中:${changeRiskProfile.triggers.join(', ')})。` +
|
|
@@ -485,7 +544,12 @@ function validateVerifyOutputs(cwd, changeName, context = {}) {
|
|
|
485
544
|
// 的结构化证据,作为附加匹配文本——有回执不依赖 agent 措辞;无回执维持原字面匹配
|
|
486
545
|
let receiptText = ''
|
|
487
546
|
try {
|
|
488
|
-
|
|
547
|
+
// 回执按变更分片(坑 verify-pids-cross-session-kill:原单份被并行会话后写覆盖),
|
|
548
|
+
// 优先读本变更分片,兼容旧单份名(升级过渡期 receipt.change 过滤仍生效)
|
|
549
|
+
const runtimeDir = join(cwd, '.sillyspec', '.runtime')
|
|
550
|
+
const receiptPath = existsSync(join(runtimeDir, `verify-services-${changeName}.receipt.json`))
|
|
551
|
+
? join(runtimeDir, `verify-services-${changeName}.receipt.json`)
|
|
552
|
+
: join(runtimeDir, 'verify-services.receipt.json')
|
|
489
553
|
if (existsSync(receiptPath)) {
|
|
490
554
|
const receipt = JSON.parse(readFileSync(receiptPath, 'utf8'))
|
|
491
555
|
if (receipt && (receipt.reapedPidCount || 0) > 0 && (!receipt.change || receipt.change === changeName)) {
|
package/src/stages/archive.js
CHANGED
|
@@ -44,9 +44,17 @@ export const definition = {
|
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
name: 'sync-module-docs',
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
47
|
+
// conditionalWait(坑 archive-subconfirm-redundant,2026-08-23 实证:requiresWait 硬门强制
|
|
48
|
+
// 三段式 --wait → --continue --answer → --done,与 verify 文档同步阻断门、归档移动前死信
|
|
49
|
+
// 校验、下一步「确认归档 --confirm」四层确认重复,交互碎。改 conditionalWait(brainstorm-auto
|
|
50
|
+
// 同款先例):常规同步直接写入 + --done(agent 写入动作在 --done 前完成,坑 verify-archive-
|
|
51
|
+
// flow-pitfalls 坑4 的「无机会写入」不回归);仅异常(needs_review/未映射/标记缺失)才 --wait
|
|
52
|
+
// 请用户裁决。用户确认收敛到「确认归档 --confirm」一处。
|
|
53
|
+
conditionalWait: true,
|
|
54
|
+
repeatableWait: true,
|
|
55
|
+
maxWaitRounds: 3,
|
|
56
|
+
waitReason: '等待用户裁决模块文档同步异常',
|
|
57
|
+
waitOptions: ['确认写入', '跳过同步'],
|
|
50
58
|
prompt: `根据 module-impact.md 同步更新模块索引和卡片文档。
|
|
51
59
|
|
|
52
60
|
### ⚠️ 核心原则:结构化事实改 _module-map.yaml,语义解释改模块卡片
|
|
@@ -109,20 +117,52 @@ created_at: <now-datetime>
|
|
|
109
117
|
<!-- MANUAL_NOTES_END -->
|
|
110
118
|
\`\`\`
|
|
111
119
|
|
|
112
|
-
4. 展示所有更新内容(diff
|
|
113
|
-
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
4. 展示所有更新内容(diff 摘要),按同步状态分流:
|
|
121
|
+
- **常规**(无 needs_review 影响、无 unmapped 模块、人工备注标记齐全)→ **直接写入** _module-map.yaml 和受影响的模块卡片,diff 摘要写入 --output 后 --done(本步不单独弹确认——用户确认收敛到下一步「确认归档 --confirm」,该步展示同一 diff 摘要)
|
|
122
|
+
- **异常**(含 needs_review 影响项 / unmapped 模块 / 人工备注标记缺失或重复 / 覆盖会丢失手动维护字段)→ 暂停请用户裁决:
|
|
123
|
+
\`sillyspec run archive --wait --reason "等待用户裁决模块文档同步异常" --options "确认写入,跳过同步" --output "diff 摘要 + 异常说明"\`
|
|
124
|
+
用户 --continue --answer "确认写入" 后写入;"跳过同步" 则不写入,提示 "module-impact.md 已保留,可稍后手动同步"
|
|
125
|
+
5. 回填 module-impact.md 的"更新结果"表格,区分目标:
|
|
118
126
|
- 目标列写 "\`_module-map.yaml: <module-id>\`" 或 "\`modules/<module-id>.md\`"
|
|
119
|
-
|
|
127
|
+
6. **同步完成后**,如需刷新 \`_module-map.yaml\` 索引:\`sillyspec modules rebuild --force\`(默认 dry-run 只预览不写;\`--force\` 才覆盖,但会清空 tags/entrypoints/main_symbols/depends_on/used_by 等手动维护字段——与 archive-impact 的「人工备注保护」约束冲突,仅当手动字段已并入骨架或可接受覆盖时用;优先手动更新 dependencies.md / 模块卡片)
|
|
120
128
|
|
|
121
129
|
### 输出
|
|
122
|
-
已更新的文件路径列表 +
|
|
130
|
+
已更新的文件路径列表 + diff 摘要(异常时附裁决结果)`,
|
|
123
131
|
outputHint: '模块文档更新结果',
|
|
124
132
|
optional: false
|
|
125
133
|
},
|
|
134
|
+
{
|
|
135
|
+
name: 'decision-distill 决策提炼',
|
|
136
|
+
// conditionalWait(同 sync-module-docs 先例):常规提炼直接写入 + --done;仅异常(rejected
|
|
137
|
+
// 条目缺否决理由/复潮条件 → distillIntoKnowledge 返回 needsWait 非空)才 --wait 请用户裁决
|
|
138
|
+
// (补录后重跑幂等 / 跳过该条)。勿引入 requiresWait 硬门——与下一步「确认归档 --confirm」
|
|
139
|
+
// 重复确认(坑 archive-subconfirm-redundant)。
|
|
140
|
+
conditionalWait: true,
|
|
141
|
+
repeatableWait: true,
|
|
142
|
+
maxWaitRounds: 3,
|
|
143
|
+
waitReason: '等待用户裁决 rejected 决策缺字段',
|
|
144
|
+
waitOptions: ['补录后继续', '跳过该条'],
|
|
145
|
+
prompt: `读取变更 decisions.md,把有实现影响的决策提炼进决策知识库(提炼/幂等/INDEX 路由行本体在 CLI 纯函数 src/decision-distill.js,本步只接线,勿手工改写条目格式)。
|
|
146
|
+
|
|
147
|
+
### 操作
|
|
148
|
+
1. 定位参数:changeDir = \`.sillyspec/changes/<change-name>\`(decisions.md 所在——本步在「确认归档」移动目录之前,仍在原位);knowledgeRoot = \`.sillyspec/knowledge\`;headHash = \`git rev-parse --short HEAD\`(落条目「最近确认」)
|
|
149
|
+
2. 调用纯函数 \`distillIntoKnowledge(changeDir, knowledgeRoot, headHash)\`,按返回的 \`{ written, skipped, needsWait }\` 分流(见第 3 点):
|
|
150
|
+
- 能解析到 sillyspec 源码/安装位置时(如 sillyspec 仓内 dogfood)直接 import 调用:\`node --input-type=module -e "import { distillIntoKnowledge } from './src/decision-distill.js'; console.log(JSON.stringify(distillIntoKnowledge(changeDir, knowledgeRoot, headHash), null, 2))"\`
|
|
151
|
+
- 解析不到(consumer 仓库无源码,node import 会 ERR_MODULE_NOT_FOUND)→ 按其语义执行:解析 D-xxx@vN 条目;入选规则 = 任意 type 的 status=rejected 留痕 + 五类 type(architecture/compatibility/boundary/definition/process)且 status∈{confirmed,accepted} 提炼 implemented,type=scope 不入选;条目幂等落 \`.sillyspec/knowledge/decisions/<模块域>.md\`(同 ID 同版本重写不重复追加、@vN+1 整段替换旧版并注 supersedes;「模块域」缺失按 impacts 路径与 _module-map.yaml paths 前缀匹配兜底,仍未中归 unmapped);implemented 条目写「状态:implemented + 锚点:<src 路径:行号,未记录则填"未记录"> + 最近确认:<headHash> + 理由:<一句话>」,rejected 条目写「状态:rejected + 否决理由: + 复潮条件:」;\`knowledge/INDEX.md\` 的 decisions 路由行(\`- <域>|decision|决策 → [decisions/<域>.md](decisions/<域>.md)\`)幂等增删
|
|
152
|
+
3. 分流规则:
|
|
153
|
+
- **常规**(needsWait 为空且 written 非空)→ 写入已由函数完成,written 摘要(decisions/<域>.md × D-xxx@vN × append/update/supersede)写入 --output 后直接 --done(本步不单独弹确认——用户确认收敛到下一步「确认归档 --confirm」)
|
|
154
|
+
- **needsWait 非空**(rejected 条目缺否决理由/复潮条件,该条目未写盘、其余条目照常提炼)→ 暂停请用户裁决:
|
|
155
|
+
\`sillyspec run archive --wait --reason "等待用户裁决 rejected 决策缺字段" --options "补录后继续,跳过该条" --output "needsWait 描述 + 已提炼摘要"\`
|
|
156
|
+
- 「补录后继续」→ 按 needsWait 描述在 changes/<change-name>/decisions.md 补齐缺失的否决理由/复潮条件,重跑第 2 步(幂等,重跑安全)
|
|
157
|
+
- 「跳过该条」→ 该条目不入库,--output 注记跳过原因后 --done
|
|
158
|
+
- **零输出**(skipped 非空 = 无 decisions.md / 无入选条目)→ 不创建任何文件、不动 INDEX,--output 注一句 skipped 原因后直接 --done(同 docs-debt 无债零输出原则)
|
|
159
|
+
4. 降级:调用或解析异常(模块加载失败 / decisions.md 结构异常抛错)→ warn 记录异常摘要后跳过本步 --done(best-effort,同 agent-session-log 先例),不阻断归档——决策提炼失败不挡归档移动
|
|
160
|
+
|
|
161
|
+
### 输出
|
|
162
|
+
已提炼条目清单(decisions/<域>.md × D-xxx@vN × action)或零输出/跳过/降级原因`,
|
|
163
|
+
outputHint: '决策提炼结果',
|
|
164
|
+
optional: false
|
|
165
|
+
},
|
|
126
166
|
{
|
|
127
167
|
name: '确认归档',
|
|
128
168
|
prompt: `确认归档内容,由 CLI 执行目录移动。
|
|
@@ -147,7 +187,8 @@ created_at: <now-datetime>
|
|
|
147
187
|
1. 如果 \`.sillyspec/ROADMAP.md\` 存在,标记对应 Phase 为已完成
|
|
148
188
|
2. \`git add .sillyspec/changes/archive/\` — 暂存归档结果(archive/ 下仅本次归档新增,不会裹挟 changes/ 下其他活跃变更;不要 commit,由用户通过统一提交工具处理)
|
|
149
189
|
3. \`git add .sillyspec/docs/<project>/modules/\` — 暂存模块文档更新(如有;精确到本次同步的模块文档,勿 add 整个 .sillyspec/docs/)
|
|
150
|
-
4.
|
|
190
|
+
4. \`git add .sillyspec/knowledge/decisions/\` — 暂存决策知识库更新(如有;decision-distill 步骤的提炼产物,精确到 decisions 子目录,勿 add 整个 .sillyspec/knowledge/;不要 commit,由用户通过统一提交工具处理)
|
|
191
|
+
5. 确认 sillyspec.db 中该变更已不再 active(确认归档步骤由 CLI 调用 unregisterChange)
|
|
151
192
|
|
|
152
193
|
### 输出
|
|
153
194
|
归档完成确认 + 累积规范统计`,
|
|
@@ -154,6 +154,13 @@ AC-011: 不涉及业务规则/产品范围/默认行为/用户可见行为变更
|
|
|
154
154
|
#### decisions.md(必填,写入变更根目录 \`decisions.md\`)
|
|
155
155
|
记录所有决策:
|
|
156
156
|
\`\`\`markdown
|
|
157
|
+
---
|
|
158
|
+
author: <git-user>
|
|
159
|
+
created_at: <now-datetime>
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
# 决策记录(Decisions)
|
|
163
|
+
|
|
157
164
|
## D-001@v1: 决策短标题
|
|
158
165
|
- type: architecture | boundary | compatibility | ...
|
|
159
166
|
- priority: P0 | P1 | P2
|
package/src/stages/brainstorm.js
CHANGED
|
@@ -46,6 +46,7 @@ export const definition = {
|
|
|
46
46
|
- 有匹配模板 → 询问是否基于模板
|
|
47
47
|
- 无相关内容 → 跳过,不输出
|
|
48
48
|
8. 查询知识库:\`cat {SPEC_ROOT}/knowledge/INDEX.md 2>/dev/null\`(无索引则跳过)——按需求关键词命中条目时读取对应知识文件;known-issues/已知坑要在方案期就规避,不要等 execute 的命中报告才发现
|
|
49
|
+
- decisions 库路由:INDEX.md \`## Decisions\` 段的路由行(\`decisions/<域>.md\`,由归档时决策提炼幂等维护)命中时,读取对应决策文件——rejected 条目须看到否决理由:除非复潮条件明确满足,否则不重新提出该方案(防复潮);确要复潮时在本变更 decisions.md 记新版本条目(D-xxx@vN+1)说明依据。CLI 预注入的否决决策提示(无命中则为空):{DECISION_HITS}
|
|
49
50
|
|
|
50
51
|
### 模块匹配方法
|
|
51
52
|
读取 _module-map.yaml 后,根据用户描述的需求关键词,匹配相关模块:
|
|
@@ -339,6 +340,11 @@ design.md 第一行标题必须用中文:# 设计文档(Design)— <变更
|
|
|
339
340
|
- 每条记录必须有稳定版本 ID:D-001@v1、D-002@v1 ...
|
|
340
341
|
- 若后续 Design Grill 修正该决策,新记录使用 D-001@v2,并写明 supersedes: D-001@v1
|
|
341
342
|
- 每条记录必须包含:type、status、source、question、answer、normalized_requirement、impacts、evidence、priority
|
|
343
|
+
- 九字段之外可另加四个可选字段,按需填写、不强制全填(旧格式决策记录缺这些字段不受影响):
|
|
344
|
+
- 锚点:决策落点的主文件,格式 <src 路径>:<行号或符号>(status=confirmed 时必填)
|
|
345
|
+
- 模块域:决策涉及的模块 ID,取自 \`{SPEC_ROOT}/docs/<project>/modules/_module-map.yaml\`,可多个、用逗号分隔
|
|
346
|
+
- 否决理由:一句话说明为何否决(status=rejected 时必填)
|
|
347
|
+
- 复潮条件:什么前提下可重新考虑该决策(status=rejected 时必填)
|
|
342
348
|
- 长期术语只在 archive/scan 时再提升到 \`{SPEC_ROOT}/docs/<project>/glossary.md\`
|
|
343
349
|
4. 格式自检(只查章节齐全;语义一致性/可行性/YAGNI 不在本步查,交给下一步 Design Grill 独立审查):
|
|
344
350
|
- design.md 含全部必填章节(背景/设计目标/非目标/总体方案/文件变更清单/接口定义/风险登记)
|
|
@@ -566,6 +572,11 @@ created_at: <now-datetime>
|
|
|
566
572
|
|
|
567
573
|
### decisions.md 格式要求(仅在有 Grill/重大决策时生成)
|
|
568
574
|
\`\`\`markdown
|
|
575
|
+
---
|
|
576
|
+
author: <git-user>
|
|
577
|
+
created_at: <now-datetime>
|
|
578
|
+
---
|
|
579
|
+
|
|
569
580
|
# 决策记录(Decisions)
|
|
570
581
|
|
|
571
582
|
## D-001@v1: 决策短标题
|
package/src/stages/doctor.js
CHANGED
|
@@ -327,9 +327,155 @@ timeout 5 which docker 2>/dev/null && echo "✅ Docker 可用" || echo "ℹ️ D
|
|
|
327
327
|
outputHint: '模块文档健康状态',
|
|
328
328
|
optional: false
|
|
329
329
|
},
|
|
330
|
+
{
|
|
331
|
+
name: '决策待复核检查',
|
|
332
|
+
prompt: `检查决策知识库(knowledge/decisions/)的待复核状态(advisory,不阻断)。
|
|
333
|
+
|
|
334
|
+
### 操作
|
|
335
|
+
运行 docs-check 决策规则族(锚点存在性 + behind 阈值复核,task: 2026-08-23-adopt-harness-practices):
|
|
336
|
+
|
|
337
|
+
\`\`\`bash
|
|
338
|
+
# 定位 sillyspec 源码根:sillyspec 本仓 dogfood 优先用仓内源码(全局安装版本可能落后于本检查项);
|
|
339
|
+
# 其余场景经 bin 符号链接解析到安装包根;均不可得时降级跳过
|
|
340
|
+
SRC_ROOT=""
|
|
341
|
+
if grep -q '"name": *"sillyspec"' package.json 2>/dev/null && [ -f "src/docs-check.js" ]; then
|
|
342
|
+
SRC_ROOT="$PWD"
|
|
343
|
+
else
|
|
344
|
+
BIN=$(command -v sillyspec)
|
|
345
|
+
if [ -n "$BIN" ]; then
|
|
346
|
+
SRC_ROOT=$(node -e 'const fs=require("fs");const p=fs.realpathSync(process.argv[1]).replace(/\\\\/g,"/");const i=p.lastIndexOf("/bin/sillyspec.js");console.log(i===-1?"":p.slice(0,i));' "$BIN")
|
|
347
|
+
fi
|
|
348
|
+
fi
|
|
349
|
+
if [ -z "$SRC_ROOT" ]; then
|
|
350
|
+
echo "ℹ️ 无法定位 sillyspec 源码(非 sillyspec 仓且未安装 sillyspec 命令),跳过决策待复核检查"
|
|
351
|
+
else
|
|
352
|
+
node --input-type=module -e '
|
|
353
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
354
|
+
import { pathToFileURL } from "node:url";
|
|
355
|
+
let specBase = process.argv[2];
|
|
356
|
+
if (existsSync(".sillyspec-platform.json")) {
|
|
357
|
+
try { const p = JSON.parse(readFileSync(".sillyspec-platform.json", "utf8")); if (p.specRoot) specBase = p.specRoot; } catch {}
|
|
358
|
+
}
|
|
359
|
+
const mod = await import(pathToFileURL(process.argv[1] + "/docs-check.js"));
|
|
360
|
+
const r = await mod.runDecisionRules({ projectRoot: process.cwd(), specBase: specBase });
|
|
361
|
+
if (r.empty) {
|
|
362
|
+
console.log("ℹ️ 决策库未初始化(" + specBase + "/knowledge/decisions/ 不存在)——冷启动空库,变更归档时 decision-distill 自动积累(R-02)");
|
|
363
|
+
} else if (r.findings.length === 0) {
|
|
364
|
+
console.log("✅ 决策待复核:无信号(" + r.implemented + "/" + r.entries + " 条 implemented,behind 阈值 " + r.threshold + ")");
|
|
365
|
+
} else {
|
|
366
|
+
console.log("⚠️ 决策待复核清单(" + r.findings.length + " 条,advisory 不阻断——D-003):");
|
|
367
|
+
for (const f of r.findings) console.log(" - " + f.message);
|
|
368
|
+
}
|
|
369
|
+
if (r.exempted.length > 0) console.log("ℹ️ 另有 " + r.exempted.length + " 条经 known_failures decisions.* 键豁免: " + r.exempted.map(e => e.key).join(", "));
|
|
370
|
+
' "$SRC_ROOT/src" "$PWD/.sillyspec" || echo "⚠️ 决策规则执行失败(降级跳过,不影响其他检查项)"
|
|
371
|
+
fi
|
|
372
|
+
\`\`\`
|
|
373
|
+
|
|
374
|
+
同步骤追加 CLI 版本漂移检测(D-002 并入既有检查段不新增步骤;D-004 git/version 双轨;advisory 不阻断)——检测全局安装的 sillyspec 流程引擎与当前仓源码是否脱节:
|
|
375
|
+
|
|
376
|
+
\`\`\`bash
|
|
377
|
+
# 安装根独立解析:command -v sillyspec → realpath → 向上找 name=sillyspec 的 package.json
|
|
378
|
+
# 勿复用上方 SRC_ROOT——sillyspec 仓场景它被 dogfood 分支强制指向当前仓,比较恒等(Grill 实证陷阱)
|
|
379
|
+
node --input-type=module -e '
|
|
380
|
+
import { execFileSync } from "node:child_process";
|
|
381
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
382
|
+
import { dirname, join } from "node:path";
|
|
383
|
+
|
|
384
|
+
const run = (cmd, args) => { try { return execFileSync(cmd, args, { encoding: "utf8", timeout: 8000, stdio: ["ignore", "pipe", "pipe"] }).trim(); } catch { return ""; } };
|
|
385
|
+
const pkgOf = (dir) => { try { return JSON.parse(readFileSync(join(dir, "package.json"), "utf8")); } catch { return null; } };
|
|
386
|
+
const short = (h) => h.slice(0, 7);
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const cur = pkgOf(process.cwd());
|
|
390
|
+
if (!cur || cur.name !== "sillyspec") process.exit(0); // 当前仓非 sillyspec(消费项目)→ 静默跳过
|
|
391
|
+
const bin = run("sh", ["-c", "command -v sillyspec"]);
|
|
392
|
+
if (!bin) process.exit(0); // 无全局安装 → 静默跳过
|
|
393
|
+
let installRoot = "";
|
|
394
|
+
try {
|
|
395
|
+
let dir = dirname(realpathSync(bin));
|
|
396
|
+
for (;;) {
|
|
397
|
+
const p = pkgOf(dir);
|
|
398
|
+
if (p && p.name === "sillyspec") { installRoot = dir; break; }
|
|
399
|
+
const up = dirname(dir);
|
|
400
|
+
if (up === dir) break;
|
|
401
|
+
dir = up;
|
|
402
|
+
}
|
|
403
|
+
} catch {}
|
|
404
|
+
if (!installRoot) { console.log("⚠️ CLI 版本漂移检测降级:sillyspec 命令存在但未定位到安装根(" + bin + "),跳过比较,不阻断其余检查项"); process.exit(0); }
|
|
405
|
+
if (installRoot === process.cwd()) process.exit(0); // 安装根=当前仓(npm link 自身)→ 恒等不误报
|
|
406
|
+
|
|
407
|
+
// 双轨比较(D-004):
|
|
408
|
+
// git 轨——安装根有 .git(开发态:npm link / file: 链接)→ rev-parse HEAD 双仓 + remote origin 归一化同源判定
|
|
409
|
+
// version 兜底轨——安装根无 .git(安装态:registry 安装 / npm i -g . 拷贝,npm 恒排除 .git)→ 比较双仓 package.json version
|
|
410
|
+
// 盲区声明(R-04):同 version 不同 commit 的源码热改不检测——version 轨只在版本号变更时感知;
|
|
411
|
+
// git 轨管开发态、version 轨管安装态,两轨并集覆盖主流形态
|
|
412
|
+
const inst = pkgOf(installRoot) || {};
|
|
413
|
+
const curVer = cur.version || "?";
|
|
414
|
+
const instVer = inst.version || "?";
|
|
415
|
+
const warn = (detail) => console.log("⚠️ CLI 版本漂移:" + detail + "——全局安装的流程引擎与当前仓源码脱节,归档等流程行为可能滞后(2026-08-23 归档实证踩坑:五步版 archive 驱动六步定义,旧引擎驱动新定义),建议同步(安装根 git pull 或在 sillyspec 仓 npm i -g .)后重跑流程(advisory,不阻断)");
|
|
416
|
+
|
|
417
|
+
if (existsSync(join(installRoot, ".git"))) {
|
|
418
|
+
// remote 归一化:https/ssh 归一、.git 后缀剥离、host 小写——同源才可比较(异源 fork 无从判定 → 静默)
|
|
419
|
+
const normRemote = (u) => {
|
|
420
|
+
if (!u) return "";
|
|
421
|
+
let s = u.trim();
|
|
422
|
+
if (s.indexOf("://") === -1) {
|
|
423
|
+
const scp = s.match(/^([^@\\/]+@)([^:\\/]+):(.+)$/); // scp 形态 git@host:owner/repo.git
|
|
424
|
+
if (scp) s = "https://" + scp[2] + "/" + scp[3];
|
|
425
|
+
}
|
|
426
|
+
s = s.replace(/^ssh:\\/\\//, "https://").replace(/^git:\\/\\//, "https://");
|
|
427
|
+
const m = s.match(/^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\/(.*)$/);
|
|
428
|
+
const rest = m ? m[1] : s;
|
|
429
|
+
const slash = rest.indexOf("/");
|
|
430
|
+
let host = slash === -1 ? rest : rest.slice(0, slash);
|
|
431
|
+
const tail = slash === -1 ? "" : rest.slice(slash);
|
|
432
|
+
const at = host.indexOf("@");
|
|
433
|
+
if (at !== -1) host = host.slice(at + 1); // 剥 userinfo(ssh://git@… / https://token@…)
|
|
434
|
+
let out = "https://" + host.toLowerCase() + tail;
|
|
435
|
+
out = out.replace(/\\/+$/, "");
|
|
436
|
+
if (out.endsWith(".git")) out = out.slice(0, -4);
|
|
437
|
+
return out;
|
|
438
|
+
};
|
|
439
|
+
const curHead = run("git", ["-C", process.cwd(), "rev-parse", "HEAD"]);
|
|
440
|
+
const instHead = run("git", ["-C", installRoot, "rev-parse", "HEAD"]);
|
|
441
|
+
if (!curHead || !instHead) { console.log("⚠️ CLI 版本漂移检测降级:git 轨 rev-parse 失败(当前仓=" + (curHead ? "ok" : "fail") + " 安装根=" + (instHead ? "ok" : "fail") + "),跳过比较,不阻断其余检查项"); process.exit(0); }
|
|
442
|
+
const curRemote = normRemote(run("git", ["-C", process.cwd(), "remote", "get-url", "origin"]));
|
|
443
|
+
const instRemote = normRemote(run("git", ["-C", installRoot, "remote", "get-url", "origin"]));
|
|
444
|
+
if (curRemote && instRemote && curRemote !== instRemote) process.exit(0); // 非同源 → 静默
|
|
445
|
+
if (curHead !== instHead) warn("全局安装 " + installRoot + "(version " + instVer + " / commit " + short(instHead) + ")与当前仓(version " + curVer + " / HEAD " + short(curHead) + ")不一致");
|
|
446
|
+
// 同 commit → 一致静默
|
|
447
|
+
} else if (instVer !== curVer) {
|
|
448
|
+
warn("全局安装 " + installRoot + "(version " + instVer + ")与当前仓(version " + curVer + ")不一致(安装根无 .git,走 version 兜底轨)");
|
|
449
|
+
// 同 version → 一致静默(同 version 不同 commit 热改盲区见上方 R-04 注释)
|
|
450
|
+
}
|
|
451
|
+
} catch (e) {
|
|
452
|
+
console.log("⚠️ CLI 版本漂移检测降级:" + (e && e.message ? e.message : String(e)) + "(不阻断其余检查项)");
|
|
453
|
+
}
|
|
454
|
+
' || echo "⚠️ CLI 版本漂移检测降级:脚本执行失败(不阻断其余检查项)"
|
|
455
|
+
\`\`\`
|
|
456
|
+
|
|
457
|
+
### 输出格式
|
|
458
|
+
\`\`\`
|
|
459
|
+
📋 决策待复核
|
|
460
|
+
✅ 决策待复核:无信号(3/5 条 implemented,behind 阈值 10)
|
|
461
|
+
⚠️ 决策待复核清单(1 条,advisory 不阻断——D-003):
|
|
462
|
+
- 「D-012@v1」决策待复核:锚定模块 docs-consistency 在最近确认 a1b2c3d 后源码已前进 15 commit,超阈值 10
|
|
463
|
+
ℹ️ 决策库未初始化 —— 冷启动空库(R-02)
|
|
464
|
+
|
|
465
|
+
⚠️ CLI 版本漂移:全局安装 /path/to/sillyspec(version 3.26.0 / commit a1b2c3d)与当前仓(version 3.27.3 / HEAD e4f5a6b)不一致——全局安装的流程引擎与当前仓源码脱节,建议同步后重跑流程(仅漂移时输出;一致/非 sillyspec 仓/无全局安装/安装根=当前仓均静默)
|
|
466
|
+
\`\`\`
|
|
467
|
+
|
|
468
|
+
### 注意
|
|
469
|
+
- 本检查项为 advisory(只 ⚠️ 不 ❌),不作为修复阻断项
|
|
470
|
+
- 待复核的复核再确认由用户完成:确认决策仍成立后,把条目「最近确认」更新为当前 HEAD——不要自动改写
|
|
471
|
+
- 全程只读,不修改 knowledge/decisions/ 下任何文件
|
|
472
|
+
- CLI 版本漂移检测(同步骤追加段,D-002/D-004)同为 advisory:漂移时一行警告;一致、非 sillyspec 仓消费场景、无全局安装、安装根=当前仓(npm link 自身)均静默;探测失败降级单行,不阻断其余检查项`,
|
|
473
|
+
outputHint: '决策待复核清单',
|
|
474
|
+
optional: false
|
|
475
|
+
},
|
|
330
476
|
{
|
|
331
477
|
name: '汇总报告',
|
|
332
|
-
prompt:
|
|
478
|
+
prompt: `汇总前面各步的所有检查结果,生成最终的自检报告。
|
|
333
479
|
|
|
334
480
|
### 输出格式
|
|
335
481
|
\`\`\`
|
|
@@ -350,6 +496,12 @@ timeout 5 which docker 2>/dev/null && echo "✅ Docker 可用" || echo "ℹ️ D
|
|
|
350
496
|
## 外部依赖
|
|
351
497
|
✅ Context7 MCP — 已配置
|
|
352
498
|
⚠️ grep.app — 不可达
|
|
499
|
+
|
|
500
|
+
## 决策待复核(advisory)
|
|
501
|
+
⚠️ 「D-012@v1」— 锚定模块源码 behind 15 超阈值 10(复核后更新「最近确认」)
|
|
502
|
+
|
|
503
|
+
## CLI 版本漂移(advisory)
|
|
504
|
+
⚠️ 全局安装 sillyspec(version 3.26.0 / commit a1b2c3d)与当前仓(version 3.27.3 / HEAD e4f5a6b)不一致 — 同步后重跑流程
|
|
353
505
|
\`\`\`
|
|
354
506
|
|
|
355
507
|
### 要求
|
|
@@ -370,8 +522,12 @@ timeout 5 which docker 2>/dev/null && echo "✅ Docker 可用" || echo "ℹ️ D
|
|
|
370
522
|
- 孤儿目录 → 确认后 \`rm -rf .sillyspec/changes/<目录名>\`
|
|
371
523
|
- Maven 私服不可达 → 检查 VPN、settings.xml 配置、私服状态
|
|
372
524
|
- Git remote 不可达 → 检查网络、SSH key 或凭证
|
|
525
|
+
- CLI 版本漂移(全局安装落后于当前仓)→ 在 sillyspec 仓执行 \`npm i -g .\` 重新全局安装(或安装根 git pull),同步后重跑流程
|
|
373
526
|
- 工具未安装 → 给出安装命令(如 \`brew install maven\`)
|
|
374
527
|
|
|
528
|
+
**状态错乱补 postmortem(advisory,不强制):**
|
|
529
|
+
若本次自检检出状态错乱或不一致(sillyspec.db 阶段状态与实际产出不符、孤儿目录、僵尸 quick 会话等)并已修复,建议补一条轻量 postmortem 记录进 QUICKLOG(走 quick 流程或既有条目正文核对),根因块内按列表行写四子字段:\`- 现象:\`(错乱表现)、\`- 根因:\`(深层原因)、\`- 护栏:\`(防再犯措施)、\`- 证据:\`(可追溯路径——\`sillyspec agent-log --json\` 输出的本地会话日志 jsonl 路径、相关变更 review.json、verify-result.md)。护栏结论经人工确认后归入 \`.sillyspec/knowledge/known-issues.md\`——走既有 knowledge 追加链路(先入 knowledge/uncategorized.md,经知识整理确认后归类),不新建链路不新建命令。
|
|
530
|
+
|
|
375
531
|
每条建议格式:
|
|
376
532
|
\`\`\`
|
|
377
533
|
💡 修复:<问题描述>
|
package/src/stages/execute.js
CHANGED
|
@@ -11,6 +11,9 @@ import { parseRepo } from './plan-postcheck.js'
|
|
|
11
11
|
// parseTaskRegistry(注册表同口径)——双向引用均为函数声明 + 调用时解引(无模块初始化期
|
|
12
12
|
// 交叉求值),ESM live binding 下安全;两边都是纯读函数,无副作用序问题。
|
|
13
13
|
import { resolveChangeModuleCards } from '../module-resolve.js'
|
|
14
|
+
// 决策锚点触碰事实(2026-08-24-decision-touch-cli-drift task-01,W-A 主渲染点 D-003):
|
|
15
|
+
// changedFiles 口径与 {DOCS_DEBT} 现算同源(collectExecuteChangedFiles 唯一实现,勿双写)
|
|
16
|
+
import { collectExecuteChangedFiles, computeDecisionTouches, renderDecisionTouchFacts } from '../docs-debt.js'
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* 任务注册表解析(2026-08-20-task-truth-unify D-001@v1:tasks.md 唯一任务真相)。
|
|
@@ -913,20 +916,43 @@ ${indexLines}
|
|
|
913
916
|
}
|
|
914
917
|
} catch { /* 热区提取 best-effort:失败不注入 */ }
|
|
915
918
|
|
|
919
|
+
// ── 决策锚点触碰事实(W-A 主渲染点,D-003):changedFiles 口径与 run/prompt.js {DOCS_DEBT}
|
|
920
|
+
// 现算同源——collectExecuteChangedFiles(porcelain 未提交 ∪ baseline..HEAD)唯一实现,勿双写。
|
|
921
|
+
// 单过流程前缀第 4 步渲染 {DOCS_DEBT} 时 changedFiles 恒空,Wave 步(重入/续跑时已含前置
|
|
922
|
+
// Wave 已提交变更)才是触碰事实的主呈现时机。advisory:无触碰 section=''(模板字节不变,
|
|
923
|
+
// 零输出零阻断);≤5 条截断(R-05)。best-effort:失败不注入不阻断。
|
|
924
|
+
let decisionTouchSection = ''
|
|
925
|
+
try {
|
|
926
|
+
if (changeDir) {
|
|
927
|
+
const dtSpecBase = path.dirname(path.dirname(changeDir))
|
|
928
|
+
const { changedFiles } = collectExecuteChangedFiles({
|
|
929
|
+
specBase: dtSpecBase,
|
|
930
|
+
changeName: path.basename(changeDir),
|
|
931
|
+
cwd: options.cwd || process.cwd(),
|
|
932
|
+
})
|
|
933
|
+
const dtFacts = renderDecisionTouchFacts(computeDecisionTouches(changedFiles, path.join(dtSpecBase, 'knowledge')).touches)
|
|
934
|
+
if (dtFacts) {
|
|
935
|
+
decisionTouchSection = `\n### 决策锚点触碰(CLI 算,advisory——改到锚定文件时复核对应决策)\n\n${dtFacts}\n`
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
} catch { /* 触碰事实 best-effort:失败不注入不阻断 */ }
|
|
939
|
+
|
|
916
940
|
// 子代理要点 4 / Wave 开始前第 1 条:模块表 / 热区存在时换成「按注入内容执行」版文案,
|
|
917
941
|
// 否则保留原文(零回归——无 map / 无 design 的项目 prompt 不变)
|
|
918
942
|
const moduleDocPoint = moduleSection
|
|
919
943
|
? '4. **模块卡分级**:把上方「模块卡分级」表中该 task 命中的卡路径写进子代理 prompt(细卡整读;粗大卡只读「契约摘要/注意事项/定位」节)——勿让子代理按目录漫游或整读根层大卡'
|
|
920
|
-
: '4.
|
|
944
|
+
: '4. 如存在模块文档({SPEC_ROOT}/docs/*/modules/),按需读取涉及模块的 <module>.md 参考接口约定和数据流——读主仓路径(CLI 已替换为绝对路径),不要读 worktree 副本'
|
|
921
945
|
const waveStartItem1 = hasDesignHotzone
|
|
922
946
|
? '1. 「非目标」「兼容策略」两节已由 CLI 提取附在上方「design.md 热区」——**不要再整读 design.md**;需要其余章节时按热区下方行号索引精准读取(Read offset/limit)。确保子代理不超范围、不破坏旧逻辑'
|
|
923
947
|
: '1. 读取 design.md 的「非目标」与「兼容策略」章节(如存在),确保子代理不超范围、不破坏旧逻辑'
|
|
924
948
|
|
|
925
|
-
//
|
|
949
|
+
// 构建任务摘要(不再内联完整蓝图,减少上下文污染)。
|
|
950
|
+
// 任务卡路径用 {SPEC_ROOT} 占位符(坑 worktree-spec-artifact-misplace):子代理 cwd=worktree 时
|
|
951
|
+
// 相对路径会解析到 worktree 的 .sillyspec 副本;占位符经 CLI 替换为主仓绝对路径。
|
|
926
952
|
const taskSummary = wave.tasks.map((t, ti) => {
|
|
927
953
|
const taskNum = String(t.index || (ti + 1)).padStart(2, '0')
|
|
928
954
|
const taskRelPath = changeDir
|
|
929
|
-
?
|
|
955
|
+
? `{SPEC_ROOT}/changes/${path.basename(changeDir)}/tasks/task-${taskNum}.md`
|
|
930
956
|
: `task-${taskNum}.md`
|
|
931
957
|
const fileInfo = t.file ? ` (${t.file})` : ''
|
|
932
958
|
return `task-${taskNum}: ${t.name}${fileInfo} → ${taskRelPath}`
|
|
@@ -1025,7 +1051,7 @@ ${workdirLines}
|
|
|
1025
1051
|
\`\`\`
|
|
1026
1052
|
|
|
1027
1053
|
### 注意
|
|
1028
|
-
蓝图文件(tasks.md / design.md / proposal.md / requirements.md / tasks/task-XX.md)在主工作区
|
|
1054
|
+
蓝图文件(tasks.md / design.md / proposal.md / requirements.md / tasks/task-XX.md)在主工作区 {SPEC_ROOT}/changes/<change>/ 下(CLI 已替换为主仓绝对路径),它们不在跨仓仓也不在 worktree 中。读取蓝图时使用主工作区路径,不要拼接到 worktree / 跨仓仓路径下。
|
|
1029
1055
|
`
|
|
1030
1056
|
} else {
|
|
1031
1057
|
worktreeSection = (worktreePath)
|
|
@@ -1044,7 +1070,7 @@ ${workdirLines}
|
|
|
1044
1070
|
\`\`\`
|
|
1045
1071
|
|
|
1046
1072
|
### 注意
|
|
1047
|
-
蓝图文件(tasks.md / design.md / proposal.md / requirements.md)在主工作区
|
|
1073
|
+
蓝图文件(tasks.md / design.md / proposal.md / requirements.md)在主工作区 {SPEC_ROOT}/changes/<change>/ 下(CLI 已替换为主仓绝对路径),它们可能不在 worktree 中。读取蓝图时使用主工作区路径,不要拼接到 worktree 路径下;同理,spec 流程产物(module-impact.md / knowledge 条目 / 模块文档)只写主仓 {SPEC_ROOT},绝不写进 worktree 副本。
|
|
1048
1074
|
`
|
|
1049
1075
|
: ''
|
|
1050
1076
|
}
|
|
@@ -1114,7 +1140,7 @@ ${taskSummary}
|
|
|
1114
1140
|
|
|
1115
1141
|
{{include: testcase-design}}
|
|
1116
1142
|
|
|
1117
|
-
${designHotzone}
|
|
1143
|
+
${designHotzone}${decisionTouchSection}
|
|
1118
1144
|
### Wave 开始前
|
|
1119
1145
|
${waveStartItem1}
|
|
1120
1146
|
2. 读取 plan.md 了解全局任务划分和依赖关系
|