speccore 6.0.1 → 6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -60,6 +60,15 @@ function slugify(name) {
60
60
  const hash = Math.abs(name.split('').reduce((a, c) => a * 31 + c.charCodeAt(0), 7)).toString(36);
61
61
  return hash.slice(0, 6);
62
62
  }
63
+ /**
64
+ * 生成子任务全局唯一 ID
65
+ * 格式: Task-{父任务完整名}-{端名}-{hash4}
66
+ * 例: Task-001-user-login-backend-a3f2, Task-001-user-login-web-b7c1
67
+ */
68
+ function generateSubtaskId(parentTaskId, platform) {
69
+ const hash = Math.abs(`${parentTaskId}-${platform}-${Date.now()}-${Math.random()}`.split('').reduce((a, c) => a * 31 + c.charCodeAt(0), 13)).toString(36);
70
+ return `Task-${parentTaskId}-${platform}-${hash.slice(0, 4)}`;
71
+ }
63
72
  /** 粒度约束常量 */
64
73
  const GRANULARITY_RULES = {
65
74
  macro: { label: '粗粒度 (macro)', minHours: 20, maxHours: 80, maxApis: 15, maxTables: 5, maxPages: 5, desc: '每个任务 1-2 周,按业务方向合并' },
@@ -505,6 +514,7 @@ async function iterationSplitCommand(options) {
505
514
  }
506
515
  const specDir2 = (0, path_1.join)(iterationDir, '020-specs');
507
516
  const specContents = [];
517
+ // 读取全局层扁平文件(迭代级跨端综合)
508
518
  for (const f of ['ANALYSIS.md', 'TECH.md', 'TEST.md', 'REVIEW.md', 'RISK.md', 'DEPS.md', 'MONITOR.md']) {
509
519
  const fp = (0, path_1.join)(specDir2, f);
510
520
  if (await (0, fs_extra_1.pathExists)(fp)) {
@@ -514,6 +524,23 @@ async function iterationSplitCommand(options) {
514
524
  }
515
525
  }
516
526
  }
527
+ // 读取各端详情(020-specs/platforms/{端}/)
528
+ const iterPlatformsDir = (0, path_1.join)(specDir2, 'platforms');
529
+ if (await (0, fs_extra_1.pathExists)(iterPlatformsDir)) {
530
+ const platformEntries = await (0, fs_extra_1.readdir)(iterPlatformsDir, { withFileTypes: true });
531
+ for (const pe of platformEntries) {
532
+ if (pe.isDirectory() && !pe.name.startsWith('.')) {
533
+ const pFiles = await (0, fs_extra_1.readdir)((0, path_1.join)(iterPlatformsDir, pe.name));
534
+ for (const pf of pFiles.filter((f) => f.endsWith('.md'))) {
535
+ const fp = (0, path_1.join)(iterPlatformsDir, pe.name, pf);
536
+ const content = await (0, fs_extra_1.readFile)(fp, 'utf-8');
537
+ if (content.trim().length > 50 && !content.trim().match(/^#+\s*\u5f85\u586b\u5145|^<!--\s*AI-FILL/m)) {
538
+ specContents.push({ name: `platforms/${pe.name}/${pf}`, content });
539
+ }
540
+ }
541
+ }
542
+ }
543
+ }
517
544
  // 粒度推荐(基于 STAFFING 人数)
518
545
  const staffing = readStaffing(iterationDir);
519
546
  const teamSize = staffing ? staffing.length : 0;
@@ -825,76 +852,56 @@ async function createTaskFromSection(iterationDir, taskId, section, allPlatforms
825
852
  // ── 2. 任务目录指引 ──
826
853
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'README.md'), `# ${section.name}
827
854
 
828
- > 任务目录使用指引
855
+ > 任务模块分组 — 按功能单元组织,各端并行开发
829
856
 
830
857
  ## 目录结构
831
858
 
832
859
  \`\`\`
833
860
  ${taskId}/
834
- ├── README.md <-- 本文件(目录指引)
861
+ ├── README.md <-- 本文件
835
862
  ├── .meta/ <-- 任务元信息(type/status/owner/created-at)
836
- ├── _shared/ <-- 共享契约(API_CONTRACT.yaml)
837
- ├── 00-specs/ <-- 执行前核心规格(AI 执行时必读)
863
+ ├── _shared/ <-- 共享规格(所有端共用)
838
864
  │ ├── REQ.md <-- 需求描述(API + 数据模型 + 业务规则)
839
- │ ├── TECH.md <-- 技术方案(架构/接口设计/数据模型/核心逻辑)
840
- │ ├── TASK.md <-- 任务执行追踪(状态/负责人/产出物清单)
841
- │ ├── SCHEMA.md <-- 数据库设计(Entity/DDL/索引)
865
+ │ ├── TECH.md <-- 技术方案(架构/接口设计/核心逻辑)
866
+ │ ├── SCHEMA.md <-- 数据库设计(如有)
867
+ │ ├── API_CONTRACT.yaml <-- API 契约
842
868
  │ └── CHANGELOG.md <-- 变更记录
843
- ├── 10-backend/ <-- 后端实现(src/tests)
844
- ├── 20-frontend/ <-- 前端实现({platform}/src/tests)
869
+ ├── {端名}/ <-- 各端独立子任务(backend/admin/web/h5/...)
870
+ ├── TASK.md <-- 子任务追踪(含子任务表、负责人、状态)
871
+ │ ├── src/ <-- 源码实现
872
+ │ └── tests/ <-- 测试用例
845
873
  ├── 99-artifacts/ <-- 执行产出
846
- │ ├── 🔒 自检门禁(verify 自动读取验证)
847
- ├── TEST.md <-- 测试用例(verify 检查覆盖率)
848
- ├── REVIEW.md <-- 评审清单(verify 检查合规性)
849
- │ ├── DEPLOY.md <-- 部署清单(verify 检查部署项)
850
- │ │ └── ERROR_CODES.md <-- 错误码表(verify 检查一致性)
851
- │ └── 📚 参考文档(AI/人参考,不参与自动验证)
852
- │ ├── RISK.md <-- 风险评估
853
- │ ├── DEPS.md <-- 依赖清单
854
- │ ├── MONITOR.md <-- 监控配置
855
- │ └── ADR.md <-- 架构决策记录
874
+ │ ├── TEST.md <-- 测试用例
875
+ │ ├── REVIEW.md <-- 评审清单
876
+ │ ├── DEPLOY.md <-- 部署清单
877
+ └── ...
856
878
  └── .issues.md <-- 问题追踪
857
879
  \`\`\`
858
880
 
859
- ## AI 执行时读取规则
860
-
861
- 运行 \`speccore execute -t ${taskId}\` 时,AI 会按以下顺序读取本文档:
862
-
863
- ### 必读文件(自动嵌入 AI Prompt)
881
+ ## 子任务命名规则
864
882
 
865
- | 文件 | 用途 |
866
- |:---|:---|
867
- | \`00-specs/REQ.md\` | 需求描述、API 列表、数据模型、业务规则 |
868
- | \`00-specs/TECH.md\` | 技术方案、架构设计、接口分层、核心逻辑 |
869
- | \`00-specs/TASK.md\` | 任务信息、状态追踪、产出物清单 |
870
- | \`00-specs/SCHEMA.md\` | 数据库表结构、字段、索引(如有) |
871
- | \`.speccore/CONSTITUTION.md\` | 技术栈、命名规范、异常码体系 |
883
+ 每个端的子任务 ID 格式: \`Task-{父任务名}-{端名}-{hash}\`
872
884
 
873
- ### 参考文件(AI 按需查阅)
874
-
875
- | 文件 | 用途 | verify 自检? |
876
- |:---|:---|:---|
877
- | \`99-artifacts/TEST.md\` | 测试计划(用例/边界/集成) | ✅ 检查用例覆盖率 |
878
- | \`99-artifacts/REVIEW.md\` | 评审清单(安全/质量/性能) | ✅ 检查评审项合规 |
879
- | \`99-artifacts/RISK.md\` | 风险评估 | ❌ 仅参考 |
880
- | \`_shared/API_CONTRACT.yaml\` | API 契约(OpenAPI 格式) | ❌ 仅参考 |
881
- | \`.issues.md\` | 已知问题和约束 | ❌ 仅参考 |
882
-
883
- ### 不会被 AI 读取的文件
885
+ | 子任务 ID | 所属端 | 负责人 | 状态 |
886
+ | :--- | :--- | :--- | :--- |
887
+ ${taskPlatforms.map((p) => `| ${generateSubtaskId(taskId.replace(/^Task-/, ''), p)} | ${p} | ${owner} | 🔲 待开发 |`).join('\n')}
884
888
 
885
- - \`10-backend/\` 和 \`20-frontend/\` 下的源码 —— 这是 AI **输出**代码的地方
886
- - \`99-artifacts/DEPLOY.md\`、\`ERROR_CODES.md\` 等 —— 执行完成后自动更新
889
+ ## AI 执行时读取规则
887
890
 
888
- ## 如何让 AI 读到你补充的文档?
891
+ 运行 \`speccore execute -t ${taskId} --platform {端}\` 时:
889
892
 
890
- **推荐做法:** 把你的补充内容写到对应的规范文件里:
893
+ ### 必读(自动嵌入)
894
+ - \`_shared/REQ.md\` — 共享需求
895
+ - \`_shared/TECH.md\` — 共享技术方案
896
+ - \`{端}/TASK.md\` — 该端子任务详情
891
897
 
892
- - 补充需求细节 → 写到 \`00-specs/REQ.md\`
893
- - 补充技术方案 → 写到 \`00-specs/TECH.md\`
894
- - 补充数据库设计 → 写到 \`00-specs/SCHEMA.md\`
895
- - 记录已知问题/约束 → 写到 \`.issues.md\`
898
+ ### 参考(按需读取)
899
+ - \`020-specs/\` 下的迭代全局文档(架构、技术方案等)
900
+ - \`.speccore/GLOBAL/\` 下的全局知识库
896
901
 
897
- AI 执行时会自动读取这些文件,作为生成代码的依据。
902
+ ### 不会被读取
903
+ - \`{端}/src/\` 和 \`{端}/tests/\` — 这是 AI **输出**代码的地方
904
+ - \`99-artifacts/\` — 执行完成后自动更新
898
905
  `);
899
906
  // ── 3. 共享契约 ──
900
907
  await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '_shared'));
@@ -902,26 +909,26 @@ AI 执行时会自动读取这些文件,作为生成代码的依据。
902
909
  if (contractYaml) {
903
910
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'API_CONTRACT.yaml'), contractYaml);
904
911
  }
905
- // ── 3. 执行前核心规格 00-specs/ ──
906
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '00-specs'));
912
+ // ── 3. 共享规格 _shared/(所有端共用) ──
913
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '_shared'));
907
914
  const acItems = generateAcceptanceCriteria(section);
908
915
  const aiReqContent = section._reqContent;
909
916
  // REQ.md: 优先用 AI 生成的实际内容,回退到模板
910
917
  if (aiReqContent && aiReqContent.length > 50) {
911
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'REQ.md'), `# ${section.name}\n\n${aiReqContent}\n\n## 验收标准\n\n${acItems}\n`);
918
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'REQ.md'), `# ${section.name}\n\n${aiReqContent}\n\n## 验收标准\n\n${acItems}\n`);
912
919
  }
913
920
  else {
914
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'REQ.md'), `# ${section.name}\n\n## 需求描述\n\n${section.content}\n\n## 验收标准\n\n${acItems}\n`);
921
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'REQ.md'), `# ${section.name}\n\n## 需求描述\n\n${section.content}\n\n## 验收标准\n\n${acItems}\n`);
915
922
  }
916
923
  const apiLines = section.content.split('\n').filter(l => l.includes('| GET') || l.includes('| POST') || l.includes('| PUT') || l.includes('| DELETE') || l.includes('| PATCH'));
917
924
  const apiDesc = apiLines.length > 0 ? apiLines.map(l => `- ${l.trim()}`).join('\n') : '- 待补充(从 REQ.md 提取接口列表)';
918
925
  const aiTechContent = section._techContent;
919
926
  // TECH.md: 优先用 AI 生成的实际内容,回退到模板
920
927
  if (aiTechContent && aiTechContent.length > 50) {
921
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案\n\n${aiTechContent}\n`);
928
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'TECH.md'), `# ${section.name} - 技术方案\n\n${aiTechContent}\n`);
922
929
  }
923
930
  else {
924
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案
931
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'TECH.md'), `# ${section.name} - 技术方案
925
932
 
926
933
  > ⚠️ 本文档由 split 自动生成框架,AI 执行时会自动填充。
927
934
 
@@ -950,48 +957,15 @@ ${apiDesc}
950
957
  `);
951
958
  }
952
959
  if (section.content.match(/数据库|数据表|表结构|DDL|ALTER|建表|索引/)) {
953
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'SCHEMA.md'), generateSchemaTemplate(section));
960
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'SCHEMA.md'), generateSchemaTemplate(section));
954
961
  }
955
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TASK.md'), `# ${section.name}
956
-
957
- ## 任务信息
958
- - 类型: ${taskType}
959
- - 状态: 🔲 待开发
960
- - 优先级: ${complexity.priority}
961
- - 负责人: ${owner}
962
- - 预计耗时: ${complexity.estimatedHours}h${complexity.complexity !== 'medium' ? ` (${complexity.complexity === 'high' ? '高复杂度' : '低复杂度'})` : ''}
963
- - 复杂度: API ${complexity.apiCount} | DB ${complexity.dbCount} | 页面 ${complexity.pageCount}
964
-
965
- ## 变更履历
966
- | 时间 | 变更内容 | 变更人 |
967
- | :--- | :--- | :--- |
968
- | ${today} | 创建任务 | CLI |
969
-
970
- ## 产出物清单
971
- | 产出物 | 状态 | 路径 | verify 自检? |
972
- | :--- | :--- | :--- | :--- |
973
- | REQ.md | ✅ | ./00-specs/REQ.md | ✅ Spec 一致性 |
974
- | TECH.md | ✅ | ./00-specs/TECH.md | — |
975
- | TASK.md | ✅ | ./00-specs/TASK.md | — |
976
- | SCHEMA.md | ⏳ | ./00-specs/SCHEMA.md | — |
977
- | API_CONTRACT.yaml | ✅ | ./_shared/API_CONTRACT.yaml | — |
978
- | TEST.md | ⏳ | ./99-artifacts/TEST.md | ✅ 用例覆盖率 |
979
- | REVIEW.md | ⏳ | ./99-artifacts/REVIEW.md | ✅ 评审项合规 |
980
- | DEPLOY.md | ⏳ | ./99-artifacts/DEPLOY.md | ✅ 部署项检查 |
981
- | ERROR_CODES.md | ⏳ | ./99-artifacts/ERROR_CODES.md | ✅ 错误码一致性 |
982
- | ADR.md | ⏳ | ./99-artifacts/ADR.md | ❌ 仅参考 |
983
- | RISK.md | ⏳ | ./99-artifacts/RISK.md | ❌ 仅参考 |
984
- | DEPS.md | ⏳ | ./99-artifacts/DEPS.md | ❌ 仅参考 |
985
- | MONITOR.md | ⏳ | ./99-artifacts/MONITOR.md | ❌ 仅参考 |
986
- | CHANGELOG.md | ✅ | ./00-specs/CHANGELOG.md | — |
987
- `);
988
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'CHANGELOG.md'), `# ${section.name} - 变更记录
962
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'CHANGELOG.md'), `# ${section.name} - 变更记录
989
963
 
990
964
  | 时间 | 版本 | 变更内容 | 变更人 |
991
965
  | :--- | :--- | :--- | :--- |
992
966
  | ${today} | v1.0 | 初始创建 | CLI |
993
967
  `);
994
- // API_CONTRACT.yaml(按接口)统一放 _shared/
968
+ // API_CONTRACT.yaml 统一放 _shared/
995
969
  if (apiLines.length > 0) {
996
970
  const contracts = apiLines.map(l => {
997
971
  const parts = l.split('|').map(p => p.trim()).filter(Boolean);
@@ -1002,7 +976,65 @@ ${apiDesc}
1002
976
  }).join('\n');
1003
977
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'API_CONTRACT.yaml'), `# ${section.name} - API Contract\n# Auto-generated from split\n\npaths:\n${contracts}\n`);
1004
978
  }
1005
- // ── 4. 执行产出 99-artifacts/ ──
979
+ // ── 4. 各端独立子任务目录 ──
980
+ const taskNum = taskId.replace(/^Task-/, '');
981
+ const sharedReqContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, '_shared', 'REQ.md'), 'utf-8');
982
+ const sharedTechContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, '_shared', 'TECH.md'), 'utf-8');
983
+ for (const platform of taskPlatforms) {
984
+ const subtaskId = generateSubtaskId(taskNum, platform);
985
+ const platformDir = (0, path_1.join)(taskDir, platform);
986
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(platformDir, 'src'));
987
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(platformDir, 'tests'));
988
+ // 各端 TASK.md — 含子任务表、负责人、状态
989
+ const isBackend = platform === 'backend' || platform.startsWith('后台');
990
+ const platformLabel = isBackend ? '后端' : platform;
991
+ const subtaskHours = section._hoursByPlatform?.[platform] || Math.ceil(complexity.estimatedHours / taskPlatforms.length);
992
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'TASK.md'), `# ${section.name} — ${platformLabel}
993
+
994
+ ## 子任务信息
995
+ - **子任务 ID**: \`${subtaskId}\`
996
+ - **所属模块**: \`${taskId}\`
997
+ - **端**: ${platform}
998
+ - **负责人**: ${owner}
999
+ - **状态**: 🔲 待开发
1000
+ - **预计耗时**: ${subtaskHours}h
1001
+
1002
+ ## 子任务列表
1003
+
1004
+ | 子任务 ID | 名称 | 负责人 | 状态 | 预估工时 | 依赖 |
1005
+ | :--- | :--- | :--- | :--- | :--- | :--- |
1006
+ | \`${subtaskId}-01\` | ${section.name} - ${platformLabel}实现 | ${owner} | 🔲 待开发 | ${subtaskHours}h | 无 |
1007
+
1008
+ ## 共享规格引用
1009
+ - REQ.md → ../_shared/REQ.md
1010
+ - TECH.md → ../_shared/TECH.md
1011
+
1012
+ ## 产出物
1013
+ | 产出物 | 状态 | 路径 |
1014
+ | :--- | :--- | :--- |
1015
+ | TASK.md | ✅ | ./${platform}/TASK.md |
1016
+ | src/ | ⏳ | ./${platform}/src/ |
1017
+ | tests/ | ⏳ | ./${platform}/tests/ |
1018
+
1019
+ ## 变更履历
1020
+ | 时间 | 变更内容 | 变更人 |
1021
+ | :--- | :--- | :--- |
1022
+ | ${today} | 创建子任务 | CLI |
1023
+ `);
1024
+ // 后端端额外生成组件/路由等前端文档
1025
+ if (!isBackend) {
1026
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'COMPONENT_TREE.md'), generateComponentTree(section, platform));
1027
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'ROUTES.md'), generateRoutesDoc(section, platform));
1028
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'STATE.md'), generateStateDoc(section, platform));
1029
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'STYLE_GUIDE.md'), generateStyleGuide(section, platform));
1030
+ }
1031
+ }
1032
+ // 确保至少有 backend 目录
1033
+ if (!taskPlatforms.some((p) => p === 'backend' || p.startsWith('后台'))) {
1034
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', 'src'));
1035
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', 'tests'));
1036
+ }
1037
+ // ── 5. 执行产出 99-artifacts/ ──
1006
1038
  await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '99-artifacts'));
1007
1039
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'TEST.md'), generateTestOutline(section));
1008
1040
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'REVIEW.md'), generateReviewChecklist(section));
@@ -1015,71 +1047,7 @@ ${apiDesc}
1015
1047
  if (adr) {
1016
1048
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'ADR.md'), adr);
1017
1049
  }
1018
- // ── 5. 实现目录 10-backend/ 20-frontend/ ──
1019
- for (const platform of taskPlatforms) {
1020
- if (platform === 'backend') {
1021
- // 纯后端任务:直接创建 10-backend/src/ 和 10-backend/tests/
1022
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '10-backend', 'src'));
1023
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '10-backend', 'tests'));
1024
- }
1025
- else if (platform.startsWith('后台')) {
1026
- // 后台服务任务:创建 10-backend/{service}/src/ 和 tests/
1027
- const service = platform.replace(/^后台/, '').trim() || 'default';
1028
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '10-backend', service, 'src'));
1029
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '10-backend', service, 'tests'));
1030
- }
1031
- else {
1032
- // 前端任务:创建 20-frontend/{platform}/src/ 和 tests/
1033
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '20-frontend', platform, 'src'));
1034
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '20-frontend', platform, 'tests'));
1035
- }
1036
- }
1037
- if (!taskPlatforms.some((p) => p === 'backend' || p.startsWith('后台'))) {
1038
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '10-backend', 'src'));
1039
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '10-backend', 'tests'));
1040
- }
1041
- // ── 6. 平台目录内复制核心规格 ──
1042
- const reqContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, '00-specs', 'REQ.md'), 'utf-8');
1043
- const techContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), 'utf-8');
1044
- const taskContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, '00-specs', 'TASK.md'), 'utf-8');
1045
- const testContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, '99-artifacts', 'TEST.md'), 'utf-8');
1046
- const reviewContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, '99-artifacts', 'REVIEW.md'), 'utf-8');
1047
- for (const platform of taskPlatforms) {
1048
- if (platform === 'backend') {
1049
- // 纯后端任务:规格写入 10-backend/ 根目录
1050
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '10-backend', 'REQ.md'), reqContent);
1051
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '10-backend', 'TECH.md'), techContent);
1052
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '10-backend', 'TASK.md'), taskContent);
1053
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '10-backend', 'TEST.md'), testContent);
1054
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '10-backend', 'REVIEW.md'), reviewContent);
1055
- }
1056
- else if (platform.startsWith('后台')) {
1057
- // 后台服务任务:规格写入 10-backend/{service}/
1058
- const service = platform.replace(/^后台/, '').trim() || platform;
1059
- const svcDir = (0, path_1.join)(taskDir, '10-backend', service);
1060
- await (0, fs_extra_1.ensureDir)(svcDir);
1061
- await (0, fs_extra_1.writeFile)((0, path_1.join)(svcDir, 'REQ.md'), reqContent);
1062
- await (0, fs_extra_1.writeFile)((0, path_1.join)(svcDir, 'TECH.md'), techContent);
1063
- await (0, fs_extra_1.writeFile)((0, path_1.join)(svcDir, 'TASK.md'), taskContent);
1064
- await (0, fs_extra_1.writeFile)((0, path_1.join)(svcDir, 'TEST.md'), testContent);
1065
- await (0, fs_extra_1.writeFile)((0, path_1.join)(svcDir, 'REVIEW.md'), reviewContent);
1066
- }
1067
- else {
1068
- // 前端任务:规格写入 20-frontend/{platform}/
1069
- const feDir = (0, path_1.join)(taskDir, '20-frontend', platform);
1070
- await (0, fs_extra_1.ensureDir)(feDir);
1071
- await (0, fs_extra_1.writeFile)((0, path_1.join)(feDir, 'REQ.md'), reqContent);
1072
- await (0, fs_extra_1.writeFile)((0, path_1.join)(feDir, 'TASK.md'), taskContent);
1073
- await (0, fs_extra_1.writeFile)((0, path_1.join)(feDir, 'TEST.md'), testContent);
1074
- await (0, fs_extra_1.writeFile)((0, path_1.join)(feDir, 'REVIEW.md'), reviewContent);
1075
- await (0, fs_extra_1.writeFile)((0, path_1.join)(feDir, 'README.md'), `# ${section.name} - ${platform}\n\n前端实现目录。\n`);
1076
- await (0, fs_extra_1.writeFile)((0, path_1.join)(feDir, 'COMPONENT_TREE.md'), generateComponentTree(section, platform));
1077
- await (0, fs_extra_1.writeFile)((0, path_1.join)(feDir, 'ROUTES.md'), generateRoutesDoc(section, platform));
1078
- await (0, fs_extra_1.writeFile)((0, path_1.join)(feDir, 'STATE.md'), generateStateDoc(section, platform));
1079
- await (0, fs_extra_1.writeFile)((0, path_1.join)(feDir, 'STYLE_GUIDE.md'), generateStyleGuide(section, platform));
1080
- }
1081
- }
1082
- // ── 7. 问题追踪 ──
1050
+ // ── 6. 问题追踪 ──
1083
1051
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.issues.md'), `# ${section.name} - 问题追踪\n\n> 执行过程中发现的问题记录于此。\n\n`);
1084
1052
  }
1085
1053
  async function updateProjectGraph(iterationDir, sections) {
@@ -1228,7 +1196,7 @@ async function strictSplitPreview(sections, platforms, iterationDir) {
1228
1196
  const taskId = s._taskId || `Task-${String(i + 1).padStart(3, '0')}`;
1229
1197
  // Determine target directory
1230
1198
  const target = s.platform
1231
- ? (s.platform.startsWith('后台') ? `backend/${s.platform.replace(/^后台/, '')}` : `20-frontend/${s.platform}`)
1199
+ ? (s.platform.startsWith('后台') ? `backend/${s.platform.replace(/^后台/, '')}` : s.platform)
1232
1200
  : platforms.join(' + ');
1233
1201
  logger_1.logger.info(`── ${taskId}: ${s.name} ──`);
1234
1202
  logger_1.logger.info(` 端: ${target}`);
@@ -1308,8 +1276,8 @@ async function generateImpactGraph(iterationDir, sections, platforms) {
1308
1276
  const taskType = s._taskType || 'feature';
1309
1277
  const taskDir = (0, path_1.join)(iterationDir, '030-tasks', taskType, taskId);
1310
1278
  if (await (0, fs_extra_1.pathExists)(taskDir)) {
1311
- // 生成风险报告并嵌入 TASK.md(去重:只写一次)
1312
- const taskMdPath = (0, path_1.join)(taskDir, '00-specs', 'TASK.md');
1279
+ // 生成风险报告并嵌入 _shared/TECH.md(去重:只写一次)
1280
+ const taskMdPath = (0, path_1.join)(taskDir, '_shared', 'TECH.md');
1313
1281
  const riskReport = (0, risk_scorer_1.generateRiskReport)(risk);
1314
1282
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.risk'), riskReport);
1315
1283
  if (await (0, fs_extra_1.pathExists)(taskMdPath)) {
@@ -1458,7 +1426,7 @@ async function injectTechFromAnalysis(iterationDir, taskDir, sectionName) {
1458
1426
  const depSection = analysis.match(/### 接口依赖[\s\S]*?(?=###|$)/);
1459
1427
  if (!techSection && !dbSection && !depSection)
1460
1428
  return;
1461
- const techPath = (0, path_1.join)(taskDir, '00-specs', 'TECH.md');
1429
+ const techPath = (0, path_1.join)(taskDir, '_shared', 'TECH.md');
1462
1430
  let tech = await (0, fs_extra_1.readFile)(techPath, 'utf-8');
1463
1431
  const note = '\n\n> 以下内容自动从 ANALYSIS.md 注入\n\n';
1464
1432
  if (techSection && !tech.includes(techSection[0].trim())) {
@@ -1722,7 +1690,7 @@ function buildSplitPrompt(iteration, constitutionContent, reqContent, specConten
1722
1690
  p += `一个原子任务 = 一个开发者在指定粒度内可独立完成的、有明确验收标准的最小工作单元。\n`;
1723
1691
  p += `判定标准(全部满足):\n`;
1724
1692
  p += `- 有独立的输入/输出(API 接口 / 页面 / 数据表)\n`;
1725
- p += `- 00-specs/ 三件套能独立写满(REQ.md + TECH.md + TASK.md)\n`;
1693
+ p += `- _shared/ + {端}/TASK.md 能独立写满(REQ.md + TECH.md + 各端子任务)\n`;
1726
1694
  p += `- execute 时不强依赖其他 Task 的运行时状态\n`;
1727
1695
  p += `- 有明确的验收标准(AC 可枚举)\n`;
1728
1696
  p += `- 可独立提 PR、独立 review\n\n`;