@gong-ym/ai-spec-auto 0.2.15 → 0.2.19
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/.agents/commands/common/spec-continue.md +1 -1
- package/.agents/commands/common/spec-start-review.md +1 -1
- package/.agents/commands/common/spec-start.md +1 -1
- package/.agents/commands/common/spec-update.md +2 -2
- package/.agents/commands/cursor/opsx-apply.md +0 -1
- package/.agents/commands/cursor/opsx-propose.md +0 -1
- package/.agents/flows/RUN_OUTPUT.md +0 -2
- package/.agents/flows/common/prd-to-delivery.md +0 -3
- package/.agents/flows/common/requirement-to-observability.md +0 -2
- package/.agents/orchestration/runtime-state-handoff-spec.md +93 -1
- package/.agents/orchestration/task-orchestrator-run-plan-template.md +85 -4
- package/.agents/registry/flows.json +5 -5
- package/.agents/registry/roles.json +13 -27
- package/.agents/roles/common/backend-implementer.md +3 -4
- package/.agents/roles/common/code-guardian.md +2 -3
- package/.agents/roles/common/frontend-implementer.md +3 -4
- package/.agents/roles/common/requirement-analyst.md +32 -18
- package/.agents/roles/common/tooling-implementer.md +3 -4
- package/.agents/rules/common/12-Superpowers/346/211/247/350/241/214/350/247/204/350/214/203.md +1 -1
- package/.agents/rules/common/14-/345/256/241/350/256/241/346/261/207/346/212/245/350/247/204/350/214/203.md +1 -2
- package/.agents/rules/profiles/react/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +1 -1
- package/.agents/rules/profiles/react/05-API/350/247/204/350/214/203.md +2 -2
- package/.agents/rules/profiles/vue/05-API/350/247/204/350/214/203.md +2 -2
- package/.agents/skills/common/archive-change/SKILL.md +0 -1
- package/.agents/skills/common/create-proposal/SKILL.md +7 -8
- package/.agents/skills/common/execute-task/SKILL.md +3 -3
- package/.agents/skills/profiles/react/create-api/SKILL.md +2 -2
- package/.agents/skills/profiles/vue/create-api/SKILL.md +2 -2
- package/bin/archive-change.js +37 -259
- package/bin/cli.js +5 -0
- package/bin/demo-runtime-smoke.js +11 -74
- package/bin/execution-semantics.js +4 -17
- package/bin/expert-executor.js +4 -7
- package/bin/protocol-workflow.js +7 -2
- package/bin/refresh-command.js +185 -0
- package/bin/runtime-state.js +18 -14
- package/bin/task-orchestrator-runner.js +2 -2
- package/internal/ai-protocol-workflow.js +80 -119
- package/openspec/config.yaml.template +6 -15
- package/openspec/schemas/expert-delivery/schema.yaml +0 -11
- package/openspec/schemas/expert-delivery/templates/checklist.md +12 -30
- package/openspec/schemas/expert-delivery/templates/iterations.md +6 -22
- package/openspec/schemas/expert-delivery/templates/proposal.md +7 -35
- package/openspec/schemas/expert-delivery/templates/tasks.md +7 -20
- package/package.json +1 -1
- package/scripts/post-publish-auto-fix-check.js +5 -61
- package/openspec/schemas/expert-delivery/templates/design.md +0 -61
|
@@ -171,10 +171,10 @@ const FALLBACK_ROLE_SKILL_PRIORITY = {
|
|
|
171
171
|
};
|
|
172
172
|
|
|
173
173
|
const FALLBACK_ROLE_OPENSPEC_RULE_SECTIONS = {
|
|
174
|
-
'requirement-analyst': ['proposal', 'specs', '
|
|
175
|
-
'frontend-implementer': ['specs', 'tasks'
|
|
176
|
-
'code-guardian': ['tasks', 'specs', '
|
|
177
|
-
'archive-change': ['specs', '
|
|
174
|
+
'requirement-analyst': ['proposal', 'specs', 'tasks'],
|
|
175
|
+
'frontend-implementer': ['specs', 'tasks'],
|
|
176
|
+
'code-guardian': ['tasks', 'specs', 'checklist', 'iterations'],
|
|
177
|
+
'archive-change': ['specs', 'checklist', 'iterations'],
|
|
178
178
|
};
|
|
179
179
|
|
|
180
180
|
const DEFAULT_FLOW_ID = 'prd-to-delivery';
|
|
@@ -186,7 +186,7 @@ const REVIEW_POLICIES = new Set(['none', 'main-flow-blocking']);
|
|
|
186
186
|
const DEFAULT_FLOW_CONSTRAINTS = {
|
|
187
187
|
required_roles: ['requirement-analyst', 'frontend-implementer', 'code-guardian'],
|
|
188
188
|
approval_gates: [],
|
|
189
|
-
required_artifacts: ['proposal.md', 'specs', '
|
|
189
|
+
required_artifacts: ['proposal.md', 'specs', 'tasks.md', 'checklist.md', 'iterations.md'],
|
|
190
190
|
};
|
|
191
191
|
|
|
192
192
|
const DEFAULT_HANDOFF_GATE_POLICY = {
|
|
@@ -266,11 +266,10 @@ const MICRO_ROLE_EXTRAS = {
|
|
|
266
266
|
],
|
|
267
267
|
},
|
|
268
268
|
'requirement-analyst': {
|
|
269
|
-
goal: '用短版 proposal.md、specs
|
|
269
|
+
goal: '用短版 proposal.md、specs/ 和 tasks.md 收敛需求,不写实现代码。',
|
|
270
270
|
must_do: [
|
|
271
271
|
'proposal.md 只保留目标、范围、默认假设、风险四块',
|
|
272
272
|
'specs/<domain>/spec.md 只保留当前变更需要的增量规范和场景',
|
|
273
|
-
'design.md 只保留真实实现落点和关键约束',
|
|
274
273
|
'tasks.md 保持 3-5 条可执行任务,覆盖实现与验收',
|
|
275
274
|
],
|
|
276
275
|
must_not: [
|
|
@@ -278,7 +277,7 @@ const MICRO_ROLE_EXTRAS = {
|
|
|
278
277
|
],
|
|
279
278
|
},
|
|
280
279
|
'frontend-implementer': {
|
|
281
|
-
goal: '基于短版 proposal/specs/
|
|
280
|
+
goal: '基于短版 proposal/specs/tasks 做最小必要实现,优先复用现有结构。',
|
|
282
281
|
must_do: [
|
|
283
282
|
'保持改动最小化,优先就地复用现有页面、组件、样式变量和 mock 约定',
|
|
284
283
|
'实现说明只保留当前变更、验证结果和残留风险',
|
|
@@ -310,17 +309,13 @@ const MICRO_OPENSPEC_RULES = {
|
|
|
310
309
|
'每条任务都要可落盘、可验证,避免空标题。',
|
|
311
310
|
'保持改动最小化,聚焦本次请求。'
|
|
312
311
|
],
|
|
313
|
-
design: [
|
|
314
|
-
'只保留当前实现真正需要的结构与样式约束。',
|
|
315
|
-
'继续使用主题变量和既有目录结构。'
|
|
316
|
-
],
|
|
317
312
|
specs: [
|
|
318
313
|
'只记录当前变更真正需要的增量规范和关键验收场景。',
|
|
319
314
|
'保留可测试的结论,不展开无关背景说明。'
|
|
320
315
|
],
|
|
321
316
|
checklist: [
|
|
322
317
|
'checklist.md 只保留关键检查项、阻断项和最终放行结论。',
|
|
323
|
-
'检查项必须能回指 proposal/specs/
|
|
318
|
+
'检查项必须能回指 proposal/specs/tasks 或实现证据。'
|
|
324
319
|
],
|
|
325
320
|
iterations: [
|
|
326
321
|
'iterations.md 只记录问题、修正动作和残留风险。',
|
|
@@ -338,25 +333,25 @@ const ROLE_GUIDANCE = {
|
|
|
338
333
|
],
|
|
339
334
|
must_not: [
|
|
340
335
|
'不要越权替代 requirement-analyst、frontend-implementer 或 code-guardian 的职责',
|
|
341
|
-
'不要在 proposal/specs/
|
|
336
|
+
'不要在 proposal/specs/tasks/checklist/iterations 缺失时跳过门禁直接推进',
|
|
342
337
|
],
|
|
343
338
|
},
|
|
344
339
|
'requirement-analyst': {
|
|
345
|
-
goal: '把需求收敛成可执行的 proposal.md、specs
|
|
340
|
+
goal: '把需求收敛成可执行的 proposal.md、specs/ 和 tasks.md,不写实现代码。',
|
|
346
341
|
must_do: [
|
|
347
342
|
'先明确目标、范围、非目标、关键假设和风险',
|
|
348
|
-
'proposal.md
|
|
343
|
+
'proposal.md 和 specs/ 需要能支撑后续实现和验收',
|
|
349
344
|
'tasks.md 必须是可执行任务清单,而不是空标题模板',
|
|
350
345
|
],
|
|
351
346
|
must_not: [
|
|
352
347
|
'不要直接开始写 Vue/TS/CSS 代码',
|
|
353
|
-
'不要在 proposal.md、specs
|
|
348
|
+
'不要在 proposal.md、specs/ 和 tasks.md 未落盘前宣称本阶段完成',
|
|
354
349
|
],
|
|
355
350
|
},
|
|
356
351
|
'frontend-implementer': {
|
|
357
|
-
goal: '基于 proposal.md、specs
|
|
352
|
+
goal: '基于 proposal.md、specs/ 和 tasks.md 完成当前范围内的前端实现。',
|
|
358
353
|
must_do: [
|
|
359
|
-
'先读 proposal.md、specs
|
|
354
|
+
'先读 proposal.md、specs/ 和 tasks.md 再改代码',
|
|
360
355
|
'严格在当前变更范围内实现,不顺手扩 scope',
|
|
361
356
|
'遵守最小改动原则,只修改与当前任务直接相关的文件',
|
|
362
357
|
'实现完成后写 expert-execution 回执并等待下一轮编排',
|
|
@@ -367,7 +362,7 @@ const ROLE_GUIDANCE = {
|
|
|
367
362
|
],
|
|
368
363
|
},
|
|
369
364
|
'code-guardian': {
|
|
370
|
-
goal: '基于 proposal、specs、
|
|
365
|
+
goal: '基于 proposal、specs、tasks 和实现结果做交付前检查,产出 checklist.md 和 iterations.md。',
|
|
371
366
|
must_do: [
|
|
372
367
|
'明确区分阻断项和非阻断项',
|
|
373
368
|
'checklist.md 记录检查项和结论',
|
|
@@ -381,7 +376,7 @@ const ROLE_GUIDANCE = {
|
|
|
381
376
|
'archive-change': {
|
|
382
377
|
goal: '在归档批准后先完成 preflight,再通过 archive-change 命令做规范合并、目录归档和运行收尾。',
|
|
383
378
|
must_do: [
|
|
384
|
-
'先确认 proposal/specs/
|
|
379
|
+
'先确认 proposal/specs/tasks/checklist/iterations 已齐备',
|
|
385
380
|
'归档摘要要说明合并的 spec、归档位置、残留风险和后续 patch 的回退阶段',
|
|
386
381
|
'优先执行 archive-change 内置命令,不手工搬运目录',
|
|
387
382
|
],
|
|
@@ -423,7 +418,7 @@ const ROLE_GUIDANCE = {
|
|
|
423
418
|
'verification-reviewer': {
|
|
424
419
|
goal: '从验收视角补强验证结论,确保交付不是“代码完成但验证不足”。',
|
|
425
420
|
must_do: [
|
|
426
|
-
'对照 proposal/specs/
|
|
421
|
+
'对照 proposal/specs/tasks 检查验收口径是否闭环',
|
|
427
422
|
'明确哪些验证已完成、哪些仍需补充',
|
|
428
423
|
],
|
|
429
424
|
must_not: [
|
|
@@ -443,7 +438,7 @@ const ROLE_GUIDANCE = {
|
|
|
443
438
|
};
|
|
444
439
|
|
|
445
440
|
const SKILL_GUIDANCE = {
|
|
446
|
-
'create-proposal': '用于快速形成 proposal/specs/
|
|
441
|
+
'create-proposal': '用于快速形成 proposal/specs/tasks 的结构和变更说明。',
|
|
447
442
|
'design-analysis': '用于整理页面结构、信息层级和交互要点。',
|
|
448
443
|
'ui-ux-pro-max': '用于设计协作阶段的 Figma 解析、标注提取和 UI/UX 设计决策收口。',
|
|
449
444
|
'create-view': '用于创建或调整 Vue 页面文件与页面目录结构。',
|
|
@@ -471,8 +466,8 @@ const FALLBACK_ROLE_RULE_CONSTRAINT_PROFILES = {
|
|
|
471
466
|
},
|
|
472
467
|
'requirement-analyst': {
|
|
473
468
|
must_follow: [
|
|
474
|
-
'先把项目定位、目录落点、路由/API/样式约定吸收到 proposal/specs/
|
|
475
|
-
'需求收敛必须同时落到 specs
|
|
469
|
+
'先把项目定位、目录落点、路由/API/样式约定吸收到 proposal/specs/tasks,不要把规范已明确的信息重复写成 missing_inputs。',
|
|
470
|
+
'需求收敛必须同时落到 specs/,不能只写 tasks 而缺实现落点。',
|
|
476
471
|
'需求收敛必须落到当前仓库可实施的页面、路由、接口或 mock 落点,而不是抽象方案。',
|
|
477
472
|
],
|
|
478
473
|
blocked_when: [
|
|
@@ -482,16 +477,16 @@ const FALLBACK_ROLE_RULE_CONSTRAINT_PROFILES = {
|
|
|
482
477
|
'frontend-implementer': {
|
|
483
478
|
must_follow: [
|
|
484
479
|
'优先复用现有目录、路由、请求封装、状态管理和样式变量约定。',
|
|
485
|
-
'实现前先对齐 proposal/specs/
|
|
480
|
+
'实现前先对齐 proposal/specs/tasks 的范围与落点,不要自行扩 scope。',
|
|
486
481
|
'坚持最小 patch,避免顺手重构无关模块或扩大改动面。',
|
|
487
482
|
],
|
|
488
483
|
blocked_when: [
|
|
489
|
-
'proposal/specs/
|
|
484
|
+
'proposal/specs/tasks 未落盘或仍处于 before-implementation 审批门禁时,禁止改业务代码。',
|
|
490
485
|
],
|
|
491
486
|
},
|
|
492
487
|
'code-guardian': {
|
|
493
488
|
must_follow: [
|
|
494
|
-
'以 proposal/specs/
|
|
489
|
+
'以 proposal/specs/tasks 和项目规则为准检查实现,而不是只做泛化 lint。',
|
|
495
490
|
'必须给出阻断项、非阻断项和交付建议,不能写成模糊建议列表。',
|
|
496
491
|
],
|
|
497
492
|
blocked_when: [
|
|
@@ -528,7 +523,7 @@ const FALLBACK_ROLE_RULE_CONSTRAINT_PROFILES = {
|
|
|
528
523
|
'核查页面是否落在 src/views、路由是否落在 src/router/modules,并保持动态导入。',
|
|
529
524
|
'核查 API 是否通过 src/api 封装、类型是否放在 src/api/types,页面中未直接调 request。',
|
|
530
525
|
'核查样式是否使用主题变量、scoped 或 CSS Modules,而不是硬编码全局样式。',
|
|
531
|
-
'核查 Pinia/store、mock 与 proposal/specs/
|
|
526
|
+
'核查 Pinia/store、mock 与 proposal/specs/tasks 的边界是否一致,避免“演示页写成生产页”。',
|
|
532
527
|
],
|
|
533
528
|
},
|
|
534
529
|
},
|
|
@@ -545,7 +540,7 @@ const ROLE_RULE_REPO_SPECIFIC = {
|
|
|
545
540
|
},
|
|
546
541
|
'requirement-analyst': {
|
|
547
542
|
repo_specific: (facts) => [
|
|
548
|
-
facts.routeModulesDir ? `当前仓库已有路由模块目录 ${facts.routeModulesDir},proposal/specs/
|
|
543
|
+
facts.routeModulesDir ? `当前仓库已有路由模块目录 ${facts.routeModulesDir},proposal/specs/tasks 需要按该目录组织。` : '若项目尚未接入 vue-router,需要在 proposal/specs/tasks 明确是补路由还是保持占位入口。',
|
|
549
544
|
facts.viewsDir ? `页面目录以 ${facts.viewsDir} 为准,任务拆解要写清页面落点。` : null,
|
|
550
545
|
].filter(Boolean),
|
|
551
546
|
},
|
|
@@ -878,7 +873,7 @@ function inferRoutingStrategy(repoConventions, rawInput) {
|
|
|
878
873
|
: `复用现有路由入口 ${repoConventions.routeEntry}`;
|
|
879
874
|
}
|
|
880
875
|
if (/页面|列表|详情|欢迎|登录|路由|router|page/i.test(text)) {
|
|
881
|
-
return '仓库未检测到显式路由入口;页面类任务需先补路由骨架或在 proposal/specs/
|
|
876
|
+
return '仓库未检测到显式路由入口;页面类任务需先补路由骨架或在 proposal/specs/tasks 中明确占位入口方案';
|
|
882
877
|
}
|
|
883
878
|
return '当前任务不强依赖新增路由,优先保持现有入口结构';
|
|
884
879
|
}
|
|
@@ -892,7 +887,7 @@ function inferApiStrategy(repoConventions, rawInput) {
|
|
|
892
887
|
return `沿用 ${repoConventions.apiDir}${repoConventions.apiTypesDir ? ` + ${repoConventions.apiTypesDir}` : ''} 进行接口与类型拆分`;
|
|
893
888
|
}
|
|
894
889
|
if (/接口|api|请求|分页|搜索|筛选|状态|重试|支付|订单|用户/i.test(text)) {
|
|
895
|
-
return '仓库尚未检测到稳定 API 封装入口;真实接口任务需先建立请求层或在 proposal/specs/
|
|
890
|
+
return '仓库尚未检测到稳定 API 封装入口;真实接口任务需先建立请求层或在 proposal/specs/tasks 中明确占位方案';
|
|
896
891
|
}
|
|
897
892
|
return '当前任务不强依赖真实接口,优先保持最小数据流';
|
|
898
893
|
}
|
|
@@ -905,7 +900,7 @@ function inferMockStrategy(repoConventions, rawInput) {
|
|
|
905
900
|
: `${repoConventions.mockDir} 可作为 mock-first 兜底方案`;
|
|
906
901
|
}
|
|
907
902
|
if (/mock|演示|占位/i.test(text)) {
|
|
908
|
-
return '仓库未检测到独立 mock 目录;若采用演示版,需要在 proposal/specs/
|
|
903
|
+
return '仓库未检测到独立 mock 目录;若采用演示版,需要在 proposal/specs/tasks 中明确本地 mock 或页面内占位方案';
|
|
909
904
|
}
|
|
910
905
|
return '未显式声明 mock-first,按真实接口交付评估';
|
|
911
906
|
}
|
|
@@ -1777,15 +1772,15 @@ function inferChangeImpactTargetRole(currentRole, changeImpact, latestInput) {
|
|
|
1777
1772
|
function inferArtifactsToUpdate(changeImpact) {
|
|
1778
1773
|
switch (changeImpact) {
|
|
1779
1774
|
case 'scope-delta':
|
|
1780
|
-
return ['proposal.md', 'specs/', '
|
|
1775
|
+
return ['proposal.md', 'specs/', 'tasks.md', 'checklist.md', 'iterations.md'];
|
|
1781
1776
|
case 'archive-fix':
|
|
1782
|
-
return ['tasks.md', '
|
|
1777
|
+
return ['tasks.md', 'checklist.md', 'iterations.md', 'code'];
|
|
1783
1778
|
case 'followup-patch':
|
|
1784
|
-
return ['proposal.md', 'specs/', '
|
|
1779
|
+
return ['proposal.md', 'specs/', 'tasks.md', 'checklist.md', 'iterations.md', 'code'];
|
|
1785
1780
|
case 'patch':
|
|
1786
|
-
return ['tasks.md', '
|
|
1781
|
+
return ['tasks.md', 'checklist.md', 'iterations.md', 'code'];
|
|
1787
1782
|
case 're-scope':
|
|
1788
|
-
return ['proposal.md', 'specs/', '
|
|
1783
|
+
return ['proposal.md', 'specs/', 'tasks.md'];
|
|
1789
1784
|
default:
|
|
1790
1785
|
return [];
|
|
1791
1786
|
}
|
|
@@ -2418,13 +2413,12 @@ function collectChangeArtifactIntentText(targetDir, changeId, currentRun) {
|
|
|
2418
2413
|
if (artifactBase) {
|
|
2419
2414
|
candidates.push(
|
|
2420
2415
|
path.join(artifactBase, 'proposal.md'),
|
|
2421
|
-
path.join(artifactBase, 'design.md'),
|
|
2422
2416
|
path.join(artifactBase, 'tasks.md'),
|
|
2423
2417
|
);
|
|
2424
2418
|
}
|
|
2425
2419
|
|
|
2426
2420
|
if (currentRun?.artifacts && typeof currentRun.artifacts === 'object') {
|
|
2427
|
-
for (const key of ['proposal', '
|
|
2421
|
+
for (const key of ['proposal', 'tasks']) {
|
|
2428
2422
|
if (typeof currentRun.artifacts[key] === 'string') {
|
|
2429
2423
|
candidates.push(path.join(targetDir, currentRun.artifacts[key]));
|
|
2430
2424
|
}
|
|
@@ -2697,7 +2691,7 @@ function buildArtifactContract(roleId, roleRuleContract, roleSkillContract, flow
|
|
|
2697
2691
|
done_when: '每个增量 spec 都能回指接口、路由或样式约束,并至少包含一个可验证场景。',
|
|
2698
2692
|
},
|
|
2699
2693
|
{
|
|
2700
|
-
artifact: '
|
|
2694
|
+
artifact: 'tasks.md',
|
|
2701
2695
|
required_rules: ruleIds.filter((id) => ['project-structure', 'component-standard', 'route-standard', 'api-standard', 'style-standard'].includes(id)),
|
|
2702
2696
|
preferred_skills: primarySkills.filter((id) => ['create-proposal', 'design-analysis'].includes(id)),
|
|
2703
2697
|
done_when: '页面、组件、路由、API、mock、状态和样式落点都已对齐到当前仓库结构。',
|
|
@@ -2779,7 +2773,7 @@ function buildSkillSelectionPolicy(roleId, roleSkillContract, flowId = DEFAULT_F
|
|
|
2779
2773
|
return {
|
|
2780
2774
|
primary_order: primaryOrder,
|
|
2781
2775
|
use_when: [
|
|
2782
|
-
{ when: '生成 proposal/specs/
|
|
2776
|
+
{ when: '生成 proposal/specs/tasks', skills: ['create-proposal'] },
|
|
2783
2777
|
{ when: '输入包含设计稿、视觉还原或复杂交互', skills: ['design-analysis'] },
|
|
2784
2778
|
],
|
|
2785
2779
|
fallback_rule: ruleFirst,
|
|
@@ -2907,7 +2901,7 @@ function buildHandoffChecklist(roleId, repoConventions, flowId = DEFAULT_FLOW_ID
|
|
|
2907
2901
|
|
|
2908
2902
|
if (roleId === 'requirement-analyst') {
|
|
2909
2903
|
return [
|
|
2910
|
-
'页面、路由、API、mock、状态和样式落点已写入
|
|
2904
|
+
'页面、路由、API、mock、状态和样式落点已写入 tasks.md。',
|
|
2911
2905
|
'真实接口与 mock-first 边界已写清,未把未确认契约伪装成已定事实。',
|
|
2912
2906
|
'待确认项已区分阻断问题还是默认假设,必要时保留 before-implementation 门禁。',
|
|
2913
2907
|
];
|
|
@@ -2997,7 +2991,6 @@ function buildArchivePreflight(targetDir, changeId, currentRun) {
|
|
|
2997
2991
|
const items = [
|
|
2998
2992
|
{ artifact: 'proposal.md', rel_path: path.join(artifactBase, 'proposal.md'), ready: fs.existsSync(path.join(targetDir, artifactBase, 'proposal.md')), source_stage: 'requirement-analyst' },
|
|
2999
2993
|
{ artifact: 'specs/', rel_path: path.join(artifactBase, 'specs'), ready: fs.existsSync(path.join(targetDir, artifactBase, 'specs')), source_stage: 'requirement-analyst' },
|
|
3000
|
-
{ artifact: 'design.md', rel_path: path.join(artifactBase, 'design.md'), ready: fs.existsSync(path.join(targetDir, artifactBase, 'design.md')), source_stage: 'requirement-analyst' },
|
|
3001
2994
|
{ artifact: 'tasks.md', rel_path: path.join(artifactBase, 'tasks.md'), ready: fs.existsSync(path.join(targetDir, artifactBase, 'tasks.md')), source_stage: 'requirement-analyst' },
|
|
3002
2995
|
{ artifact: 'code', rel_path: null, ready: hasFrontendDelivery, source_stage: 'frontend-implementer' },
|
|
3003
2996
|
{ artifact: 'implementation-notes', rel_path: null, ready: hasFrontendDelivery, source_stage: 'frontend-implementer' },
|
|
@@ -3009,7 +3002,7 @@ function buildArchivePreflight(targetDir, changeId, currentRun) {
|
|
|
3009
3002
|
return {
|
|
3010
3003
|
ready: missingArtifacts.length === 0,
|
|
3011
3004
|
summary: missingArtifacts.length === 0
|
|
3012
|
-
? 'proposal/specs/
|
|
3005
|
+
? 'proposal/specs/tasks/code/implementation-notes/checklist/iterations 已齐备,可以执行归档命令。'
|
|
3013
3006
|
: `仍缺少 ${missingArtifacts.join('、')},当前不允许执行归档命令。`,
|
|
3014
3007
|
missing_artifacts: missingArtifacts,
|
|
3015
3008
|
items,
|
|
@@ -3099,7 +3092,7 @@ function buildRoleSpecificContract(
|
|
|
3099
3092
|
],
|
|
3100
3093
|
blocking_checks: [
|
|
3101
3094
|
'是否新增真实 API、路由、全局状态、权限/支付/风控/合规逻辑;若是,立即阻断 quick-fix。',
|
|
3102
|
-
'是否出现跨模块范围扩张、验收口径变化或需要补 proposal/specs/
|
|
3095
|
+
'是否出现跨模块范围扩张、验收口径变化或需要补 proposal/specs/tasks 的情况。',
|
|
3103
3096
|
'checklist.md 是否明确写出通过 / 未通过 / 阻断项 / 证据 / 是否放行。',
|
|
3104
3097
|
],
|
|
3105
3098
|
verification_expectations: verificationExpectations,
|
|
@@ -3176,11 +3169,11 @@ function buildRoleSpecificContract(
|
|
|
3176
3169
|
if (roleId === 'requirement-analyst') {
|
|
3177
3170
|
return {
|
|
3178
3171
|
...base,
|
|
3179
|
-
summary: '先按项目规则把需求收敛成 proposal/specs/
|
|
3180
|
-
expected_outputs: ['proposal.md', 'specs/', '
|
|
3172
|
+
summary: '先按项目规则把需求收敛成 proposal/specs/tasks,再把高风险缺口转成门禁或待确认项。',
|
|
3173
|
+
expected_outputs: ['proposal.md', 'specs/', 'tasks.md'],
|
|
3181
3174
|
must_resolve: [
|
|
3182
3175
|
'页面/路由/API/mock/样式落点需和仓库约定一致',
|
|
3183
|
-
'至少产出一个 specs/<domain>/spec.md,并在
|
|
3176
|
+
'至少产出一个 specs/<domain>/spec.md,并在 tasks.md 说明实现落点',
|
|
3184
3177
|
'能从项目规则与代码推断的信息优先转成 assumptions',
|
|
3185
3178
|
'在 tasks.md 中把关键规则约束转成可执行任务项,而不是保留成抽象建议',
|
|
3186
3179
|
],
|
|
@@ -3190,7 +3183,7 @@ function buildRoleSpecificContract(
|
|
|
3190
3183
|
if (roleId === 'frontend-implementer') {
|
|
3191
3184
|
return {
|
|
3192
3185
|
...base,
|
|
3193
|
-
summary: '按 proposal/specs/
|
|
3186
|
+
summary: '按 proposal/specs/tasks 与项目目录、路由、API、样式约定完成实现,不擅自扩 scope。',
|
|
3194
3187
|
implementation_focus: [
|
|
3195
3188
|
repoConventions.viewsDir ? `页面落点优先对齐 ${repoConventions.viewsDir}` : '页面落点需与仓库 views 约定一致',
|
|
3196
3189
|
repoConventions.routeModulesDir ? `路由修改优先对齐 ${repoConventions.routeModulesDir}` : '若新增路由,需先确认路由入口与模块组织方式',
|
|
@@ -3212,10 +3205,10 @@ function buildRoleSpecificContract(
|
|
|
3212
3205
|
|
|
3213
3206
|
return {
|
|
3214
3207
|
...base,
|
|
3215
|
-
summary: '按 proposal/specs/
|
|
3208
|
+
summary: '按 proposal/specs/tasks 和项目规范核查目录落点、路由/API/样式/Test 合规性,再给交付结论。',
|
|
3216
3209
|
review_focus: [
|
|
3217
3210
|
'页面/组件/路由/API/mock/store 是否落到正确目录',
|
|
3218
|
-
'实现边界是否仍符合 proposal/specs/
|
|
3211
|
+
'实现边界是否仍符合 proposal/specs/tasks 与审批限制',
|
|
3219
3212
|
'样式是否继续使用主题变量与作用域样式',
|
|
3220
3213
|
],
|
|
3221
3214
|
evidence_targets: evidenceTargets,
|
|
@@ -3227,10 +3220,10 @@ function buildRoleSpecificContract(
|
|
|
3227
3220
|
'审计结论是否按 14-审计汇报规范给出读取记录、操作记录、规范对齐、技能状态与偏差披露',
|
|
3228
3221
|
repoConventions.styleEntry ? `样式是否沿用 ${repoConventions.styleEntry} 及主题变量,不存在硬编码颜色或全局污染` : '样式是否继续使用主题变量和作用域样式',
|
|
3229
3222
|
'是否出现与本次任务无关的扩改、顺手重构或越权补功能',
|
|
3230
|
-
'实现是否越过 proposal/specs/
|
|
3223
|
+
'实现是否越过 proposal/specs/tasks 或审批约束,把演示页扩成生产能力',
|
|
3231
3224
|
],
|
|
3232
3225
|
scope_guard: [
|
|
3233
|
-
'只按 proposal/specs/
|
|
3226
|
+
'只按 proposal/specs/tasks 与已批准范围审查,不接受静默扩 scope',
|
|
3234
3227
|
'高风险领域未批准的真实支付、敏感采集、风控/权限逻辑必须继续阻断',
|
|
3235
3228
|
'mock / 占位实现不得伪装成可直接上线的真实交付',
|
|
3236
3229
|
],
|
|
@@ -3284,7 +3277,7 @@ function buildRoleSpecificContract(
|
|
|
3284
3277
|
summary: '从验收视角检查验证链路是否闭环,并补强交付前的验证结论。',
|
|
3285
3278
|
expected_outputs: ['verification-review-notes', 'acceptance-risks'],
|
|
3286
3279
|
must_resolve: [
|
|
3287
|
-
'proposal/specs/
|
|
3280
|
+
'proposal/specs/tasks 的关键验收项是否已有对应验证',
|
|
3288
3281
|
'指出仍需补充的验证动作或残留风险',
|
|
3289
3282
|
],
|
|
3290
3283
|
};
|
|
@@ -3579,24 +3572,10 @@ function findArchivedChangeDir(targetDir, changeId) {
|
|
|
3579
3572
|
if (fs.existsSync(activeDir) && fs.existsSync(path.join(activeDir, 'proposal.md'))) {
|
|
3580
3573
|
return { dir: activeDir, status: 'active' };
|
|
3581
3574
|
}
|
|
3582
|
-
// 2.
|
|
3583
|
-
const
|
|
3584
|
-
if (
|
|
3585
|
-
return
|
|
3586
|
-
}
|
|
3587
|
-
const entries = fs.readdirSync(archiveRoot, { withFileTypes: true });
|
|
3588
|
-
for (const entry of entries) {
|
|
3589
|
-
if (!entry.isDirectory()) continue;
|
|
3590
|
-
// 归档目录格式: YYYY-MM-DD-{changeId} 或 YYYY-MM-DD-{changeId}-{suffix}
|
|
3591
|
-
const match = entry.name.match(/^\d{4}-\d{2}-\d{2}-(.+)$/);
|
|
3592
|
-
if (!match) continue;
|
|
3593
|
-
const archivedId = match[1].replace(/-\d+$/, ''); // 去掉末尾的冲突后缀
|
|
3594
|
-
if (archivedId === changeId || entry.name.endsWith(`-${changeId}`) || entry.name === changeId) {
|
|
3595
|
-
const dir = path.join(archiveRoot, entry.name);
|
|
3596
|
-
if (fs.existsSync(path.join(dir, 'proposal.md')) || fs.existsSync(path.join(dir, 'tasks.md'))) {
|
|
3597
|
-
return { dir, status: 'archived', archiveName: entry.name };
|
|
3598
|
-
}
|
|
3599
|
-
}
|
|
3575
|
+
// 2. 检查 openspec/specs/{changeId}.md 是否已归档
|
|
3576
|
+
const specFile = path.join(targetDir, 'openspec', 'specs', `${changeId}.md`);
|
|
3577
|
+
if (fs.existsSync(specFile)) {
|
|
3578
|
+
return { dir: path.dirname(specFile), status: 'archived', specFile };
|
|
3600
3579
|
}
|
|
3601
3580
|
return null;
|
|
3602
3581
|
}
|
|
@@ -3604,53 +3583,42 @@ function findArchivedChangeDir(targetDir, changeId) {
|
|
|
3604
3583
|
function matchArchivedChanges(targetDir, userInput) {
|
|
3605
3584
|
const path = require('path');
|
|
3606
3585
|
const fs = require('fs');
|
|
3607
|
-
const
|
|
3608
|
-
if (!fs.existsSync(
|
|
3586
|
+
const specsDir = path.join(targetDir, 'openspec', 'specs');
|
|
3587
|
+
if (!fs.existsSync(specsDir)) {
|
|
3609
3588
|
return [];
|
|
3610
3589
|
}
|
|
3611
3590
|
const text = String(userInput || '').trim().toLowerCase();
|
|
3612
3591
|
if (!text) return [];
|
|
3613
3592
|
|
|
3614
|
-
const
|
|
3593
|
+
const files = fs.readdirSync(specsDir, { withFileTypes: true });
|
|
3615
3594
|
const candidates = [];
|
|
3616
3595
|
|
|
3617
|
-
for (const entry of
|
|
3618
|
-
if (!entry.
|
|
3619
|
-
const
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
const archiveDate = match[1];
|
|
3623
|
-
const rawId = match[2].replace(/-\d+$/, ''); // 去掉冲突后缀
|
|
3624
|
-
const dir = path.join(archiveRoot, entry.name);
|
|
3596
|
+
for (const entry of files) {
|
|
3597
|
+
if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
|
|
3598
|
+
const rawId = entry.name.replace(/\.md$/, '');
|
|
3599
|
+
const filePath = path.join(specsDir, entry.name);
|
|
3625
3600
|
|
|
3626
|
-
// 读
|
|
3627
|
-
const proposalPath = path.join(dir, 'proposal.md');
|
|
3601
|
+
// 读 spec 文件提取标题和摘要
|
|
3628
3602
|
let title = rawId;
|
|
3629
3603
|
let summary = '';
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
summary = content.slice(0, 200).replace(/\n/g, ' ').trim();
|
|
3635
|
-
}
|
|
3604
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
3605
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
3606
|
+
if (titleMatch) title = titleMatch[1].trim();
|
|
3607
|
+
summary = content.slice(0, 200).replace(/\n/g, ' ').trim();
|
|
3636
3608
|
|
|
3637
|
-
//
|
|
3609
|
+
// 双向子串匹配
|
|
3638
3610
|
let score = 0;
|
|
3639
3611
|
const rawIdLower = rawId.toLowerCase().replace(/-/g, '');
|
|
3640
3612
|
const titleLower = title.toLowerCase();
|
|
3641
3613
|
const summaryLower = summary.toLowerCase();
|
|
3642
3614
|
|
|
3643
|
-
// 用户输入包含标题
|
|
3644
3615
|
if (titleLower.length >= 2 && text.includes(titleLower)) score += 5;
|
|
3645
|
-
// 用户输入包含 change-id
|
|
3646
3616
|
if (rawIdLower.length >= 3 && text.includes(rawIdLower)) score += 4;
|
|
3647
|
-
// 标题包含用户输入中的2-4字中文片段
|
|
3648
3617
|
const chineseSegments = text.match(/[\u4e00-\u9fa5]{2,4}/g) || [];
|
|
3649
3618
|
for (const seg of chineseSegments) {
|
|
3650
3619
|
if (titleLower.includes(seg)) score += 2;
|
|
3651
3620
|
else if (summaryLower.includes(seg)) score += 1;
|
|
3652
3621
|
}
|
|
3653
|
-
// 英文单词匹配
|
|
3654
3622
|
const englishWords = text.match(/[a-z][a-z0-9-]{2,}/g) || [];
|
|
3655
3623
|
for (const word of englishWords) {
|
|
3656
3624
|
const wordNorm = word.replace(/-/g, '');
|
|
@@ -3662,8 +3630,7 @@ function matchArchivedChanges(targetDir, userInput) {
|
|
|
3662
3630
|
if (score > 0) {
|
|
3663
3631
|
candidates.push({
|
|
3664
3632
|
change_id: rawId,
|
|
3665
|
-
|
|
3666
|
-
archive_date: archiveDate,
|
|
3633
|
+
spec_file: entry.name,
|
|
3667
3634
|
title,
|
|
3668
3635
|
summary: summary.slice(0, 100),
|
|
3669
3636
|
score,
|
|
@@ -3787,7 +3754,7 @@ function buildExecutionExamplePayload(dispatch, roleDefinition) {
|
|
|
3787
3754
|
: {};
|
|
3788
3755
|
const examples = {
|
|
3789
3756
|
'requirement-analyst': {
|
|
3790
|
-
summary: '已完成 proposal/specs/
|
|
3757
|
+
summary: '已完成 proposal/specs/tasks,并标记实现前审批关注点。',
|
|
3791
3758
|
next_action: '执行 protocol-advance,进入 before-implementation 审批。',
|
|
3792
3759
|
assumptions: ['默认沿用当前仓库的 mock-first 与目录落点约定。'],
|
|
3793
3760
|
},
|
|
@@ -3895,7 +3862,6 @@ function buildExpertExpectedOutput(dispatch, writes, runtimePaths, deliveryProfi
|
|
|
3895
3862
|
if (dispatch.role?.id === 'requirement-analyst') {
|
|
3896
3863
|
outputs.push(deliveryProfile === 'micro' ? '完成短版 openspec proposal.md' : '完成 openspec proposal.md');
|
|
3897
3864
|
outputs.push(deliveryProfile === 'micro' ? '完成短版 openspec specs/<domain>/spec.md(可多份)' : '完成 openspec specs/<domain>/spec.md(可多份)');
|
|
3898
|
-
outputs.push(deliveryProfile === 'micro' ? '完成短版 openspec design.md' : '完成 openspec design.md');
|
|
3899
3865
|
outputs.push(deliveryProfile === 'micro' ? '完成短版 openspec tasks.md' : '完成 openspec tasks.md');
|
|
3900
3866
|
} else if (dispatch.role?.id === 'frontend-implementer') {
|
|
3901
3867
|
outputs.push('完成当前范围内的代码实现');
|
|
@@ -4618,11 +4584,6 @@ function buildApprovalGateTurn(targetDir, status, currentArtifacts, snapshot = n
|
|
|
4618
4584
|
label: 'specs for approval review',
|
|
4619
4585
|
}));
|
|
4620
4586
|
}
|
|
4621
|
-
if (!useCompactArchiveGate && currentArtifacts.run?.artifacts?.design) {
|
|
4622
|
-
reads.push(buildReadableTarget(targetDir, currentArtifacts.run.artifacts.design, {
|
|
4623
|
-
label: 'design for approval review',
|
|
4624
|
-
}));
|
|
4625
|
-
}
|
|
4626
4587
|
if (pendingGate === 'before-archive') {
|
|
4627
4588
|
if (currentArtifacts.run?.artifacts?.checklist) {
|
|
4628
4589
|
reads.push(buildReadableTarget(targetDir, currentArtifacts.run.artifacts.checklist, {
|
|
@@ -4849,7 +4810,7 @@ function buildUpdateReviewTurn(targetDir, status, currentArtifacts, snapshot = n
|
|
|
4849
4810
|
target_role: changeDecision?.target_role || null,
|
|
4850
4811
|
handoff_gate: changeDecision?.handoff_gate || null,
|
|
4851
4812
|
delta_rules: [
|
|
4852
|
-
'proposal/specs/
|
|
4813
|
+
'proposal/specs/tasks/checklist/iterations 只改受影响章节,不整份重写',
|
|
4853
4814
|
'优先在同一 change 内吸收 patch / scope-delta / archive-fix',
|
|
4854
4815
|
'只有明显跨范围时才建议新建 change',
|
|
4855
4816
|
],
|
|
@@ -5362,7 +5323,7 @@ function tryApplyBeforeArchiveFastPath(targetDir, userInput) {
|
|
|
5362
5323
|
action: 'complete-without-archive',
|
|
5363
5324
|
updated,
|
|
5364
5325
|
final_state: completed.state,
|
|
5365
|
-
|
|
5326
|
+
specs_stored_at: null,
|
|
5366
5327
|
};
|
|
5367
5328
|
}
|
|
5368
5329
|
|
|
@@ -5385,7 +5346,7 @@ function tryApplyBeforeArchiveFastPath(targetDir, userInput) {
|
|
|
5385
5346
|
action: 'archive-approved',
|
|
5386
5347
|
updated,
|
|
5387
5348
|
final_state: archiveResult.runtime_transition?.state || approved.state,
|
|
5388
|
-
|
|
5349
|
+
specs_stored_at: archiveResult.specs_stored_at || null,
|
|
5389
5350
|
};
|
|
5390
5351
|
}
|
|
5391
5352
|
|
|
@@ -5534,7 +5495,7 @@ function updateProtocolInput(options = {}) {
|
|
|
5534
5495
|
if (explicitChangeId) {
|
|
5535
5496
|
const archived = findArchivedChangeDir(targetDir, explicitChangeId);
|
|
5536
5497
|
if (!archived) {
|
|
5537
|
-
throw new Error(`找不到变更 ${explicitChangeId}:未找到 openspec/changes/${explicitChangeId} 或 openspec/
|
|
5498
|
+
throw new Error(`找不到变更 ${explicitChangeId}:未找到 openspec/changes/${explicitChangeId} 或 openspec/specs/${explicitChangeId}.md`);
|
|
5538
5499
|
}
|
|
5539
5500
|
const patchChangeId = createFollowupPatchChangeId(explicitChangeId, userInput);
|
|
5540
5501
|
const flowDefinition = loadFlowDefinition(targetDir, DEFAULT_FLOW_ID);
|
|
@@ -5603,7 +5564,7 @@ function updateProtocolInput(options = {}) {
|
|
|
5603
5564
|
fast_path: {
|
|
5604
5565
|
executed: true,
|
|
5605
5566
|
action: 'followup-patch-opened',
|
|
5606
|
-
|
|
5567
|
+
specs_stored_at: null,
|
|
5607
5568
|
run_status: bootstrap.state?.status || null,
|
|
5608
5569
|
current_role: bootstrap.state?.current_role || null,
|
|
5609
5570
|
requires_followup_turn: true,
|
|
@@ -5631,14 +5592,14 @@ function updateProtocolInput(options = {}) {
|
|
|
5631
5592
|
fast_path: {
|
|
5632
5593
|
executed: true,
|
|
5633
5594
|
action: 'archived-changes-matched',
|
|
5634
|
-
|
|
5595
|
+
specs_stored_at: null,
|
|
5635
5596
|
run_status: null,
|
|
5636
5597
|
current_role: null,
|
|
5637
5598
|
requires_followup_turn: false,
|
|
5638
5599
|
candidates: candidates.map((c) => ({
|
|
5639
5600
|
change_id: c.change_id,
|
|
5640
5601
|
title: c.title,
|
|
5641
|
-
|
|
5602
|
+
spec_file: c.spec_file,
|
|
5642
5603
|
summary: c.summary,
|
|
5643
5604
|
})),
|
|
5644
5605
|
hint: '请使用 --change-id <change_id> 确认要对哪个需求做增量修改',
|
|
@@ -5662,7 +5623,7 @@ function updateProtocolInput(options = {}) {
|
|
|
5662
5623
|
fast_path: {
|
|
5663
5624
|
executed: true,
|
|
5664
5625
|
action: 'resume-paused-run',
|
|
5665
|
-
|
|
5626
|
+
specs_stored_at: null,
|
|
5666
5627
|
run_status: resumed.state?.status || null,
|
|
5667
5628
|
current_role: resumed.state?.current_role || null,
|
|
5668
5629
|
requires_followup_turn: true,
|
|
@@ -5691,7 +5652,7 @@ function updateProtocolInput(options = {}) {
|
|
|
5691
5652
|
fast_path: {
|
|
5692
5653
|
executed: true,
|
|
5693
5654
|
action: 'confirm-resume',
|
|
5694
|
-
|
|
5655
|
+
specs_stored_at: null,
|
|
5695
5656
|
run_status: resumed.state?.status || null,
|
|
5696
5657
|
current_role: resumed.state?.current_role || null,
|
|
5697
5658
|
requires_followup_turn: true,
|
|
@@ -5713,7 +5674,7 @@ function updateProtocolInput(options = {}) {
|
|
|
5713
5674
|
fast_path: {
|
|
5714
5675
|
executed: true,
|
|
5715
5676
|
action: approvalGateFastPath.action,
|
|
5716
|
-
|
|
5677
|
+
specs_stored_at: null,
|
|
5717
5678
|
run_status: approvalGateFastPath.final_state?.status || null,
|
|
5718
5679
|
current_role: approvalGateFastPath.final_state?.current_role || null,
|
|
5719
5680
|
requires_followup_turn: true,
|
|
@@ -5735,7 +5696,7 @@ function updateProtocolInput(options = {}) {
|
|
|
5735
5696
|
fast_path: {
|
|
5736
5697
|
executed: true,
|
|
5737
5698
|
action: fastPath.action,
|
|
5738
|
-
|
|
5699
|
+
specs_stored_at: fastPath.specs_stored_at,
|
|
5739
5700
|
run_status: fastPath.final_state?.status || null,
|
|
5740
5701
|
current_role: fastPath.final_state?.current_role || null,
|
|
5741
5702
|
requires_followup_turn: false,
|
|
@@ -5757,7 +5718,7 @@ function updateProtocolInput(options = {}) {
|
|
|
5757
5718
|
fast_path: {
|
|
5758
5719
|
executed: true,
|
|
5759
5720
|
action: followupPatch.action,
|
|
5760
|
-
|
|
5721
|
+
specs_stored_at: null,
|
|
5761
5722
|
run_status: followupPatch.bootstrap.state?.status || null,
|
|
5762
5723
|
current_role: followupPatch.bootstrap.state?.current_role || null,
|
|
5763
5724
|
requires_followup_turn: true,
|
|
@@ -5795,7 +5756,7 @@ function updateProtocolInput(options = {}) {
|
|
|
5795
5756
|
fast_path: {
|
|
5796
5757
|
executed: false,
|
|
5797
5758
|
action: null,
|
|
5798
|
-
|
|
5759
|
+
specs_stored_at: null,
|
|
5799
5760
|
run_status: updated.state?.status || null,
|
|
5800
5761
|
current_role: updated.state?.current_role || null,
|
|
5801
5762
|
requires_followup_turn: true,
|
|
@@ -21,10 +21,9 @@ context: |
|
|
|
21
21
|
|
|
22
22
|
rules:
|
|
23
23
|
proposal:
|
|
24
|
-
- "
|
|
25
|
-
- "能从项目规则和代码推断的内容,优先写入 assumptions,不重复标记为缺失输入。"
|
|
24
|
+
- "先收敛目标、范围和风险,再进入实现。"
|
|
26
25
|
- "涉及页面、路由、接口、状态、样式时,必须对齐项目既有落点。"
|
|
27
|
-
- "proposal.md
|
|
26
|
+
- "proposal.md 写清业务与工程目标、纳入/排除范围、约束与风险。"
|
|
28
27
|
specs:
|
|
29
28
|
- "需求必须落为可测试的增量规范和场景。"
|
|
30
29
|
- "涉及 UI 时,验收场景应覆盖关键布局、状态和交互。"
|
|
@@ -36,17 +35,9 @@ rules:
|
|
|
36
35
|
- "涉及 UI 时必须明确验收方式;涉及接口时必须明确封装方式。"
|
|
37
36
|
- "保持改动最小化,聚焦于请求的变更。"
|
|
38
37
|
- "涉及安装、路径、runtime、归档或 IDE 适配时,必须包含跨平台验证任务。"
|
|
39
|
-
- "tasks.md
|
|
40
|
-
design:
|
|
41
|
-
- "技术方案必须对齐项目目录、路由、API、状态、样式和测试约定。"
|
|
42
|
-
- "新增能力优先复用现有结构,不因单次变更引入无关重构。"
|
|
43
|
-
- "涉及路径处理时,必须使用跨平台路径能力,不硬编码路径分隔符。"
|
|
44
|
-
- "优先复用现有 registry、常量和显式映射,不引入启发式检测、模糊匹配或正则猜测。"
|
|
45
|
-
- "design.md 需要补齐信息结构、状态管理、组件复用策略和验收路径。"
|
|
38
|
+
- "tasks.md 每个子任务写明做什么和怎么验。"
|
|
46
39
|
checklist:
|
|
47
|
-
- "
|
|
48
|
-
- "
|
|
49
|
-
- "checklist.md 需要沉淀本地验证、浏览器验证、范围一致性和组件复用检查摘要。"
|
|
40
|
+
- "检查结论必须基于 specs/tasks、项目规则和实现证据。"
|
|
41
|
+
- "checklist.md 沉淀通过项、验证结果、未通过/阻断项和建议。"
|
|
50
42
|
iterations:
|
|
51
|
-
- "
|
|
52
|
-
- "iterations.md 需要记录问题来源、修正动作、残留风险和下轮提醒。"
|
|
43
|
+
- "iterations.md 记录问题与修正、残留风险与下轮提醒。"
|