sillyspec 3.28.13 → 3.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/blast-surface.js +168 -0
- package/src/ceremony-tier.js +359 -0
- package/src/change-risk-profile.js +72 -199
- package/src/config-schema.js +22 -1
- package/src/decisions-io.js +32 -0
- package/src/design-facts.js +1 -0
- package/src/doctor-diagnostics.js +119 -1
- package/src/index.js +92 -5
- package/src/knowledge-stats.js +25 -0
- package/src/modules.js +28 -0
- package/src/prefill.js +491 -0
- package/src/probe7-anchor-check.js +8 -6
- package/src/quick-gate-profile.js +8 -4
- package/src/quick-session-owner.js +63 -0
- package/src/redlines.js +195 -0
- package/src/review-dispatch.js +473 -1
- package/src/review-material-pack.js +265 -0
- package/src/review-tier.js +185 -41
- package/src/run/archive-distill.js +6 -0
- package/src/run/command.js +158 -87
- package/src/run/complete-handlers.js +82 -1
- package/src/run/complete.js +90 -3
- package/src/run/gate-snapshot.js +30 -1
- package/src/run/gates.js +301 -19
- package/src/run/prompt.js +423 -21
- package/src/run/quick-audit.js +12 -2
- package/src/run/shared.js +36 -0
- package/src/run/verify-quality-scan.js +15 -7
- package/src/scope-audit.js +8 -1
- package/src/span-risk-surface.js +127 -0
- package/src/stage-contract-spec.js +2 -2
- package/src/stage-contract.js +102 -81
- package/src/stage-review.js +26 -2
- package/src/stages/brainstorm-auto.js +1 -1
- package/src/stages/brainstorm.js +35 -19
- package/src/stages/execute.js +11 -3
- package/src/stages/plan.js +45 -24
- package/src/stages/scan.js +1 -1
- package/src/stages/verify.js +7 -7
- package/src/taskcard.js +3 -0
- package/src/verify-postcheck.js +375 -20
- package/src/verify-probes.js +194 -10
- package/src/worktree-apply.js +1 -1
- package/src/worktree.js +1 -1
- package/templates/prompts/taskcard-rules.md +1 -0
- package/templates/prompts/verify-probes.md +1 -1
package/src/verify-probes.js
CHANGED
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
* 探针7 验收×测试覆盖矩阵:task 卡 acceptance 自解析(jsYaml,string/array 双形态)+ 双源结构
|
|
13
13
|
* 归属(allowed_paths 测试模式 ∪ execute-runs review.json changedFiles test/ 前缀)+
|
|
14
14
|
* 关键词命中提示(命中≠判定,不参与门禁;2026-09-14-acceptance-test-matrix)
|
|
15
|
+
* 探针10 预填注清零(error 门):design.md + tasks/task-*.md 白名单槽宿主文件集逐个
|
|
16
|
+
* hasUnconfirmedPrefill(注在场=未确认)——--init 预填进探针段 + gates.js verify 收尾
|
|
17
|
+
* 复跑同一实现阻断(门禁梯度 error 档;2026-09-18-artifact-prefill task-03)
|
|
15
18
|
* 探针2(关键词提取半语义)/探针3.4 集成盲区/3.5 断言抽查/探针4(决策追踪语义)留 agent。
|
|
16
19
|
*
|
|
17
20
|
* verify-result.md 骨架:七章节固定结构 + 探针结果机械预填 + 其余章节 <!--TODO--> 占位。 probe1-noqa
|
|
@@ -22,9 +25,10 @@
|
|
|
22
25
|
* 供事后独立复跑审计;重复 --init 覆盖为最近一次 init 快照)。
|
|
23
26
|
*/
|
|
24
27
|
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from 'fs'
|
|
25
|
-
import { join, dirname, basename, resolve, isAbsolute } from 'path'
|
|
28
|
+
import { join, dirname, basename, resolve, isAbsolute, relative } from 'path'
|
|
26
29
|
import jsYaml from 'js-yaml'
|
|
27
30
|
import { gitQuiet, unquoteGitPath } from './git-helper.js'
|
|
31
|
+
import { hasUnconfirmedPrefill } from './prefill.js'
|
|
28
32
|
import {
|
|
29
33
|
FACTS_SCHEMA_VERSION, EVIDENCE_SLOT_HEADING, RECEIPT_SLOT_HEADING, parseEvidenceSlots,
|
|
30
34
|
} from './verify-facts-schema.js'
|
|
@@ -35,6 +39,9 @@ import { verifyApiParity, _readWorktreeMeta } from './contract-matrix.js'
|
|
|
35
39
|
import { RECEIPT_SOURCE_CROSS_LAYER_RE as RECEIPT_CROSS_LAYER_RE, RECEIPT_SOURCE_UNIT_RE as RECEIPT_UNIT_RE, CLI_SMOKE_SOURCE_MARK as SMOKE_RECEIPT_SOURCE_MARK } from './change-risk-profile.js'
|
|
36
40
|
import { splitOwnVsForeignDiffFiles } from './foreign-declared.js'
|
|
37
41
|
import { resolveSpecDir, resolveRuntimeRoot, detectWorktreeSpecDrift } from './run/shared.js'
|
|
42
|
+
// 探针 11(2026-09-20-redline-machine-check):红线机检评估器——纯函数模块,依赖
|
|
43
|
+
// (fs/js-yaml)已在模块图内,静态导入零增量开销;评估器异常由探针 fail-soft 捕获。
|
|
44
|
+
import { parseRedlines, evaluateRedlines } from './redlines.js'
|
|
38
45
|
|
|
39
46
|
// 探针1 未实现标记匹配(坑 probe1-literal-false-positive,2026-09-15 复盘实证:16 命中全
|
|
40
47
|
// 字面误报——TODO_FLAG_TODO 业务常量、「XXX完成处置」中文占位模板)。口径分三级:
|
|
@@ -1642,6 +1649,58 @@ export function runProbe9GuardConsistency({ specBase, cwd, wtRoot = null, change
|
|
|
1642
1649
|
}
|
|
1643
1650
|
|
|
1644
1651
|
|
|
1652
|
+
// ── 探针 10(预填注清零,2026-09-18-artifact-prefill task-03 / FR-03 / D-003@v1 门禁梯度·error 档)──
|
|
1653
|
+
// 背景:三槽预填(src/prefill.js,task-01)的确认动作是「删注」——注在场 = 白名单槽未确认
|
|
1654
|
+
// (预填≠结论)。brainstorm/plan --done 是 advisory(忘删=提示,gates.js 轻档),本探针是梯度
|
|
1655
|
+
// 收口的 error 档:--init 预填进 verify-result.md 探针段(agent 在 verify 阶段看到 ❌ 面),
|
|
1656
|
+
// gates.js verify 收尾复跑本函数同源阻断(verify 完成是 archive 前置——「归档前注清零」的落点)。
|
|
1657
|
+
// 检测复用 task-01 的 hasUnconfirmedPrefill(PREFILL_NOTE 字面包含即未确认);已知误报面:
|
|
1658
|
+
// 散文引用注字面量(如设计文档描述注协议本身)会命中——不追求语义区分,❌ 面向 agent 核对
|
|
1659
|
+
// (真未确认 → 删注;纯散文 → 改写措辞),gate 侧文案同口径。
|
|
1660
|
+
const PROBE10_HEADING = '#### 探针 10:预填注清零(error 门)'
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* 探针 10 主体:changeDir 的 design.md + tasks/task-*.md(预填白名单槽宿主文件集——与
|
|
1664
|
+
* gates.js advisory 门 / verify 收尾 error 门同一检测面)逐文件 hasUnconfirmedPrefill。
|
|
1665
|
+
* - applicable:至少一个在检文件存在(变更目录缺失 / 全缺席 → false + 注记,纯骨架零红门禁);
|
|
1666
|
+
* - unclearedFiles:含未删预填注的文件(changeDir 相对、正斜杠,design.md 在前、task 卡按名序);
|
|
1667
|
+
* - 只读不写、无 git 依赖(与 probe8/9 的 design 清单解析面独立——design.md 缺失仍可查
|
|
1668
|
+
* task 卡,反之亦然)。
|
|
1669
|
+
* @param {{ specBase: string, changeName: string }} args
|
|
1670
|
+
* @returns {{applicable: boolean, checkedFiles: number, unclearedFiles: string[], notes: string[]}}
|
|
1671
|
+
*/
|
|
1672
|
+
export function runProbe10PrefillNoteClearance({ specBase, changeName }) {
|
|
1673
|
+
const out = { applicable: false, checkedFiles: 0, unclearedFiles: [], notes: [] }
|
|
1674
|
+
if (!specBase || !changeName) {
|
|
1675
|
+
out.notes.push('入参缺失(specBase/changeName)——检测面空')
|
|
1676
|
+
return out
|
|
1677
|
+
}
|
|
1678
|
+
const changeDir = join(specBase, 'changes', changeName)
|
|
1679
|
+
if (!existsSync(changeDir)) {
|
|
1680
|
+
out.notes.push(`变更目录不存在(${changeDir})`)
|
|
1681
|
+
return out
|
|
1682
|
+
}
|
|
1683
|
+
const candidates = [join(changeDir, 'design.md')]
|
|
1684
|
+
try {
|
|
1685
|
+
const tasksDir = join(changeDir, 'tasks')
|
|
1686
|
+
if (existsSync(tasksDir)) {
|
|
1687
|
+
candidates.push(...readdirSync(tasksDir).filter(n => /^task-.+\.md$/.test(n)).sort().map(n => join(tasksDir, n)))
|
|
1688
|
+
}
|
|
1689
|
+
} catch { /* tasks 目录不可读 → 只检 design.md */ }
|
|
1690
|
+
for (const p of candidates) {
|
|
1691
|
+
if (!existsSync(p)) continue
|
|
1692
|
+
out.checkedFiles++
|
|
1693
|
+
if (hasUnconfirmedPrefill(p)) out.unclearedFiles.push(relative(changeDir, p).split('\\').join('/'))
|
|
1694
|
+
}
|
|
1695
|
+
if (out.checkedFiles === 0) {
|
|
1696
|
+
out.notes.push('无在检文件(design.md 与 tasks/task-*.md 均缺席——纯骨架/中间态)')
|
|
1697
|
+
return out
|
|
1698
|
+
}
|
|
1699
|
+
out.applicable = true
|
|
1700
|
+
return out
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
|
|
1645
1704
|
const PROBE7_HEADING = '#### 探针 7:验收×测试覆盖矩阵'
|
|
1646
1705
|
// execute run id 格式(与 task-review.js isValidExecuteRunId 同口径锚定,防提示词注入/路径穿越)
|
|
1647
1706
|
const PROBE7_EXEC_RUN_ID_RE = /^exec-\d{4}-\d{2}-\d{2}-\d{6}(?:-[a-z0-9]{1,8}){0,2}$/
|
|
@@ -1870,8 +1929,8 @@ function renderProbe7Lines(p7) {
|
|
|
1870
1929
|
L.push('- 不适用(无 TaskCard)')
|
|
1871
1930
|
return L
|
|
1872
1931
|
}
|
|
1873
|
-
L.push('<!-- 口径注记:探针 3 = 模块目录递归存在性面(allowed_paths 目录附近有没有测试);探针 7 = allowed_paths ∪ review changedFiles ∪ 直接下游卡测试 结构归属承接面(每条 acceptance 由哪些测试承接;下游消费卡的测试可承接上游 provider 的 acceptance——probe7-provider-tests-in-consumer-card);两者并排冲突以 7
|
|
1874
|
-
L.push('<!-- 预填说明(ql-20260915-004):判定列为 CLI 机械预填,agent 逐格复核改写——规则:无归属→uncovered(文档/部署/doc/deploy/manual/config 类词→non-testable);有归属且命中≥1→covered;有归属零命中→partial。covered/partial 证据须含测试锚点三形态之一(file:line / `.test.` 测试文件名 / 反引号包裹的路径或测试名),行号可省;uncovered/non-testable 证据自由形态。预填≠结论:与事实不符的格子必须改写(枚举须保持 covered/partial/uncovered/non-testable 纯值,备注写在证据列)。 -->')
|
|
1932
|
+
L.push('<!-- 口径注记:探针 3 = 模块目录递归存在性面(allowed_paths 目录附近有没有测试);探针 7 = allowed_paths ∪ review changedFiles ∪ 直接下游卡测试 结构归属承接面(每条 acceptance 由哪些测试承接;下游消费卡的测试可承接上游 provider 的 acceptance——probe7-provider-tests-in-consumer-card);两者并排冲突以 7 为准。判定枚举(五选一):covered / covered-service / partial / uncovered / non-testable(covered-service 适用:端点行为由 service 层等非端点层测试锁定,证据附测试锚点;non-testable 是文档/部署类显式逃生门)。关键词命中只是提示,命中≠判定。 -->')
|
|
1933
|
+
L.push('<!-- 预填说明(ql-20260915-004):判定列为 CLI 机械预填,agent 逐格复核改写——规则:无归属→uncovered(文档/部署/doc/deploy/manual/config 类词→non-testable);有归属且命中≥1→covered;有归属零命中→partial。covered/covered-service/partial 证据须含测试锚点三形态之一(file:line / `.test.` 测试文件名 / 反引号包裹的路径或测试名),行号可省;uncovered/non-testable 证据自由形态。预填≠结论:与事实不符的格子必须改写(枚举须保持 covered/covered-service/partial/uncovered/non-testable 纯值,备注写在证据列)。 -->')
|
|
1875
1934
|
// 零自动化承接计数(坑 review-zero-coverage-unwalked,2026-09-16 EHS 二次复核实证:execute
|
|
1876
1935
|
// 期 review 走查面事实上跟着测试覆盖走——「新增-部门支线测试充分,编辑路径与相关方支线
|
|
1877
1936
|
// 零覆盖」恰是 5 个 P1 的藏身处。矩阵渲染时顺带计数零承接条目,verify 复核强制显式走查
|
|
@@ -2163,6 +2222,27 @@ export function runVerifyProbes({ cwd, changeName, specDir = null }) {
|
|
|
2163
2222
|
probe9.notes = [`探针 9 执行失败(fail-soft 跳过):${e && e.message ? e.message : e}`]
|
|
2164
2223
|
}
|
|
2165
2224
|
|
|
2225
|
+
// ── 探针 10:预填注清零(error 门;2026-09-18-artifact-prefill task-03——门禁梯度 error 档
|
|
2226
|
+
// 的检测单点:--init 预填本段 + gates.js verify 收尾 error 门 / brainstorm·plan advisory 复跑
|
|
2227
|
+
// 同一实现,不二算。fail-soft 同探针 8/9:异常降级 not-applicable 注记不炸整体。──
|
|
2228
|
+
let probe10 = { applicable: false, checkedFiles: 0, unclearedFiles: [], notes: [] }
|
|
2229
|
+
try {
|
|
2230
|
+
probe10 = runProbe10PrefillNoteClearance({ specBase, changeName })
|
|
2231
|
+
} catch (e) {
|
|
2232
|
+
probe10.notes = [`探针 10 执行失败(fail-soft 跳过):${e && e.message ? e.message : e}`]
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
// ── 探针 11:红线一致性(advisory;2026-09-20-redline-machine-check——对撞实验核心
|
|
2236
|
+
// 教训转化:语义红线活在散文里无机器可查形态)。消费者仓自持 .sillyspec/redlines.yaml
|
|
2237
|
+
// (缺=不适用零打扰,D-002);forbid 命中 ❌/⚠️(severity 驱动)、require 缺失 ⚠️,渲染进
|
|
2238
|
+
// 骨架供裁定;不进 PASS 封顶(D-003:攒误报率后另案升硬门)。fail-soft 同 8/9/10。──
|
|
2239
|
+
let probe11 = { applicable: false, entryCount: 0, findings: [], warnings: [] }
|
|
2240
|
+
try {
|
|
2241
|
+
probe11 = runRedlineConsistencyProbe({ specBase, cwd, wtRoot, changeName })
|
|
2242
|
+
} catch (e) {
|
|
2243
|
+
probe11.warnings = [`探针 11 执行失败(fail-soft 跳过):${e && e.message ? e.message : e}`]
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2166
2246
|
// ── 接口面 + 消费端归类(task-04 / FR-05 / D-005~D-007):design.md 接口段 tolerant
|
|
2167
2247
|
// 解析 + 清单消费端归类——骨架「## 接口验证覆盖矩阵」段预填与 facts 落盘
|
|
2168
2248
|
// (backfillFactsFromMdAndTests)共用同一解析器(单一产物源,不各读各的)。fail-soft:
|
|
@@ -2176,7 +2256,7 @@ export function runVerifyProbes({ cwd, changeName, specDir = null }) {
|
|
|
2176
2256
|
}
|
|
2177
2257
|
} catch { /* design 读/解析异常 → 空面(fail-soft) */ }
|
|
2178
2258
|
|
|
2179
|
-
return { probe1, probe3, probe5, probe6, probe7, probe8, probe9, apiFace, consumerHints }
|
|
2259
|
+
return { probe1, probe3, probe5, probe6, probe7, probe8, probe9, probe10, apiFace, consumerHints }
|
|
2180
2260
|
}
|
|
2181
2261
|
|
|
2182
2262
|
/**
|
|
@@ -2283,6 +2363,11 @@ export function renderVerifyProbesReport(result) {
|
|
|
2283
2363
|
// 探针 9 紧随探针 8;旧 result 无 probe9 键(存量调用方/合成 result)→ applicable=false
|
|
2284
2364
|
// 渲染「不适用」行 + notes,零回归(探针 7 兜底口径同款)。
|
|
2285
2365
|
L.push(...(renderProbe9Lines(result.probe9 || { applicable: false, javaFileCount: 0, groupCount: 0, inconsistentGroups: [], notes: [] })))
|
|
2366
|
+
// 探针 10 紧随探针 9;旧 result 无 probe10 键(存量调用方/合成 result)→ applicable=false
|
|
2367
|
+
// 渲染「不适用」行 + notes,零回归(探针 8/9 兜底口径同款)。
|
|
2368
|
+
L.push(...(renderProbe10Lines(result.probe10 || { applicable: false, checkedFiles: 0, unclearedFiles: [], notes: [] })))
|
|
2369
|
+
// 探针 11 紧随 10;旧 result 无 probe11 键 → 不适用兜底(8/9/10 同款零回归口径)。
|
|
2370
|
+
L.push(...(renderProbe11Lines(result.probe11 || { applicable: false, entryCount: 0, findings: [], warnings: [] })))
|
|
2286
2371
|
return L.join('\n')
|
|
2287
2372
|
}
|
|
2288
2373
|
|
|
@@ -2369,6 +2454,92 @@ function renderProbe9Lines(p9) {
|
|
|
2369
2454
|
return L
|
|
2370
2455
|
}
|
|
2371
2456
|
|
|
2457
|
+
/**
|
|
2458
|
+
* 渲染探针 10 段(renderProbe9Lines 同构;error 门口径注记随段输出——❌ 面是 verify 完成
|
|
2459
|
+
* 前须清零的死信,不是 advisory 复核面)。汇总行「预填注未清 N 处」的 N 为后续一致性抽查
|
|
2460
|
+
* 锚点扩展留位(verify-postcheck 锚点面归后续任务,本卡只出 producer 侧——probe9 先例同款)。
|
|
2461
|
+
* @param {{applicable: boolean, checkedFiles?: number, unclearedFiles: string[], notes: string[]}} p10
|
|
2462
|
+
* @returns {string[]} 行数组(含段标题)
|
|
2463
|
+
*/
|
|
2464
|
+
function renderProbe10Lines(p10) {
|
|
2465
|
+
const L = [PROBE10_HEADING]
|
|
2466
|
+
if (!p10 || !p10.applicable) {
|
|
2467
|
+
L.push('- 不适用(design.md 与 tasks/task-*.md 均缺席——纯骨架/中间态,预填注无从在场)')
|
|
2468
|
+
for (const n of (p10 && p10.notes) || []) L.push(`- ℹ️ ${n}`)
|
|
2469
|
+
return L
|
|
2470
|
+
}
|
|
2471
|
+
L.push('<!-- 口径注记:预填注(来源注协议)在场 = 白名单槽未确认(预填≠结论);删注 = 确认动作。本探针是门禁梯度 error 档——verify --done 时 gate 复跑同源检测,注未清零阻断完成(归档前清零兜底)。已知误报面:散文引用注字面量会命中(如文档描述注协议本身)——核对后真未确认则删注,纯散文则改写措辞,不得删探针段。 -->')
|
|
2472
|
+
if ((p10.unclearedFiles || []).length > 0) {
|
|
2473
|
+
L.push(`- ❌ 预填注未清 ${p10.unclearedFiles.length} 处(error 门——verify 完成前须逐槽核对后删注):`)
|
|
2474
|
+
for (const f of p10.unclearedFiles) L.push(`- ❌ \`${mdEscapeCell(f, 120)}\` 仍含未删预填注`)
|
|
2475
|
+
} else {
|
|
2476
|
+
L.push(`- ✅ 预填注清零(${p10.checkedFiles ?? 0} 个在检文件无未确认预填)`)
|
|
2477
|
+
}
|
|
2478
|
+
for (const n of p10.notes || []) L.push(`- ℹ️ ${n}`)
|
|
2479
|
+
return L
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
// ── 探针 11:红线一致性(2026-09-20-redline-machine-check,advisory)──────────────
|
|
2483
|
+
|
|
2484
|
+
const PROBE11_HEADING = '#### 探针 11:红线一致性(advisory)'
|
|
2485
|
+
|
|
2486
|
+
/**
|
|
2487
|
+
* 红线一致性探针(advisory / fail-open 全链,D-003/D-004):
|
|
2488
|
+
* 读 <specBase>/redlines.yaml(消费者仓自持,缺=不适用零打扰),调 redlines.js
|
|
2489
|
+
* 评估器(纯函数,root=wtRoot||cwd 定位 scope 文件)。坏 yaml → 不适用 + 注记;
|
|
2490
|
+
* 不进 PASS 封顶(D-003:攒误报率后另案升硬门)。
|
|
2491
|
+
*/
|
|
2492
|
+
export function runRedlineConsistencyProbe({ specBase, cwd, wtRoot = null }) {
|
|
2493
|
+
const yamlPath = join(specBase, 'redlines.yaml')
|
|
2494
|
+
if (!existsSync(yamlPath)) {
|
|
2495
|
+
return { applicable: false, entryCount: 0, findings: [], warnings: [] }
|
|
2496
|
+
}
|
|
2497
|
+
let yamlText
|
|
2498
|
+
try {
|
|
2499
|
+
yamlText = readFileSync(yamlPath, 'utf8')
|
|
2500
|
+
} catch (e) {
|
|
2501
|
+
return { applicable: false, entryCount: 0, findings: [], warnings: [`redlines.yaml 读取失败(不适用):${e && e.code ? e.code : 'error'}`] }
|
|
2502
|
+
}
|
|
2503
|
+
let parsed
|
|
2504
|
+
try {
|
|
2505
|
+
parsed = parseRedlines(yamlText)
|
|
2506
|
+
} catch (e) {
|
|
2507
|
+
return { applicable: false, entryCount: 0, findings: [], warnings: [`redlines.yaml 解析失败(不适用):${e && e.message ? e.message : e}`] }
|
|
2508
|
+
}
|
|
2509
|
+
const root = wtRoot || cwd
|
|
2510
|
+
const result = evaluateRedlines({ entries: parsed.entries, root })
|
|
2511
|
+
return {
|
|
2512
|
+
applicable: true,
|
|
2513
|
+
entryCount: result.entryCount,
|
|
2514
|
+
findings: result.findings,
|
|
2515
|
+
warnings: [...(parsed.warnings || []), ...(result.warnings || [])],
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
/** renderProbe11Lines:不适用 / 命中逐条(severity 驱动 ❌/⚠️ + statement + origin)/ ✅ 计数。 */
|
|
2520
|
+
function renderProbe11Lines(p11) {
|
|
2521
|
+
const L = [PROBE11_HEADING]
|
|
2522
|
+
if (!p11 || !p11.applicable) {
|
|
2523
|
+
L.push('- 不适用(仓未配置 .sillyspec/redlines.yaml——红线机检零打扰,D-002)')
|
|
2524
|
+
for (const w of (p11 && p11.warnings) || []) L.push(`- ℹ️ ${w}`)
|
|
2525
|
+
return L
|
|
2526
|
+
}
|
|
2527
|
+
L.push('<!-- 口径注记:模式级断言(正则×scope,人写人负责)非语义审计——命中是「定向复核提示」;advisory 不阻断(D-003:误报率数据攒够后另案升硬门)。命中处置:真违规则修代码或升红线版本;正则过宽则收窄条目。 -->')
|
|
2528
|
+
const findings = p11.findings || []
|
|
2529
|
+
if (findings.length === 0) {
|
|
2530
|
+
L.push(`- ✅ 红线全过(${p11.entryCount ?? 0} 条断言,0 命中 0 缺失)`)
|
|
2531
|
+
} else {
|
|
2532
|
+
for (const f of findings) {
|
|
2533
|
+
const marker = f.kind === 'forbid' && f.severity === 'error' ? '❌' : '⚠️'
|
|
2534
|
+
const where = f.file ? `\`${f.file}:${f.line}\`` : 'scope 全集'
|
|
2535
|
+
L.push(`- ${marker} ${f.id}(${f.kind === 'forbid' ? '禁式命中' : '要求缺失'})@ ${where}${f.snippet ? `:\`${mdEscapeCell(f.snippet, 100)}\`` : f.detail ? `:${f.detail}` : ''}`)
|
|
2536
|
+
if (f.statement) L.push(` - 红线:${f.statement}${f.origin ? `(溯源:${f.origin})` : ''}`)
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
for (const w of p11.warnings || []) L.push(`- ℹ️ ${w}`)
|
|
2540
|
+
return L
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2372
2543
|
// ── 接口验证覆盖矩阵段渲染(task-04 / FR-05 / D-005~D-007)──
|
|
2373
2544
|
// 骨架新写路径(generateVerifyResultSkeleton)与缺段补齐(ensureApiCoverageMatrixSection)
|
|
2374
2545
|
// 共用单一实现——renderProbe7Lines 先例。不动 probe7 既有矩阵(独立 ## 章节并行存在,口径
|
|
@@ -2380,7 +2551,7 @@ const API_MATRIX_WRITE_METHODS = new Set(['POST', 'PUT', 'DELETE', 'PATCH'])
|
|
|
2380
2551
|
|
|
2381
2552
|
/**
|
|
2382
2553
|
* 渲染「## 接口验证覆盖矩阵」段(task-04)。
|
|
2383
|
-
* 三态:解析有端点 → 逐端点预填行(判定列
|
|
2554
|
+
* 三态:解析有端点 → 逐端点预填行(判定列 `<待填:五选一>` 占位,同 probe7 预填口径供 agent
|
|
2384
2555
|
* 复核);解析零行有声明 → 声明占位行「本变更接口面:<N> 端点(agent 声明)」(D-005 零解析
|
|
2385
2556
|
* 降级,对账分母=声明数);零解析零声明 → 「无接口面」注记行(非判级 critical 变更零行为
|
|
2386
2557
|
* 注记即可——判级 critical 的 error 拦截归 task-05 validator)。段尾 advisory 占位注记
|
|
@@ -2393,7 +2564,7 @@ function renderApiCoverageMatrixLines(apiFace) {
|
|
|
2393
2564
|
const endpoints = face && Array.isArray(face.endpoints) ? face.endpoints : []
|
|
2394
2565
|
const declared = face && typeof face.declared === 'number' ? face.declared : null
|
|
2395
2566
|
const L = [`${API_MATRIX_HEADING} [层:人工判断——CLI 预填复核]`]
|
|
2396
|
-
L.push('<!-- 口径注记(与探针 7 互指,R-07):探针 7 = 验收项 × 测试承接面(每条 acceptance 由哪些测试承接);本矩阵 = 接口端点 × 验证用例面(design 接口段每个端点由哪些验证用例/冒烟步骤覆盖)——两者并排互补,双矩阵并行存在。端点集来自 design.md 接口段 tolerant 解析(parseDesignApiTable:段头宽收 + 方法/路径双条件),预填≠结论,agent
|
|
2567
|
+
L.push('<!-- 口径注记(与探针 7 互指,R-07):探针 7 = 验收项 × 测试承接面(每条 acceptance 由哪些测试承接);本矩阵 = 接口端点 × 验证用例面(design 接口段每个端点由哪些验证用例/冒烟步骤覆盖)——两者并排互补,双矩阵并行存在。端点集来自 design.md 接口段 tolerant 解析(parseDesignApiTable:段头宽收 + 方法/路径双条件),预填≠结论,agent 逐行复核。判定枚举(五选一):covered / covered-service / partial / uncovered / non-testable——covered-service 适用:端点行为由 service 层等非端点层测试锁定;证据须含测试文件锚点三形态之一(`.test.` / file:line / 反引号包裹的路径或测试名)。 -->')
|
|
2397
2568
|
L.push('<!-- 预填说明:端点行由 CLI 机械预填,判定/用例依据 ID/结果/证据由 agent 逐格填写——用例依据 ID 锚点五形态:design接口表#METHOD /path、权限矩阵[角色×动作]、契约表@行标识、DDL@列名、载荷@构造点路径(须真实命中对应表/段,防空指)。 -->')
|
|
2398
2569
|
L.push('<!-- 文法注释:子行 = 端点行下一行、两空格缩进、以「↳ <消费端>:」前缀书写(消费端细分承接面,不计矩阵行账);探索行 = 判定 uncovered 且证据列含 [探索] 标记(探索性验证不算覆盖)。 -->')
|
|
2399
2570
|
if (endpoints.length === 0 && declared === null) {
|
|
@@ -2407,10 +2578,10 @@ function renderApiCoverageMatrixLines(apiFace) {
|
|
|
2407
2578
|
L.push('|---|---|---|---|---|')
|
|
2408
2579
|
if (endpoints.length > 0) {
|
|
2409
2580
|
for (const ep of endpoints) {
|
|
2410
|
-
L.push(`| ${mdEscapeCell(`${ep.method} ${ep.path}`, 160)} |
|
|
2581
|
+
L.push(`| ${mdEscapeCell(`${ep.method} ${ep.path}`, 160)} | <待填:五选一> | <待填:用例 ID> | <待填> | <待填:锚点> |`)
|
|
2411
2582
|
}
|
|
2412
2583
|
} else {
|
|
2413
|
-
L.push(`| 本变更接口面:${declared} 端点(agent 声明) |
|
|
2584
|
+
L.push(`| 本变更接口面:${declared} 端点(agent 声明) | <待填:五选一> | <待填:用例 ID> | <待填> | <待填:锚点> |`)
|
|
2414
2585
|
L.push('<!-- 解析零行降级(D-005):接口面以 agent 声明为准(对账分母=声明数);声明与实际不符时补 design 接口段表格后重跑 --init 刷新本段 -->')
|
|
2415
2586
|
}
|
|
2416
2587
|
L.push('<!-- advisory 尾注(warning 计算归 validator,本段只留位):有消费端未填子行的端点将列于此(advisory——消费端归类=design 清单启发式,数据面 facts.consumerHints);写端点(POST/PUT/DELETE/PATCH)未在权限矩阵段声明的将列于此(advisory——补行或显式豁免「无权限约束」,数据面 facts.apiFace.writeEndpoints;表缺行会让派生框架继承你的洞) -->')
|
|
@@ -2437,6 +2608,7 @@ export function buildVerifyFacts(result, { changeName, now } = {}) {
|
|
|
2437
2608
|
const p6 = (result && result.probe6) || {}
|
|
2438
2609
|
const p8 = (result && result.probe8) || {}
|
|
2439
2610
|
const p9 = (result && result.probe9) || {}
|
|
2611
|
+
const p10 = (result && result.probe10) || {}
|
|
2440
2612
|
// v2(2026-09-08-ir-verify-facts):probes 机器段原样;conclusion/tests/requiredEvidence/
|
|
2441
2613
|
// runtimeEvidence/factsConsistency 五段是 slot-backfill/实测回填段(D-001@v2),--init 快照
|
|
2442
2614
|
// 不落键(writeVerifyFacts 分段合并时保留既有固化段),由 backfillFactsFromMdAndTests 填。
|
|
@@ -2496,6 +2668,13 @@ export function buildVerifyFacts(result, { changeName, now } = {}) {
|
|
|
2496
2668
|
inconsistentGroups: len(p9.inconsistentGroups),
|
|
2497
2669
|
}),
|
|
2498
2670
|
},
|
|
2671
|
+
probe10: {
|
|
2672
|
+
command,
|
|
2673
|
+
metrics: defined({
|
|
2674
|
+
checkedFiles: num(p10.checkedFiles),
|
|
2675
|
+
unclearedFiles: len(p10.unclearedFiles),
|
|
2676
|
+
}),
|
|
2677
|
+
},
|
|
2499
2678
|
},
|
|
2500
2679
|
}
|
|
2501
2680
|
}
|
|
@@ -3239,10 +3418,13 @@ export function buildDecisionChainMatrix(changeDir) {
|
|
|
3239
3418
|
const id = (raw.match(/^id:\s*(\S+)/m) || [])[1] || f.replace(/\.md$/, '')
|
|
3240
3419
|
const decLine = (raw.match(/^decision_ids:\s*\[([^\]]*)\]/m) || [])[1] || ''
|
|
3241
3420
|
const reqLine = (raw.match(/^requirement_ids:\s*\[([^\]]*)\]/m) || [])[1] || ''
|
|
3421
|
+
// token 剥首尾成对引号:流式数组的引号写法(['D-001@v1'])是合法 YAML 且 agent Edit 填值
|
|
3422
|
+
// 主流形态,只 trim 不剥会让引号卡与 decisions.md 裸 id 永不相等(矩阵静默全量 ⚠️)
|
|
3423
|
+
const stripQuotes = (s) => s.trim().replace(/^['"](.*)['"]$/, '$1').trim()
|
|
3242
3424
|
taskRefs.push({
|
|
3243
3425
|
task: id,
|
|
3244
|
-
decisions: new Set(decLine.split(',').map(
|
|
3245
|
-
reqs: new Set(reqLine.split(',').map(
|
|
3426
|
+
decisions: new Set(decLine.split(',').map(stripQuotes).filter(Boolean)),
|
|
3427
|
+
reqs: new Set(reqLine.split(',').map(stripQuotes).filter(Boolean)),
|
|
3246
3428
|
})
|
|
3247
3429
|
}
|
|
3248
3430
|
}
|
|
@@ -3308,6 +3490,8 @@ export function generateVerifyResultSkeleton(result) {
|
|
|
3308
3490
|
const L = [
|
|
3309
3491
|
'# 验证报告(骨架由 `sillyspec verify-probes --change <变更名> --init` 生成)',
|
|
3310
3492
|
'',
|
|
3493
|
+
'> 引用规范:矩阵证据/测试结果等处的源码位置写仓根相对全路径+行号(src/foo.js:123)——裸文件名在 docs-check 层1 靠 basename 全仓扫描找候选,找不到候选或关键词窗口不匹配即失效,到 pre-push 才拦(2026-09-19 实证 64 处返工)。',
|
|
3494
|
+
'',
|
|
3311
3495
|
'> 探针结果已机械预填;其余章节把 `<!--TODO-->` 替换为真实内容。**结论只认「结论枚举:」槽行**——', /* probe1-noqa */
|
|
3312
3496
|
'> 槽行留「<待填:三选一>」会被 gate 判不过(fail-closed),正文其他位置的 PASS/FAIL 字样不参与判定。',
|
|
3313
3497
|
'>',
|
package/src/worktree-apply.js
CHANGED
|
@@ -1519,7 +1519,7 @@ export function applyWorktree(changeName, { cwd, checkOnly = false, merge = fals
|
|
|
1519
1519
|
if (derivedRisk.length > 0) {
|
|
1520
1520
|
result.warnings = (result.warnings || []).concat([
|
|
1521
1521
|
`${derivedRisk.length} 个变更文件在 worktree 基线后主仓已有新提交(并行变更可能已合入):${derivedRisk.slice(0, 5).join(', ')}${derivedRisk.length > 5 ? ' 等' : ''}` +
|
|
1522
|
-
|
|
1522
|
+
`——若其中含生成产物(类型生成/代码生成产物等),本次 apply 可能以旧基线内容覆盖已合入的新内容;apply 后在新基线重跑生成命令再验证`
|
|
1523
1523
|
]);
|
|
1524
1524
|
}
|
|
1525
1525
|
}
|
package/src/worktree.js
CHANGED
|
@@ -1751,7 +1751,7 @@ export class WorktreeManager {
|
|
|
1751
1751
|
const escapes = detectEditableInstallEscape(wtPath);
|
|
1752
1752
|
if (escapes.length > 0) {
|
|
1753
1753
|
issues.push({ type: 'editable-install-escape', name, fixable: false,
|
|
1754
|
-
detail: `worktree venv 含 ${escapes.length} 个 editable install 指向 worktree 外(${escapes.slice(0, 5).map(e => `${e.pkg}→${e.target}`).join(', ')}${escapes.length > 5 ? ' 等' : ''}
|
|
1754
|
+
detail: `worktree venv 含 ${escapes.length} 个 editable install 指向 worktree 外(${escapes.slice(0, 5).map(e => `${e.pkg}→${e.target}`).join(', ')}${escapes.length > 5 ? ' 等' : ''})——生成链/后端命令会静默加载 worktree 外旧代码。轻量出口:跑生成链前设 PYTHONPATH=<worktree>/<项目源码根>(PYTHONPATH 优先于 .pth);彻底修复:worktree 内重装(uv sync / uv pip install -e .)` });
|
|
1755
1755
|
}
|
|
1756
1756
|
} catch { /* 探测失败不阻断 doctor */ }
|
|
1757
1757
|
}
|
|
@@ -6,6 +6,7 @@ TaskCard 格式规则(必须严格遵守):
|
|
|
6
6
|
- implementation: 列表,每条一个具体步骤
|
|
7
7
|
- acceptance: 列表,每条可独立验证(不是表格)
|
|
8
8
|
- verify: 列表,实际可执行的命令
|
|
9
|
+
- 中间验证定向优先:node --test <本任务测试文件>;全量 npm test 留 task 收口与 verify --done
|
|
9
10
|
- constraints: 列表,明确边界(含 brownfield 兼容、异常处理)
|
|
10
11
|
- 不需要:修改文件章节、覆盖来源章节、接口定义章节、TDD 步骤章节、参考章节
|
|
11
12
|
- frontmatter 必须以三减号结尾闭合(开头和结尾各一行 ---)。缺结尾会让 postcheck 提取不到 frontmatter,误报「字段缺失 / allowed_paths 为空」
|
|
@@ -33,4 +33,4 @@ CLI 的 missing backend endpoint 是 advisory(不硬阻断归档)——但 c
|
|
|
33
33
|
以 git 事实为准(真实 > 声明);CLI 的三态(合规/高风险/未声明)是机械判定,是否构成 FAIL blocker 由你诚实判定,务必如实记录——静默删除代码是 verify 的最大盲区。
|
|
34
34
|
|
|
35
35
|
**探针 7 补充(CLI 已预填矩阵归属与提示,你填判定与证据)**
|
|
36
|
-
验收×测试覆盖矩阵:CLI 机械半边已预填——逐 task 解析 TaskCard acceptance(frontmatter)、双源结构归属测试文件(allowed_paths 测试模式 ∪ execute review changedFiles 中 test/
|
|
36
|
+
验收×测试覆盖矩阵:CLI 机械半边已预填——逐 task 解析 TaskCard acceptance(frontmatter)、双源结构归属测试文件(allowed_paths 测试模式 ∪ execute review changedFiles 中 test/ 路径)、关键词命中提示(命中≠判定,仅提示)。你逐行填判定槽五枚举(covered / covered-service / partial / uncovered / non-testable——covered-service 适用:端点行为由 service 层等非端点层测试锁定,证据附测试锚点;non-testable 是文档/部署类显式逃生门)与证据:covered/covered-service/partial 附测试锚点(`.test.` 文件或 file:line),non-testable 附一句理由;判定或证据未填 fail-closed,阻断 verify `--done`。与探针 3 口径差异:探针 3 查模块目录有没有测试文件(存在性面),探针 7 查每条 acceptance 由哪些测试承接(承接面);两者并排冲突以 7 为准。
|