sillyspec 3.20.2 → 3.20.4
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-archive/SKILL.md +21 -21
- package/.claude/skills/sillyspec-auto/SKILL.md +83 -83
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +44 -44
- package/.claude/skills/sillyspec-commit/SKILL.md +106 -106
- package/.claude/skills/sillyspec-continue/SKILL.md +45 -45
- package/.claude/skills/sillyspec-doctor/SKILL.md +31 -31
- package/.claude/skills/sillyspec-execute/SKILL.md +30 -30
- package/.claude/skills/sillyspec-explore/SKILL.md +109 -109
- package/.claude/skills/sillyspec-knowledge/SKILL.md +269 -269
- package/.claude/skills/sillyspec-plan/SKILL.md +21 -21
- package/.claude/skills/sillyspec-propose/SKILL.md +21 -21
- package/.claude/skills/sillyspec-quick/SKILL.md +21 -21
- package/.claude/skills/sillyspec-resume/SKILL.md +68 -68
- package/.claude/skills/sillyspec-scan/SKILL.md +21 -21
- package/.claude/skills/sillyspec-state/SKILL.md +54 -54
- package/.claude/skills/sillyspec-status/SKILL.md +21 -21
- package/.claude/skills/sillyspec-verify/SKILL.md +21 -21
- package/.claude/skills/sillyspec-workspace/SKILL.md +157 -157
- package/.husky/pre-push +13 -13
- package/CLAUDE.md +18 -18
- package/README.md +198 -188
- package/SKILL.md +90 -91
- package/bin/sillyspec.js +2 -2
- package/docs/brainstorm-plan-contract.md +64 -64
- package/docs/plan-execute-contract.md +123 -123
- package/docs/platform-scan-protocol.md +298 -298
- package/docs/revision-mode.md +115 -115
- package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +99 -99
- package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +218 -218
- package/docs/sillyspec/file-lifecycle/stage-artifacts.md +167 -167
- package/docs/sillyspec/file-lifecycle/storage-and-state.md +148 -148
- package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +211 -193
- package/docs/sillyspec/file-lifecycle.md +125 -125
- package/docs/workflow-contract-regression.md +106 -106
- package/docs/worktree-isolation.md +252 -252
- package/package.json +40 -40
- package/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -7446
- package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -1
- package/packages/dashboard/dist/index.html +16 -16
- package/packages/dashboard/index.html +15 -15
- package/packages/dashboard/package-lock.json +2384 -2384
- package/packages/dashboard/package.json +25 -25
- package/packages/dashboard/server/executor.js +86 -86
- package/packages/dashboard/server/index.js +588 -588
- package/packages/dashboard/server/parser.js +526 -526
- package/packages/dashboard/server/watcher.js +344 -344
- package/packages/dashboard/src/App.vue +558 -558
- package/packages/dashboard/src/components/ActionBar.vue +93 -93
- package/packages/dashboard/src/components/CommandPalette.vue +96 -96
- package/packages/dashboard/src/components/DetailPanel.vue +137 -137
- package/packages/dashboard/src/components/LogStream.vue +65 -65
- package/packages/dashboard/src/components/PipelineStage.vue +95 -95
- package/packages/dashboard/src/components/PipelineView.vue +156 -156
- package/packages/dashboard/src/components/ProjectList.vue +210 -210
- package/packages/dashboard/src/components/StageBadge.vue +67 -67
- package/packages/dashboard/src/components/StepCard.vue +94 -94
- package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -48
- package/packages/dashboard/src/components/detail/GitDetail.vue +61 -61
- package/packages/dashboard/src/components/detail/TechDetail.vue +43 -43
- package/packages/dashboard/src/composables/useDashboard.js +170 -170
- package/packages/dashboard/src/composables/useKeyboard.js +119 -119
- package/packages/dashboard/src/composables/useWebSocket.js +129 -129
- package/packages/dashboard/src/main.js +8 -8
- package/packages/dashboard/src/style.css +132 -132
- package/packages/dashboard/vite.config.js +18 -18
- package/src/brainstorm-postcheck.js +158 -158
- package/src/change-list.js +52 -52
- package/src/change-risk-profile.js +352 -352
- package/src/classify-change.js +73 -73
- package/src/constants.js +70 -70
- package/src/contract-matrix.js +278 -278
- package/src/db.js +201 -201
- package/src/endpoint-extractor.js +315 -315
- package/src/hooks/claude-pre-tool-use.cjs +125 -125
- package/src/hooks/worktree-guard.js +653 -653
- package/src/index.js +922 -900
- package/src/init.js +431 -431
- package/src/knowledge-match.js +130 -130
- package/src/migrate.js +117 -117
- package/src/modules.js +482 -482
- package/src/progress.js +1734 -1734
- package/src/run.js +3465 -3358
- package/src/scan-postcheck.js +387 -383
- package/src/setup.js +398 -398
- package/src/stage-contract.js +700 -700
- package/src/stages/archive.js +160 -160
- package/src/stages/brainstorm-auto.js +229 -229
- package/src/stages/brainstorm.js +645 -645
- package/src/stages/doctor.js +365 -365
- package/src/stages/execute.js +625 -625
- package/src/stages/explore.js +34 -34
- package/src/stages/index.js +29 -29
- package/src/stages/knowledge.js +498 -498
- package/src/stages/plan-postcheck.js +511 -513
- package/src/stages/plan.js +582 -582
- package/src/stages/propose.js +174 -174
- package/src/stages/quick.js +82 -82
- package/src/stages/scan.js +558 -558
- package/src/stages/status.js +65 -65
- package/src/stages/verify.js +322 -322
- package/src/sync.js +497 -497
- package/src/task-review.js +346 -346
- package/src/workflow.js +785 -785
- package/src/worktree-apply.js +549 -549
- package/src/worktree-deps.js +185 -0
- package/src/worktree.js +982 -932
- package/templates/workflows/archive-impact.yaml +79 -79
- package/templates/workflows/scan-docs.yaml +132 -132
- package/test/brainstorm-plan-contract.test.mjs +273 -273
- package/test/check-syntax.mjs +26 -26
- package/test/contract-artifacts.test.mjs +323 -323
- package/test/decision-supersede.test.mjs +277 -277
- package/test/knowledge-match.test.mjs +231 -231
- package/test/plan-execute-contract.test.mjs +330 -330
- package/test/plan-optimization.test.mjs +572 -572
- package/test/platform-artifacts.test.mjs +166 -166
- package/test/platform-failure-samples.test.mjs +199 -199
- package/test/platform-recovery-chain.test.mjs +167 -167
- package/test/platform-recovery.test.mjs +136 -136
- package/test/platform-scan-p0.test.mjs +168 -168
- package/test/revision-v1.test.mjs +1145 -1145
- package/test/run-scan-project-parse.test.mjs +200 -200
- package/test/run-tests.mjs +48 -48
- package/test/scan-knowledge.test.mjs +175 -175
- package/test/scan-paths.test.mjs +68 -68
- package/test/scan-postcheck.test.mjs +197 -197
- package/test/spec-dir.test.mjs +206 -206
- package/test/stage-contract.test.mjs +299 -299
- package/test/stage-definitions.test.mjs +39 -39
- package/test/wait-gates.test.mjs +496 -496
- package/test/worktree-deps-provision.test.mjs +148 -0
- package/test/worktree-guard.test.mjs +71 -71
- package/test/worktree-native-overlay.test.mjs +188 -188
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 平台 scan 产物协议测试
|
|
3
|
-
*
|
|
4
|
-
* 验证:
|
|
5
|
-
* 1. saveWorkflowRun 传入 runtimeRoot + scanRunId 时写到正确路径
|
|
6
|
-
* 2. manifest.json 结构包含产物指针(postcheck_result_path, workflow_runs_dir)
|
|
7
|
-
* 3. 非平台模式下 workflow-runs 写入 cwd/.sillyspec/.runtime/
|
|
8
|
-
*
|
|
9
|
-
* 跑法: node test/platform-artifacts.test.mjs
|
|
10
|
-
*/
|
|
11
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 平台 scan 产物协议测试
|
|
3
|
+
*
|
|
4
|
+
* 验证:
|
|
5
|
+
* 1. saveWorkflowRun 传入 runtimeRoot + scanRunId 时写到正确路径
|
|
6
|
+
* 2. manifest.json 结构包含产物指针(postcheck_result_path, workflow_runs_dir)
|
|
7
|
+
* 3. 非平台模式下 workflow-runs 写入 cwd/.sillyspec/.runtime/
|
|
8
|
+
*
|
|
9
|
+
* 跑法: node test/platform-artifacts.test.mjs
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
12
|
import { join, dirname } from 'path'
|
|
13
13
|
import { existsSync, mkdirSync, rmSync, readFileSync, readdirSync, writeFileSync } from 'fs'
|
|
14
14
|
import { fileURLToPath } from 'url'
|
|
15
15
|
import { randomUUID } from 'crypto'
|
|
16
16
|
import { tmpdir } from 'os'
|
|
17
|
-
|
|
18
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
19
|
-
const passed = []
|
|
20
|
-
const failed = []
|
|
21
|
-
|
|
22
|
-
function assert(label, condition, detail) {
|
|
23
|
-
if (condition) {
|
|
24
|
-
passed.push(label)
|
|
25
|
-
console.log(` ✅ PASS: ${label}`)
|
|
26
|
-
} else {
|
|
27
|
-
failed.push({ label, detail })
|
|
28
|
-
console.log(` ❌ FAIL: ${label}`)
|
|
29
|
-
if (detail) console.log(` ${detail}`)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
19
|
+
const passed = []
|
|
20
|
+
const failed = []
|
|
21
|
+
|
|
22
|
+
function assert(label, condition, detail) {
|
|
23
|
+
if (condition) {
|
|
24
|
+
passed.push(label)
|
|
25
|
+
console.log(` ✅ PASS: ${label}`)
|
|
26
|
+
} else {
|
|
27
|
+
failed.push({ label, detail })
|
|
28
|
+
console.log(` ❌ FAIL: ${label}`)
|
|
29
|
+
if (detail) console.log(` ${detail}`)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
33
|
function cleanup(dir) {
|
|
34
34
|
try { rmSync(dir, { recursive: true, force: true }) } catch {}
|
|
35
35
|
}
|
|
@@ -41,150 +41,150 @@ function hasPathSegments(value, segments) {
|
|
|
41
41
|
}
|
|
42
42
|
return false
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
// ── 测试 1:saveWorkflowRun 平台模式路径正确 ──
|
|
46
|
-
console.log('\n=== Test 1: saveWorkflowRun 平台模式写入路径 ===')
|
|
47
|
-
{
|
|
44
|
+
|
|
45
|
+
// ── 测试 1:saveWorkflowRun 平台模式路径正确 ──
|
|
46
|
+
console.log('\n=== Test 1: saveWorkflowRun 平台模式写入路径 ===')
|
|
47
|
+
{
|
|
48
48
|
const { saveWorkflowRun } = await import('../src/workflow.js')
|
|
49
49
|
const tmpRoot = join(tmpdir(), `test-artifacts-${randomUUID().slice(0, 8)}`)
|
|
50
|
-
const runtimeRoot = join(tmpRoot, 'runtime')
|
|
51
|
-
const scanRunId = 'scan-20260614-test-001'
|
|
52
|
-
|
|
53
|
-
const result = {
|
|
54
|
-
workflow: 'scan-docs',
|
|
55
|
-
project: 'test-project',
|
|
56
|
-
status: 'pass',
|
|
57
|
-
spec_version: 1,
|
|
58
|
-
roles: [],
|
|
59
|
-
workflow_checks: [],
|
|
60
|
-
failures: [],
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const saved = saveWorkflowRun(result, {
|
|
64
|
-
cwd: '/fake/cwd',
|
|
65
|
-
source: 'test',
|
|
66
|
-
stage: 'scan',
|
|
67
|
-
runtimeRoot,
|
|
68
|
-
scanRunId,
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
const expectedDir = join(runtimeRoot, 'scan-runs', scanRunId, 'workflow-runs')
|
|
72
|
-
assert('workflow-runs 目录存在', existsSync(expectedDir))
|
|
73
|
-
assert('workflow-runs 文件存在', existsSync(saved), `路径: ${saved}`)
|
|
50
|
+
const runtimeRoot = join(tmpRoot, 'runtime')
|
|
51
|
+
const scanRunId = 'scan-20260614-test-001'
|
|
52
|
+
|
|
53
|
+
const result = {
|
|
54
|
+
workflow: 'scan-docs',
|
|
55
|
+
project: 'test-project',
|
|
56
|
+
status: 'pass',
|
|
57
|
+
spec_version: 1,
|
|
58
|
+
roles: [],
|
|
59
|
+
workflow_checks: [],
|
|
60
|
+
failures: [],
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const saved = saveWorkflowRun(result, {
|
|
64
|
+
cwd: '/fake/cwd',
|
|
65
|
+
source: 'test',
|
|
66
|
+
stage: 'scan',
|
|
67
|
+
runtimeRoot,
|
|
68
|
+
scanRunId,
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const expectedDir = join(runtimeRoot, 'scan-runs', scanRunId, 'workflow-runs')
|
|
72
|
+
assert('workflow-runs 目录存在', existsSync(expectedDir))
|
|
73
|
+
assert('workflow-runs 文件存在', existsSync(saved), `路径: ${saved}`)
|
|
74
74
|
assert('路径在 runtime-root 下', saved.startsWith(runtimeRoot), `路径: ${saved}`)
|
|
75
75
|
assert('路径包含 scan-runs', hasPathSegments(saved, ['scan-runs', scanRunId, 'workflow-runs']), `路径: ${saved}`)
|
|
76
76
|
assert('路径包含 scanRunId', hasPathSegments(saved, [scanRunId]), `路径: ${saved}`)
|
|
77
|
-
|
|
78
|
-
// 验证 JSON 内容
|
|
79
|
-
const content = JSON.parse(readFileSync(saved, 'utf8'))
|
|
80
|
-
assert('JSON 有 run_id', !!content.run_id)
|
|
81
|
-
assert('JSON 有 created_at', !!content.created_at)
|
|
82
|
-
assert('JSON source = test', content.source === 'test')
|
|
83
|
-
assert('JSON stage = scan', content.stage === 'scan')
|
|
84
|
-
assert('JSON workflow = scan-docs', content.workflow === 'scan-docs')
|
|
85
|
-
|
|
86
|
-
cleanup(tmpRoot)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ── 测试 2:saveWorkflowRun 本地模式路径正确 ──
|
|
90
|
-
console.log('\n=== Test 2: saveWorkflowRun 本地模式写入路径 ===')
|
|
91
|
-
{
|
|
77
|
+
|
|
78
|
+
// 验证 JSON 内容
|
|
79
|
+
const content = JSON.parse(readFileSync(saved, 'utf8'))
|
|
80
|
+
assert('JSON 有 run_id', !!content.run_id)
|
|
81
|
+
assert('JSON 有 created_at', !!content.created_at)
|
|
82
|
+
assert('JSON source = test', content.source === 'test')
|
|
83
|
+
assert('JSON stage = scan', content.stage === 'scan')
|
|
84
|
+
assert('JSON workflow = scan-docs', content.workflow === 'scan-docs')
|
|
85
|
+
|
|
86
|
+
cleanup(tmpRoot)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── 测试 2:saveWorkflowRun 本地模式路径正确 ──
|
|
90
|
+
console.log('\n=== Test 2: saveWorkflowRun 本地模式写入路径 ===')
|
|
91
|
+
{
|
|
92
92
|
const { saveWorkflowRun } = await import('../src/workflow.js')
|
|
93
93
|
const tmpCwd = join(tmpdir(), `test-artifacts-local-${randomUUID().slice(0, 8)}`)
|
|
94
|
-
const sillyspecDir = join(tmpCwd, '.sillyspec', '.runtime', 'workflow-runs')
|
|
95
|
-
|
|
96
|
-
const result = {
|
|
97
|
-
workflow: 'test-wf',
|
|
98
|
-
project: 'default',
|
|
99
|
-
status: 'fail',
|
|
100
|
-
spec_version: 1,
|
|
101
|
-
roles: [],
|
|
102
|
-
workflow_checks: [],
|
|
103
|
-
failures: ['check-1 failed'],
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const saved = saveWorkflowRun(result, {
|
|
107
|
-
cwd: tmpCwd,
|
|
108
|
-
source: 'test',
|
|
109
|
-
stage: 'scan',
|
|
110
|
-
// 不传 runtimeRoot 和 scanRunId
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
assert('本地模式文件存在', existsSync(saved))
|
|
94
|
+
const sillyspecDir = join(tmpCwd, '.sillyspec', '.runtime', 'workflow-runs')
|
|
95
|
+
|
|
96
|
+
const result = {
|
|
97
|
+
workflow: 'test-wf',
|
|
98
|
+
project: 'default',
|
|
99
|
+
status: 'fail',
|
|
100
|
+
spec_version: 1,
|
|
101
|
+
roles: [],
|
|
102
|
+
workflow_checks: [],
|
|
103
|
+
failures: ['check-1 failed'],
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const saved = saveWorkflowRun(result, {
|
|
107
|
+
cwd: tmpCwd,
|
|
108
|
+
source: 'test',
|
|
109
|
+
stage: 'scan',
|
|
110
|
+
// 不传 runtimeRoot 和 scanRunId
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
assert('本地模式文件存在', existsSync(saved))
|
|
114
114
|
assert('本地路径在 .sillyspec/.runtime 下', hasPathSegments(saved, ['.sillyspec', '.runtime', 'workflow-runs']), `路径: ${saved}`)
|
|
115
|
-
|
|
116
|
-
const content = JSON.parse(readFileSync(saved, 'utf8'))
|
|
117
|
-
assert('本地 JSON status = fail', content.status === 'fail')
|
|
118
|
-
assert('本地 JSON 有 failures', Array.isArray(content.failures))
|
|
119
|
-
|
|
120
|
-
cleanup(tmpCwd)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// ── 测试 3:manifest 结构验证(从 run.js 源码静态检查) ──
|
|
124
|
-
console.log('\n=== Test 3: manifest.json 结构字段 ===')
|
|
125
|
-
{
|
|
126
|
-
const { readFile } = await import('fs/promises')
|
|
127
|
-
const runSrc = await readFile(join(__dirname, '..', 'src', 'run.js'), 'utf8')
|
|
128
|
-
|
|
129
|
-
// 平台模式 manifest 初始化
|
|
130
|
-
assert('manifest 包含 workspace_id', runSrc.includes('workspace_id:'))
|
|
131
|
-
assert('manifest 包含 scan_run_id', runSrc.includes('scan_run_id:'))
|
|
132
|
-
assert('manifest 包含 source_commit', runSrc.includes('source_commit:'))
|
|
133
|
-
assert('manifest 包含 source_commit_error', runSrc.includes('source_commit_error:'))
|
|
134
|
-
assert('manifest 包含 generated_at', runSrc.includes('generated_at:'))
|
|
135
|
-
assert('manifest 包含 schema_version', runSrc.includes('schema_version:'))
|
|
136
|
-
assert('manifest 包含 postcheck_result_path', runSrc.includes('postcheck_result_path:'))
|
|
137
|
-
assert('manifest 包含 workflow_runs_dir', runSrc.includes('workflow_runs_dir:'))
|
|
138
|
-
|
|
139
|
-
// postcheck_result_path 在 postcheck 写入后填充
|
|
140
|
-
assert('manifest.postcheck_result_path 下游填充', runSrc.includes('manifest.postcheck_result_path = postcheckJsonPath'))
|
|
141
|
-
|
|
142
|
-
// workflow_runs_dir 使用 runtimeRoot + scanRunId
|
|
143
|
-
assert('workflow_runs_dir 基于 runtimeRoot', runSrc.includes("join(platformOpts.runtimeRoot, 'scan-runs'"))
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// ── 测试 4:平台指针状态更新(源码检查) ──
|
|
147
|
-
console.log('\n=== Test 4: 平台指针 scan 完成后状态更新 ===')
|
|
148
|
-
{
|
|
149
|
-
const { readFile } = await import('fs/promises')
|
|
150
|
-
const runSrc = await readFile(join(__dirname, '..', 'src', 'run.js'), 'utf8')
|
|
151
|
-
|
|
152
|
-
assert('scan 完成后读取 pointer 文件', runSrc.includes('pointerPath'))
|
|
153
|
-
assert('pointer status 使用 POINTER_STATUS 枚举', runSrc.includes('POINTER_STATUS'))
|
|
154
|
-
assert('pointer 记录 completedAt', runSrc.includes('pointer.completedAt'))
|
|
155
|
-
assert('pointer 记录 scanStatus', runSrc.includes('pointer.scanStatus'))
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ── 测试 5:saveWorkflowRun 调用点传入 runtimeRoot(源码检查) ──
|
|
159
|
-
console.log('\n=== Test 5: run.js 调用 saveWorkflowRun 传入平台参数 ===')
|
|
160
|
-
{
|
|
161
|
-
const { readFile } = await import('fs/promises')
|
|
162
|
-
const runSrc = await readFile(join(__dirname, '..', 'src', 'run.js'), 'utf8')
|
|
163
|
-
|
|
164
|
-
// 找到 saveWorkflowRun 调用
|
|
165
|
-
const calls = runSrc.match(/saveWorkflowRun\([^)]+\{[^}]+\}/gs)
|
|
166
|
-
assert('至少有 2 处 saveWorkflowRun 调用', calls && calls.length >= 2, `实际: ${calls?.length}`)
|
|
167
|
-
|
|
168
|
-
// 检查调用是否包含 runtimeRoot 传递
|
|
169
|
-
const hasRuntimeRoot = runSrc.includes('platformOpts.runtimeRoot ? { runtimeRoot: platformOpts.runtimeRoot }')
|
|
170
|
-
assert('saveWorkflowRun 调用传入 runtimeRoot', hasRuntimeRoot, '未发现 runtimeRoot 传递')
|
|
171
|
-
|
|
172
|
-
const hasScanRunId = runSrc.includes('platformOpts.scanRunId ? { scanRunId: platformOpts.scanRunId }')
|
|
173
|
-
assert('saveWorkflowRun 调用传入 scanRunId', hasScanRunId, '未发现 scanRunId 传递')
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// ── 结果 ──
|
|
177
|
-
console.log(`\n${'='.repeat(50)}`)
|
|
178
|
-
console.log(`✅ 通过: ${passed.length} ❌ 失败: ${failed.length}`)
|
|
179
|
-
console.log(`${'='.repeat(50)}`)
|
|
180
|
-
|
|
181
|
-
if (failed.length > 0) {
|
|
182
|
-
console.log('\n失败详情:')
|
|
183
|
-
for (const f of failed) {
|
|
184
|
-
console.log(` ❌ ${f.label}`)
|
|
185
|
-
if (f.detail) console.log(` ${f.detail}`)
|
|
186
|
-
}
|
|
187
|
-
throw new Error('platform-artifacts test failed')
|
|
188
|
-
} else {
|
|
189
|
-
console.log('\n🎉 平台产物协议测试全部通过!')
|
|
190
|
-
}
|
|
115
|
+
|
|
116
|
+
const content = JSON.parse(readFileSync(saved, 'utf8'))
|
|
117
|
+
assert('本地 JSON status = fail', content.status === 'fail')
|
|
118
|
+
assert('本地 JSON 有 failures', Array.isArray(content.failures))
|
|
119
|
+
|
|
120
|
+
cleanup(tmpCwd)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── 测试 3:manifest 结构验证(从 run.js 源码静态检查) ──
|
|
124
|
+
console.log('\n=== Test 3: manifest.json 结构字段 ===')
|
|
125
|
+
{
|
|
126
|
+
const { readFile } = await import('fs/promises')
|
|
127
|
+
const runSrc = await readFile(join(__dirname, '..', 'src', 'run.js'), 'utf8')
|
|
128
|
+
|
|
129
|
+
// 平台模式 manifest 初始化
|
|
130
|
+
assert('manifest 包含 workspace_id', runSrc.includes('workspace_id:'))
|
|
131
|
+
assert('manifest 包含 scan_run_id', runSrc.includes('scan_run_id:'))
|
|
132
|
+
assert('manifest 包含 source_commit', runSrc.includes('source_commit:'))
|
|
133
|
+
assert('manifest 包含 source_commit_error', runSrc.includes('source_commit_error:'))
|
|
134
|
+
assert('manifest 包含 generated_at', runSrc.includes('generated_at:'))
|
|
135
|
+
assert('manifest 包含 schema_version', runSrc.includes('schema_version:'))
|
|
136
|
+
assert('manifest 包含 postcheck_result_path', runSrc.includes('postcheck_result_path:'))
|
|
137
|
+
assert('manifest 包含 workflow_runs_dir', runSrc.includes('workflow_runs_dir:'))
|
|
138
|
+
|
|
139
|
+
// postcheck_result_path 在 postcheck 写入后填充
|
|
140
|
+
assert('manifest.postcheck_result_path 下游填充', runSrc.includes('manifest.postcheck_result_path = postcheckJsonPath'))
|
|
141
|
+
|
|
142
|
+
// workflow_runs_dir 使用 runtimeRoot + scanRunId
|
|
143
|
+
assert('workflow_runs_dir 基于 runtimeRoot', runSrc.includes("join(platformOpts.runtimeRoot, 'scan-runs'"))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ── 测试 4:平台指针状态更新(源码检查) ──
|
|
147
|
+
console.log('\n=== Test 4: 平台指针 scan 完成后状态更新 ===')
|
|
148
|
+
{
|
|
149
|
+
const { readFile } = await import('fs/promises')
|
|
150
|
+
const runSrc = await readFile(join(__dirname, '..', 'src', 'run.js'), 'utf8')
|
|
151
|
+
|
|
152
|
+
assert('scan 完成后读取 pointer 文件', runSrc.includes('pointerPath'))
|
|
153
|
+
assert('pointer status 使用 POINTER_STATUS 枚举', runSrc.includes('POINTER_STATUS'))
|
|
154
|
+
assert('pointer 记录 completedAt', runSrc.includes('pointer.completedAt'))
|
|
155
|
+
assert('pointer 记录 scanStatus', runSrc.includes('pointer.scanStatus'))
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ── 测试 5:saveWorkflowRun 调用点传入 runtimeRoot(源码检查) ──
|
|
159
|
+
console.log('\n=== Test 5: run.js 调用 saveWorkflowRun 传入平台参数 ===')
|
|
160
|
+
{
|
|
161
|
+
const { readFile } = await import('fs/promises')
|
|
162
|
+
const runSrc = await readFile(join(__dirname, '..', 'src', 'run.js'), 'utf8')
|
|
163
|
+
|
|
164
|
+
// 找到 saveWorkflowRun 调用
|
|
165
|
+
const calls = runSrc.match(/saveWorkflowRun\([^)]+\{[^}]+\}/gs)
|
|
166
|
+
assert('至少有 2 处 saveWorkflowRun 调用', calls && calls.length >= 2, `实际: ${calls?.length}`)
|
|
167
|
+
|
|
168
|
+
// 检查调用是否包含 runtimeRoot 传递
|
|
169
|
+
const hasRuntimeRoot = runSrc.includes('platformOpts.runtimeRoot ? { runtimeRoot: platformOpts.runtimeRoot }')
|
|
170
|
+
assert('saveWorkflowRun 调用传入 runtimeRoot', hasRuntimeRoot, '未发现 runtimeRoot 传递')
|
|
171
|
+
|
|
172
|
+
const hasScanRunId = runSrc.includes('platformOpts.scanRunId ? { scanRunId: platformOpts.scanRunId }')
|
|
173
|
+
assert('saveWorkflowRun 调用传入 scanRunId', hasScanRunId, '未发现 scanRunId 传递')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ── 结果 ──
|
|
177
|
+
console.log(`\n${'='.repeat(50)}`)
|
|
178
|
+
console.log(`✅ 通过: ${passed.length} ❌ 失败: ${failed.length}`)
|
|
179
|
+
console.log(`${'='.repeat(50)}`)
|
|
180
|
+
|
|
181
|
+
if (failed.length > 0) {
|
|
182
|
+
console.log('\n失败详情:')
|
|
183
|
+
for (const f of failed) {
|
|
184
|
+
console.log(` ❌ ${f.label}`)
|
|
185
|
+
if (f.detail) console.log(` ${f.detail}`)
|
|
186
|
+
}
|
|
187
|
+
throw new Error('platform-artifacts test failed')
|
|
188
|
+
} else {
|
|
189
|
+
console.log('\n🎉 平台产物协议测试全部通过!')
|
|
190
|
+
}
|