sillyspec 3.26.10 → 3.26.11
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 +85 -85
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -114
- package/.claude/skills/sillyspec-commit/SKILL.md +109 -109
- package/.claude/skills/sillyspec-continue/SKILL.md +45 -45
- package/.claude/skills/sillyspec-doctor/SKILL.md +76 -76
- package/.claude/skills/sillyspec-execute/SKILL.md +200 -200
- package/.claude/skills/sillyspec-explore/SKILL.md +95 -95
- package/.claude/skills/sillyspec-knowledge/SKILL.md +269 -269
- package/.claude/skills/sillyspec-plan/SKILL.md +148 -148
- package/.claude/skills/sillyspec-quick/SKILL.md +111 -111
- package/.claude/skills/sillyspec-resume/SKILL.md +68 -68
- package/.claude/skills/sillyspec-scan/SKILL.md +79 -79
- package/.claude/skills/sillyspec-state/SKILL.md +64 -64
- package/.claude/skills/sillyspec-status/SKILL.md +54 -54
- package/.claude/skills/sillyspec-verify/SKILL.md +83 -83
- package/.claude/skills/sillyspec-workspace/SKILL.md +169 -169
- package/.gitattributes +2 -0
- package/.husky/pre-push +20 -20
- package/.npmrc-publish +2 -0
- package/README.md +201 -201
- package/SKILL.md +90 -90
- package/package.json +39 -39
- 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/package.json +25 -25
- package/packages/dashboard/server/executor.js +86 -86
- package/packages/dashboard/server/index.js +601 -601
- package/packages/dashboard/server/parser.js +526 -526
- package/src/change-risk-profile.js +254 -254
- package/src/classify-change.js +109 -109
- package/src/config-schema.js +310 -310
- package/src/contract-matrix.js +379 -379
- package/src/db.js +350 -350
- package/src/dispatch/backends/local-agent.js +155 -155
- package/src/dispatch/backends/sillyhub-mcp.js +245 -245
- package/src/dispatch/probe.js +210 -210
- package/src/dispatch/strategy.js +121 -121
- package/src/docs-check.js +131 -4
- package/src/doctor-diagnostics.js +711 -711
- package/src/endpoint-extractor.js +401 -401
- package/src/fs-atomic.js +69 -69
- package/src/git-helper.js +79 -79
- package/src/hooks/claude-pre-tool-use.cjs +125 -125
- package/src/hooks/worktree-guard.js +848 -848
- package/src/index.js +90 -8
- package/src/init.js +623 -623
- package/src/knowledge-match.js +130 -130
- package/src/local-detect.js +120 -120
- package/src/machine-interface.js +543 -543
- package/src/migrate.js +127 -127
- package/src/progress/change-registry.js +297 -260
- package/src/progress/consistency-doctor.js +355 -355
- package/src/progress/shared.js +36 -36
- package/src/progress/stage-machine.js +29 -1
- package/src/progress/step-store.js +261 -261
- package/src/progress.js +1131 -1127
- package/src/quicklog.js +693 -693
- package/src/review-tier.js +77 -77
- package/src/run/command.js +1385 -1364
- package/src/run/complete-handlers.js +1443 -1399
- package/src/run/complete.js +225 -24
- package/src/run/gates.js +755 -755
- package/src/run/prompt.js +761 -761
- package/src/run/quick-audit.js +155 -146
- package/src/run/shared.js +1268 -1215
- package/src/run/stage.js +487 -479
- package/src/scan-postcheck.js +371 -371
- package/src/setup.js +416 -416
- package/src/sillyhub-mcp/client.js +529 -529
- package/src/spec-sync.js +323 -319
- package/src/stage-contract.js +897 -897
- package/src/stage-review.js +549 -549
- package/src/stages/archive.js +155 -155
- package/src/stages/brainstorm-auto.js +236 -236
- package/src/stages/brainstorm.js +596 -597
- package/src/stages/cmd-existence.js +145 -145
- package/src/stages/doctor.js +371 -371
- package/src/stages/execute.js +1003 -1003
- 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 +1302 -1302
- package/src/stages/plan.js +625 -623
- package/src/stages/quick.js +151 -151
- package/src/stages/scan.js +607 -607
- package/src/stages/status.js +64 -64
- package/src/stages/verify.js +264 -264
- package/src/sync.js +1223 -1135
- package/src/task-review.js +1019 -1019
- package/src/verify-postcheck.js +1186 -1186
- package/src/workflow.js +600 -600
- package/src/worktree-apply.js +1213 -1136
- package/src/worktree-deps.js +353 -353
- package/src/worktree.js +1481 -1481
- package/templates/workflows/archive-impact.yaml +79 -79
- package/templates/workflows/scan-docs.yaml +132 -132
|
@@ -1,255 +1,255 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* change-risk-profile.js — 变更风险分级检测
|
|
3
|
-
*
|
|
4
|
-
* 根据变更涉及的文件类型、关键词、git diff、brainstorm 产物,
|
|
5
|
-
* 判定 5 级风险(doc-only / unit-sufficient / contract-required /
|
|
6
|
-
* integration-critical / deployment-critical),产出门控验证需求。
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// ============ 向后兼容:旧的 INTEGRATION_CRITICAL_PATTERNS ============
|
|
10
|
-
|
|
11
|
-
const INTEGRATION_CRITICAL_PATTERNS = [
|
|
12
|
-
/\bdaemon\b/i,
|
|
13
|
-
/\bbackend\b/i,
|
|
14
|
-
/\bclient.*api\b/i,
|
|
15
|
-
/\bgrpc\b/i,
|
|
16
|
-
/\bwebsocket\b/i,
|
|
17
|
-
/\bhttp.*client\b/i,
|
|
18
|
-
/\bsession\b/i,
|
|
19
|
-
/\blease\b/i,
|
|
20
|
-
/\bagent.?run\b/i,
|
|
21
|
-
/\blifecycle\b/i,
|
|
22
|
-
/\bstate.?transition\b/i,
|
|
23
|
-
/\bclaim\b/i,
|
|
24
|
-
/\bheartbeat\b/i,
|
|
25
|
-
/\bcross.?process\b/i,
|
|
26
|
-
/\bipc\b/i,
|
|
27
|
-
/\bmessage.?queue\b/i,
|
|
28
|
-
/\bpub.?sub\b/i,
|
|
29
|
-
/\bcli\.ts\b/i,
|
|
30
|
-
/\bmain\.ts\b/i,
|
|
31
|
-
/\bentrypoint\b/i,
|
|
32
|
-
/\bserver\.(js|ts)\b/i,
|
|
33
|
-
/\bbootstrap\b/i,
|
|
34
|
-
/\bdockerfile\b/i,
|
|
35
|
-
/\bdocker.?compose\b/i,
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
const INTEGRATION_FILE_PATTERNS = [
|
|
39
|
-
/daemon/i,
|
|
40
|
-
/session.?manager/i,
|
|
41
|
-
/agent.?run/i,
|
|
42
|
-
/lifecycle/i,
|
|
43
|
-
/state.?machine/i,
|
|
44
|
-
/lease/i,
|
|
45
|
-
/cli\.(js|ts)$/,
|
|
46
|
-
/main\.(js|ts)$/,
|
|
47
|
-
/server\.(js|ts)$/,
|
|
48
|
-
/bootstrap/i,
|
|
49
|
-
/startup/i,
|
|
50
|
-
]
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* task 级「端到端/deployment-critical」文本判定(execute 批量完成 autoCheckPlanFromReviews 用)。
|
|
54
|
-
*
|
|
55
|
-
* 聚焦需要真实集成/启动验证的 task,区别于 change 级 INTEGRATION_CRITICAL_PATTERNS(含 backend/session
|
|
56
|
-
* 等泛词——task 级用会误伤普通后端 task)。命中端到端集成语义(端到端/真实集成/daemon↔backend/
|
|
57
|
-
* integration test/e2e)或部署启动入口(cli.ts/main.ts/entrypoint/server/bootstrap/startup/docker)。
|
|
58
|
-
* 坑 execute-batch-complete-endtoend-checkbox:这类 task 的 review cannot_verify 不算 checked,
|
|
59
|
-
* 阻断 execute 批量完成(必须 pass),防端到端未真验就被批量放行(verify integration-evidence 兜底外的
|
|
60
|
-
* execute 侧前置门)。
|
|
61
|
-
* @param {string} text task 描述文本(plan.md task 行 + tasks/task-XX.md 内容)
|
|
62
|
-
* @returns {boolean}
|
|
63
|
-
*/
|
|
64
|
-
const END_TO_END_TASK_KEYWORDS = [
|
|
65
|
-
/端到端/, /真实集成/, /daemon.*backend/i, /integration\s*test/i, /e2e\s*test/i,
|
|
66
|
-
/runtime\s*evidence/i, /运行时证据/,
|
|
67
|
-
/\bcli\.ts\b/i, /\bmain\.ts\b/i, /\bentrypoint\b/i, /\bserver\.(js|ts)\b/i,
|
|
68
|
-
/\bbootstrap\b/i, /\bstartup\b/i, /启动/, /\bdockerfile\b/i, /\bdocker.?compose\b/i,
|
|
69
|
-
]
|
|
70
|
-
export function isEndToEndTaskText(text = '') {
|
|
71
|
-
return END_TO_END_TASK_KEYWORDS.some(re => re.test(text))
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ============ 门控可执行化(A:报错说人话) ============
|
|
75
|
-
//
|
|
76
|
-
// 历史教训:integration-critical / deployment-critical 门控只报「缺少真实集成证据 / 需要真实
|
|
77
|
-
// 启动验证证据」,agent 看不出具体缺哪一项、要写/做什么才算过,只能靠改结论文案撞墙。
|
|
78
|
-
// 此处把每一项 requiredVerification 的需求(写什么 / 做什么)和判级原因显式导出,
|
|
79
|
-
// 供 stage-contract 拼成可执行的报错信息。
|
|
80
|
-
|
|
81
|
-
/** 每项 requiredVerification 的需求描述 + 字面期望 */
|
|
82
|
-
export const VERIFICATION_NEEDS = {
|
|
83
|
-
unit_tests: {
|
|
84
|
-
desc: '单元测试(verify 必做;在 verify-result.md 写明测试套件与结果)',
|
|
85
|
-
literals: [],
|
|
86
|
-
},
|
|
87
|
-
contract_tests: {
|
|
88
|
-
desc: '契约测试(前后端/跨进程 API parity 对账,写明结论)',
|
|
89
|
-
literals: [],
|
|
90
|
-
},
|
|
91
|
-
real_daemon_backend_integration: {
|
|
92
|
-
desc: '真实 daemon↔backend 集成验证(非仅 mock 单测)。写「真实集成/端到端」的证据;',
|
|
93
|
-
literals: ['端到端', 'integration test', 'e2e test', 'daemon.*backend', '真实集成', 'runtime evidence', '运行时证据'],
|
|
94
|
-
},
|
|
95
|
-
runtime_log_evidence: {
|
|
96
|
-
desc: 'Runtime Evidence section + 指向真实日志/证据片段。',
|
|
97
|
-
literals: ['Runtime Evidence', '运行时证据', 'daemon log', '日志片段'],
|
|
98
|
-
},
|
|
99
|
-
real_startup_once: {
|
|
100
|
-
desc: '真实启动一次本变更触及的部署/启动入口(服务入口、CLI 主入口、守护进程等——须是本变更实际改动的那一类入口,不能拿无关进程的启动来凑数)。',
|
|
101
|
-
literals: ['启动.*一次', '实际.*启动', 'real startup', 'docker up', 'npm start', 'node server'],
|
|
102
|
-
},
|
|
103
|
-
terminal_state_assertion: {
|
|
104
|
-
desc: '终态断言(建议项,不阻断):AgentRun running→completed/failed、session/lease end 状态同步。',
|
|
105
|
-
literals: ['terminal state', '终态', 'completed failed', 'session end', 'lease end'],
|
|
106
|
-
},
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** 风险分级的判定来源(命中关键词来源 = design.md / plan.md 内容,非改动文件本身) */
|
|
110
|
-
export const RISK_LEVEL_CAUSES = {
|
|
111
|
-
'deployment-critical':
|
|
112
|
-
'design.md / plan.md 命中启动入口关键词(cli.ts / main.ts / server.(js|ts) / bootstrap / entrypoint)。' +
|
|
113
|
-
'注意:这是按 design/plan 里的措辞判定的,不一定代表你真改了启动入口;若属误判可在 design.md frontmatter 用 risk_level 显式声明真实等级(如 unit-sufficient)覆盖——不要靠删措辞绕门控,危险链路该有真实启动证据。',
|
|
114
|
-
'integration-critical':
|
|
115
|
-
'design.md / plan.md 命中跨进程/状态机关键词(daemon / backend / session / lease / lifecycle / heartbeat 等)。',
|
|
116
|
-
'contract-required': 'design.md / plan.md 命中 API contract 关键词(api / client / contract / dto)。',
|
|
117
|
-
'explicit': 'design.md frontmatter 的 risk_level 显式声明(覆盖关键词判级)。',
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** design.md frontmatter 可显式声明的合法 risk_level 值(与 detectChangeRisk 判级结果同集合) */
|
|
121
|
-
const RISK_LEVELS = ['doc-only', 'unit-sufficient', 'contract-required', 'integration-critical', 'deployment-critical']
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* 从 design.md 顶部 frontmatter 提取显式 risk_level 声明。
|
|
125
|
-
* 只认文档最开头 `---\n...\n---` 块内的 `risk_level: <level>` 单行(与 plan.md 的 plan_level 同款解析),
|
|
126
|
-
* 不扫正文——避免正文里讨论 risk_level 措辞时被误当声明。返回合法 level 或 null。
|
|
127
|
-
*/
|
|
128
|
-
export function extractExplicitRiskLevel(designContent = '') {
|
|
129
|
-
const fm = designContent.match(/^---\r?\n([\s\S]*?)\r?\n---/)
|
|
130
|
-
if (!fm) return null
|
|
131
|
-
const line = fm[1].split('\n').map(l => l.trim()).find(l => l.startsWith('risk_level:'))
|
|
132
|
-
if (!line) return null
|
|
133
|
-
const value = line.slice('risk_level:'.length).trim().replace(/^["']|["']$/g, '').toLowerCase()
|
|
134
|
-
return RISK_LEVELS.includes(value) ? value : null
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function detectChangeRisk({ designContent = '', planContent = '', changedFiles = [] } = {}) {
|
|
138
|
-
const triggers = []
|
|
139
|
-
|
|
140
|
-
// ── 显式豁免优先:design.md frontmatter 声明 risk_level → 以声明为准,跳过关键词判级 ──
|
|
141
|
-
// 历史教训:detectChangeRisk 是机械字面匹配,不认否定语境——design 写「本次不改动 daemon/session」
|
|
142
|
-
// 仍命中关键词被误判 integration/deployment-critical,强制要全套集成证据,对无状态后端变更误伤。
|
|
143
|
-
// 与其在正则层做脆弱的否定识别,不如给一条显式、诚实、可审计(落在 design frontmatter + verify-result)
|
|
144
|
-
// 的覆盖通道。声明仍走门控(结论 FAIL 仍拦),但豁免级(unit-sufficient 等)不再强制集成证据。
|
|
145
|
-
const explicit = extractExplicitRiskLevel(designContent)
|
|
146
|
-
if (explicit) {
|
|
147
|
-
const requiredVerification =
|
|
148
|
-
explicit === 'deployment-critical' ? ['unit_tests', 'contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'real_startup_once'] :
|
|
149
|
-
explicit === 'integration-critical' ? ['unit_tests', 'contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'terminal_state_assertion'] :
|
|
150
|
-
explicit === 'contract-required' ? ['unit_tests', 'contract_tests'] :
|
|
151
|
-
explicit === 'doc-only' ? ['static_check'] :
|
|
152
|
-
['unit_tests']
|
|
153
|
-
return { level: explicit, triggers: ['risk_level (explicit)'], requiredVerification, explicit: true }
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const combined = [designContent, planContent].join('\n')
|
|
157
|
-
|
|
158
|
-
for (const pattern of INTEGRATION_CRITICAL_PATTERNS) {
|
|
159
|
-
if (pattern.test(combined)) {
|
|
160
|
-
pattern.lastIndex = 0
|
|
161
|
-
const match = combined.match(pattern)
|
|
162
|
-
if (match && !triggers.includes(match[0])) triggers.push(match[0])
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
for (const file of changedFiles) {
|
|
167
|
-
for (const pattern of INTEGRATION_FILE_PATTERNS) {
|
|
168
|
-
if (pattern.test(file)) {
|
|
169
|
-
pattern.lastIndex = 0
|
|
170
|
-
const match = file.match(pattern)
|
|
171
|
-
if (match && !triggers.includes(match[0])) triggers.push(match[0])
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (triggers.length === 0) {
|
|
177
|
-
return { level: 'doc-only', triggers: [], requiredVerification: ['static_check'] }
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const deploymentTrigger = triggers.some(t => /cli\.ts|main\.ts|server\.(js|ts)|bootstrap|entrypoint/i.test(t))
|
|
181
|
-
const lifecycleTrigger = triggers.some(t => /session|lease|agent.?run|lifecycle|state.?transition|claim|heartbeat/i.test(t))
|
|
182
|
-
const crossProcessTrigger = triggers.some(t => /daemon|backend|client.*api|grpc|websocket|cross.?process|ipc|message.?queue/i.test(t))
|
|
183
|
-
|
|
184
|
-
let level
|
|
185
|
-
const requiredVerification = ['unit_tests']
|
|
186
|
-
|
|
187
|
-
if (deploymentTrigger) {
|
|
188
|
-
level = 'deployment-critical'
|
|
189
|
-
requiredVerification.push('contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'real_startup_once')
|
|
190
|
-
} else if (lifecycleTrigger || crossProcessTrigger) {
|
|
191
|
-
level = 'integration-critical'
|
|
192
|
-
requiredVerification.push('contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'terminal_state_assertion')
|
|
193
|
-
} else if (triggers.some(t => /api|client|contract|dto/i.test(t))) {
|
|
194
|
-
level = 'contract-required'
|
|
195
|
-
requiredVerification.push('contract_tests')
|
|
196
|
-
} else {
|
|
197
|
-
level = 'unit-sufficient'
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return { level, triggers, requiredVerification }
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* 检查 verify-result.md 是否包含集成验证证据
|
|
205
|
-
*/
|
|
206
|
-
export function checkIntegrationEvidence(verifyContent, requiredVerification) {
|
|
207
|
-
const errors = []
|
|
208
|
-
const warnings = []
|
|
209
|
-
const lower = verifyContent.toLowerCase()
|
|
210
|
-
|
|
211
|
-
// 字面证据正则从 VERIFICATION_NEEDS[k].literals 派生——与报错描述、prompt 事前契约严格同源,
|
|
212
|
-
// 杜绝历史上"描述说 A、正则查 B"的分叉(checkIntegrationEvidence 正则曾比 VERIFICATION_NEEDS
|
|
213
|
-
// 描述多匹配 集成测试/backend.*daemon/real.*integration 等)。literals 为空的 need
|
|
214
|
-
// (unit_tests/contract_tests)无字面校验,视为满足。
|
|
215
|
-
const hasEvidence = (k) => {
|
|
216
|
-
const n = VERIFICATION_NEEDS[k]
|
|
217
|
-
if (!n || !n.literals || n.literals.length === 0) return true
|
|
218
|
-
return new RegExp(n.literals.join('|'), 'i').test(lower)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const needsIntegration = requiredVerification.includes('real_daemon_backend_integration')
|
|
222
|
-
const needsLogEvidence = requiredVerification.includes('runtime_log_evidence')
|
|
223
|
-
const needsTerminalState = requiredVerification.includes('terminal_state_assertion')
|
|
224
|
-
const needsRealStartup = requiredVerification.includes('real_startup_once')
|
|
225
|
-
|
|
226
|
-
if (needsIntegration) {
|
|
227
|
-
const hasMockOnly = /mock.*test.*passed|unit.*test.*passed/i.test(lower)
|
|
228
|
-
const hasIntegrationEvidence = hasEvidence('real_daemon_backend_integration')
|
|
229
|
-
if (!hasIntegrationEvidence && hasMockOnly) {
|
|
230
|
-
errors.push('integration-critical 变更只提供了 mock 单测证据,缺少真实 daemon↔backend 集成验证')
|
|
231
|
-
} else if (!hasIntegrationEvidence) {
|
|
232
|
-
errors.push('integration-critical 变更缺少集成验证证据 — 需要真实 daemon↔backend 测试结果或运行时日志')
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (needsLogEvidence) {
|
|
237
|
-
if (!hasEvidence('runtime_log_evidence')) {
|
|
238
|
-
errors.push('integration-critical 变更的 verify-result.md 缺少 Runtime Evidence section')
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (needsTerminalState) {
|
|
243
|
-
if (!hasEvidence('terminal_state_assertion')) {
|
|
244
|
-
warnings.push('建议检查终态断言:AgentRun running→completed/failed、session end 状态同步')
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (needsRealStartup) {
|
|
249
|
-
if (!hasEvidence('real_startup_once')) {
|
|
250
|
-
errors.push('deployment-critical 变更需要真实启动验证证据')
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return { ok: errors.length === 0, errors, warnings }
|
|
1
|
+
/**
|
|
2
|
+
* change-risk-profile.js — 变更风险分级检测
|
|
3
|
+
*
|
|
4
|
+
* 根据变更涉及的文件类型、关键词、git diff、brainstorm 产物,
|
|
5
|
+
* 判定 5 级风险(doc-only / unit-sufficient / contract-required /
|
|
6
|
+
* integration-critical / deployment-critical),产出门控验证需求。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ============ 向后兼容:旧的 INTEGRATION_CRITICAL_PATTERNS ============
|
|
10
|
+
|
|
11
|
+
const INTEGRATION_CRITICAL_PATTERNS = [
|
|
12
|
+
/\bdaemon\b/i,
|
|
13
|
+
/\bbackend\b/i,
|
|
14
|
+
/\bclient.*api\b/i,
|
|
15
|
+
/\bgrpc\b/i,
|
|
16
|
+
/\bwebsocket\b/i,
|
|
17
|
+
/\bhttp.*client\b/i,
|
|
18
|
+
/\bsession\b/i,
|
|
19
|
+
/\blease\b/i,
|
|
20
|
+
/\bagent.?run\b/i,
|
|
21
|
+
/\blifecycle\b/i,
|
|
22
|
+
/\bstate.?transition\b/i,
|
|
23
|
+
/\bclaim\b/i,
|
|
24
|
+
/\bheartbeat\b/i,
|
|
25
|
+
/\bcross.?process\b/i,
|
|
26
|
+
/\bipc\b/i,
|
|
27
|
+
/\bmessage.?queue\b/i,
|
|
28
|
+
/\bpub.?sub\b/i,
|
|
29
|
+
/\bcli\.ts\b/i,
|
|
30
|
+
/\bmain\.ts\b/i,
|
|
31
|
+
/\bentrypoint\b/i,
|
|
32
|
+
/\bserver\.(js|ts)\b/i,
|
|
33
|
+
/\bbootstrap\b/i,
|
|
34
|
+
/\bdockerfile\b/i,
|
|
35
|
+
/\bdocker.?compose\b/i,
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const INTEGRATION_FILE_PATTERNS = [
|
|
39
|
+
/daemon/i,
|
|
40
|
+
/session.?manager/i,
|
|
41
|
+
/agent.?run/i,
|
|
42
|
+
/lifecycle/i,
|
|
43
|
+
/state.?machine/i,
|
|
44
|
+
/lease/i,
|
|
45
|
+
/cli\.(js|ts)$/,
|
|
46
|
+
/main\.(js|ts)$/,
|
|
47
|
+
/server\.(js|ts)$/,
|
|
48
|
+
/bootstrap/i,
|
|
49
|
+
/startup/i,
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* task 级「端到端/deployment-critical」文本判定(execute 批量完成 autoCheckPlanFromReviews 用)。
|
|
54
|
+
*
|
|
55
|
+
* 聚焦需要真实集成/启动验证的 task,区别于 change 级 INTEGRATION_CRITICAL_PATTERNS(含 backend/session
|
|
56
|
+
* 等泛词——task 级用会误伤普通后端 task)。命中端到端集成语义(端到端/真实集成/daemon↔backend/
|
|
57
|
+
* integration test/e2e)或部署启动入口(cli.ts/main.ts/entrypoint/server/bootstrap/startup/docker)。
|
|
58
|
+
* 坑 execute-batch-complete-endtoend-checkbox:这类 task 的 review cannot_verify 不算 checked,
|
|
59
|
+
* 阻断 execute 批量完成(必须 pass),防端到端未真验就被批量放行(verify integration-evidence 兜底外的
|
|
60
|
+
* execute 侧前置门)。
|
|
61
|
+
* @param {string} text task 描述文本(plan.md task 行 + tasks/task-XX.md 内容)
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
const END_TO_END_TASK_KEYWORDS = [
|
|
65
|
+
/端到端/, /真实集成/, /daemon.*backend/i, /integration\s*test/i, /e2e\s*test/i,
|
|
66
|
+
/runtime\s*evidence/i, /运行时证据/,
|
|
67
|
+
/\bcli\.ts\b/i, /\bmain\.ts\b/i, /\bentrypoint\b/i, /\bserver\.(js|ts)\b/i,
|
|
68
|
+
/\bbootstrap\b/i, /\bstartup\b/i, /启动/, /\bdockerfile\b/i, /\bdocker.?compose\b/i,
|
|
69
|
+
]
|
|
70
|
+
export function isEndToEndTaskText(text = '') {
|
|
71
|
+
return END_TO_END_TASK_KEYWORDS.some(re => re.test(text))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============ 门控可执行化(A:报错说人话) ============
|
|
75
|
+
//
|
|
76
|
+
// 历史教训:integration-critical / deployment-critical 门控只报「缺少真实集成证据 / 需要真实
|
|
77
|
+
// 启动验证证据」,agent 看不出具体缺哪一项、要写/做什么才算过,只能靠改结论文案撞墙。
|
|
78
|
+
// 此处把每一项 requiredVerification 的需求(写什么 / 做什么)和判级原因显式导出,
|
|
79
|
+
// 供 stage-contract 拼成可执行的报错信息。
|
|
80
|
+
|
|
81
|
+
/** 每项 requiredVerification 的需求描述 + 字面期望 */
|
|
82
|
+
export const VERIFICATION_NEEDS = {
|
|
83
|
+
unit_tests: {
|
|
84
|
+
desc: '单元测试(verify 必做;在 verify-result.md 写明测试套件与结果)',
|
|
85
|
+
literals: [],
|
|
86
|
+
},
|
|
87
|
+
contract_tests: {
|
|
88
|
+
desc: '契约测试(前后端/跨进程 API parity 对账,写明结论)',
|
|
89
|
+
literals: [],
|
|
90
|
+
},
|
|
91
|
+
real_daemon_backend_integration: {
|
|
92
|
+
desc: '真实 daemon↔backend 集成验证(非仅 mock 单测)。写「真实集成/端到端」的证据;',
|
|
93
|
+
literals: ['端到端', 'integration test', 'e2e test', 'daemon.*backend', '真实集成', 'runtime evidence', '运行时证据'],
|
|
94
|
+
},
|
|
95
|
+
runtime_log_evidence: {
|
|
96
|
+
desc: 'Runtime Evidence section + 指向真实日志/证据片段。',
|
|
97
|
+
literals: ['Runtime Evidence', '运行时证据', 'daemon log', '日志片段'],
|
|
98
|
+
},
|
|
99
|
+
real_startup_once: {
|
|
100
|
+
desc: '真实启动一次本变更触及的部署/启动入口(服务入口、CLI 主入口、守护进程等——须是本变更实际改动的那一类入口,不能拿无关进程的启动来凑数)。',
|
|
101
|
+
literals: ['启动.*一次', '实际.*启动', 'real startup', 'docker up', 'npm start', 'node server'],
|
|
102
|
+
},
|
|
103
|
+
terminal_state_assertion: {
|
|
104
|
+
desc: '终态断言(建议项,不阻断):AgentRun running→completed/failed、session/lease end 状态同步。',
|
|
105
|
+
literals: ['terminal state', '终态', 'completed failed', 'session end', 'lease end'],
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** 风险分级的判定来源(命中关键词来源 = design.md / plan.md 内容,非改动文件本身) */
|
|
110
|
+
export const RISK_LEVEL_CAUSES = {
|
|
111
|
+
'deployment-critical':
|
|
112
|
+
'design.md / plan.md 命中启动入口关键词(cli.ts / main.ts / server.(js|ts) / bootstrap / entrypoint)。' +
|
|
113
|
+
'注意:这是按 design/plan 里的措辞判定的,不一定代表你真改了启动入口;若属误判可在 design.md frontmatter 用 risk_level 显式声明真实等级(如 unit-sufficient)覆盖——不要靠删措辞绕门控,危险链路该有真实启动证据。',
|
|
114
|
+
'integration-critical':
|
|
115
|
+
'design.md / plan.md 命中跨进程/状态机关键词(daemon / backend / session / lease / lifecycle / heartbeat 等)。',
|
|
116
|
+
'contract-required': 'design.md / plan.md 命中 API contract 关键词(api / client / contract / dto)。',
|
|
117
|
+
'explicit': 'design.md frontmatter 的 risk_level 显式声明(覆盖关键词判级)。',
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** design.md frontmatter 可显式声明的合法 risk_level 值(与 detectChangeRisk 判级结果同集合) */
|
|
121
|
+
const RISK_LEVELS = ['doc-only', 'unit-sufficient', 'contract-required', 'integration-critical', 'deployment-critical']
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 从 design.md 顶部 frontmatter 提取显式 risk_level 声明。
|
|
125
|
+
* 只认文档最开头 `---\n...\n---` 块内的 `risk_level: <level>` 单行(与 plan.md 的 plan_level 同款解析),
|
|
126
|
+
* 不扫正文——避免正文里讨论 risk_level 措辞时被误当声明。返回合法 level 或 null。
|
|
127
|
+
*/
|
|
128
|
+
export function extractExplicitRiskLevel(designContent = '') {
|
|
129
|
+
const fm = designContent.match(/^---\r?\n([\s\S]*?)\r?\n---/)
|
|
130
|
+
if (!fm) return null
|
|
131
|
+
const line = fm[1].split('\n').map(l => l.trim()).find(l => l.startsWith('risk_level:'))
|
|
132
|
+
if (!line) return null
|
|
133
|
+
const value = line.slice('risk_level:'.length).trim().replace(/^["']|["']$/g, '').toLowerCase()
|
|
134
|
+
return RISK_LEVELS.includes(value) ? value : null
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function detectChangeRisk({ designContent = '', planContent = '', changedFiles = [] } = {}) {
|
|
138
|
+
const triggers = []
|
|
139
|
+
|
|
140
|
+
// ── 显式豁免优先:design.md frontmatter 声明 risk_level → 以声明为准,跳过关键词判级 ──
|
|
141
|
+
// 历史教训:detectChangeRisk 是机械字面匹配,不认否定语境——design 写「本次不改动 daemon/session」
|
|
142
|
+
// 仍命中关键词被误判 integration/deployment-critical,强制要全套集成证据,对无状态后端变更误伤。
|
|
143
|
+
// 与其在正则层做脆弱的否定识别,不如给一条显式、诚实、可审计(落在 design frontmatter + verify-result)
|
|
144
|
+
// 的覆盖通道。声明仍走门控(结论 FAIL 仍拦),但豁免级(unit-sufficient 等)不再强制集成证据。
|
|
145
|
+
const explicit = extractExplicitRiskLevel(designContent)
|
|
146
|
+
if (explicit) {
|
|
147
|
+
const requiredVerification =
|
|
148
|
+
explicit === 'deployment-critical' ? ['unit_tests', 'contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'real_startup_once'] :
|
|
149
|
+
explicit === 'integration-critical' ? ['unit_tests', 'contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'terminal_state_assertion'] :
|
|
150
|
+
explicit === 'contract-required' ? ['unit_tests', 'contract_tests'] :
|
|
151
|
+
explicit === 'doc-only' ? ['static_check'] :
|
|
152
|
+
['unit_tests']
|
|
153
|
+
return { level: explicit, triggers: ['risk_level (explicit)'], requiredVerification, explicit: true }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const combined = [designContent, planContent].join('\n')
|
|
157
|
+
|
|
158
|
+
for (const pattern of INTEGRATION_CRITICAL_PATTERNS) {
|
|
159
|
+
if (pattern.test(combined)) {
|
|
160
|
+
pattern.lastIndex = 0
|
|
161
|
+
const match = combined.match(pattern)
|
|
162
|
+
if (match && !triggers.includes(match[0])) triggers.push(match[0])
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for (const file of changedFiles) {
|
|
167
|
+
for (const pattern of INTEGRATION_FILE_PATTERNS) {
|
|
168
|
+
if (pattern.test(file)) {
|
|
169
|
+
pattern.lastIndex = 0
|
|
170
|
+
const match = file.match(pattern)
|
|
171
|
+
if (match && !triggers.includes(match[0])) triggers.push(match[0])
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (triggers.length === 0) {
|
|
177
|
+
return { level: 'doc-only', triggers: [], requiredVerification: ['static_check'] }
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const deploymentTrigger = triggers.some(t => /cli\.ts|main\.ts|server\.(js|ts)|bootstrap|entrypoint/i.test(t))
|
|
181
|
+
const lifecycleTrigger = triggers.some(t => /session|lease|agent.?run|lifecycle|state.?transition|claim|heartbeat/i.test(t))
|
|
182
|
+
const crossProcessTrigger = triggers.some(t => /daemon|backend|client.*api|grpc|websocket|cross.?process|ipc|message.?queue/i.test(t))
|
|
183
|
+
|
|
184
|
+
let level
|
|
185
|
+
const requiredVerification = ['unit_tests']
|
|
186
|
+
|
|
187
|
+
if (deploymentTrigger) {
|
|
188
|
+
level = 'deployment-critical'
|
|
189
|
+
requiredVerification.push('contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'real_startup_once')
|
|
190
|
+
} else if (lifecycleTrigger || crossProcessTrigger) {
|
|
191
|
+
level = 'integration-critical'
|
|
192
|
+
requiredVerification.push('contract_tests', 'real_daemon_backend_integration', 'runtime_log_evidence', 'terminal_state_assertion')
|
|
193
|
+
} else if (triggers.some(t => /api|client|contract|dto/i.test(t))) {
|
|
194
|
+
level = 'contract-required'
|
|
195
|
+
requiredVerification.push('contract_tests')
|
|
196
|
+
} else {
|
|
197
|
+
level = 'unit-sufficient'
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return { level, triggers, requiredVerification }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 检查 verify-result.md 是否包含集成验证证据
|
|
205
|
+
*/
|
|
206
|
+
export function checkIntegrationEvidence(verifyContent, requiredVerification) {
|
|
207
|
+
const errors = []
|
|
208
|
+
const warnings = []
|
|
209
|
+
const lower = verifyContent.toLowerCase()
|
|
210
|
+
|
|
211
|
+
// 字面证据正则从 VERIFICATION_NEEDS[k].literals 派生——与报错描述、prompt 事前契约严格同源,
|
|
212
|
+
// 杜绝历史上"描述说 A、正则查 B"的分叉(checkIntegrationEvidence 正则曾比 VERIFICATION_NEEDS
|
|
213
|
+
// 描述多匹配 集成测试/backend.*daemon/real.*integration 等)。literals 为空的 need
|
|
214
|
+
// (unit_tests/contract_tests)无字面校验,视为满足。
|
|
215
|
+
const hasEvidence = (k) => {
|
|
216
|
+
const n = VERIFICATION_NEEDS[k]
|
|
217
|
+
if (!n || !n.literals || n.literals.length === 0) return true
|
|
218
|
+
return new RegExp(n.literals.join('|'), 'i').test(lower)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const needsIntegration = requiredVerification.includes('real_daemon_backend_integration')
|
|
222
|
+
const needsLogEvidence = requiredVerification.includes('runtime_log_evidence')
|
|
223
|
+
const needsTerminalState = requiredVerification.includes('terminal_state_assertion')
|
|
224
|
+
const needsRealStartup = requiredVerification.includes('real_startup_once')
|
|
225
|
+
|
|
226
|
+
if (needsIntegration) {
|
|
227
|
+
const hasMockOnly = /mock.*test.*passed|unit.*test.*passed/i.test(lower)
|
|
228
|
+
const hasIntegrationEvidence = hasEvidence('real_daemon_backend_integration')
|
|
229
|
+
if (!hasIntegrationEvidence && hasMockOnly) {
|
|
230
|
+
errors.push('integration-critical 变更只提供了 mock 单测证据,缺少真实 daemon↔backend 集成验证')
|
|
231
|
+
} else if (!hasIntegrationEvidence) {
|
|
232
|
+
errors.push('integration-critical 变更缺少集成验证证据 — 需要真实 daemon↔backend 测试结果或运行时日志')
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (needsLogEvidence) {
|
|
237
|
+
if (!hasEvidence('runtime_log_evidence')) {
|
|
238
|
+
errors.push('integration-critical 变更的 verify-result.md 缺少 Runtime Evidence section')
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (needsTerminalState) {
|
|
243
|
+
if (!hasEvidence('terminal_state_assertion')) {
|
|
244
|
+
warnings.push('建议检查终态断言:AgentRun running→completed/failed、session end 状态同步')
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (needsRealStartup) {
|
|
249
|
+
if (!hasEvidence('real_startup_once')) {
|
|
250
|
+
errors.push('deployment-critical 变更需要真实启动验证证据')
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return { ok: errors.length === 0, errors, warnings }
|
|
255
255
|
}
|