sillyspec 3.32.9 → 3.32.10
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 +2 -2
- package/.claude/skills/sillyspec-execute/SKILL.md +2 -2
- package/.claude/skills/sillyspec-flow/SKILL.md +1 -1
- package/.claude/skills/sillyspec-plan/SKILL.md +2 -2
- package/AGENTS.md +1 -1
- package/assets/command-cards/flow.md +1 -1
- package/package.json +1 -1
- package/src/commit-suggest.js +45 -4
- package/src/config-schema.js +14 -0
- package/src/decision-distill.js +77 -21
- package/src/decision-renumber.js +157 -0
- package/src/docs-check.js +30 -15
- package/src/docs-debt.js +3 -2
- package/src/doctor-diagnostics.js +3 -1
- package/src/flow-parity.js +18 -2
- package/src/flow.js +75 -12
- package/src/fr-index.js +15 -1
- package/src/hooks/worktree-guard.js +3 -3
- package/src/index.js +7 -5
- package/src/knowledge-digest.js +23 -6
- package/src/knowledge-graph.js +92 -14
- 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/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 +93 -89
- package/src/run/prompt.js +3 -3
- package/src/run/stage.js +1 -1
- package/src/sentinel-assertions.js +43 -1
- package/src/stage-contract.js +26 -3
- package/src/stage-review.js +44 -11
- package/src/stage-templates.js +2 -0
- package/src/stages/brainstorm.js +5 -1
- package/src/stages/knowledge.js +56 -5
- package/src/sync.js +28 -2
- package/src/task-tick.js +12 -0
- package/src/timeline.js +60 -19
- package/src/verify-postcheck.js +49 -1
- package/src/verify-probes.js +61 -18
- package/templates/agents-instruction.md +1 -1
package/src/verify-probes.js
CHANGED
|
@@ -1952,7 +1952,7 @@ function prefillMatrixCells(item, testFiles, hint) {
|
|
|
1952
1952
|
* @param {{ applicable: boolean, tasks: Array<{task: string, acceptance: string[], testFiles: string[], hints: Record<number, {terms: string[], files: string[], anchors?: Array<{term: string, file: string, line: number}>}>}> }} p7
|
|
1953
1953
|
* @returns {string[]} 行数组(含段标题;调用方自理前后空行)
|
|
1954
1954
|
*/
|
|
1955
|
-
function renderProbe7Lines(p7) {
|
|
1955
|
+
export function renderProbe7Lines(p7) {
|
|
1956
1956
|
const L = [PROBE7_HEADING]
|
|
1957
1957
|
if (!p7 || !p7.applicable) {
|
|
1958
1958
|
L.push('- 不适用(无 TaskCard)')
|
|
@@ -1986,8 +1986,12 @@ function renderProbe7Lines(p7) {
|
|
|
1986
1986
|
L.push('| acceptance 条目 | 归属测试文件 | 关键词命中(提示,命中≠判定) | 判定 | 证据 |')
|
|
1987
1987
|
L.push('|---|---|---|---|---|')
|
|
1988
1988
|
t.acceptance.forEach((item, i) => {
|
|
1989
|
+
// 展示帽(坑 probe-noise-and-commit-suggest-quality 坑A,2026-10-15 消费仓实证:宽目录
|
|
1990
|
+
// allowed_paths 递归把 82 个测试文件全量塞进单格(单行数千字符 ×3 行),信号全淹——照
|
|
1991
|
+
// 探针 3 既有 slice 截断先例收敛为前 5 + 「共 N 个」折叠;路径经 mdEscapeCell 防竖线破表)
|
|
1989
1992
|
const attribCell = (t.testFiles && t.testFiles.length > 0)
|
|
1990
|
-
? t.testFiles.map(f => `\`${f}\``).join('<br>')
|
|
1993
|
+
? t.testFiles.slice(0, 5).map(f => `\`${mdEscapeCell(f, 120)}\``).join('<br>')
|
|
1994
|
+
+ (t.testFiles.length > 5 ? `<br>…共 ${t.testFiles.length} 个(目录级候选全量在 facts/trace,归属按关键词命中与证据锚点收窄)` : '')
|
|
1991
1995
|
: '无归属测试——判定大概率 uncovered'
|
|
1992
1996
|
const h = (t.hints && t.hints[i]) || null
|
|
1993
1997
|
const hintCell = h
|
|
@@ -2251,7 +2255,9 @@ export function runVerifyProbes({ cwd, changeName, specDir = null }) {
|
|
|
2251
2255
|
if (t && isProbe7TestPath(t) && !fromDependents.includes(t)) fromDependents.push(t)
|
|
2252
2256
|
}
|
|
2253
2257
|
}
|
|
2254
|
-
|
|
2258
|
+
// 收集帽(坑 probe-noise-and-commit-suggest-quality 坑A):目录级 allowed_paths 递归可卷
|
|
2259
|
+
// 入整仓测试树(实证 82 个)——去重后截前 20(展示层另有 5 个硬帽;hints/trace 消费同集)
|
|
2260
|
+
const testFiles = [...new Set([...fromAllowed, ...fromReview, ...fromDependents])].slice(0, 20)
|
|
2255
2261
|
// 无归属卡注入 FR 关联回归既有用例候选(task-02;有归属卡 existingTests 恒空——零噪音)
|
|
2256
2262
|
const existingTests = []
|
|
2257
2263
|
if (frLinkedExisting && testFiles.length === 0) {
|
|
@@ -2259,7 +2265,7 @@ export function runVerifyProbes({ cwd, changeName, specDir = null }) {
|
|
|
2259
2265
|
if (isProbe7TestPath(f) && !existingTests.includes(f)) existingTests.push(f)
|
|
2260
2266
|
}
|
|
2261
2267
|
}
|
|
2262
|
-
const finalTestFiles = existingTests.length > 0 ? [...new Set([...testFiles, ...existingTests])] : testFiles
|
|
2268
|
+
const finalTestFiles = existingTests.length > 0 ? [...new Set([...testFiles, ...existingTests])].slice(0, 20) : testFiles
|
|
2263
2269
|
probe7.tasks.push({
|
|
2264
2270
|
task: card.task,
|
|
2265
2271
|
acceptance: card.acceptance,
|
|
@@ -2663,6 +2669,33 @@ const API_MATRIX_HEADING = '## 接口验证覆盖矩阵'
|
|
|
2663
2669
|
// 权限矩阵段有对应行或显式豁免(「无权限约束」)——warning 计算归 task-05,此处只定集合。
|
|
2664
2670
|
const API_MATRIX_WRITE_METHODS = new Set(['POST', 'PUT', 'DELETE', 'PATCH'])
|
|
2665
2671
|
|
|
2672
|
+
/**
|
|
2673
|
+
* design.md 接口段实况解析面(gate/derive 预检注入用,2026-10-14-gate-verify-apiface-preflight):
|
|
2674
|
+
* 与 backfillFactsFromMdAndTests 落盘的 facts.apiFace 同形同源(同一 parseDesignApiTable、同一
|
|
2675
|
+
* writeEndpoints 过滤——单一产物源,禁第二套解析文法),零落盘。背景:facts.apiFace 唯一生产者
|
|
2676
|
+
* 是 run verify --done 收尾前置 backfill(run/gates.js),gate 路径不回填且 --init 刻意不落键 →
|
|
2677
|
+
* 预检 parseSet 空/旧 → design接口表# 锚点恒空指(平台 6 轮 gate 实证)。gate 侧经本 helper
|
|
2678
|
+
* 实况解析后经 context.apiFaceLive 注入 validator(stage-contract 零 import 纪律:注入不做
|
|
2679
|
+
* 函数调用),「预检过=必过」对锚点核对成立;design.md 缺失/不可读返回 null(调用方不注入,
|
|
2680
|
+
* 退回 facts 口径——2026-10-14 评审 P1:空面注入会覆写有效 facts.apiFace 反致误阻断)、解析
|
|
2681
|
+
* 异常由调用方 fail-soft 承接(不注入退回 facts)。
|
|
2682
|
+
* @param {string} designPath changeDir/design.md 路径(缺失/不可读 → null=不注入信号)
|
|
2683
|
+
* @returns {{ endpoints: Array<{method: string, path: string, rowIdx: number}>, declared: number|null, writeEndpoints: Array }|null}
|
|
2684
|
+
*/
|
|
2685
|
+
export function computeApiFaceFromDesign(designPath) {
|
|
2686
|
+
let designText = null
|
|
2687
|
+
if (existsSync(designPath)) {
|
|
2688
|
+
try { designText = readFileSync(designPath, 'utf8') } catch { designText = null }
|
|
2689
|
+
}
|
|
2690
|
+
if (designText === null) return null
|
|
2691
|
+
const face = parseDesignApiTable(designText)
|
|
2692
|
+
return {
|
|
2693
|
+
endpoints: face.endpoints,
|
|
2694
|
+
declared: face.declared,
|
|
2695
|
+
writeEndpoints: face.endpoints.filter(e => e && API_MATRIX_WRITE_METHODS.has(e.method)),
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2666
2699
|
/**
|
|
2667
2700
|
* 渲染「## 接口验证覆盖矩阵」段(task-04)。
|
|
2668
2701
|
* 三态:解析有端点 → 逐端点预填行(判定列 `<待填:五选一>` 占位,同 probe7 预填口径供 agent
|
|
@@ -2731,7 +2764,12 @@ export function buildVerifyFacts(result, { changeName, now } = {}) {
|
|
|
2731
2764
|
// v2(2026-09-08-ir-verify-facts):probes 机器段原样;conclusion/tests/requiredEvidence/
|
|
2732
2765
|
// runtimeEvidence/factsConsistency 五段是 slot-backfill/实测回填段(D-001@v2),--init 快照
|
|
2733
2766
|
// 不落键(writeVerifyFacts 分段合并时保留既有固化段),由 backfillFactsFromMdAndTests 填。
|
|
2734
|
-
|
|
2767
|
+
// apiFace 例外(坑 verify-close-gate-staggered-exposure 建议③连带,2026-10-15 消费仓实证):
|
|
2768
|
+
// --init 即落键——runVerifyProbes 的 apiFace 与 --done backfill 同源同形(computeApiFaceFromDesign),
|
|
2769
|
+
// 原「刻意不落」使「重跑 --init 刷新 facts」救不了空指预检(报错文案也没提这一点),且空指
|
|
2770
|
+
// 诊断方向被误导去改 design 文档。键只在值在场时出现(undefined 值键会在 writeVerifyFacts
|
|
2771
|
+
// 合并 spread 时覆盖 prev 固化段)。
|
|
2772
|
+
const factsSkeleton = {
|
|
2735
2773
|
schemaVersion: 2,
|
|
2736
2774
|
change: changeName,
|
|
2737
2775
|
generatedAt: now || new Date().toISOString(),
|
|
@@ -2796,6 +2834,8 @@ export function buildVerifyFacts(result, { changeName, now } = {}) {
|
|
|
2796
2834
|
},
|
|
2797
2835
|
},
|
|
2798
2836
|
}
|
|
2837
|
+
if (result && result.apiFace && typeof result.apiFace === 'object') factsSkeleton.apiFace = result.apiFace
|
|
2838
|
+
return factsSkeleton
|
|
2799
2839
|
}
|
|
2800
2840
|
|
|
2801
2841
|
/**
|
|
@@ -2832,7 +2872,16 @@ export function writeVerifyFacts(changeDir, result, changeName, opts = {}) {
|
|
|
2832
2872
|
try {
|
|
2833
2873
|
const prev = JSON.parse(readFileSync(factsPath, 'utf8'))
|
|
2834
2874
|
if (prev && prev.schemaVersion === FACTS_SCHEMA_VERSION) {
|
|
2875
|
+
// backfill 固化段保留(坑 verify-close-gate-staggered-exposure,2026-10-15):旧白名单只保
|
|
2876
|
+
// 五段,re-init 会把 --done 回填的 apiFace/consumerHints/handover/dbScriptDeclarations 等
|
|
2877
|
+
// 整个抹掉(--init 后预检又空指)。此处先铺 prev 固化段再被 fresh 覆盖:apiFace 由 fresh
|
|
2878
|
+
// 侧重算(同源 computeApiFaceFromDesign,值应等价),fresh 未带的键原样保留。
|
|
2879
|
+
const preservedBackfill = {}
|
|
2880
|
+
for (const k of ['apiFace', 'consumerHints', 'handover', 'dbScriptDeclarations', 'matrixPartialRows', 'smokeRan']) {
|
|
2881
|
+
if (prev[k] !== undefined) preservedBackfill[k] = prev[k]
|
|
2882
|
+
}
|
|
2835
2883
|
facts = {
|
|
2884
|
+
...preservedBackfill,
|
|
2836
2885
|
...fresh,
|
|
2837
2886
|
conclusion: prev.conclusion,
|
|
2838
2887
|
tests: prev.tests,
|
|
@@ -3140,20 +3189,14 @@ export function backfillFactsFromMdAndTests(factsPath, { verifyMd, testCheckResu
|
|
|
3140
3189
|
// validator 判 N=0 走零行为注记);与 runVerifyProbes 同一解析器(单一产物源)。
|
|
3141
3190
|
{
|
|
3142
3191
|
const changeDir = dirname(factsPath)
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
const face = parseDesignApiTable(designText || '')
|
|
3149
|
-
facts.apiFace = {
|
|
3150
|
-
endpoints: face.endpoints,
|
|
3151
|
-
declared: face.declared,
|
|
3152
|
-
writeEndpoints: face.endpoints.filter(e => e && API_MATRIX_WRITE_METHODS.has(e.method)),
|
|
3153
|
-
}
|
|
3192
|
+
// backfill 契约=恒落对象面(缺失/不可读 → 空面,基线口径不变):helper 的 null 是 gate
|
|
3193
|
+
// 注入通道的「不注入」信号,非 backfill 落盘形态(2026-10-14 评审 P3——null 落盘会让
|
|
3194
|
+
// 「design 蒸发×矩阵段缺失×严格档」窄角从基线 error 降 warning,重构不得夹带行为变化)。
|
|
3195
|
+
facts.apiFace = computeApiFaceFromDesign(join(changeDir, 'design.md'))
|
|
3196
|
+
|| { endpoints: [], declared: null, writeEndpoints: [] }
|
|
3154
3197
|
let designPaths = []
|
|
3155
|
-
if (
|
|
3156
|
-
try { designPaths = parseFileChangeListDetailed(
|
|
3198
|
+
if (existsSync(join(changeDir, 'design.md'))) {
|
|
3199
|
+
try { designPaths = parseFileChangeListDetailed(join(changeDir, 'design.md')).map(e => String(e.path)) } catch { designPaths = [] }
|
|
3157
3200
|
}
|
|
3158
3201
|
facts.consumerHints = classifyConsumerHints(designPaths)
|
|
3159
3202
|
}
|