sillyspec 3.32.9 → 3.33.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.
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -114
- package/.claude/skills/sillyspec-execute/SKILL.md +222 -222
- package/.claude/skills/sillyspec-explore/SKILL.md +107 -107
- package/.claude/skills/sillyspec-flow/SKILL.md +1 -1
- package/.claude/skills/sillyspec-plan/SKILL.md +156 -156
- package/.github/workflows/ci.yml +58 -0
- package/.husky/pre-push +7 -6
- package/AGENTS.md +1 -1
- package/assets/command-cards/flow.md +1 -1
- package/eval/eval.config.json +3 -3
- package/eval/lib/config.mjs +6 -3
- package/eval/lib/util.mjs +16 -3
- package/eval/lib/workdir.mjs +4 -1
- package/eval/results/eval.db +0 -0
- package/eval/results/logs/2026-10-15T12-37-22-986Z-004-phase-stats-armA/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T12-37-22-986Z-004-phase-stats-armA/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T12-37-22-986Z-004-phase-stats-armA/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T12-45-37-124Z-004-phase-stats-armB/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T12-45-37-124Z-004-phase-stats-armB/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T12-45-37-124Z-004-phase-stats-armB/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T12-56-19-259Z-004-phase-stats-armA/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T12-56-19-259Z-004-phase-stats-armA/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T12-56-19-259Z-004-phase-stats-armA/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T13-00-05-625Z-004-phase-stats-armB/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T13-04-51-003Z-004-phase-stats-armA/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T13-04-51-003Z-004-phase-stats-armA/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T13-04-51-003Z-004-phase-stats-armA/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T13-06-25-271Z-004-phase-stats-armB/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T13-06-25-271Z-004-phase-stats-armB/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T13-06-25-271Z-004-phase-stats-armB/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T13-24-38-308Z-004-phase-stats-armB/prompt.txt +10 -0
- package/eval/results/logs/2026-10-15T13-24-38-308Z-004-phase-stats-armB/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T13-24-38-308Z-004-phase-stats-armB/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T14-50-01-950Z-005-orders-export-armA/prompt.txt +8 -0
- package/eval/results/logs/2026-10-15T14-50-01-950Z-005-orders-export-armA/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T14-50-01-950Z-005-orders-export-armA/stdout.txt +1 -0
- package/eval/results/logs/2026-10-15T14-51-56-316Z-005-orders-export-armB/prompt.txt +8 -0
- package/eval/results/logs/2026-10-15T14-51-56-316Z-005-orders-export-armB/stderr.txt +0 -0
- package/eval/results/logs/2026-10-15T14-51-56-316Z-005-orders-export-armB/stdout.txt +1 -0
- package/eval/results/workdirs/001-slugify__armA/package.json +6 -0
- package/eval/results/workdirs/001-slugify__armA/src/slugify.mjs +26 -0
- package/eval/results/workdirs/001-slugify__armB/package.json +6 -0
- package/eval/results/workdirs/001-slugify__armB/src/slugify.mjs +26 -0
- package/eval/results/workdirs/002-each-day__armA/package.json +6 -0
- package/eval/results/workdirs/002-each-day__armA/src/dates.mjs +30 -0
- package/eval/results/workdirs/002-each-day__armB/DRY_RUN_WRONG_ANSWER.mjs +1 -0
- package/eval/results/workdirs/002-each-day__armB/package.json +6 -0
- package/eval/results/workdirs/002-each-day__armB/src/dates.mjs +4 -0
- package/eval/results/workdirs/003-word-report__armA/bin/report.mjs +36 -0
- package/eval/results/workdirs/003-word-report__armA/data/notes.txt +5 -0
- package/eval/results/workdirs/003-word-report__armA/package.json +6 -0
- package/eval/results/workdirs/003-word-report__armB/bin/report.mjs +36 -0
- package/eval/results/workdirs/003-word-report__armB/data/notes.txt +5 -0
- package/eval/results/workdirs/003-word-report__armB/package.json +6 -0
- package/eval/results/workdirs/004-phase-stats__armA/data/events.log +16 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/flow.md +44 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/run-archive.md +27 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/run-brainstorm.md +34 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/run-execute.md +30 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/run-plan.md +28 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/run-verify.md +30 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/commands/sillyspec/status.md +28 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-archive/SKILL.md +87 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-auto/SKILL.md +54 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-commit/SKILL.md +67 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-continue/SKILL.md +17 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-execute/SKILL.md +222 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-explore/SKILL.md +107 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-export/SKILL.md +57 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-flow/SKILL.md +62 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-init/SKILL.md +177 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-knowledge/SKILL.md +40 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-plan/SKILL.md +156 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-propose/SKILL.md +16 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-quick/SKILL.md +116 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-resume/SKILL.md +21 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-scan/SKILL.md +79 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-state/SKILL.md +25 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-status/SKILL.md +54 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-verify/SKILL.md +87 -0
- package/eval/results/workdirs/004-phase-stats__armB/.claude/skills/sillyspec-workspace/SKILL.md +33 -0
- package/eval/results/workdirs/004-phase-stats__armB/AGENTS.md +45 -0
- package/eval/results/workdirs/004-phase-stats__armB/CLAUDE.md +3 -0
- package/eval/results/workdirs/004-phase-stats__armB/data/events.log +16 -0
- package/eval/results/workdirs/run-001-slugify__armA/package.json +6 -0
- package/eval/results/workdirs/run-001-slugify__armA/src/slugify.mjs +26 -0
- package/eval/results/workdirs/run-001-slugify__armB/package.json +6 -0
- package/eval/results/workdirs/run-001-slugify__armB/src/slugify.mjs +26 -0
- package/eval/results/workdirs/run-002-each-day__armA/package.json +6 -0
- package/eval/results/workdirs/run-002-each-day__armA/src/dates.mjs +30 -0
- package/eval/results/workdirs/run-002-each-day__armB/DRY_RUN_WRONG_ANSWER.mjs +1 -0
- package/eval/results/workdirs/run-002-each-day__armB/package.json +6 -0
- package/eval/results/workdirs/run-002-each-day__armB/src/dates.mjs +4 -0
- package/eval/results/workdirs/run-003-word-report__armA/bin/report.mjs +36 -0
- package/eval/results/workdirs/run-003-word-report__armA/data/notes.txt +5 -0
- package/eval/results/workdirs/run-003-word-report__armA/package.json +6 -0
- package/eval/results/workdirs/run-003-word-report__armB/bin/report.mjs +36 -0
- package/eval/results/workdirs/run-003-word-report__armB/data/notes.txt +5 -0
- package/eval/results/workdirs/run-003-word-report__armB/package.json +6 -0
- package/eval/results/workdirs/run-004-phase-stats__armA/bin/phase-stats.mjs +37 -0
- package/eval/results/workdirs/run-004-phase-stats__armA/data/events.log +16 -0
- package/eval/results/workdirs/run-004-phase-stats__armB/DRY_RUN_WRONG_ANSWER.mjs +1 -0
- package/eval/results/workdirs/run-004-phase-stats__armB/data/events.log +16 -0
- package/eval/results/workdirs/run-005-orders-export__armA/bin/export-orders.mjs +57 -0
- package/eval/results/workdirs/run-005-orders-export__armA/data/.gitattributes +1 -0
- package/eval/results/workdirs/run-005-orders-export__armA/data/orders.csv +9 -0
- package/eval/results/workdirs/run-005-orders-export__armA/out/summary.json +1 -0
- package/eval/results/workdirs/run-005-orders-export__armB/bin/export-orders.mjs +57 -0
- package/eval/results/workdirs/run-005-orders-export__armB/data/.gitattributes +1 -0
- package/eval/results/workdirs/run-005-orders-export__armB/data/orders.csv +9 -0
- package/eval/results/workdirs/run-005-orders-export__armB/out/summary.json +1 -0
- package/eval/tasks/004-phase-stats/files/data/events.log +16 -0
- package/eval/tasks/004-phase-stats/solution/bin/phase-stats.mjs +37 -0
- package/eval/tasks/004-phase-stats/task.json +6 -0
- package/eval/tasks/004-phase-stats/verify.mjs +38 -0
- package/eval/tasks/005-orders-export/files/data/.gitattributes +1 -0
- package/eval/tasks/005-orders-export/files/data/orders.csv +9 -0
- package/eval/tasks/005-orders-export/solution/bin/export-orders.mjs +57 -0
- package/eval/tasks/005-orders-export/task.json +6 -0
- package/eval/tasks/005-orders-export/verify.mjs +28 -0
- package/package.json +1 -1
- package/quicklog/tmp-fix-review.mjs +21 -0
- package/src/ceremony-tier.js +2 -1
- package/src/commit-suggest.js +45 -4
- package/src/config-schema.js +14 -0
- package/src/decision-distill.js +106 -24
- package/src/decision-redomain.js +148 -0
- package/src/decision-renumber.js +157 -0
- package/src/design-facts.js +3 -3
- package/src/docs-check.js +30 -15
- package/src/docs-debt.js +6 -5
- package/src/doctor-diagnostics.js +3 -1
- package/src/flow-parity.js +28 -4
- package/src/flow.js +76 -13
- package/src/forensics.js +236 -0
- package/src/fr-index.js +17 -2
- package/src/hooks/worktree-guard.js +3 -3
- package/src/index.js +41 -6
- package/src/knowledge-digest.js +23 -6
- package/src/knowledge-graph.js +95 -18
- package/src/knowledge-match.js +34 -9
- package/src/knowledge-stats.js +12 -13
- package/src/knowledge-vector.js +98 -7
- package/src/machine-interface.js +26 -0
- package/src/module-impact.js +2 -5
- package/src/modules.js +31 -0
- package/src/quicklog.js +6 -1
- package/src/run/archive-distill.js +3 -0
- package/src/run/command.js +3 -3
- package/src/run/complete-handlers.js +8 -3
- package/src/run/complete.js +113 -44
- package/src/run/gates.js +100 -103
- package/src/run/lint-ownership.js +37 -0
- package/src/run/prompt.js +43 -28
- package/src/run/stage.js +1 -1
- package/src/run/verify-quality-scan.js +10 -1
- package/src/sentinel-assertions.js +43 -1
- package/src/spec-sync.js +81 -2
- package/src/stage-contract.js +26 -3
- package/src/stage-review.js +46 -12
- package/src/stage-templates.js +2 -0
- package/src/stages/brainstorm.js +76 -21
- package/src/stages/knowledge.js +56 -5
- package/src/stages/plan-postcheck.js +13 -2
- package/src/stages/plan.js +19 -0
- package/src/sync.js +28 -2
- package/src/task-tick.js +12 -0
- package/src/test-bindings.js +32 -3
- package/src/timeline.js +60 -19
- package/src/ui-visual.js +119 -4
- package/src/verify-postcheck.js +166 -18
- package/src/verify-probes.js +61 -18
- package/src/worktree-apply.js +26 -2
- package/templates/agents-instruction.md +1 -1
package/src/docs-check.js
CHANGED
|
@@ -1038,12 +1038,20 @@ export function readDecisionRulesConfig(projectRoot) {
|
|
|
1038
1038
|
}
|
|
1039
1039
|
}
|
|
1040
1040
|
|
|
1041
|
-
/**
|
|
1042
|
-
*
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1041
|
+
/** 豁免键匹配(键匹配非子串):域限定键 `decisions.<域>/<id>.<kind>`(规则级精确)与
|
|
1042
|
+
* `decisions.<域>/<id>`(条目级伞形)——2026-10-14-decision-id-p2-hardening:裸键无域限定,
|
|
1043
|
+
* 一条 decisions.<id> 可静默豁免全库同号条目(D-001@v1 ×296 潜伏面),域限定键收窄命中面;
|
|
1044
|
+
* 裸键 `decisions.<id>[.<kind>]` 保留兼容(消费仓既有配置不失效)。id 与 originId(原编号,
|
|
1045
|
+
* 2026-10-14-decision-legacy-renumber)双匹配——存量迁移后旧号键继续命中。
|
|
1046
|
+
* 无 decisions. 前缀的键不参与。导出(测试面先例 anchorFilePath)。 */
|
|
1047
|
+
export function isDecisionExempt(knownFailures, entryId, kind, domain = null, originId = null) {
|
|
1048
|
+
const ids = originId ? [entryId, originId] : [entryId]
|
|
1049
|
+
const keys = []
|
|
1050
|
+
for (const id of ids) {
|
|
1051
|
+
keys.push(`decisions.${id}.${kind}`, `decisions.${id}`)
|
|
1052
|
+
if (domain) keys.push(`decisions.${domain}/${id}.${kind}`, `decisions.${domain}/${id}`)
|
|
1053
|
+
}
|
|
1054
|
+
return knownFailures.some(k => keys.includes(k))
|
|
1047
1055
|
}
|
|
1048
1056
|
|
|
1049
1057
|
/**
|
|
@@ -1051,25 +1059,32 @@ function isDecisionExempt(knownFailures, entryId, kind) {
|
|
|
1051
1059
|
* 的写入契约对偶:producer=decision-distill → consumer=本规则族;字段行非列表项,顶层
|
|
1052
1060
|
* 「状态:/锚点:/最近确认:」标签精确匹配,防误吞正文)。旧格式字段缺失容错为 null。
|
|
1053
1061
|
* @param {string} md 域文件全文
|
|
1054
|
-
* @returns {Array<{ id: string, title: string, status: string|null, anchor: string|null, lastConfirmed: string|null }>}
|
|
1062
|
+
* @returns {Array<{ id: string, title: string, status: string|null, anchor: string|null, lastConfirmed: string|null, change: string|null }>}
|
|
1055
1063
|
*/
|
|
1056
1064
|
export function parseDecisionEntries(md) {
|
|
1057
1065
|
const entries = []
|
|
1058
1066
|
let cur = null
|
|
1059
1067
|
const flush = () => { if (cur) entries.push(cur) }
|
|
1060
1068
|
for (const line of String(md || '').replace(/\r\n?/g, '\n').split('\n')) {
|
|
1061
|
-
const h = line.match(/^##\s+(D-\d+)(?:@v(\d+))?\s*(.*)$/)
|
|
1069
|
+
const h = line.match(/^##\s+(D-(?:[A-Za-z0-9_\-\u4e00-\u9fff]+-)?\d+)(?:@v(\d+))?\s*(.*)$/)
|
|
1062
1070
|
if (h) {
|
|
1063
1071
|
flush()
|
|
1064
1072
|
const version = h[2] ? parseInt(h[2], 10) : 1
|
|
1065
|
-
cur = { id: `${h[1]}@v${version}`, title: h[3].trim(), status: null, anchor: null, lastConfirmed: null }
|
|
1073
|
+
cur = { id: `${h[1]}@v${version}`, title: h[3].trim(), status: null, anchor: null, lastConfirmed: null, change: null, originId: null }
|
|
1066
1074
|
continue
|
|
1067
1075
|
}
|
|
1068
1076
|
if (!cur) continue
|
|
1069
|
-
|
|
1077
|
+
// 变更/来源/原编号:标签读入(变更:2026-10-14-supersedes-mislink-and-change-display 加法字段——
|
|
1078
|
+
// docs-debt 触碰事实行渲染变更名限定词消歧需读入;来源:2026-10-14-decision-id-p2-hardening legacy
|
|
1079
|
+
// 兜底——变更行无条件覆盖在前、来源仅补缺;原编号:2026-10-14-decision-legacy-renumber——存量迁移
|
|
1080
|
+
// 后条目 id 为全局号,消费仓旧豁免键(decisions.D-002@vN)经原编号继续命中;旧条目缺行容错 null)
|
|
1081
|
+
const f = line.match(/^(状态|锚点|最近确认|变更|来源|原编号)\s*[::]\s*(.*)$/)
|
|
1070
1082
|
if (!f) continue
|
|
1071
1083
|
if (f[1] === '状态') cur.status = f[2].trim().split(/[\s((]/)[0] || null
|
|
1072
1084
|
else if (f[1] === '锚点') cur.anchor = f[2].trim()
|
|
1085
|
+
else if (f[1] === '变更') cur.change = f[2].trim() || null
|
|
1086
|
+
else if (f[1] === '来源') cur.change = cur.change || (f[2].trim() || null)
|
|
1087
|
+
else if (f[1] === '原编号') cur.originId = f[2].trim() || null
|
|
1073
1088
|
else cur.lastConfirmed = f[2].trim()
|
|
1074
1089
|
}
|
|
1075
1090
|
flush()
|
|
@@ -1148,8 +1163,8 @@ export async function runDecisionRules(opts) {
|
|
|
1148
1163
|
let implementedTotal = 0
|
|
1149
1164
|
const treeCache = new Map() // resolveCandidates 全树扫描复用(同 docs check 主链路口径)
|
|
1150
1165
|
|
|
1151
|
-
const emit = (id, domain, file, kind, extra, message) => {
|
|
1152
|
-
if (isDecisionExempt(failures, id, kind)) {
|
|
1166
|
+
const emit = (id, domain, file, kind, extra, message, originId = null) => {
|
|
1167
|
+
if (isDecisionExempt(failures, id, kind, domain, originId)) {
|
|
1153
1168
|
exempted.push({ key: `decisions.${id}.${kind}`, id, kind })
|
|
1154
1169
|
return
|
|
1155
1170
|
}
|
|
@@ -1169,10 +1184,10 @@ export async function runDecisionRules(opts) {
|
|
|
1169
1184
|
// 规则 1:锚点存在性(未记录 → 补录提示;路径不存在 → 失效提示)
|
|
1170
1185
|
if (!anchorPath) {
|
|
1171
1186
|
emit(e.id, domain, rel, 'anchor', { anchor: e.anchor || null },
|
|
1172
|
-
`「${e.id}」锚点未记录(${rel})——旧格式条目,补「锚点:src/…」后可机械校验(advisory
|
|
1187
|
+
`「${e.id}」锚点未记录(${rel})——旧格式条目,补「锚点:src/…」后可机械校验(advisory)`, e.originId)
|
|
1173
1188
|
} else if (resolveCandidates(projectRoot, anchorPath, treeCache).length === 0) {
|
|
1174
1189
|
emit(e.id, domain, rel, 'anchor', { anchor: e.anchor },
|
|
1175
|
-
`「${e.id}」锚点文件不存在:${anchorPath}(${rel})——决策落点已漂移,人工复核(advisory
|
|
1190
|
+
`「${e.id}」锚点文件不存在:${anchorPath}(${rel})——决策落点已漂移,人工复核(advisory)`, e.originId)
|
|
1176
1191
|
}
|
|
1177
1192
|
// 规则 2:behind 复核(「最近确认」后锚定模块源码前进数超阈值 → 决策待复核;
|
|
1178
1193
|
// 「最近确认」未记录/域无源码集(无 map 无锚点)→ 无法计算,静默跳过——advisory 不产噪声)
|
|
@@ -1185,7 +1200,7 @@ export async function runDecisionRules(opts) {
|
|
|
1185
1200
|
const { behind } = computeModuleBehind(domain, e.lastConfirmed, { projectRoot, srcPaths })
|
|
1186
1201
|
if (behind !== null && behind > threshold) {
|
|
1187
1202
|
emit(e.id, domain, rel, 'behind', { behind, threshold },
|
|
1188
|
-
`「${e.id}」决策待复核:锚定模块 ${domain} 在最近确认 ${e.lastConfirmed} 后源码已前进 ${behind} commit,超阈值 ${threshold}(${rel}
|
|
1203
|
+
`「${e.id}」决策待复核:锚定模块 ${domain} 在最近确认 ${e.lastConfirmed} 后源码已前进 ${behind} commit,超阈值 ${threshold}(${rel})——复核后更新「最近确认」`, e.originId)
|
|
1189
1204
|
}
|
|
1190
1205
|
}
|
|
1191
1206
|
}
|
package/src/docs-debt.js
CHANGED
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs'
|
|
14
14
|
import { join, relative } from 'node:path'
|
|
15
15
|
import { safeGit } from './git-helper.js'
|
|
16
|
-
import { parseModuleMapSimple } from './modules.js'
|
|
16
|
+
import { modulePathMatches, parseModuleMapSimple } from './modules.js'
|
|
17
17
|
import { runDocsCheck, readDocsCheckConfig, parseDecisionEntries, anchorFilePath } from './docs-check.js'
|
|
18
|
+
import { decisionChangeQualifier } from './knowledge-match.js'
|
|
18
19
|
|
|
19
20
|
/** safeGit 超时(大仓 git log 慢时降级该模块,不挂起 Wave 渲染) */
|
|
20
21
|
const GIT_TIMEOUT_MS = 5000
|
|
@@ -54,9 +55,9 @@ export function matchFilesToModules(changedFiles, moduleIndex, opts = {}) {
|
|
|
54
55
|
|
|
55
56
|
for (const f of changedFiles) {
|
|
56
57
|
let matched = null
|
|
57
|
-
// 一级:paths/core_files
|
|
58
|
+
// 一级:paths/core_files 前缀或字面量命中(含 glob 形态——modulePathMatches 统一入口)
|
|
58
59
|
for (const [id, ps] of modulePaths) {
|
|
59
|
-
if (ps.some(
|
|
60
|
+
if (ps.some((p) => modulePathMatches(f, p))) {
|
|
60
61
|
matched = id; break
|
|
61
62
|
}
|
|
62
63
|
}
|
|
@@ -323,7 +324,7 @@ export function computeDecisionTouches(changedFiles, knowledgeRoot) {
|
|
|
323
324
|
for (const c of changed) {
|
|
324
325
|
// 前缀匹配粒度 = 文件或其子路径(R-03:锚点是文件级非目录级,防同前缀不同文件误报)
|
|
325
326
|
if (c === anchorPath || c.startsWith(anchorPath + '/')) {
|
|
326
|
-
touches.push({ id: e.id, title: e.title, anchorFile: anchorPath, touchedFile: c, file })
|
|
327
|
+
touches.push({ id: e.id, title: e.title, anchorFile: anchorPath, touchedFile: c, file, change: e.change || '' })
|
|
327
328
|
}
|
|
328
329
|
}
|
|
329
330
|
}
|
|
@@ -344,7 +345,7 @@ export function renderDecisionTouchFacts(touches) {
|
|
|
344
345
|
const shown = touches.slice(0, DECISION_TOUCH_RENDER_LIMIT)
|
|
345
346
|
const lines = [`[decision-touch] 本次变更触碰 ${touches.length} 条决策锚点(改动可能使该决策需复核):`]
|
|
346
347
|
for (const t of shown) {
|
|
347
|
-
lines.push(` - ${t.id}(${t.title || '无标题'},锚点 ${t.anchorFile})← 触碰文件 ${t.touchedFile}(${t.file})`)
|
|
348
|
+
lines.push(` - ${t.id}(${t.title || '无标题'},锚点 ${t.anchorFile})← 触碰文件 ${t.touchedFile}(${t.file}|${decisionChangeQualifier(t.change)})`)
|
|
348
349
|
}
|
|
349
350
|
if (touches.length > DECISION_TOUCH_RENDER_LIMIT) {
|
|
350
351
|
lines.push(` …另有 ${touches.length - DECISION_TOUCH_RENDER_LIMIT} 条`)
|
|
@@ -946,7 +946,9 @@ function detectDecisionDrift(cwd) {
|
|
|
946
946
|
let count = 0
|
|
947
947
|
for (const f of readdirSync(decDir)) {
|
|
948
948
|
if (!f.endsWith('.md')) continue
|
|
949
|
-
|
|
949
|
+
// 双形态计数(2026-10-14-decision-global-id-epoch):局部 D-011@v1 与全局 D-<域>-042@v1;
|
|
950
|
+
// 旧正则 @\d+ 漏 @v 前缀恒零——随本单修正
|
|
951
|
+
count += (readFileSync(join(decDir, f), 'utf8').match(/^## D-(?:[A-Za-z0-9_\-\u4e00-\u9fff]+-)?\d+@v\d+/gm) || []).length
|
|
950
952
|
}
|
|
951
953
|
return { ...base, findings: [`decisions 库 ${count} 条(behind 精确复核在 verify evidence-auto 与 docs-check 决策规则族)`], pass: true, severity: CHECK_SEVERITY.PASSED }
|
|
952
954
|
} catch (e) {
|
package/src/flow-parity.js
CHANGED
|
@@ -19,18 +19,21 @@
|
|
|
19
19
|
* 命中模块变更索引机械回写(sidecar 优先、幂等)、start 注入面欠账事实行(提示前移)。
|
|
20
20
|
*/
|
|
21
21
|
import { existsSync, readFileSync, writeFileSync, readdirSync } from 'node:fs'
|
|
22
|
+
import { modulePathMatches } from './modules.js'
|
|
22
23
|
import { join, relative } from 'node:path'
|
|
23
24
|
import { createHash } from 'node:crypto'
|
|
24
25
|
import { execFileSync } from 'node:child_process'
|
|
25
26
|
import { splitOwnVsForeignDiffFiles, commitAttributionForChange, isForeignByCommit, parseChangeNamesFromSubject } from './foreign-declared.js'
|
|
26
27
|
import { collectModuleMaps, normalizeMapPath } from './module-resolve.js'
|
|
27
28
|
import { computeDocsDebt } from './docs-debt.js'
|
|
29
|
+
import { anchorBackfillCandidate } from './decision-distill.js'
|
|
28
30
|
import { writeAtomicSync } from './fs-atomic.js'
|
|
29
31
|
import { normalizePath } from './change-list.js'
|
|
30
32
|
import { readV2SectionAnswer } from './flow-review.js'
|
|
31
33
|
import { DESIGN_QUESTIONS } from './flow-draft.js'
|
|
32
34
|
import { pathMatches } from './change-list.js'
|
|
33
35
|
import { classifyToolScaffold, filterDeliverableFiles } from './worktree-apply.js'
|
|
36
|
+
import { deriveChangeTitle } from './quicklog.js'
|
|
34
37
|
|
|
35
38
|
/**
|
|
36
39
|
* 模块文档对账(advisory)+ 结构化落盘面(2026-09-27-thin-module-scope-persist)。
|
|
@@ -71,7 +74,7 @@ export function reconcileModuleDocs({ cwd, specBase, ownFiles, committedRaw }) {
|
|
|
71
74
|
.map((pp) => (map.prefix + normalizeMapPath(pp)).replace(/\/+$/, ''))
|
|
72
75
|
.filter(Boolean)
|
|
73
76
|
if (effs.length === 0) continue
|
|
74
|
-
const hitFiles = deliverables.filter((f) => effs.some((e) => f
|
|
77
|
+
const hitFiles = deliverables.filter((f) => effs.some((e) => modulePathMatches(f, e)))
|
|
75
78
|
if (hitFiles.length === 0) continue
|
|
76
79
|
hits++
|
|
77
80
|
const docRel = entry.doc ? `docs/${map.project}/${String(entry.doc).replace(/^\/+/, '')}` : null
|
|
@@ -92,7 +95,7 @@ export function reconcileModuleDocs({ cwd, specBase, ownFiles, committedRaw }) {
|
|
|
92
95
|
for (const pp of entry.paths || []) allPaths.push((map.prefix + normalizeMapPath(pp)).replace(/\/+$/, ''))
|
|
93
96
|
}
|
|
94
97
|
}
|
|
95
|
-
const isCovered = (f) => allPaths.some((pp) => pp && (f
|
|
98
|
+
const isCovered = (f) => allPaths.some((pp) => pp && modulePathMatches(f, pp))
|
|
96
99
|
const uncoveredMap = new Map()
|
|
97
100
|
for (const f of deliverables) {
|
|
98
101
|
if (isCovered(f)) continue
|
|
@@ -133,6 +136,19 @@ export function evaluateModuleGate({ moduleScope, ignore } = {}) {
|
|
|
133
136
|
return { block: maps.length > 0 && dirs.length > 0, dirs, maps }
|
|
134
137
|
}
|
|
135
138
|
|
|
139
|
+
/**
|
|
140
|
+
* ⑥-0 motiveFromInput:模块变更索引摘要提取(2026-10-15-title-label-skip)。与变更标题
|
|
141
|
+
* 同源走 deriveChangeTitle——此前 flow.js 裸取 --input 首行,「动机与背景:」段头行整行
|
|
142
|
+
* 入摘要(stages.changelog 实证)。段头行跳过/标签剥离随 deriveChangeTitle 单源;提取为
|
|
143
|
+
* 空回退变更名(可辨识不达意优于空串;appendChangeIndexEntries 侧另有 '轻量变更' 二级兜底)。
|
|
144
|
+
* @returns {string} 摘要(≤50 字)
|
|
145
|
+
*/
|
|
146
|
+
export function motiveFromInput(input, change = '') {
|
|
147
|
+
let derived = ''
|
|
148
|
+
try { derived = deriveChangeTitle(input) } catch { derived = '' }
|
|
149
|
+
return derived || String(change || '')
|
|
150
|
+
}
|
|
151
|
+
|
|
136
152
|
/**
|
|
137
153
|
* ⑥ appendChangeIndexEntries:命中模块变更索引机械回写(二档)。零语义判断——日期/变更名/
|
|
138
154
|
* 动机首行全是已知量,与 decision-distill 降 noAI 同性质。目标判定遵守 split-changelog 契约
|
|
@@ -302,6 +318,11 @@ export function harvestSlot4Decision({ changeDir, change }) {
|
|
|
302
318
|
answer = readV2SectionAnswer(dText, risksSec.heading, DESIGN_QUESTIONS)
|
|
303
319
|
}
|
|
304
320
|
if (!answer || /^不适用/.test(answer)) return { harvested: false, reason: '槽4 空/不适用' }
|
|
321
|
+
// 锚点自动回填(2026-10-15-decision-anchor-backfill 收割端):风险段作答文本恰含一个
|
|
322
|
+
// 路径 token → 写入 - 锚点:行(源 decisions.md 是真相源,蒸馏自然带上、人可改);
|
|
323
|
+
// 0/多候选不写(蒸馏端渲染时还会再兜底一次同款判定)。单一源 anchorBackfillCandidate。
|
|
324
|
+
const anchorCand = anchorBackfillCandidate(answer)
|
|
325
|
+
const anchorLine = anchorCand ? [`- 锚点:${anchorCand}`] : []
|
|
305
326
|
const text = [
|
|
306
327
|
'---',
|
|
307
328
|
`author: flow-machine-draft`,
|
|
@@ -309,9 +330,12 @@ export function harvestSlot4Decision({ changeDir, change }) {
|
|
|
309
330
|
'---',
|
|
310
331
|
`# 决策记录(Decisions)— ${change}`,
|
|
311
332
|
'',
|
|
312
|
-
|
|
333
|
+
// 标题自解释(2026-10-15-harvest-title-clarify):读者面是 decisions 文档与 knowledge,
|
|
334
|
+
// 机制出处(槽4 收割)由 frontmatter author: flow-machine-draft 承载,标题只说内容与性质。
|
|
335
|
+
`## D-001@v1: 设计期风险与放弃方案(底稿)`,
|
|
313
336
|
`- 类型:process`,
|
|
314
337
|
`- 状态:confirmed`,
|
|
338
|
+
...anchorLine,
|
|
315
339
|
// 类型/状态字段(2026-09-26-slot4-distill-fix):蒸馏链入选要 status∈{confirmed|accepted|rejected}
|
|
316
340
|
// 且 type∈七类白名单,字段解析只认 `- 字段:值` 列表行形态——此前收割条目零字段致永不入选,
|
|
317
341
|
// 「随蒸馏链进 knowledge」断链(thin-agent-tasks 教训留档归档而 knowledge 零落地的实证)。
|
|
@@ -326,7 +350,7 @@ export function harvestSlot4Decision({ changeDir, change }) {
|
|
|
326
350
|
return { harvested: true }
|
|
327
351
|
}
|
|
328
352
|
|
|
329
|
-
export default { reconcileModuleDocs, renderVerifyReceipt, harvestSlot4Decision, collectFreezeFiles, backfillGateSummary }
|
|
353
|
+
export default { reconcileModuleDocs, renderVerifyReceipt, harvestSlot4Decision, collectFreezeFiles, backfillGateSummary, motiveFromInput }
|
|
330
354
|
|
|
331
355
|
/**
|
|
332
356
|
* patch 冻结面的提交面过滤(2026-09-27-tool-debt-cleanup 抽出为纯函数供单测锁定):
|
package/src/flow.js
CHANGED
|
@@ -224,6 +224,23 @@ export async function extractRoutingInputPaths(cwd, specBase, input) {
|
|
|
224
224
|
})
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
/**
|
|
228
|
+
* 轻量道知识注入查询串瘦身(2026-10-15-thin-query-slim):--input 的「成功标准:」段是验收
|
|
229
|
+
* 判据语料(文件路径/测试名/断言词——对知识匹配是纯噪音,且路径词会过触路由关键词把命中面
|
|
230
|
+
* 撑爆:本仓实证整段 input 匹配出 76 条目、58 个 fr 域文件全命中)。查询只取动机段——
|
|
231
|
+
* 「成功标准:」标记前正文;标记缺席=无标准段,全文照旧(兼容旧形态);标记前为空白则退全 input。
|
|
232
|
+
* @param {string} change 变更名(查询首行,既有口径)
|
|
233
|
+
* @param {string} input --input 全文
|
|
234
|
+
* @returns {string} 瘦身后的查询串
|
|
235
|
+
*/
|
|
236
|
+
export function thinKnowledgeQuery(change, input) {
|
|
237
|
+
const raw = String(input || '')
|
|
238
|
+
const markerIdx = raw.search(/成功标准[::]/)
|
|
239
|
+
const motivation = markerIdx >= 0 ? raw.slice(0, markerIdx) : raw
|
|
240
|
+
const body = motivation.trim() || raw.trim()
|
|
241
|
+
return `${change || ''}\n${body}`
|
|
242
|
+
}
|
|
243
|
+
|
|
227
244
|
/**
|
|
228
245
|
* 轻量道知识注入段(2026-09-25-thin-fr-inject-parity):默认快道读取面对齐——
|
|
229
246
|
* {FR_INDEX_DIGEST}/{DECISION_HITS}/execute 知识命中报告此前只在 run 族装配,轻量道 fresh
|
|
@@ -295,10 +312,11 @@ export async function flowKnowledgeDigest({ specBase, change, changeDir, input,
|
|
|
295
312
|
}
|
|
296
313
|
}
|
|
297
314
|
const { matchKnowledgeHybrid } = await import('./knowledge-vector.js')
|
|
298
|
-
const { deathPathNote } = await import('./knowledge-match.js')
|
|
315
|
+
const { deathPathNote, decisionChangeQualifier } = await import('./knowledge-match.js')
|
|
299
316
|
// scope 遍历召回(2026-10-08-knowledge-graph,FR-04):touched=机器算的图查询键
|
|
300
317
|
// (filesOverride ∪ design 交付表——上方既有变量),词面三层零命中时沿强边保底防复潮
|
|
301
|
-
const
|
|
318
|
+
const kmQuery = thinKnowledgeQuery(change, input)
|
|
319
|
+
const km = await matchKnowledgeHybrid(knowledgeRoot, kmQuery, { cwd: dirname(specBase), scopeFiles: touched })
|
|
302
320
|
// 防复潮面 = 死路注记 ∪ 有主题重叠的 rejected(2026-09-28-knowledge-inject-ranking 起 rejected∪死路;
|
|
303
321
|
// 2026-09-28-knowledge-gate-denoise 收紧:score 零的 rejected 与查询零主题重叠——空标题条目靠状态
|
|
304
322
|
// 蹭进回显是行为实测三例的噪音源,不再注入)
|
|
@@ -306,20 +324,51 @@ export async function flowKnowledgeDigest({ specBase, change, changeDir, input,
|
|
|
306
324
|
summary.rejectedDecisions = rejected.length
|
|
307
325
|
summary.knowledgeEntries = km.matched ? (km.entries || []).length : 0
|
|
308
326
|
if (rejected.length > 0) {
|
|
309
|
-
lines.push(` ⚠️ 否决决策/死路注记(历史已否决或已记死路,防复潮——除非复潮条件满足勿重提,复潮须在本变更 decisions.md
|
|
327
|
+
lines.push(` ⚠️ 否决决策/死路注记(历史已否决或已记死路,防复潮——除非复潮条件满足勿重提,复潮须在本变更 decisions.md 记新版本;跨变更引用带变更名 变更名#D-xxx@vN):`)
|
|
310
328
|
for (const h of rejected.slice(0, 5)) {
|
|
311
329
|
const note = h.status === 'rejected' ? (h.reason || '(未记录)') : `死路——${deathPathNote(h.reason)}`
|
|
312
|
-
lines.push(` - ${h.id} ${h.title}(${h.file})${h.status === 'rejected' ? '否决理由' : '⚰️'}:${note}`)
|
|
330
|
+
lines.push(` - ${h.id} ${h.title}(${h.file}|${decisionChangeQualifier(h.change)})${h.status === 'rejected' ? '否决理由' : '⚰️'}:${note}`)
|
|
313
331
|
}
|
|
314
332
|
}
|
|
315
333
|
if (km.matched && (km.entries || []).length > 0) {
|
|
316
334
|
const src = km.entries.slice(0, 3).map((e) => (e.anchor ? `${e.file}#${e.anchor}` : e.file)).join(';')
|
|
317
335
|
lines.push(` 📚 知识命中(INDEX 关键词,按需 Read):${src}`)
|
|
318
336
|
}
|
|
337
|
+
// 遥测导线(2026-10-15-thin-inject-telemetry):轻量道注入此前不落 hits.jsonl——thin 常态化后
|
|
338
|
+
// inject 事件流断流(本仓 10-09 后归零实证),knowledge stats 从未命中判定被盲区污染。
|
|
339
|
+
// 分级字段(A2-lite):injectedFiles=正文实际注入(thin 面恒空——只出按需 Read 行)、
|
|
340
|
+
// mentionedFiles=显式展示给 agent 的锚点(📚 行 top-3)。既有 matchedFiles 口径不变
|
|
341
|
+
// (knowledge-stats 消费面零回归)。仅命中面非空才落盘;fail-soft(R-04)不阻断注入。
|
|
342
|
+
const thinMatchedFiles = km.matched ? (km.entries || []).map((e) => (e.anchor ? `${e.file}#${e.anchor}` : e.file)) : []
|
|
343
|
+
if (thinMatchedFiles.length > 0 || rejected.length > 0) {
|
|
344
|
+
try {
|
|
345
|
+
const { appendKnowledgeHit } = await import('./knowledge-hits.js')
|
|
346
|
+
appendKnowledgeHit(join(specBase, '.runtime'), {
|
|
347
|
+
type: 'inject',
|
|
348
|
+
change,
|
|
349
|
+
query: kmQuery,
|
|
350
|
+
matchedFiles: thinMatchedFiles,
|
|
351
|
+
injectedFiles: [],
|
|
352
|
+
mentionedFiles: thinMatchedFiles.slice(0, 3),
|
|
353
|
+
rejectedCount: rejected.length,
|
|
354
|
+
source: 'thin-start',
|
|
355
|
+
})
|
|
356
|
+
} catch { /* 遥测 fail-soft(R-04):hits 落盘失败不影响注入本体 */ }
|
|
357
|
+
}
|
|
319
358
|
if (domains.length === 0 && rejected.length === 0 && !km.matched) {
|
|
320
359
|
lines.length = 0
|
|
321
360
|
lines.push(`🧠 知识注入(轻量道读取面):语料未命中知识库(无触达域依据、无否决命中、INDEX 无对齐条目)——可自行查 knowledge/INDEX.md。`)
|
|
322
361
|
}
|
|
362
|
+
// 向量开关发现性提示(2026-10-15-vector-switch-discoverability):零命中=正需要语义召回
|
|
363
|
+
// 而未获得的时刻——平台已连接但开关未开(缺省关)时给一行可执行提示;有命中/未连接/
|
|
364
|
+
// 已开启零新增(vectorSwitchHint 单源,同款提示也挂 knowledge search hint 键)。
|
|
365
|
+
if (!km.matched && rejected.length === 0) {
|
|
366
|
+
try {
|
|
367
|
+
const { vectorSwitchHint } = await import('./knowledge-vector.js')
|
|
368
|
+
const hint = vectorSwitchHint(dirname(specBase))
|
|
369
|
+
if (hint) lines.push(` ${hint}`)
|
|
370
|
+
} catch { /* 提示 fail-open */ }
|
|
371
|
+
}
|
|
323
372
|
// 模块卡欠账提示前移(2026-10-12-thin-module-gate-and-feed 三档):触碰命中模块 behind>0
|
|
324
373
|
// 才出事实行——动手前看到「你要动的卡已落后」比收口时警告有效;computeDocsDebt(厚道
|
|
325
374
|
// execute {DOCS_DEBT})同源,空债/无图零输出,异常走外层 catch fail-open 不阻断 start。
|
|
@@ -1089,7 +1138,7 @@ export async function cmdFlowStart({ change, input, title: titleFlag = null, thi
|
|
|
1089
1138
|
` ②③断点 agent 自主跳过。`,
|
|
1090
1139
|
` 随时可查进度:sillyspec flow status --change ${change}`,
|
|
1091
1140
|
...(detectUiTouch(input)
|
|
1092
|
-
? [...buildUiGuidanceLines(), '']
|
|
1141
|
+
? [...buildUiGuidanceLines(specBase), '']
|
|
1093
1142
|
: []),
|
|
1094
1143
|
``,
|
|
1095
1144
|
`【协议调用 2/2(干完后)】sillyspec flow done --change ${change}`,
|
|
@@ -1425,11 +1474,22 @@ export async function cmdFlowDone({ change, cwd, specBase, runtimeRootOpt = null
|
|
|
1425
1474
|
// 采样去重(2026-10-07-thin-tasks-v3):task tick 直写的精确事件(source:'task-tick')为
|
|
1426
1475
|
// 权威口径,watcher 3s 采样合并跳被去重——CLI 快速连续 tick 不再误伤;观测旁路缺席
|
|
1427
1476
|
// (无流/读失败)→ 跳过(fail-open,watcher 非真相源);--allow-batch-tick 显式旁路留痕。
|
|
1477
|
+
// 侧车突发回退(2026-10-14-tick-burst-sidecar-gate):CLI 收口端快连点对事件流不可见(单格
|
|
1478
|
+
// 精确跳),空结果时回退 task-ticks.jsonl 毫秒时戳判突发——CLI 与直接 Edit 两条路径同判。
|
|
1428
1479
|
try {
|
|
1429
1480
|
const { readWatcherEvents } = await import('./watcher.js')
|
|
1430
|
-
const { detectBatchCheckCadence, resolveBatchTickAction } = await import('./sentinel-assertions.js')
|
|
1481
|
+
const { detectBatchCheckCadence, detectTickBurst, resolveBatchTickAction } = await import('./sentinel-assertions.js')
|
|
1431
1482
|
const stream = readWatcherEvents({ runtimeRoot, change })
|
|
1432
|
-
|
|
1483
|
+
let batch = stream.exists ? detectBatchCheckCadence(stream.events) : null
|
|
1484
|
+
if (!batch) {
|
|
1485
|
+
// 侧车突发回退(2026-10-14-tick-burst-sidecar-gate):事件流空结果时读 change 目录
|
|
1486
|
+
// task-ticks.jsonl(CLI tick 毫秒时戳事实源,随归档进 git、不受观测盲窗影响)判快连点;
|
|
1487
|
+
// 事件流结果优先不双重报告;侧车缺失/坏文件按无证据静默(fail-open)。
|
|
1488
|
+
try {
|
|
1489
|
+
const { parseTickLog } = await import('./timeline.js')
|
|
1490
|
+
batch = detectTickBurst(parseTickLog(readFileSync(join(changeDir, 'task-ticks.jsonl'), 'utf8')))
|
|
1491
|
+
} catch { /* 侧车缺失/不可读 → 无侧车证据(fail-open) */ }
|
|
1492
|
+
}
|
|
1433
1493
|
if (batch) {
|
|
1434
1494
|
// 代勾计数取「本拍实时代勾 ∨ 持久化代勾」较大者(二轮 P2-B:重入漂移防护);决策纯函数
|
|
1435
1495
|
// 单源(resolveBatchTickAction)四态与豁免优先序见其 JSDoc——代勾须解释整跳才降级
|
|
@@ -1454,7 +1514,8 @@ export async function cmdFlowDone({ change, cwd, specBase, runtimeRootOpt = null
|
|
|
1454
1514
|
console.warn(`⚠️ 勾选节奏:单拍多格勾选(${batch.detail},${at})——${why},降级提醒不拒`)
|
|
1455
1515
|
}
|
|
1456
1516
|
} else if (v.action === 'reject') {
|
|
1457
|
-
|
|
1517
|
+
const _via = batch.source === 'tick-burst' ? '侧车时戳观测到' : '事件流观测到'
|
|
1518
|
+
console.error(`🚫 单拍勾选拒收:${_via}一拍勾选 ${batch.detail}(${at})——勾选纪律要求逐格(做一件→勾一格→下一个),勾选任务 ${_sentinelNonMirrorTasks} 格`)
|
|
1458
1519
|
console.error(' 出口:①节奏违例已既成——认知后重跑带 --allow-batch-tick 显式留痕过门(平台时间线可见旁路);②疑观测误判→sillyspec doctor 核对事件流')
|
|
1459
1520
|
appendTelemetry({ sentinel: 'batch-tick', jump: batch.detail })
|
|
1460
1521
|
reportMidFail('ledger')
|
|
@@ -2272,8 +2333,10 @@ export async function cmdFlowDone({ change, cwd, specBase, runtimeRootOpt = null
|
|
|
2272
2333
|
// 回读块保证在场(patch 本轮算出或 change-patch.json 回读)。
|
|
2273
2334
|
try {
|
|
2274
2335
|
if (moduleScope && Array.isArray(moduleScope.modules) && moduleScope.modules.length > 0) {
|
|
2275
|
-
const { appendChangeIndexEntries } = await import('./flow-parity.js')
|
|
2276
|
-
|
|
2336
|
+
const { appendChangeIndexEntries, motiveFromInput } = await import('./flow-parity.js')
|
|
2337
|
+
// 摘要与变更标题同源(2026-10-15-title-label-skip):段头行(动机与背景:等)跳过取次行,
|
|
2338
|
+
// 空提取回退变更名——此前裸取首行致「动机与背景:」整行入摘要(stages.changelog 实证)。
|
|
2339
|
+
const motive = motiveFromInput(st.input, change)
|
|
2277
2340
|
const wr = appendChangeIndexEntries({ specBase, modules: moduleScope.modules, change, summary: motive, date: new Date().toISOString().slice(0, 10) })
|
|
2278
2341
|
changelogAppended = wr.appended
|
|
2279
2342
|
if (wr.appended.length > 0) console.log(`📖 模块变更索引回写:${wr.appended.length} 个目标(${wr.appended.join('、')})——sidecar 优先/卡内节次之,随归档窄化入暂存`)
|
|
@@ -2600,7 +2663,7 @@ export async function cmdFlow(args, cwd, specDir = null, opts = {}) {
|
|
|
2600
2663
|
console.error(`❌ ${e.message}`)
|
|
2601
2664
|
// 可照抄实例(2026-10-05-input-teach-copyable):引号内换行合法,行级 trim 容忍缩进
|
|
2602
2665
|
console.error(' 重试(--input 引号内换行合法,可照抄):')
|
|
2603
|
-
console.error(' sillyspec flow start --change <YYYY-MM-DD-简短描述> --input "
|
|
2666
|
+
console.error(' sillyspec flow start --change <YYYY-MM-DD-简短描述> --input "<一句中文概括(首行即变更标题);随后动机与背景')
|
|
2604
2667
|
console.error('')
|
|
2605
2668
|
console.error(' 成功标准:')
|
|
2606
2669
|
console.error(' - <可验证标准>"')
|
|
@@ -2829,9 +2892,9 @@ export async function cmdFlow(args, cwd, specDir = null, opts = {}) {
|
|
|
2829
2892
|
console.log(`✅ flow amend-draft 留痕重锚:${r.reanchored.join('、')}——ledger amendment 审计在案;editRatio=${maxRatio}(段级最大;基准=首版原文,AGENT 槽不计入)`)
|
|
2830
2893
|
return r
|
|
2831
2894
|
}
|
|
2832
|
-
console.error('用法: sillyspec flow start --change <名> --input "
|
|
2895
|
+
console.error('用法: sillyspec flow start --change <名> --input "<一句中文概括(首行即变更标题)+动机与背景详述+独立一行『成功标准:』+每行一条『- 可验证标准』>" [--thick|--with-tasks] [--no-prestage(hindsight 标记在场时声明需求已含决策,留痕放行)] | sillyspec flow done --change <名> | sillyspec flow status --change <名> [--json] | sillyspec flow diff --change <名> [--json](只读文件统计)')
|
|
2833
2896
|
console.error(' --input 可照抄形态(引号内换行合法):')
|
|
2834
|
-
console.error(' sillyspec flow start --change <名> --input "
|
|
2897
|
+
console.error(' sillyspec flow start --change <名> --input "<一句中文概括(首行即变更标题);随后动机与背景')
|
|
2835
2898
|
console.error('')
|
|
2836
2899
|
console.error(' 成功标准:')
|
|
2837
2900
|
console.error(' - <可验证标准>"')
|