sillyspec 3.18.2 → 3.18.3
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/docs/brainstorm-plan-contract.md +64 -0
- package/docs/plan-execute-contract.md +123 -0
- package/docs/revision-mode.md +115 -0
- package/docs/sillyspec/file-lifecycle.md +13 -4
- package/docs/workflow-contract-regression.md +106 -0
- package/package.json +1 -1
- package/packages/dashboard/dist/assets/{index-DpLHK4jv.js → index-Bq_Z2hne.js} +568 -568
- package/packages/dashboard/dist/assets/{index-BcM2J-hv.css → index-O2W5RV4z.css} +1 -1
- package/packages/dashboard/dist/index.html +16 -16
- package/packages/dashboard/src/components/PipelineStage.vue +22 -2
- package/packages/dashboard/src/components/PipelineView.vue +10 -2
- package/packages/dashboard/src/components/StageBadge.vue +17 -3
- package/packages/dashboard/src/components/StepCard.vue +7 -2
- package/src/change-risk-profile.js +167 -0
- package/src/db.js +6 -0
- package/src/index.js +17 -1
- package/src/knowledge-match.js +130 -0
- package/src/progress.js +464 -11
- package/src/run.js +200 -3
- package/src/scan-postcheck.js +34 -2
- package/src/stage-contract.js +86 -6
- package/src/stages/brainstorm.js +23 -0
- package/src/stages/execute.js +110 -2
- package/src/stages/plan.js +82 -0
- package/src/stages/scan.js +40 -0
- package/src/stages/verify.js +38 -2
- package/test/brainstorm-plan-contract.test.mjs +273 -0
- package/test/knowledge-match.test.mjs +231 -0
- package/test/plan-execute-contract.test.mjs +330 -0
- package/test/platform-failure-samples.test.mjs +4 -0
- package/test/revision-v1.test.mjs +1145 -0
- package/test/scan-knowledge.test.mjs +175 -0
- package/test/scan-postcheck.test.mjs +3 -0
- package/test/spec-dir.test.mjs +8 -3
- package/test/stage-definitions.test.mjs +1 -1
package/src/stages/plan.js
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* 校验 design.md 是否满足 plan 执行契约
|
|
6
|
+
* 第一版是轻量 markdown 结构检查,不强 schema。
|
|
7
|
+
* @param {string} designContent - design.md 文件内容
|
|
8
|
+
* @returns {{ ok: boolean, errors: string[], warnings: string[] }}
|
|
9
|
+
*/
|
|
10
|
+
export function validateDesignForPlan(designContent) {
|
|
11
|
+
const errors = []
|
|
12
|
+
const warnings = []
|
|
13
|
+
|
|
14
|
+
if (!designContent || !designContent.trim()) {
|
|
15
|
+
return { ok: false, errors: ['design.md 内容为空'], warnings }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const lower = designContent.toLowerCase()
|
|
19
|
+
|
|
20
|
+
// 检查 1: 必须包含目标/问题描述(error)
|
|
21
|
+
const hasGoal = /(^|\n)#{2,}\s*.*(目标|goal|objective|背景|background|问题|problem|purpose|目的)/i.test(designContent)
|
|
22
|
+
if (!hasGoal) {
|
|
23
|
+
errors.push('design.md 缺少「目标/背景/问题描述」章节 — plan 需要知道要达成什么')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 检查 2: 必须包含范围/scope(error)
|
|
27
|
+
const hasScope = /(^|\n)#{2,}\s*.*(范围|scope|总体方案|方案|approach|solution|设计|design)/i.test(designContent)
|
|
28
|
+
if (!hasScope) {
|
|
29
|
+
errors.push('design.md 缺少「范围/总体方案/设计」章节 — plan 需要知道做什么和怎么做')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 检查 3: 必须包含决策/方案选择(error)
|
|
33
|
+
const hasDecisions = /(^|\n)#{2,}\s*.*(决策|decision|选择|choice|方案选择)/i.test(designContent)
|
|
34
|
+
|| /d-\d+@v\d+/i.test(designContent) // decisions.md 引用 ID
|
|
35
|
+
|| /decisions?\.md/i.test(designContent) // 引用 decisions.md
|
|
36
|
+
if (!hasDecisions) {
|
|
37
|
+
errors.push('design.md 缺少「决策/方案选择」— plan 需要基于明确的技术决策来拆分任务')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 检查 4 (warning): 缺非目标/non-goals
|
|
41
|
+
const hasNonGoals = /(^|\n)#{2,}\s*.*(非目标|non-goals?|不做|out of scope|不在范围)/i.test(designContent)
|
|
42
|
+
if (!hasNonGoals) {
|
|
43
|
+
warnings.push('design.md 缺少「非目标/Non-goals」— 建议明确不做什么,防止 scope creep')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 检查 5 (warning): 缺约束/风险
|
|
47
|
+
const hasConstraints = /(^|\n)#{2,}\s*.*(约束|constraint|限制|limitation|风险|risk|trade-?off)/i.test(designContent)
|
|
48
|
+
if (!hasConstraints) {
|
|
49
|
+
warnings.push('design.md 缺少「约束/风险/Trade-off」— 建议记录已知约束和风险')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 检查 6 (warning): 缺文件变更清单
|
|
53
|
+
const hasFileChanges = /文件变更|file change|变更清单|changed files/i.test(designContent)
|
|
54
|
+
|| /^\|\s*(新增|修改|删除|new|modify|delete|update)\s*\|/im.test(designContent)
|
|
55
|
+
if (!hasFileChanges) {
|
|
56
|
+
warnings.push('design.md 缺少「文件变更清单」— 建议列出预期改动的文件')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { ok: errors.length === 0, errors, warnings }
|
|
60
|
+
}
|
|
61
|
+
|
|
4
62
|
export const definition = {
|
|
5
63
|
name: 'plan',
|
|
6
64
|
title: '实现计划',
|
|
@@ -326,6 +384,8 @@ plan_level + 计划内容(none 级别输出建议操作)`,
|
|
|
326
384
|
- [ ] (brownfield)全局验收包含兼容性条款
|
|
327
385
|
- [ ] 没有实现细节(接口定义、代码示例等不应该在 plan.md 里)
|
|
328
386
|
- [ ] plan.md 与 design.md 的文件变更清单一致
|
|
387
|
+
- [ ] 如果涉及构造函数/接口/DTO/client 方法变更,是否搜索了所有调用点并纳入任务范围?
|
|
388
|
+
- [ ] 调用点搜索命令的输出是否记录在 plan.md 或 task-NN.md 中?
|
|
329
389
|
- [ ] 如果有 Mermaid 图,依赖关系确实非平凡(非线性/非全并行)
|
|
330
390
|
- [ ] 没有泛泛风险分析(如"需要充分测试")
|
|
331
391
|
|
|
@@ -376,6 +436,28 @@ export const fixedSuffix = [
|
|
|
376
436
|
- 依赖关系和 plan.md 的 Wave 分组是否一致
|
|
377
437
|
- 验收标准和 plan.md 的全局标准是否矛盾
|
|
378
438
|
- 接口定义是否自洽
|
|
439
|
+
5. **生产接线路径检查**(Critical):
|
|
440
|
+
- 读取 design.md,搜索以下关键词:
|
|
441
|
+
- 注入 / inject / 构造 / constructor / 初始化 / init / 启动路径 / startup / main / cli / entrypoint / daemon start / bootstrap
|
|
442
|
+
- 如果命中,提取提到的具体文件名(如 "在 cli.ts 中"、"main.ts 中实例化"、"Daemon 构造函数")
|
|
443
|
+
- 收集所有 tasks/task-NN.md 的 frontmatter 中 \`allowed_paths\` 列表
|
|
444
|
+
- 检查:design 中提到的生产入口文件是否在某个 task 的 allowed_paths 中?
|
|
445
|
+
- 如果 design 提到了入口文件但所有 task 的 allowed_paths 都不含该文件:
|
|
446
|
+
- **这是 plan contract 失败**
|
|
447
|
+
- 列出具体矛盾:\`design says: X 实例化 Y,但 allowed_paths 不含 Z\`
|
|
448
|
+
- **不自动修复**,暂停等待用户决定
|
|
449
|
+
- 如果 design 明确说"不需要改入口文件"并给出了理由,视为通过
|
|
450
|
+
- 调用:\`sillyspec run plan --wait --reason "生产接线路径检查失败" --options "修改 allowed_paths,修改 design,确认不需要改入口" --output "矛盾清单"\`
|
|
451
|
+
6. **符号影响面检查**(Critical):
|
|
452
|
+
- 对每个 task 涉及的文件,检查是否修改了构造函数、接口、DTO、API client 方法签名
|
|
453
|
+
- 如果是,搜索所有调用点:
|
|
454
|
+
\`\`\`bash
|
|
455
|
+
rg "new <ClassName>" src/
|
|
456
|
+
rg "<methodName>" src/ --type ts --type js
|
|
457
|
+
\`\`\`
|
|
458
|
+
- 对比调用点与 plan.md/task 的 allowed_paths
|
|
459
|
+
- 发现调用点不在任务范围内 → **plan contract 失败**
|
|
460
|
+
- 调用:\`sillyspec run plan --wait --reason "符号影响面检查发现遗漏调用点" --options "扩展 allowed_paths,添加新任务,确认不需要改" --output "遗漏调用点清单"\`
|
|
379
461
|
3. 发现问题 → 列出问题清单,暂停等待用户决定
|
|
380
462
|
- 调用:\`sillyspec run plan --wait --reason "审查发现一致性问题" --options "自动修复,手动修复,忽略并继续" --output "问题清单"\`
|
|
381
463
|
- **绝对禁止**:自己决定修复方向然后自动修复
|
package/src/stages/scan.js
CHANGED
|
@@ -454,6 +454,46 @@ step1 → step2 → step3
|
|
|
454
454
|
outputHint: '流程和术语表生成状态',
|
|
455
455
|
optional: true
|
|
456
456
|
},
|
|
457
|
+
{
|
|
458
|
+
name: 'Extract Project Knowledge',
|
|
459
|
+
perProject: true,
|
|
460
|
+
prompt: `从本次 scan 产物中提取长期有效、跨变更复用的项目知识,写入知识库。
|
|
461
|
+
|
|
462
|
+
### 知识分类
|
|
463
|
+
| 文件 | 内容 |
|
|
464
|
+
|------|------|
|
|
465
|
+
| conventions.md | 项目约定:目录规范、命名规范、提交规范、测试规范 |
|
|
466
|
+
| patterns.md | 可复用模式:鉴权方式、错误处理方式、模块组织方式 |
|
|
467
|
+
| known-issues.md | 已知坑:不可直接改的模块、历史兼容问题、代理限制 |
|
|
468
|
+
| uncategorized.md | 不确定分类、需要人工确认的知识 |
|
|
469
|
+
|
|
470
|
+
INDEX.md 维护索引,格式(每行:关键词1|关键词2 → [条目名](文件#锚点);关键词用于 execute 阶段命中匹配,必须给出能区分该知识的词):
|
|
471
|
+
\`\`\`markdown
|
|
472
|
+
# Knowledge Index
|
|
473
|
+
|
|
474
|
+
## Conventions
|
|
475
|
+
- ESM|module|import → [ESM Only](conventions.md#esm-only)
|
|
476
|
+
\`\`\`
|
|
477
|
+
|
|
478
|
+
### ⛔ 硬规则(必须遵守)
|
|
479
|
+
1. **只写未来变更会反复用到的知识** — 不要把 scan 报告摘要塞进知识库
|
|
480
|
+
2. **不要重复 knowledge 文件中已有的内容** — 读取现有文件,追加新条目,不覆盖
|
|
481
|
+
3. **不确定分类或不确定长期有效 → uncategorized.md** — 宁可不确定也不要放错
|
|
482
|
+
4. **每个正式分类条目必须更新 INDEX.md** — 添加对应分类下的链接
|
|
483
|
+
5. **每个条目用 markdown 锚点格式** — 文件内用 \`## 标题\`,INDEX 用 \`[#标题]\` 或 \`(文件名#标题)\`
|
|
484
|
+
|
|
485
|
+
### 操作
|
|
486
|
+
1. 读取现有 knowledge 文件:\`{KNOWLEDGE_ROOT}/INDEX.md\`、\`{KNOWLEDGE_ROOT}/conventions.md\` 等
|
|
487
|
+
2. 遍历 scan 产物(\`{DOCS_ROOT}/scan/*.md\`、\`{DOCS_ROOT}/modules/*.md\`),识别可复用知识
|
|
488
|
+
3. 将新知识按分类写入对应文件(追加模式,不覆盖已有内容)
|
|
489
|
+
4. 更新 INDEX.md 索引
|
|
490
|
+
5. 如果确实没有新知识可提取(已有文件已覆盖),输出"无新知识"而非创建空条目
|
|
491
|
+
|
|
492
|
+
### 输出
|
|
493
|
+
新增知识条目数量 + 分类分布(或"无新知识")`,
|
|
494
|
+
outputHint: '知识条目数量',
|
|
495
|
+
optional: false
|
|
496
|
+
},
|
|
457
497
|
{
|
|
458
498
|
name: '自检和提交',
|
|
459
499
|
perProject: true,
|
package/src/stages/verify.js
CHANGED
|
@@ -202,8 +202,32 @@ grep -rl "<关键词>" <源码目录>/ --include="*.java" --include="*.js" --inc
|
|
|
202
202
|
|
|
203
203
|
### 操作
|
|
204
204
|
1. 汇总以上所有检查结果
|
|
205
|
-
2.
|
|
206
|
-
|
|
205
|
+
2. **判定变更风险等级(change_risk_profile)**:
|
|
206
|
+
|
|
207
|
+
### 变更风险分级规则
|
|
208
|
+
扫描 design.md 和 plan.md 的关键词,自动判定 verify 强度:
|
|
209
|
+
|
|
210
|
+
| 触发条件 | verify 要求 |
|
|
211
|
+
|---|---|
|
|
212
|
+
| 只改文案/文档 | 静态检查即可 |
|
|
213
|
+
| 单模块纯函数 | 单测即可 |
|
|
214
|
+
| API contract / DTO / client | 单测 + contract test |
|
|
215
|
+
| daemon/backend 跨进程 | **必须真实集成** |
|
|
216
|
+
| session/lease/run 状态机 | **必须生命周期端到端验证** |
|
|
217
|
+
| 部署启动路径 | **必须真实启动一次** |
|
|
218
|
+
|
|
219
|
+
触发关键词:daemon, backend, session, lease, agent_run, lifecycle, state_transition, claim, heartbeat, cross-process, cli.ts, main.ts, server.ts, bootstrap, entrypoint
|
|
220
|
+
|
|
221
|
+
### 风险门控规则
|
|
222
|
+
- **integration-critical** 或 **deployment-critical** 变更:
|
|
223
|
+
- 结论为 PASS WITH NOTES → **降级为 FAIL**
|
|
224
|
+
- mock 单测通过但没有真实集成证据 → **FAIL**
|
|
225
|
+
- 必须在 verify-result.md 中包含 **Runtime Evidence** section
|
|
226
|
+
- **contract-required** 变更:需要 contract test 证据
|
|
227
|
+
- **unit-sufficient** 变更:单测即可
|
|
228
|
+
|
|
229
|
+
3. 生成 verify-result.md 文件,保存到 \`.sillyspec/changes/<change-name>/verify-result.md\`
|
|
230
|
+
4. 给出结论:PASS / PASS WITH NOTES / FAIL(受风险门控约束)
|
|
207
231
|
|
|
208
232
|
### verify-result.md 格式
|
|
209
233
|
\`\`\`markdown
|
|
@@ -235,6 +259,18 @@ PASS / PASS WITH NOTES / FAIL
|
|
|
235
259
|
## 技术债务
|
|
236
260
|
(TODO/FIXME/HACK 统计)
|
|
237
261
|
|
|
262
|
+
## 变更风险等级
|
|
263
|
+
(自动检测的 change_risk_profile: doc-only / unit-sufficient / contract-required / integration-critical / deployment-critical)
|
|
264
|
+
|
|
265
|
+
## Runtime Evidence(integration-critical / deployment-critical 必填)
|
|
266
|
+
- daemon 启动命令:
|
|
267
|
+
- backend 地址:
|
|
268
|
+
- 创建 session / 调用核心 API 的请求:
|
|
269
|
+
- daemon 日志关键片段(不能出现 session_control_no_manager / fallback to task_runner / submitMessages agent_run_id empty / 422):
|
|
270
|
+
- backend 状态(AgentRun running -> completed/failed):
|
|
271
|
+
- session / lease end 状态:
|
|
272
|
+
- 失败模式排除:
|
|
273
|
+
|
|
238
274
|
## 代码审查
|
|
239
275
|
(问题列表 + 总体评价)
|
|
240
276
|
\`\`\`
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brainstorm → Plan Contract v1 测试
|
|
3
|
+
*
|
|
4
|
+
* 验证 design.md 到 plan 的输入契约:
|
|
5
|
+
* 1. 合法 design 通过
|
|
6
|
+
* 2. 缺关键章节失败
|
|
7
|
+
* 3. warning 不阻断
|
|
8
|
+
*/
|
|
9
|
+
import { validateDesignForPlan } from '../src/stages/plan.js'
|
|
10
|
+
|
|
11
|
+
let failed = 0
|
|
12
|
+
const failures = []
|
|
13
|
+
|
|
14
|
+
function assert(condition, msg) {
|
|
15
|
+
if (!condition) {
|
|
16
|
+
failed++
|
|
17
|
+
failures.push(msg)
|
|
18
|
+
console.log(` ❌ FAIL: ${msg}`)
|
|
19
|
+
} else {
|
|
20
|
+
console.log(` ✅ PASS: ${msg}`)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.log('=== Brainstorm → Plan Contract v1 测试 ===\n')
|
|
25
|
+
|
|
26
|
+
// ─────────────────────────────────────────
|
|
27
|
+
// Case 1: valid design 通过
|
|
28
|
+
// ─────────────────────────────────────────
|
|
29
|
+
console.log('--- Case 1: valid design 通过 ---')
|
|
30
|
+
{
|
|
31
|
+
const design = `# Design: 用户认证系统
|
|
32
|
+
|
|
33
|
+
## 背景
|
|
34
|
+
需要实现用户认证。
|
|
35
|
+
|
|
36
|
+
## 设计目标
|
|
37
|
+
- 支持 OAuth2
|
|
38
|
+
- 支持手机号登录
|
|
39
|
+
|
|
40
|
+
## 非目标
|
|
41
|
+
- 不做 SSO
|
|
42
|
+
|
|
43
|
+
## 总体方案
|
|
44
|
+
使用 JWT + Refresh Token。
|
|
45
|
+
|
|
46
|
+
## 决策
|
|
47
|
+
- D-001@v1: 选择 JWT 而非 Session
|
|
48
|
+
|
|
49
|
+
## 约束
|
|
50
|
+
- 必须兼容现有 API
|
|
51
|
+
|
|
52
|
+
## 文件变更清单
|
|
53
|
+
| 操作 | 文件路径 | 说明 |
|
|
54
|
+
|------|---------|------|
|
|
55
|
+
| 新增 | src/auth.js | 认证模块 |
|
|
56
|
+
`
|
|
57
|
+
const result = validateDesignForPlan(design)
|
|
58
|
+
assert(result.ok, '完整 design 应校验通过')
|
|
59
|
+
assert(result.errors.length === 0, '不应有 errors')
|
|
60
|
+
assert(result.warnings.length === 0, '不应有 warnings')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─────────────────────────────────────────
|
|
64
|
+
// Case 2: empty design 失败
|
|
65
|
+
// ─────────────────────────────────────────
|
|
66
|
+
console.log('\n--- Case 2: empty design 失败 ---')
|
|
67
|
+
{
|
|
68
|
+
assert(!validateDesignForPlan('').ok, '空字符串应失败')
|
|
69
|
+
assert(!validateDesignForPlan(null).ok, 'null 应失败')
|
|
70
|
+
assert(!validateDesignForPlan(' ').ok, '纯空格应失败')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─────────────────────────────────────────
|
|
74
|
+
// Case 3: missing goal 失败
|
|
75
|
+
// ─────────────────────────────────────────
|
|
76
|
+
console.log('\n--- Case 3: missing goal/背景 失败 ---')
|
|
77
|
+
{
|
|
78
|
+
const design = `# Design
|
|
79
|
+
|
|
80
|
+
## 总体方案
|
|
81
|
+
用 JWT。
|
|
82
|
+
|
|
83
|
+
## 决策
|
|
84
|
+
D-001@v1: 选 JWT
|
|
85
|
+
`
|
|
86
|
+
const result = validateDesignForPlan(design)
|
|
87
|
+
assert(!result.ok, '缺目标/背景应失败')
|
|
88
|
+
assert(result.errors.some(e => e.includes('目标') || e.includes('背景')), '错误应提到目标/背景')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ─────────────────────────────────────────
|
|
92
|
+
// Case 4: missing scope/方案 失败
|
|
93
|
+
// ─────────────────────────────────────────
|
|
94
|
+
console.log('\n--- Case 4: missing scope/方案 失败 ---')
|
|
95
|
+
{
|
|
96
|
+
const design = `# Design
|
|
97
|
+
|
|
98
|
+
## 背景
|
|
99
|
+
需要认证。
|
|
100
|
+
|
|
101
|
+
## 决策
|
|
102
|
+
D-001@v1: 选 JWT
|
|
103
|
+
`
|
|
104
|
+
const result = validateDesignForPlan(design)
|
|
105
|
+
assert(!result.ok, '缺范围/方案应失败')
|
|
106
|
+
assert(result.errors.some(e => e.includes('范围') || e.includes('方案')), '错误应提到范围/方案')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─────────────────────────────────────────
|
|
110
|
+
// Case 5: missing decisions 失败
|
|
111
|
+
// ─────────────────────────────────────────
|
|
112
|
+
console.log('\n--- Case 5: missing decisions 失败 ---')
|
|
113
|
+
{
|
|
114
|
+
const design = `# Design
|
|
115
|
+
|
|
116
|
+
## 背景
|
|
117
|
+
需要认证。
|
|
118
|
+
|
|
119
|
+
## 总体方案
|
|
120
|
+
用 JWT。
|
|
121
|
+
`
|
|
122
|
+
const result = validateDesignForPlan(design)
|
|
123
|
+
assert(!result.ok, '缺决策应失败')
|
|
124
|
+
assert(result.errors.some(e => e.includes('决策')), '错误应提到决策')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─────────────────────────────────────────
|
|
128
|
+
// Case 6: decisions.md 引用也算决策
|
|
129
|
+
// ─────────────────────────────────────────
|
|
130
|
+
console.log('\n--- Case 6: decisions.md 引用算决策 ---')
|
|
131
|
+
{
|
|
132
|
+
const design = `# Design
|
|
133
|
+
|
|
134
|
+
## 背景
|
|
135
|
+
需要认证。
|
|
136
|
+
|
|
137
|
+
## 总体方案
|
|
138
|
+
用 JWT。详见 decisions.md。
|
|
139
|
+
|
|
140
|
+
## 文件变更清单
|
|
141
|
+
| 操作 | 文件 | 说明 |
|
|
142
|
+
`
|
|
143
|
+
const result = validateDesignForPlan(design)
|
|
144
|
+
assert(result.ok, 'decisions.md 引用应满足决策检查')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ─────────────────────────────────────────
|
|
148
|
+
// Case 7: missing non-goals 只有 warning
|
|
149
|
+
// ─────────────────────────────────────────
|
|
150
|
+
console.log('\n--- Case 7: missing non-goals warning ---')
|
|
151
|
+
{
|
|
152
|
+
const design = `# Design
|
|
153
|
+
|
|
154
|
+
## 背景
|
|
155
|
+
需要认证。
|
|
156
|
+
|
|
157
|
+
## 总体方案
|
|
158
|
+
用 JWT。
|
|
159
|
+
|
|
160
|
+
## 决策
|
|
161
|
+
D-001@v1: 选 JWT
|
|
162
|
+
|
|
163
|
+
## 约束
|
|
164
|
+
必须兼容现有 API。
|
|
165
|
+
`
|
|
166
|
+
const result = validateDesignForPlan(design)
|
|
167
|
+
assert(result.ok, '缺非目标不应阻断')
|
|
168
|
+
assert(result.warnings.some(w => w.includes('非目标') || w.includes('Non-goals')), '应有非目标 warning')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ─────────────────────────────────────────
|
|
172
|
+
// Case 8: missing constraints 只有 warning
|
|
173
|
+
// ─────────────────────────────────────────
|
|
174
|
+
console.log('\n--- Case 8: missing constraints warning ---')
|
|
175
|
+
{
|
|
176
|
+
const design = `# Design
|
|
177
|
+
|
|
178
|
+
## 目标
|
|
179
|
+
实现认证。
|
|
180
|
+
|
|
181
|
+
## 设计方案
|
|
182
|
+
用 JWT。
|
|
183
|
+
|
|
184
|
+
## 决策
|
|
185
|
+
选择 JWT。
|
|
186
|
+
|
|
187
|
+
## 非目标
|
|
188
|
+
不做 SSO。
|
|
189
|
+
`
|
|
190
|
+
const result = validateDesignForPlan(design)
|
|
191
|
+
assert(result.ok, '缺约束不应阻断')
|
|
192
|
+
assert(result.warnings.some(w => w.includes('约束') || w.includes('风险') || w.includes('Trade-off')), '应有约束/风险 warning')
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ─────────────────────────────────────────
|
|
196
|
+
// Case 9: missing 文件变更清单 warning
|
|
197
|
+
// ─────────────────────────────────────────
|
|
198
|
+
console.log('\n--- Case 9: missing 文件变更清单 warning ---')
|
|
199
|
+
{
|
|
200
|
+
const design = `# Design
|
|
201
|
+
|
|
202
|
+
## 背景
|
|
203
|
+
需要认证。
|
|
204
|
+
|
|
205
|
+
## 总体方案
|
|
206
|
+
用 JWT。
|
|
207
|
+
|
|
208
|
+
## 决策
|
|
209
|
+
D-001@v1: 选 JWT
|
|
210
|
+
`
|
|
211
|
+
const result = validateDesignForPlan(design)
|
|
212
|
+
assert(result.ok, '缺文件变更清单不应阻断')
|
|
213
|
+
assert(result.warnings.some(w => w.includes('文件变更')), '应有文件变更 warning')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ─────────────────────────────────────────
|
|
217
|
+
// Case 10: 英文 design 通过
|
|
218
|
+
// ─────────────────────────────────────────
|
|
219
|
+
console.log('\n--- Case 10: 英文 design 通过 ---')
|
|
220
|
+
{
|
|
221
|
+
const design = `# Design: Auth System
|
|
222
|
+
|
|
223
|
+
## Background
|
|
224
|
+
We need authentication.
|
|
225
|
+
|
|
226
|
+
## Solution
|
|
227
|
+
Use JWT + Refresh Token.
|
|
228
|
+
|
|
229
|
+
## Decision
|
|
230
|
+
D-001@v1: Choose JWT over Session
|
|
231
|
+
|
|
232
|
+
## Non-goals
|
|
233
|
+
No SSO.
|
|
234
|
+
|
|
235
|
+
## Constraints
|
|
236
|
+
Must be backwards compatible.
|
|
237
|
+
`
|
|
238
|
+
const result = validateDesignForPlan(design)
|
|
239
|
+
assert(result.ok, '英文 design 应校验通过')
|
|
240
|
+
assert(result.errors.length === 0, '不应有 errors')
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ─────────────────────────────────────────
|
|
244
|
+
// Case 11: 最小合法 design
|
|
245
|
+
// ─────────────────────────────────────────
|
|
246
|
+
console.log('\n--- Case 11: 最小合法 design ---')
|
|
247
|
+
{
|
|
248
|
+
const design = `# Design
|
|
249
|
+
|
|
250
|
+
## 目标
|
|
251
|
+
修 bug。
|
|
252
|
+
|
|
253
|
+
## 方案
|
|
254
|
+
改代码。
|
|
255
|
+
|
|
256
|
+
## 决策
|
|
257
|
+
D-001@v1: 直接改。
|
|
258
|
+
`
|
|
259
|
+
const result = validateDesignForPlan(design)
|
|
260
|
+
assert(result.ok, '最小合法 design 应通过')
|
|
261
|
+
// 可能有 warning 但 ok
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ── 结果 ──
|
|
265
|
+
console.log(`\n${'='.repeat(50)}`)
|
|
266
|
+
console.log(`✅ 通过: ${11 - failed} ❌ 失败: ${failed}`)
|
|
267
|
+
if (failures.length > 0) {
|
|
268
|
+
console.log(`失败项:`)
|
|
269
|
+
failures.forEach(f => console.log(` - ${f}`))
|
|
270
|
+
}
|
|
271
|
+
console.log(`${'='.repeat(50)}`)
|
|
272
|
+
|
|
273
|
+
if (failed > 0) process.exit(1)
|