sillyspec 3.32.10 → 3.33.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/.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 +66 -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/archive-delta.js +4 -3
- package/src/ceremony-tier.js +2 -1
- package/src/change-delete.js +6 -5
- package/src/contract-matrix.js +2 -1
- package/src/cross-placement.js +5 -1
- package/src/db.js +8 -2
- package/src/decision-distill.js +29 -3
- package/src/decision-redomain.js +148 -0
- package/src/deleted-ledger.js +2 -1
- package/src/design-facts.js +3 -3
- package/src/docs-debt.js +3 -3
- package/src/doctor-diagnostics.js +8 -3
- package/src/flow-draft.js +2 -1
- package/src/flow-parity.js +10 -2
- package/src/flow.js +112 -63
- package/src/forensics.js +236 -0
- package/src/fr-index.js +2 -1
- package/src/friction-ledger.js +2 -1
- package/src/friction-tally.js +8 -4
- package/src/handoff.js +2 -1
- package/src/hooks/worktree-guard.js +5 -5
- package/src/index.js +75 -8
- package/src/knowledge-graph.js +3 -4
- package/src/knowledge-hits.js +2 -1
- package/src/knowledge-vector.js +2 -1
- package/src/module-impact.js +2 -5
- package/src/modules.js +31 -0
- package/src/progress/consistency-doctor.js +5 -2
- package/src/progress/stage-machine.js +6 -2
- package/src/progress.js +19 -8
- package/src/quicklog.js +194 -9
- package/src/review-dispatch.js +4 -3
- package/src/review-tier.js +2 -1
- package/src/route-hindsight.js +4 -3
- package/src/run/bg-sync.js +10 -3
- package/src/run/command.js +13 -11
- package/src/run/complete-handlers.js +40 -37
- package/src/run/complete.js +12 -8
- package/src/run/gate-snapshot-ledger.js +2 -1
- package/src/run/gate-snapshot.js +57 -11
- package/src/run/gates.js +38 -21
- package/src/run/lint-ownership.js +37 -0
- package/src/run/prompt.js +46 -30
- package/src/run/scan-profile.js +2 -1
- package/src/run/shared.js +109 -5
- package/src/run/stage.js +5 -4
- package/src/run/test-ledger.js +2 -1
- package/src/run/verify-quality-scan.js +21 -6
- package/src/runtime-paths.js +189 -0
- package/src/scan-refresh.js +4 -3
- package/src/scope-audit.js +7 -6
- package/src/sentinel-assertions.js +37 -0
- package/src/spec-sync.js +118 -23
- package/src/stage-contract.js +24 -2
- package/src/stage-review.js +5 -3
- package/src/stages/brainstorm.js +72 -21
- package/src/stages/plan-postcheck.js +13 -2
- package/src/stages/plan.js +19 -0
- package/src/sync-noise.js +3 -2
- package/src/sync.js +71 -15
- package/src/task-done.js +2 -1
- package/src/task-review.js +7 -5
- package/src/task-tick.js +23 -1
- package/src/test-bindings.js +32 -3
- package/src/ui-visual.js +119 -4
- package/src/validate-artifacts.js +2 -1
- package/src/verify-draft.js +2 -1
- package/src/verify-postcheck.js +128 -27
- package/src/verify-probes.js +157 -52
- package/src/watcher.js +15 -9
- package/src/worktree-apply.js +28 -3
- package/src/write-audit.js +2 -1
- package/templates/agents-instruction.md +1 -1
package/src/verify-draft.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* 纯 --init(无 --draft)路径零变化;无 sidecar → 门禁 not-applicable(存量变更零红)。
|
|
22
22
|
*/
|
|
23
23
|
import { existsSync, readFileSync } from 'node:fs'
|
|
24
|
+
import { runtimeStatePath } from './runtime-paths.js'
|
|
24
25
|
import { join } from 'node:path'
|
|
25
26
|
import { writeAtomicSync } from './fs-atomic.js'
|
|
26
27
|
// 三件套原语消费方化(R7 切片三 task-04):标记格式/指纹/三态判定/重锚的机械半边在本模块,
|
|
@@ -33,7 +34,7 @@ const AMEND_CMD = 'sillyspec verify-probes --change <变更名> --amend-draft'
|
|
|
33
34
|
|
|
34
35
|
/** sidecar 路径(.runtime 下,与质量扫描记录/探针回执同族) */
|
|
35
36
|
export function verifyDraftSidecarPath(runtimeRoot, changeName) {
|
|
36
|
-
return
|
|
37
|
+
return runtimeStatePath(runtimeRoot, `verify-draft-${changeName}.json`)
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/**
|
package/src/verify-postcheck.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { execSync, spawnSync } from 'child_process'
|
|
26
|
+
import { runtimeStatePath } from './runtime-paths.js'
|
|
26
27
|
import { createHash } from 'crypto'
|
|
27
28
|
import { IR_STRICT_SINCE } from './constants.js'
|
|
28
29
|
import { gitQuiet } from './git-helper.js'
|
|
@@ -236,7 +237,7 @@ function recordVerifyLintTally({ specBase, result }) {
|
|
|
236
237
|
try {
|
|
237
238
|
const runtimeDir = join(specBase, '.runtime')
|
|
238
239
|
mkdirSync(runtimeDir, { recursive: true })
|
|
239
|
-
const tallyPath =
|
|
240
|
+
const tallyPath = runtimeStatePath(runtimeDir, 'verify-lint-tally.json')
|
|
240
241
|
let tally = { totalRuns: 0, failedRuns: 0, history: [] }
|
|
241
242
|
try {
|
|
242
243
|
tally = { ...tally, ...JSON.parse(readFileSync(tallyPath, 'utf8')) }
|
|
@@ -1424,7 +1425,7 @@ function readTestRerunConfig(specBase) {
|
|
|
1424
1425
|
}
|
|
1425
1426
|
|
|
1426
1427
|
function testRerunLedgerPath(specBase, changeName) {
|
|
1427
|
-
return join(specBase, '.runtime', `test-rerun-${changeName || 'change'}.json`)
|
|
1428
|
+
return runtimeStatePath(join(specBase, '.runtime'), `test-rerun-${changeName || 'change'}.json`)
|
|
1428
1429
|
}
|
|
1429
1430
|
|
|
1430
1431
|
/**
|
|
@@ -1861,7 +1862,7 @@ export function resolveVerifyChangedFiles(cwd, changeName, ctx = null, opts = {}
|
|
|
1861
1862
|
if (!ctx || typeof ctx.repos !== 'object' || ctx.repos === null) {
|
|
1862
1863
|
try {
|
|
1863
1864
|
const rtRoot = resolveRuntimeRoot({ runtimeRoot: opts.runtimeRoot, specDriftAnchor: opts.specDriftAnchor }, opts.specBase || join(cwd, '.sillyspec'))
|
|
1864
|
-
const pathspecFile =
|
|
1865
|
+
const pathspecFile = runtimeStatePath(rtRoot, `apply-pathspec-${changeName}.txt`)
|
|
1865
1866
|
if (existsSync(pathspecFile)) {
|
|
1866
1867
|
const declared = readFileSync(pathspecFile, 'utf8').split('\n').map(l => l.trim()).filter(Boolean)
|
|
1867
1868
|
.map(p => p.replace(/\\/g, '/'))
|
|
@@ -2821,6 +2822,31 @@ function inferFullTestCommand(projectRoot) {
|
|
|
2821
2822
|
* 相关回归)。组卷改配额制:每组优先面整跑,普通 import 依赖只填剩余席位(可为 0)——帽
|
|
2822
2823
|
* 只界普通依赖尾。批尺寸上界护栏由 TEST_TIMEOUT_MS 兜底(超时=失败 fail-closed 不变)。
|
|
2823
2824
|
*/
|
|
2825
|
+
/**
|
|
2826
|
+
* 批命令长度分块(2026-10-15 平台侧实证,跨仓事故驱动最小修复):Windows cmd 执行器
|
|
2827
|
+
* 命令行上限 ~8191 字符——大依赖面(广被引用的前端文件变更 → 依赖测试 100+ 个)拼单条
|
|
2828
|
+
* vitest/node --test 命令必炸「命令行太长」exit 1,且失败批进增量账本自我循环恒败
|
|
2829
|
+
* (实测:137 文件批反复重跑复败,verify 门禁无法收口)。按「命令前缀+文件参数拼接
|
|
2830
|
+
* 总长」分块:单块保持原名(零回归),多块命名 <base>:N(N 从 1 起);每块独立计账。
|
|
2831
|
+
*/
|
|
2832
|
+
function chunkByCommandLength(prefix, files, maxLen = 6000) {
|
|
2833
|
+
const chunks = []
|
|
2834
|
+
let cur = []
|
|
2835
|
+
let curLen = prefix.length
|
|
2836
|
+
for (const f of files) {
|
|
2837
|
+
const add = f.length + 1
|
|
2838
|
+
if (cur.length > 0 && curLen + add > maxLen) {
|
|
2839
|
+
chunks.push(cur)
|
|
2840
|
+
cur = []
|
|
2841
|
+
curLen = prefix.length
|
|
2842
|
+
}
|
|
2843
|
+
cur.push(f)
|
|
2844
|
+
curLen += add
|
|
2845
|
+
}
|
|
2846
|
+
if (cur.length > 0) chunks.push(cur)
|
|
2847
|
+
return chunks
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2824
2850
|
function buildDepsBatches({ deps, changedFiles = [], hits = [], cwd = null, priorityFiles = [] }) {
|
|
2825
2851
|
const norm = (p2) => String(p2).replace(/\\/g, '/')
|
|
2826
2852
|
const changedSet = new Set((changedFiles || []).map(norm))
|
|
@@ -2930,10 +2956,11 @@ function buildDepsBatches({ deps, changedFiles = [], hits = [], cwd = null, prio
|
|
|
2930
2956
|
const jsProjectRun = jsProject.filter(f => !E2E_DIR_RE.test(_norm(f)))
|
|
2931
2957
|
const e2eDroppedFiles = jsProject.filter(f => E2E_DIR_RE.test(_norm(f)))
|
|
2932
2958
|
let jsxRunner = null
|
|
2959
|
+
let jsxRunnerFromHits = false
|
|
2933
2960
|
for (const h of hits || []) {
|
|
2934
2961
|
const cmd = String(h.test || '')
|
|
2935
2962
|
const m = /^((?:cd\s+[^&|;]+&&\s*)*[^&|;]*?(?:vitest|jest))(?=\s|$)/.exec(cmd) || /(?:^|&&|;|\|\|)\s*([^&|;]*(?:vitest|jest)(?:\s|$))/.exec(cmd)
|
|
2936
|
-
if (m) { jsxRunner = m[1].trim(); break }
|
|
2963
|
+
if (m) { jsxRunner = m[1].trim(); jsxRunnerFromHits = true; break }
|
|
2937
2964
|
}
|
|
2938
2965
|
if (!jsxRunner && cwd && jsProjectRun.length > 0) {
|
|
2939
2966
|
const inferred = inferJsxRunner(cwd, jsProjectRun[0])
|
|
@@ -2958,24 +2985,59 @@ function buildDepsBatches({ deps, changedFiles = [], hits = [], cwd = null, prio
|
|
|
2958
2985
|
gi++
|
|
2959
2986
|
}
|
|
2960
2987
|
}
|
|
2961
|
-
if (jsNative.length > 0)
|
|
2988
|
+
if (jsNative.length > 0) {
|
|
2989
|
+
const jsPrefix = 'node --test --test-reporter=spec --test-reporter-destination=stderr --test-reporter=tap --test-reporter-destination=stdout '
|
|
2990
|
+
const jsChunks = chunkByCommandLength(jsPrefix, jsNative)
|
|
2991
|
+
jsChunks.forEach((chunk, i) => {
|
|
2992
|
+
const isMulti = jsChunks.length > 1
|
|
2993
|
+
batches.push({ name: isMulti ? `deps(auto-js:${i + 1}/${jsChunks.length})` : 'deps(auto-js)', short: 'js', command: jsPrefix + chunk.join(' '), count: chunk.length, prioCount: chunk.filter(f => prio(f) === 0).length, dropped: i === 0 ? (jsTest.length - jsRun.length) : 0, prioDropped: i === 0 ? jsQ.prioDropped : 0, tap: true, files: chunk })
|
|
2994
|
+
})
|
|
2995
|
+
}
|
|
2962
2996
|
if (jsMeta.length > 0) batches.push({ name: 'deps(auto-js-meta-skip)', short: 'js-meta-skip', command: null, count: jsMeta.length, dropped: 0, skip: true, files: jsMeta, reason: '套件编排器(run-tests.mjs=npm test 组卷入口)非单测文件——内嵌执行=递归全量套件(嵌套 runner 污染假败);全量门由收口实测/CI 的 npm test 承担' })
|
|
2963
2997
|
|
|
2964
2998
|
if (nonTestFiles.length > 0) batches.push({ name: 'deps(auto-nontest-skip)', short: 'nontest-skip', command: null, count: nonTestFiles.length, dropped: 0, skip: true, files: nonTestFiles, reason: `非测试形态文件 ${nonTestFiles.length} 个不进单测执行批(node --test 语义「文件即测试」/ vitest No test suite found / pytest 0 collected exit 5——直跑必假败;坑 verify-dynamic-subset-node-test-ts-source):${nonTestFiles.slice(0, 3).join('、')}${nonTestFiles.length > 3 ? ' 等' : ''}——FR 绑定面脏数据可 sillyspec tests --unbind --tests <路径> 清行` })
|
|
2965
2999
|
if (jsProjectRun.length > 0) {
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
3000
|
+
// R20(2026-09-27-thin-display-fix 实证,用户授权修复):Windows cmd 执行器对含
|
|
3001
|
+
// 括号/方括号/空格的路径(src/app/(dashboard)/workspaces/[id]/...)不加引号会以
|
|
3002
|
+
// 「此时不应有 <。」语法错误炸整批——文件参数含特殊字符时逐个 wrap 双引号。
|
|
3003
|
+
const quoteWinPath = (f) => (/[()\[\]\s&^%,;!]/.test(f) ? `"${f}"` : f)
|
|
3004
|
+
// jsx 批按包根分组(2026-10-15-zcode-engine-appserver verify 实证,跨仓事故驱动修复):
|
|
3005
|
+
// 混合包根(frontend/ 的 tsx 测试 + sillyhub-daemon/ 的 ts 测试同批)共享单一 runner
|
|
3006
|
+
// 前缀时,非本根路径在 runner 的 cwd 下「No test files found」exit 1 恒败(分块修复
|
|
3007
|
+
// 后暴露:第二块全 daemon 路径仍跑 cd frontend)。与 pyGroups 同款:每文件按最近
|
|
3008
|
+
// package.json 推断自身 runner/根;显式命中命令(hits)保持整批单组旧语义。
|
|
3009
|
+
const jsxGroups = new Map() // key: 组命令前缀
|
|
3010
|
+
const jsxUngrouped = []
|
|
3011
|
+
for (const f of jsProjectRun) {
|
|
3012
|
+
// 命中命令(hits)→ 整批单组旧语义;全局推断(jsProjectRun[0])仅兜底,逐文件按自身包根推断优先。
|
|
3013
|
+
let command = null
|
|
3014
|
+
let dirKey = null
|
|
3015
|
+
if (!jsxRunnerFromHits && cwd) {
|
|
3016
|
+
const inferred = inferJsxRunner(cwd, f)
|
|
3017
|
+
if (inferred) { command = inferred.command; dirKey = inferred.dir || null }
|
|
3018
|
+
}
|
|
3019
|
+
if (!command) command = jsxRunner
|
|
3020
|
+
if (!command) { jsxUngrouped.push(f); continue }
|
|
3021
|
+
if (!jsxGroups.has(command)) jsxGroups.set(command, { files: [], dirKey })
|
|
3022
|
+
jsxGroups.get(command).files.push(f)
|
|
3023
|
+
}
|
|
3024
|
+
const multiGroup = jsxGroups.size > 1
|
|
3025
|
+
for (const [command, grp] of jsxGroups) {
|
|
3026
|
+
const isVitest = /vitest/.test(command)
|
|
3027
|
+
const jsxPrefix = `${command} ${isVitest ? 'run ' : ''}`
|
|
3028
|
+
const grpDir = grp.dirKey || /(?:^|\s)cd\s+(\S+)\s*&&/.exec(command)?.[1] || null
|
|
3029
|
+
const rebaseJsx = (f) => (grpDir && f.startsWith(grpDir + '/')) ? f.slice(grpDir.length + 1) : f
|
|
3030
|
+
const jsxArgs = grp.files.map(rebaseJsx).map(quoteWinPath)
|
|
3031
|
+
const jsxChunks = chunkByCommandLength(jsxPrefix, jsxArgs)
|
|
3032
|
+
const baseName = multiGroup ? `deps(auto-jsx:${grp.dirKey || 'root'})` : 'deps(auto-jsx)'
|
|
3033
|
+
jsxChunks.forEach((chunk, i) => {
|
|
3034
|
+
const isMulti = jsxChunks.length > 1
|
|
3035
|
+
batches.push({ name: isMulti ? `${baseName}:${i + 1}/${jsxChunks.length}` : baseName, short: 'jsx', command: jsxPrefix + chunk.join(' '), count: chunk.length, dropped: 0, files: chunk })
|
|
3036
|
+
})
|
|
3037
|
+
}
|
|
3038
|
+
if (jsxUngrouped.length > 0) {
|
|
3039
|
+
// 无运行器可推断的文件(含 !jsxRunner && !cwd 全量形态):skip 批如实披露(原整批 skip 语义按文件收窄)。
|
|
3040
|
+
batches.push({ name: multiGroup || jsxGroups.size > 0 ? 'deps(auto-jsx-skip:unresolved)' : 'deps(auto-jsx-skip)', short: 'jsx-skip', command: null, count: jsxUngrouped.length, dropped: 0, skip: true, files: jsxUngrouped, reason: `JSX 测试文件(tsx/jsx,${jsxUngrouped.length} 个)node 原生不可跑且各自最近 package.json 无 vitest/jest 运行器可推断——转项目运行器执行并如实披露,不制造恒败段(R18-SF-full 残差段 11 连败的坑)` })
|
|
2979
3041
|
}
|
|
2980
3042
|
}
|
|
2981
3043
|
if (e2eDroppedFiles.length > 0) {
|
|
@@ -3042,6 +3104,26 @@ function traceRepoRoots({ specBase, cwd }) {
|
|
|
3042
3104
|
}
|
|
3043
3105
|
|
|
3044
3106
|
/** 残差解析:active·非 superseded·锚命中的 trace 行 → { anchors, rows, files, dangling, perFile } */
|
|
3107
|
+
/**
|
|
3108
|
+
* 主仓侧候选解析根(worktree 感知;2026-10-15-zcode-engine-appserver verify 实证,跨仓
|
|
3109
|
+
* 事故驱动最小修复):变更在隔离 worktree 内实现时,新增测试文件在主仓根不可见——
|
|
3110
|
+
* 悬空判定对主仓文件按「主仓根 ∪ 变更 worktree 根」多根解析(任一存在即非悬空);
|
|
3111
|
+
* 残差执行根同款(文件在 worktree 存在则从 worktree 根跑)。meta 缺失/不可读 → 仅主仓。
|
|
3112
|
+
*/
|
|
3113
|
+
function traceMainRoots({ specBase, changeName, cwd }) {
|
|
3114
|
+
const roots = [cwd]
|
|
3115
|
+
try {
|
|
3116
|
+
const metaPath = join(specBase, '.runtime', 'worktrees', changeName, 'meta.json')
|
|
3117
|
+
if (existsSync(metaPath)) {
|
|
3118
|
+
const meta = JSON.parse(readFileSync(metaPath, 'utf8'))
|
|
3119
|
+
if (meta && typeof meta.worktreePath === 'string' && meta.worktreePath && existsSync(meta.worktreePath) && meta.worktreePath !== cwd) {
|
|
3120
|
+
roots.push(meta.worktreePath)
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
} catch { /* meta 不可读 → 仅主仓(零回归) */ }
|
|
3124
|
+
return roots
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3045
3127
|
export function resolveTraceResidual({ specBase, changeName, cwd }) {
|
|
3046
3128
|
const anchors = resolveVerifyAnchorSet({ specBase, changeName })
|
|
3047
3129
|
if (anchors.length === 0) return { anchors, rows: [], files: [], dangling: [], perFile: [] }
|
|
@@ -3050,19 +3132,25 @@ export function resolveTraceResidual({ specBase, changeName, cwd }) {
|
|
|
3050
3132
|
// tests 条目可携带用例锚(2026-09-26-binding-anchor-fidelity)——文件面/悬空判定走剥锚。
|
|
3051
3133
|
// FR-06(2026-10-09):文件按行 repo 归属配对(perFile),悬空判定/残差执行按行根解析。
|
|
3052
3134
|
const repoRoots = traceRepoRoots({ specBase, cwd })
|
|
3135
|
+
const mainRoots = traceMainRoots({ specBase, changeName, cwd })
|
|
3053
3136
|
const seen = new Set()
|
|
3054
3137
|
const perFile = []
|
|
3055
3138
|
for (const r of rows) {
|
|
3056
3139
|
const repoKey = r.repo && r.repo !== 'main' ? r.repo : null
|
|
3057
3140
|
for (const t of (r.tests || [])) {
|
|
3058
3141
|
const f = testAnchorFile(t)
|
|
3059
|
-
const key = (repoKey ? repoKey + '
|
|
3142
|
+
const key = (repoKey ? repoKey + '\u0000' : '') + f
|
|
3060
3143
|
if (!seen.has(key)) { seen.add(key); perFile.push({ repo: repoKey, file: f }) }
|
|
3061
3144
|
}
|
|
3062
3145
|
}
|
|
3063
3146
|
const files = [...new Set(perFile.map(x => x.file))].sort()
|
|
3147
|
+
// 主仓文件多根解析:解析到 worktree 根则记录 resolvedRoot(残差执行同款消费)。
|
|
3148
|
+
for (const x of perFile) {
|
|
3149
|
+
if (x.repo) { x.resolvedRoot = repoRoots.get(x.repo) || cwd; continue }
|
|
3150
|
+
x.resolvedRoot = mainRoots.find(root => existsSync(resolve(root, x.file))) || cwd
|
|
3151
|
+
}
|
|
3064
3152
|
const dangling = perFile
|
|
3065
|
-
.filter(x => !existsSync(resolve(
|
|
3153
|
+
.filter(x => !existsSync(resolve(x.resolvedRoot, x.file)))
|
|
3066
3154
|
.map(x => x.repo ? `${x.repo}:${x.file}` : x.file)
|
|
3067
3155
|
return { anchors, rows, files, dangling, perFile }
|
|
3068
3156
|
}
|
|
@@ -3141,8 +3229,9 @@ export function applyTraceResidual({ mainResult, action, cwd, specBase, changeNa
|
|
|
3141
3229
|
for (const x of (tr.perFile || [])) {
|
|
3142
3230
|
if (provable.has(String(x.file).replace(/\\/g, '/'))) continue
|
|
3143
3231
|
const _rk = x.repo || null
|
|
3144
|
-
|
|
3145
|
-
const
|
|
3232
|
+
// worktree 感知(同悬空判定口径):主仓行按 resolvedRoot(主仓或变更 worktree 根)取执行根。
|
|
3233
|
+
const _root = (_rk && _repoRoots.get(_rk)) || x.resolvedRoot || cwd
|
|
3234
|
+
const _gk = _rk + '\u0000' + _root
|
|
3146
3235
|
if (!_gmap.has(_gk)) _gmap.set(_gk, { repo: _rk, root: _root, files: [] })
|
|
3147
3236
|
_gmap.get(_gk).files.push(x.file)
|
|
3148
3237
|
}
|
|
@@ -3284,8 +3373,20 @@ export function runModuleSubset({ cwd, specBase, changeName, hits, knownFailures
|
|
|
3284
3373
|
// not ok 以测试文件为套件名(not ok N - test/b.test.mjs),用判账行集匹配批内文件名把
|
|
3285
3374
|
// 「批失败」收窄到「文件失败」;归因不出(非 TAP/行集缺文件名)保守全记批文件。
|
|
3286
3375
|
failedFiles: (() => {
|
|
3376
|
+
// 基建失败甄别(坑 verify-incremental-rerun-infra-batch-growth,2026-10-15 两变更实证:
|
|
3377
|
+
// Windows argv 超限/runner 启动失败类批失败无任何测试级失败行,重跑不可能转绿——计入
|
|
3378
|
+
// 前轮失败批只会让增量面随轮膨胀(141→159 自锁)。判据:输出无 not ok/FAILED/ERROR/
|
|
3379
|
+
// AssertionError 任何测试级痕迹,且 reason 命中基建关键词(命令行太长/E2BIG/spawn/
|
|
3380
|
+
// Cannot find module/No test files found)——此类批不进失败批账本,loud 留痕供人工。
|
|
3381
|
+
const INFRA_REASON_RE = /命令行太长|E2BIG|spawn|Cannot find module|No test files found|ENAMETOOLONG/i
|
|
3382
|
+
const TEST_FAILURE_RE = /not ok \d|FAILED|ERROR|AssertionError|assert/i
|
|
3287
3383
|
const out = new Set()
|
|
3288
3384
|
for (const r of perModule.filter(x => x.status === 'failed')) {
|
|
3385
|
+
const text = String(r.outputFull || r.outputTail || '')
|
|
3386
|
+
if (!TEST_FAILURE_RE.test(text) && INFRA_REASON_RE.test(String(r.reason || '') + text)) {
|
|
3387
|
+
console.warn(`⚠️ 批 ${r.name} 判定基建失败(${String(r.reason || 'runner 层失败').slice(0, 80)})——不进增量失败批账本(重跑不可能转绿),修复基建后整轮重测`)
|
|
3388
|
+
continue
|
|
3389
|
+
}
|
|
3289
3390
|
for (const f of attributeFailedFiles(r)) out.add(f)
|
|
3290
3391
|
}
|
|
3291
3392
|
return [...out].sort()
|
|
@@ -3680,7 +3781,7 @@ export function trackVerifyResultRegression(specBase, changeName, absPath) {
|
|
|
3680
3781
|
if (!absPath || !existsSync(absPath)) return { regressed: false, prevMtime: null }
|
|
3681
3782
|
const st = statSync(absPath)
|
|
3682
3783
|
const hash = createHash('sha256').update(readFileSync(absPath)).digest('hex')
|
|
3683
|
-
const hwmPath = join(specBase, '.runtime', `verify-result-hwm-${changeName}.json`)
|
|
3784
|
+
const hwmPath = runtimeStatePath(join(specBase, '.runtime'), `verify-result-hwm-${changeName}.json`)
|
|
3684
3785
|
let prev = null
|
|
3685
3786
|
try { prev = JSON.parse(readFileSync(hwmPath, 'utf8')) } catch { prev = null }
|
|
3686
3787
|
const regressed = !!(prev && typeof prev.hash === 'string' &&
|
|
@@ -3690,7 +3791,7 @@ export function trackVerifyResultRegression(specBase, changeName, absPath) {
|
|
|
3690
3791
|
if (!prev || st.mtimeMs >= (prev.mtimeMs || 0)) {
|
|
3691
3792
|
try {
|
|
3692
3793
|
mkdirSync(join(specBase, '.runtime'), { recursive: true })
|
|
3693
|
-
writeFileSync(hwmPath, JSON.stringify({ hash, mtimeMs: st.mtimeMs, ts: Date.now() }) + '\n')
|
|
3794
|
+
writeFileSync(hwmPath, JSON.stringify({ hash, mtimeMs: Math.round(st.mtimeMs), ts: Date.now() }) + '\n')
|
|
3694
3795
|
} catch { /* hwm 落盘失败 → 下次当首次(fail-open) */ }
|
|
3695
3796
|
}
|
|
3696
3797
|
return { regressed, prevMtime: prev && prev.mtimeMs ? prev.mtimeMs : null }
|
|
@@ -4207,7 +4308,7 @@ export function resolveReconcileActualFiles({ cwd, specBase, runtimeRoot, change
|
|
|
4207
4308
|
}
|
|
4208
4309
|
} catch { /* 窗口源失败静默省略 */ }
|
|
4209
4310
|
// B3 apply-pathspec 兜底(存在则并入,读取失败静默忽略——前两源不受影响)
|
|
4210
|
-
const pathspecFile =
|
|
4311
|
+
const pathspecFile = runtimeStatePath(runtimeRoot, `apply-pathspec-${changeName}.txt`)
|
|
4211
4312
|
if (existsSync(pathspecFile)) {
|
|
4212
4313
|
try {
|
|
4213
4314
|
const lines = readFileSync(pathspecFile, 'utf8').split('\n').map(l => l.trim()).filter(Boolean).map(normalizeReconcilePath)
|
|
@@ -4604,7 +4705,7 @@ export async function runCeremonyDualRunCheck({ cwd, specBase = null, changeName
|
|
|
4604
4705
|
const rt = runtimeRoot || join(sb, '.runtime')
|
|
4605
4706
|
|
|
4606
4707
|
// —— 声明侧:开跑定价落档(task-03 产物,tier/components/reasons/transitions)——
|
|
4607
|
-
const tierPath =
|
|
4708
|
+
const tierPath = runtimeStatePath(rt, `ceremony-tier-${changeName}.json`)
|
|
4608
4709
|
let tierFile = null
|
|
4609
4710
|
try { tierFile = JSON.parse(readFileSync(tierPath, 'utf8')) } catch { /* 缺失/坏 JSON → 下方跳过 */ }
|
|
4610
4711
|
if (tierFile == null || typeof tierFile !== 'object' || Array.isArray(tierFile)) {
|
|
@@ -4701,7 +4802,7 @@ export async function runCeremonyDualRunCheck({ cwd, specBase = null, changeName
|
|
|
4701
4802
|
recomputedReasons: [`双跑低报自动追赶(事实面 tier=${verdict.factTier},证据面 seed ${actual.files.length} 文件)`],
|
|
4702
4803
|
frictionCounts: {},
|
|
4703
4804
|
})
|
|
4704
|
-
const tierPathForReprice =
|
|
4805
|
+
const tierPathForReprice = runtimeStatePath(rt, `ceremony-tier-${changeName}.json`)
|
|
4705
4806
|
const { writeAtomicSync } = await import('./fs-atomic.js')
|
|
4706
4807
|
writeAtomicSync(tierPathForReprice, JSON.stringify(caught, null, 2) + '\n')
|
|
4707
4808
|
result.violation.message += `;档位文件已自动按事实面追赶重定价(${String(declaredTier)} → ${caught.tier})——修复低报根因后重跑 --done 即对账通过,勿手动改档(ql-013 死锁修复)`
|