coding-agent-harness 1.0.5 → 1.0.7

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.
Files changed (260) hide show
  1. package/CONTRIBUTING.md +2 -2
  2. package/README.md +63 -3
  3. package/README.zh-CN.md +52 -3
  4. package/SKILL.md +43 -43
  5. package/dist/build-dist.mjs +189 -0
  6. package/dist/check-dist-observation.mjs +428 -0
  7. package/dist/check-harness.mjs +489 -0
  8. package/dist/check-import-graph.mjs +511 -0
  9. package/dist/check-runtime-emit.mjs +304 -0
  10. package/dist/check-type-boundaries.mjs +139 -0
  11. package/dist/commands/dashboard-command.mjs +80 -0
  12. package/dist/commands/migration-command.mjs +152 -0
  13. package/dist/commands/preset-command.mjs +91 -0
  14. package/dist/commands/task-command.mjs +324 -0
  15. package/dist/harness.mjs +304 -0
  16. package/dist/lib/capability-registry.mjs +643 -0
  17. package/dist/lib/check-module-parallel.mjs +227 -0
  18. package/dist/lib/check-profiles.mjs +414 -0
  19. package/dist/lib/check-task-contracts.mjs +54 -0
  20. package/dist/lib/core-shared.mjs +254 -0
  21. package/dist/lib/dashboard-data.mjs +608 -0
  22. package/dist/lib/dashboard-workbench.mjs +334 -0
  23. package/dist/lib/dashboard-writer.mjs +200 -0
  24. package/dist/lib/git-status-summary.mjs +45 -0
  25. package/dist/lib/governance-index-generator.mjs +236 -0
  26. package/dist/lib/governance-sync.mjs +617 -0
  27. package/dist/lib/governance-table-boundary.mjs +161 -0
  28. package/{scripts → dist}/lib/harness-core.mjs +2 -0
  29. package/dist/lib/harness-paths.mjs +338 -0
  30. package/dist/lib/lesson-maintenance.mjs +139 -0
  31. package/dist/lib/markdown-utils.mjs +193 -0
  32. package/dist/lib/migration-planner.mjs +439 -0
  33. package/dist/lib/migration-support.mjs +317 -0
  34. package/dist/lib/phase-kind.mjs +46 -0
  35. package/dist/lib/preset-audit-contracts.mjs +40 -0
  36. package/dist/lib/preset-engine.mjs +516 -0
  37. package/dist/lib/preset-registry.mjs +831 -0
  38. package/dist/lib/preset-resource-contracts.mjs +83 -0
  39. package/dist/lib/review-confirm-git-gate.mjs +244 -0
  40. package/dist/lib/status-builder.mjs +87 -0
  41. package/{scripts → dist}/lib/status-dashboard-renderer.mjs +44 -46
  42. package/dist/lib/structure-migration.mjs +404 -0
  43. package/dist/lib/subagent-authorization-audit.mjs +198 -0
  44. package/dist/lib/task-audit-metadata.mjs +376 -0
  45. package/dist/lib/task-audit-migration.mjs +355 -0
  46. package/dist/lib/task-completion-consistency.mjs +29 -0
  47. package/dist/lib/task-index.mjs +133 -0
  48. package/dist/lib/task-lesson-candidates.mjs +239 -0
  49. package/dist/lib/task-lesson-sedimentation.mjs +300 -0
  50. package/dist/lib/task-lifecycle/create-task-helpers.mjs +84 -0
  51. package/dist/lib/task-lifecycle/phase-sync.mjs +82 -0
  52. package/dist/lib/task-lifecycle/review-confirm.mjs +93 -0
  53. package/dist/lib/task-lifecycle/review-gates.mjs +62 -0
  54. package/dist/lib/task-lifecycle/review-submission.mjs +52 -0
  55. package/dist/lib/task-lifecycle/scaffold-provenance.mjs +54 -0
  56. package/dist/lib/task-lifecycle/template-files.mjs +52 -0
  57. package/dist/lib/task-lifecycle/text-utils.mjs +26 -0
  58. package/dist/lib/task-lifecycle.mjs +611 -0
  59. package/dist/lib/task-metadata.mjs +116 -0
  60. package/dist/lib/task-review-model.mjs +474 -0
  61. package/dist/lib/task-scanner.mjs +439 -0
  62. package/dist/lib/task-tombstone-commands.mjs +125 -0
  63. package/dist/postinstall.mjs +14 -0
  64. package/dist/run-built-tests.mjs +84 -0
  65. package/docs-release/README.md +1 -0
  66. package/docs-release/architecture/overview.md +12 -12
  67. package/docs-release/architecture/overview.zh-CN.md +12 -12
  68. package/docs-release/architecture/system-explainer/01-system-overview.md +15 -14
  69. package/docs-release/architecture/system-explainer/02-module-dependency.md +8 -8
  70. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +3 -3
  71. package/docs-release/architecture/system-explainer/04-check-and-governance.md +9 -7
  72. package/docs-release/architecture/system-explainer/05-data-flow.md +5 -5
  73. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +1 -4
  74. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +15 -14
  75. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +8 -8
  76. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +3 -3
  77. package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +10 -8
  78. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +5 -5
  79. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +1 -4
  80. package/docs-release/guides/agent-installation.en-US.md +14 -8
  81. package/docs-release/guides/agent-installation.md +14 -8
  82. package/docs-release/guides/contributing.md +3 -3
  83. package/docs-release/guides/contributing.zh-CN.md +3 -3
  84. package/docs-release/guides/document-audience-and-surfaces.en-US.md +10 -10
  85. package/docs-release/guides/document-audience-and-surfaces.md +10 -10
  86. package/docs-release/guides/legacy-migration-agent-prompt.md +25 -2
  87. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +25 -2
  88. package/docs-release/guides/migration-playbook.en-US.md +63 -1
  89. package/docs-release/guides/migration-playbook.md +59 -1
  90. package/docs-release/guides/parent-control-repository-pattern.en-US.md +25 -25
  91. package/docs-release/guides/parent-control-repository-pattern.md +25 -25
  92. package/docs-release/guides/preset-development.md +2 -2
  93. package/docs-release/guides/repository-operating-models.en-US.md +21 -21
  94. package/docs-release/guides/repository-operating-models.md +21 -21
  95. package/docs-release/guides/task-state-machine.en-US.md +5 -5
  96. package/docs-release/guides/task-state-machine.md +5 -5
  97. package/docs-release/guides/typescript-runtime-migration-closeout.md +96 -0
  98. package/examples/minimal-project/AGENTS.md +2 -2
  99. package/examples/minimal-project/coding-agent-harness/harness.yaml +14 -0
  100. package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/progress.md +11 -0
  101. package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/review.md +1 -1
  102. package/package.json +20 -12
  103. package/presets/legacy-migration/preset.yaml +5 -5
  104. package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
  105. package/presets/lesson-sedimentation/preset.yaml +3 -3
  106. package/presets/module/preset.yaml +2 -2
  107. package/presets/module/templates/execution_strategy.append.md +1 -1
  108. package/presets/module/templates/task_plan.append.md +3 -3
  109. package/presets/standard-task/preset.yaml +2 -2
  110. package/references/adversarial-review-standard.md +2 -2
  111. package/references/agents-md-pattern.md +14 -14
  112. package/references/cadence-ledger.md +1 -1
  113. package/references/ci-cd-standard.md +1 -1
  114. package/references/delivery-operating-model-standard.md +4 -4
  115. package/references/docs-directory-standard.md +65 -159
  116. package/references/external-source-intake-standard.md +10 -10
  117. package/references/harness-ledger.md +5 -5
  118. package/references/legacy-12-phase-bootstrap.md +2 -2
  119. package/references/lessons-governance.md +15 -15
  120. package/references/long-running-task-standard.md +6 -6
  121. package/references/module-parallel-standard.md +34 -34
  122. package/references/planning-loop.md +6 -6
  123. package/references/project-onboarding-audit.md +4 -4
  124. package/references/regression-system.md +2 -2
  125. package/references/repo-governance-standard.md +4 -4
  126. package/references/review-routing-standard.md +1 -1
  127. package/references/ssot-governance.md +19 -19
  128. package/references/taskr-gap-analysis.md +5 -5
  129. package/references/walkthrough-closeout.md +14 -14
  130. package/references/worktree-parallel.md +3 -3
  131. package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +1 -1
  132. package/skills/preset-creator/references/preset-package-skeleton.md +5 -5
  133. package/templates/AGENTS.md.template +26 -26
  134. package/templates/architecture/README.md +4 -4
  135. package/templates/architecture/service-catalog.md +2 -2
  136. package/templates/architecture/services/service-template.md +1 -1
  137. package/templates/dashboard/assets/app-src/20-overview.js +11 -5
  138. package/templates/dashboard/assets/app-src/40-modules.js +1 -1
  139. package/templates/dashboard/assets/app.js +12 -6
  140. package/templates/dashboard/assets/i18n.js +4 -2
  141. package/templates/development/README.md +10 -10
  142. package/templates/development/cross-repo-debugging.md +3 -3
  143. package/templates/development/external-context/service-template.md +2 -2
  144. package/templates/development/external-source-packs/README.md +4 -4
  145. package/templates/integrations/README.md +4 -4
  146. package/templates/integrations/api-contract.md +2 -2
  147. package/templates/integrations/event-contract.md +2 -2
  148. package/templates/integrations/third-party/vendor-template.md +2 -2
  149. package/templates/integrations/webhook-contract.md +2 -2
  150. package/templates/ledger/Harness-Ledger.md +1 -1
  151. package/templates/planning/INDEX.md +1 -0
  152. package/templates/planning/module_session_prompt.md +1 -1
  153. package/templates/planning/task_plan.md +1 -1
  154. package/templates/planning/walkthrough.md +47 -0
  155. package/templates/reference/docs-library-standard.md +8 -8
  156. package/templates/reference/external-source-intake-standard.md +15 -15
  157. package/templates/reference/repo-governance-standard.md +1 -1
  158. package/templates/ssot/Module-Registry.md +1 -1
  159. package/templates/walkthrough/walkthrough-template.md +2 -2
  160. package/templates-zh-CN/AGENTS.md.template +26 -26
  161. package/templates-zh-CN/CLAUDE.md.template +1 -1
  162. package/templates-zh-CN/architecture/README.md +4 -4
  163. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  164. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  165. package/templates-zh-CN/development/README.md +10 -10
  166. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  167. package/templates-zh-CN/development/external-context/service-template.md +2 -2
  168. package/templates-zh-CN/development/external-source-packs/README.md +4 -4
  169. package/templates-zh-CN/integrations/README.md +4 -4
  170. package/templates-zh-CN/integrations/api-contract.md +2 -2
  171. package/templates-zh-CN/integrations/event-contract.md +2 -2
  172. package/templates-zh-CN/integrations/third-party/vendor-template.md +2 -2
  173. package/templates-zh-CN/integrations/webhook-contract.md +2 -2
  174. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  175. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  176. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  177. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  178. package/templates-zh-CN/planning/module_session_prompt.md +11 -11
  179. package/templates-zh-CN/planning/walkthrough.md +47 -0
  180. package/templates-zh-CN/reference/adversarial-review-standard.md +2 -2
  181. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  182. package/templates-zh-CN/reference/docs-library-standard.md +28 -28
  183. package/templates-zh-CN/reference/execution-workflow-standard.md +1 -1
  184. package/templates-zh-CN/reference/external-source-intake-standard.md +16 -16
  185. package/templates-zh-CN/reference/harness-ledger-standard.md +6 -6
  186. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  187. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  188. package/templates-zh-CN/reference/review-routing-standard.md +1 -1
  189. package/templates-zh-CN/reference/walkthrough-standard.md +7 -7
  190. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  191. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  192. package/templates-zh-CN/ssot/Delivery-SSoT.md +3 -3
  193. package/templates-zh-CN/ssot/Module-Registry.md +3 -3
  194. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  195. package/templates-zh-CN/walkthrough/walkthrough-template.md +5 -5
  196. package/tsconfig.dist.json +16 -0
  197. package/tsconfig.json +25 -0
  198. package/tsconfig.runtime.json +24 -0
  199. package/examples/minimal-project/.harness-capabilities.json +0 -8
  200. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +0 -11
  201. package/scripts/check-harness.mjs +0 -508
  202. package/scripts/commands/dashboard-command.mjs +0 -67
  203. package/scripts/commands/migration-command.mjs +0 -126
  204. package/scripts/commands/preset-command.mjs +0 -73
  205. package/scripts/commands/task-command.mjs +0 -328
  206. package/scripts/harness.mjs +0 -291
  207. package/scripts/lib/capability-registry.mjs +0 -587
  208. package/scripts/lib/check-module-parallel.mjs +0 -230
  209. package/scripts/lib/check-profiles.mjs +0 -372
  210. package/scripts/lib/check-task-contracts.mjs +0 -55
  211. package/scripts/lib/core-shared.mjs +0 -249
  212. package/scripts/lib/dashboard-data.mjs +0 -520
  213. package/scripts/lib/dashboard-workbench.mjs +0 -336
  214. package/scripts/lib/dashboard-writer.mjs +0 -202
  215. package/scripts/lib/git-status-summary.mjs +0 -46
  216. package/scripts/lib/governance-index-generator.mjs +0 -174
  217. package/scripts/lib/governance-sync.mjs +0 -611
  218. package/scripts/lib/governance-table-boundary.mjs +0 -175
  219. package/scripts/lib/lesson-maintenance.mjs +0 -152
  220. package/scripts/lib/markdown-utils.mjs +0 -191
  221. package/scripts/lib/migration-planner.mjs +0 -476
  222. package/scripts/lib/migration-support.mjs +0 -312
  223. package/scripts/lib/phase-kind.mjs +0 -50
  224. package/scripts/lib/preset-audit-contracts.mjs +0 -37
  225. package/scripts/lib/preset-engine.mjs +0 -494
  226. package/scripts/lib/preset-registry.mjs +0 -776
  227. package/scripts/lib/preset-resource-contracts.mjs +0 -83
  228. package/scripts/lib/review-confirm-git-gate.mjs +0 -248
  229. package/scripts/lib/status-builder.mjs +0 -88
  230. package/scripts/lib/subagent-authorization-audit.mjs +0 -196
  231. package/scripts/lib/task-audit-metadata.mjs +0 -385
  232. package/scripts/lib/task-audit-migration.mjs +0 -350
  233. package/scripts/lib/task-completion-consistency.mjs +0 -26
  234. package/scripts/lib/task-index.mjs +0 -93
  235. package/scripts/lib/task-lesson-candidates.mjs +0 -242
  236. package/scripts/lib/task-lesson-sedimentation.mjs +0 -326
  237. package/scripts/lib/task-lifecycle/create-task-helpers.mjs +0 -67
  238. package/scripts/lib/task-lifecycle/phase-sync.mjs +0 -88
  239. package/scripts/lib/task-lifecycle/review-confirm.mjs +0 -112
  240. package/scripts/lib/task-lifecycle/review-gates.mjs +0 -73
  241. package/scripts/lib/task-lifecycle/review-submission.mjs +0 -63
  242. package/scripts/lib/task-lifecycle/scaffold-provenance.mjs +0 -49
  243. package/scripts/lib/task-lifecycle/template-files.mjs +0 -53
  244. package/scripts/lib/task-lifecycle/text-utils.mjs +0 -24
  245. package/scripts/lib/task-lifecycle.mjs +0 -616
  246. package/scripts/lib/task-metadata.mjs +0 -118
  247. package/scripts/lib/task-review-model.mjs +0 -455
  248. package/scripts/lib/task-scanner.mjs +0 -503
  249. package/scripts/lib/task-tombstone-commands.mjs +0 -140
  250. package/scripts/postinstall.mjs +0 -14
  251. package/templates/walkthrough/Closeout-SSoT.md +0 -43
  252. package/templates-zh-CN/walkthrough/Closeout-SSoT.md +0 -42
  253. /package/examples/minimal-project/{docs → coding-agent-harness/governance/generated}/Harness-Ledger.md +0 -0
  254. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/INDEX.md +0 -0
  255. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/brief.md +0 -0
  256. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/execution_strategy.md +0 -0
  257. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/findings.md +0 -0
  258. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/lesson_candidates.md +0 -0
  259. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/task_plan.md +0 -0
  260. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/visual_map.md +0 -0
@@ -0,0 +1,227 @@
1
+ // @ts-nocheck
2
+ // Module-parallel private harness checks stay behavior-first until legacy module context types are modeled.
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { walkFiles } from "./core-shared.mjs";
6
+ import { legacyLedgerFile, legacyModuleRoot, legacyPath, legacyPlanningRoot, } from "./harness-paths.mjs";
7
+ const moduleRegistryPath = legacyPath(legacyPlanningRoot, "Module-Registry.md");
8
+ const legacyModulesPath = legacyPath(legacyModuleRoot);
9
+ const legacyLedgerPath = legacyPath(legacyLedgerFile);
10
+ function stripMarkdownCode(value) {
11
+ return String(value || "").replace(/`/g, "").trim();
12
+ }
13
+ function modulePromptBlock(content, key) {
14
+ const heading = `## Module: ${key}`;
15
+ const start = content.indexOf(heading);
16
+ if (start < 0)
17
+ return "";
18
+ const rest = content.slice(start + heading.length);
19
+ const next = rest.search(/\n## Module: /);
20
+ return next >= 0 ? rest.slice(0, next) : rest;
21
+ }
22
+ function listModuleTaskPlans({ targetRoot, rel, filePath }) {
23
+ const modulesRoot = filePath(legacyModulesPath);
24
+ if (!fs.existsSync(modulesRoot))
25
+ return [];
26
+ return walkFiles(modulesRoot, {
27
+ dirFilter: (_dirName, fullPath) => {
28
+ const relativePath = rel(path.relative(targetRoot, fullPath));
29
+ return !relativePath.includes("/_archive/") && !relativePath.endsWith("/_task-template");
30
+ },
31
+ })
32
+ .map((file) => rel(path.relative(targetRoot, file)))
33
+ .filter((relativePath) => /\/TASKS\/[^/]+\/task_plan\.md$/.test(relativePath));
34
+ }
35
+ function parseModuleTaskPath(taskPlanPath) {
36
+ const match = taskPlanPath.match(new RegExp(`^${escapeRegExp(legacyModulesPath)}/([^/]+)/TASKS/([^/]+)/task_plan\\.md$`));
37
+ if (!match)
38
+ return null;
39
+ return { moduleKey: match[1], taskDir: match[2] };
40
+ }
41
+ function extractStepId(taskPlanContent, taskDir) {
42
+ const fromPlan = taskPlanContent.match(/^- Step ID:\s*`?([A-Z]{2,5}-\d{2})`?/m);
43
+ if (fromPlan)
44
+ return fromPlan[1];
45
+ const fromModuleSection = taskPlanContent.match(/^- Step:\s*`?([A-Z]{2,5}-\d{2})`?/m);
46
+ if (fromModuleSection)
47
+ return fromModuleSection[1];
48
+ const fromDir = taskDir.match(/^([A-Z]{2,5}-\d{2})-/);
49
+ return fromDir ? fromDir[1] : "";
50
+ }
51
+ function readTaskProgress(taskPlanPath, { exists, read }) {
52
+ const progressPath = taskPlanPath.replace(/task_plan\.md$/, "progress.md");
53
+ return exists(progressPath) ? read(progressPath) : "";
54
+ }
55
+ function normalizeModuleTaskStatus(status) {
56
+ const value = String(status || "").trim().toLowerCase();
57
+ const aliases = new Map([
58
+ ["未开始", "not-started"],
59
+ ["未启动", "not-started"],
60
+ ["进行中", "in-progress"],
61
+ ["开发中", "in-progress"],
62
+ ["规划审查", "planning-review"],
63
+ ["已完成", "completed"],
64
+ ["完成", "completed"],
65
+ ["已关闭", "closed"],
66
+ ["关闭", "closed"],
67
+ ["已阻塞", "blocked"],
68
+ ["阻塞", "blocked"],
69
+ ]);
70
+ return aliases.get(value) || value;
71
+ }
72
+ function readTaskProgressStatus(taskPlanPath, context) {
73
+ const progress = readTaskProgress(taskPlanPath, context);
74
+ if (!progress)
75
+ return "";
76
+ const match = progress.match(/^##\s*(?:Status|状态)\s*[::]?\s*(?:\n\s*)?([^\n]+)/im);
77
+ return match ? normalizeModuleTaskStatus(stripMarkdownCode(match[1])) : "";
78
+ }
79
+ function isActiveModuleTaskStatus(status) {
80
+ if (!status)
81
+ return false;
82
+ return !new Set([
83
+ "not-started",
84
+ "blocked-not-started",
85
+ "complete",
86
+ "completed",
87
+ "closed",
88
+ "closed-with-residual",
89
+ "closed-local-only",
90
+ "superseded",
91
+ ]).has(status);
92
+ }
93
+ function hasPendingCoordinatorHandoff(taskPlanContent, progressContent) {
94
+ const combined = `${taskPlanContent}\n${progressContent}`;
95
+ return /Coordinator Handoff/i.test(combined) && /pending-coordinator-pass/i.test(combined);
96
+ }
97
+ function checkModuleTaskSsotIndex(registryRows, context) {
98
+ const { exists, read, fail, warn, requireGlobalModuleSync } = context;
99
+ const registryByModule = new Map(registryRows.map((cells) => [cells[0], cells]));
100
+ const ledgerContent = exists(legacyLedgerPath) ? read(legacyLedgerPath) : "";
101
+ const taskPlans = listModuleTaskPlans(context);
102
+ for (const taskPlanPath of taskPlans) {
103
+ const parsed = parseModuleTaskPath(taskPlanPath);
104
+ if (!parsed)
105
+ continue;
106
+ const { moduleKey, taskDir } = parsed;
107
+ const modulePlanPath = legacyPath(legacyModuleRoot, moduleKey, "module_plan.md");
108
+ if (!exists(modulePlanPath))
109
+ continue;
110
+ const taskPlan = read(taskPlanPath);
111
+ const taskProgress = readTaskProgress(taskPlanPath, context);
112
+ const taskProgressStatus = readTaskProgressStatus(taskPlanPath, context);
113
+ const taskIsActive = isActiveModuleTaskStatus(taskProgressStatus);
114
+ const stepId = extractStepId(taskPlan, taskDir);
115
+ if (!stepId) {
116
+ if (taskIsActive) {
117
+ fail(`${taskPlanPath} does not expose a Step ID and task directory does not start with <PREFIX-NN>`);
118
+ }
119
+ continue;
120
+ }
121
+ const modulePlan = read(modulePlanPath);
122
+ const moduleRelativeTaskPlan = `TASKS/${taskDir}/task_plan.md`;
123
+ if (!modulePlan.includes(stepId) || !modulePlan.includes(moduleRelativeTaskPlan)) {
124
+ fail(`${modulePlanPath} does not index ${stepId} task plan ${moduleRelativeTaskPlan}`);
125
+ }
126
+ if (!taskIsActive)
127
+ continue;
128
+ const registryRow = registryByModule.get(moduleKey);
129
+ const reviewPath = taskPlanPath.replace(/task_plan\.md$/, "review.md");
130
+ const registrySynced = Boolean(registryRow && registryRow[4] === stepId);
131
+ const ledgerSynced = ledgerContent.includes(taskPlanPath) && (!exists(reviewPath) || ledgerContent.includes(reviewPath));
132
+ if (registrySynced && ledgerSynced)
133
+ continue;
134
+ if (requireGlobalModuleSync) {
135
+ if (!registryRow) {
136
+ fail(`${moduleRegistryPath} does not include active module ${moduleKey} for ${taskPlanPath}`);
137
+ }
138
+ else if (registryRow[4] !== stepId) {
139
+ fail(`${moduleRegistryPath} row ${moduleKey} current step is ${registryRow[4]}, but active task is ${stepId}`);
140
+ }
141
+ if (!ledgerContent.includes(taskPlanPath)) {
142
+ fail(`${legacyLedgerPath} does not index active module task plan ${taskPlanPath}`);
143
+ }
144
+ if (exists(reviewPath) && !ledgerContent.includes(reviewPath)) {
145
+ fail(`${legacyLedgerPath} does not index active module review ${reviewPath}`);
146
+ }
147
+ continue;
148
+ }
149
+ if (hasPendingCoordinatorHandoff(taskPlan, taskProgress)) {
150
+ warn(`${taskPlanPath} has pending coordinator handoff; run coordinator pass before final integration or set HARNESS_REQUIRE_GLOBAL_MODULE_SYNC=1 for strict gate`);
151
+ continue;
152
+ }
153
+ fail(`${taskPlanPath} is active but is neither globally synced nor marked with Coordinator Handoff: pending-coordinator-pass`);
154
+ }
155
+ }
156
+ export function checkModuleParallelStructure(context) {
157
+ const { exists, read, fail, requireFile, markdownTable } = context;
158
+ if (!exists(moduleRegistryPath))
159
+ return;
160
+ requireFile(legacyPath(legacyModuleRoot, "Session-Prompt-Pack.md"));
161
+ const hasPromptPack = exists(legacyPath(legacyModuleRoot, "Session-Prompt-Pack.md"));
162
+ for (const templateFile of [
163
+ legacyPath(legacyModuleRoot, "_task-template/task_plan.md"),
164
+ legacyPath(legacyModuleRoot, "_task-template/progress.md"),
165
+ legacyPath(legacyModuleRoot, "_task-template/findings.md"),
166
+ legacyPath(legacyModuleRoot, "_task-template/review.md"),
167
+ ]) {
168
+ requireFile(templateFile);
169
+ }
170
+ const registryContent = read(moduleRegistryPath);
171
+ for (const term of ["PREFIX", "Current Step", "Status", "Write Scope"]) {
172
+ if (!registryContent.includes(term)) {
173
+ fail(`${moduleRegistryPath} missing registry column or section: ${term}`);
174
+ }
175
+ }
176
+ const registryRows = markdownTable(registryContent)
177
+ .filter((cells) => cells.length >= 6)
178
+ .filter((cells) => /^(_shared|[a-z][a-z0-9-]*)$/.test(cells[0] || "") && /^[A-Z]{2,5}$/.test(cells[2] || ""));
179
+ if (registryRows.length === 0) {
180
+ fail(`${moduleRegistryPath} has no active module rows`);
181
+ }
182
+ const promptPack = hasPromptPack ? read(legacyPath(legacyModuleRoot, "Session-Prompt-Pack.md")) : "";
183
+ if (hasPromptPack && !/Subagent Worker Invariant|worker[\s\S]{0,120}worktree[\s\S]{0,120}commit SHA/i.test(promptPack)) {
184
+ fail(`${legacyPath(legacyModuleRoot, "Session-Prompt-Pack.md")} missing subagent worker worktree/commit handoff rule`);
185
+ }
186
+ for (const cells of registryRows) {
187
+ const [key, , prefix, branch, currentStep, status] = cells;
188
+ requireFile(legacyPath(legacyModuleRoot, key, "module_plan.md"));
189
+ if (!/^(planned|in-progress|paused|completed)$/.test(status)) {
190
+ fail(`${moduleRegistryPath} row ${key} has invalid status: ${status}`);
191
+ }
192
+ if (currentStep !== `${prefix}-00` && !currentStep.startsWith(`${prefix}-`)) {
193
+ fail(`${moduleRegistryPath} row ${key} current step does not match prefix ${prefix}: ${currentStep}`);
194
+ }
195
+ const branchName = stripMarkdownCode(branch);
196
+ if (!branchName.startsWith("codex/")) {
197
+ fail(`${moduleRegistryPath} row ${key} branch must use codex/ prefix: ${branch}`);
198
+ }
199
+ const block = modulePromptBlock(promptPack, key);
200
+ if (!block) {
201
+ if (!exists(legacyPath(legacyModuleRoot, key, "session_prompt.md"))) {
202
+ fail(`missing module session prompt for ${key}`);
203
+ }
204
+ continue;
205
+ }
206
+ for (const term of [
207
+ "Current Step",
208
+ branchName,
209
+ "Preflight:",
210
+ "Before code edits:",
211
+ "Write scope:",
212
+ "Forbidden without coordination:",
213
+ "Shared Coordination:",
214
+ "Verification:",
215
+ "Closeout:",
216
+ "Stop conditions:",
217
+ ]) {
218
+ if (!block.includes(term)) {
219
+ fail(`module session prompt for ${key} missing required term: ${term}`);
220
+ }
221
+ }
222
+ }
223
+ checkModuleTaskSsotIndex(registryRows, context);
224
+ }
225
+ function escapeRegExp(value) {
226
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
227
+ }
@@ -0,0 +1,414 @@
1
+ // @ts-nocheck
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { spawnSync } from "node:child_process";
5
+ import { repoRoot, bundledCheckScript, visualMapFile, legacyVisualRoadmapFile, allowedReviewDispositions, allowedPhaseStates, allowedEvidenceStatus, normalizeTarget, toPosix, readFileSafe, walkFiles, isArchivedHarnessPath, } from "./core-shared.mjs";
6
+ import { tableAfterHeading, getColumn, getColumnAny, splitList, firstColumn, contentHasAny, } from "./markdown-utils.mjs";
7
+ import { validateCapabilities } from "./capability-registry.mjs";
8
+ import { readPresetPackage } from "./preset-registry.mjs";
9
+ import { validateTaskPresetAuditSnapshot } from "./preset-audit-contracts.mjs";
10
+ import { validatePresetResourcesForTask } from "./preset-resource-contracts.mjs";
11
+ import { collectTasks, listTaskPlanPaths, parseTaskBudget, readVisualMapContractFile, parsePhases } from "./task-scanner.mjs";
12
+ import { normalizeReviewBoolean, reviewFindingColumns } from "./task-review-model.mjs";
13
+ import { allowedPhaseActors, allowedPhaseKinds } from "./phase-kind.mjs";
14
+ import { validateTaskCompletionConsistency } from "./task-completion-consistency.mjs";
15
+ import { validatePlanContracts } from "./check-task-contracts.mjs";
16
+ import { validateGovernanceTableBoundaries } from "./governance-table-boundary.mjs";
17
+ import { validateSubagentAuthorization } from "./subagent-authorization-audit.mjs";
18
+ import { summarizeGitState } from "./git-status-summary.mjs";
19
+ import { buildStatusData } from "./status-builder.mjs";
20
+ import { legacyCloseoutFile, legacyCompatMode, legacyLedgerFile, legacyPath, legacyPlanningRoot, legacyWalkthroughRoot, safeAdoptionCapability, } from "./harness-paths.mjs";
21
+ export { renderDashboard } from "./status-dashboard-renderer.mjs";
22
+ export function runCompatibilityCheck(target) {
23
+ const checkTarget = target.docsOnly ? target.projectRoot : target.input;
24
+ const result = spawnSync(process.execPath, [bundledCheckScript, checkTarget], {
25
+ cwd: repoRoot,
26
+ encoding: "utf8",
27
+ });
28
+ return {
29
+ status: result.status === 0 ? "pass" : "fail",
30
+ code: result.status ?? 1,
31
+ stdout: result.stdout || "",
32
+ stderr: result.stderr || "",
33
+ };
34
+ }
35
+ export function validateReviewSchema(target, { strict = true } = {}) {
36
+ const failures = [];
37
+ const warnings = [];
38
+ const report = (message) => {
39
+ if (strict)
40
+ failures.push(message);
41
+ else
42
+ warnings.push(`adoption-needed: ${message}`);
43
+ };
44
+ const reviewPaths = walkFiles(target.docsRoot)
45
+ .filter((file) => file.endsWith("review.md"))
46
+ .filter((file) => !file.includes(`${path.sep}_task-template${path.sep}`))
47
+ .filter((file) => !file.includes(`${path.sep}_optional-structures${path.sep}`))
48
+ .filter((file) => !isArchivedHarnessPath(file));
49
+ for (const reviewPath of reviewPaths) {
50
+ const relative = toPosix(path.relative(target.projectRoot, reviewPath));
51
+ const content = readFileSafe(reviewPath);
52
+ const requiredSections = [
53
+ ["Reviewer Identity", "Reviewer 身份", "审查者身份"],
54
+ ["Confidence Challenge", "信心挑战"],
55
+ ["Evidence Checked", "已检查 Evidence", "已检查证据"],
56
+ ["Final Confidence Basis", "最终信心依据"],
57
+ ];
58
+ for (const [label, ...aliases] of requiredSections) {
59
+ if (!contentHasAny(content, [label, ...aliases])) {
60
+ if (strict)
61
+ failures.push(`${relative} missing ${label}`);
62
+ else
63
+ warnings.push(`${relative} missing ${label}`);
64
+ }
65
+ }
66
+ const evidenceTable = tableAfterHeading(content, /^(Evidence ID|证据 ID)$/i);
67
+ if (strict && evidenceTable.rows.length === 0) {
68
+ failures.push(`${relative} Evidence Checked table needs at least one evidence row`);
69
+ }
70
+ const usesVerifier = /verifier-backed|(^|\|)[^|\n]*\|\s*verifier\s*\|/im.test(content);
71
+ if (usesVerifier) {
72
+ if (!/template_id:\s*`?harness-verifier\/v1`?/i.test(content)) {
73
+ report(`${relative} verifier-backed review missing template_id: harness-verifier/v1`);
74
+ }
75
+ if (!/verdict:\s*`?(pass|fail|inconclusive)`?/i.test(content)) {
76
+ report(`${relative} verifier-backed review missing verdict`);
77
+ }
78
+ }
79
+ const { header, rows } = tableAfterHeading(content, /^ID$/i);
80
+ if (rows.length === 0)
81
+ continue;
82
+ const severityIndex = getColumnAny(header, reviewFindingColumns.severity);
83
+ const openIndex = getColumnAny(header, reviewFindingColumns.open);
84
+ const dispositionIndex = getColumnAny(header, reviewFindingColumns.disposition);
85
+ const blocksIndex = getColumnAny(header, reviewFindingColumns.blocksRelease);
86
+ const followUpIndex = getColumnAny(header, ["Follow-up", "跟进"]);
87
+ const evidenceCheckedIndex = getColumnAny(header, ["Evidence Checked", "已检查证据"]);
88
+ if ([severityIndex, openIndex, dispositionIndex, blocksIndex].some((index) => index < 0)) {
89
+ report(`${relative} findings table missing Severity/Open/Disposition/Blocks Release columns`);
90
+ continue;
91
+ }
92
+ for (const row of rows) {
93
+ const id = row[0] || "";
94
+ const severity = row[severityIndex] || "";
95
+ if (!/^P[0-3]$/.test(severity) && !/^(R|SR)-\d+/i.test(id))
96
+ continue;
97
+ const open = normalizeReviewBoolean(row[openIndex] || "");
98
+ const disposition = (row[dispositionIndex] || "").toLowerCase();
99
+ const blocks = normalizeReviewBoolean(row[blocksIndex] || "");
100
+ const followUp = row[followUpIndex] || "";
101
+ if (!/^P[0-3]$/.test(severity))
102
+ report(`${relative} ${id} invalid severity: ${severity}`);
103
+ if (!["yes", "no"].includes(open))
104
+ report(`${relative} ${id} invalid Open value: ${open}`);
105
+ if (!allowedReviewDispositions.has(disposition))
106
+ report(`${relative} ${id} invalid Disposition: ${disposition}`);
107
+ if (!["yes", "no"].includes(blocks))
108
+ report(`${relative} ${id} invalid Blocks Release value: ${blocks}`);
109
+ if ((open === "yes" || blocks === "yes") && /^P[01]$/.test(severity)) {
110
+ report(`${relative} ${id} has release-blocking open ${severity}`);
111
+ }
112
+ if (["accepted-risk", "deferred"].includes(disposition) && (!followUp || /^none|无$/i.test(followUp))) {
113
+ report(`${relative} ${id} ${disposition} requires follow-up routing`);
114
+ }
115
+ if (strict && evidenceCheckedIndex >= 0) {
116
+ const refs = splitList(row[evidenceCheckedIndex] || "");
117
+ const evidenceIds = new Set(evidenceTable.rows.map((evidenceRow) => evidenceRow[0]));
118
+ for (const ref of refs) {
119
+ if (ref !== "none" && /^E-\d+/i.test(ref) && !evidenceIds.has(ref)) {
120
+ failures.push(`${relative} ${id} references missing evidence id: ${ref}`);
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ return { failures, warnings };
127
+ }
128
+ export function validateVisualMaps(target, { taskPlanPaths } = {}) {
129
+ const failures = [];
130
+ const warnings = [];
131
+ for (const taskPlanPath of taskPlanPaths || listTaskPlanPaths(target)) {
132
+ const taskDir = path.dirname(taskPlanPath);
133
+ const visualMapPath = path.join(taskDir, visualMapFile);
134
+ const legacyPath = path.join(taskDir, legacyVisualRoadmapFile);
135
+ const relative = toPosix(path.relative(target.projectRoot, visualMapPath));
136
+ const taskPlan = readFileSafe(taskPlanPath);
137
+ const visualMap = readVisualMapContractFile(taskDir, taskPlan);
138
+ const { header, rows } = tableAfterHeading(visualMap.content, /^Phase ID$/i);
139
+ if (rows.length > 0) {
140
+ for (const column of ["Phase ID", "Depends On", "State", "Completion", "Output", "Required Evidence", "Evidence Status", "Blocking Risk", "Owner / Handoff"]) {
141
+ if (getColumn(header, column) < 0)
142
+ failures.push(`${relative} Visual Map missing column: ${column}`);
143
+ }
144
+ }
145
+ const phases = parsePhases(visualMap.content);
146
+ const budget = parseTaskBudget(taskPlan);
147
+ for (const phase of phases) {
148
+ if (!allowedPhaseKinds.has(phase.kind))
149
+ failures.push(`${relative} phase ${phase.id} invalid kind: ${phase.kind}`);
150
+ if (!allowedPhaseActors.has(phase.actor))
151
+ failures.push(`${relative} phase ${phase.id} invalid actor: ${phase.actor}`);
152
+ if (!allowedPhaseStates.has(phase.state))
153
+ failures.push(`${relative} phase ${phase.id} invalid state: ${phase.state}`);
154
+ if (!allowedEvidenceStatus.has(phase.evidenceStatus)) {
155
+ failures.push(`${relative} phase ${phase.id} invalid evidence status: ${phase.evidenceStatus}`);
156
+ }
157
+ if (!Number.isInteger(phase.completion) || phase.completion < 0 || phase.completion > 100) {
158
+ failures.push(`${relative} phase ${phase.id} completion must be integer 0..100`);
159
+ }
160
+ if (phase.state === "done" && phase.completion !== 100)
161
+ failures.push(`${relative} phase ${phase.id} done must be 100`);
162
+ if (phase.state === "planned" && phase.completion !== 0)
163
+ failures.push(`${relative} phase ${phase.id} planned must be 0`);
164
+ }
165
+ if (visualMap.source === "canonical" && !/Visual Map Contract:\s*v1\.0/i.test(visualMap.content)) {
166
+ failures.push(`${relative} missing Visual Map Contract: v1.0`);
167
+ }
168
+ if (visualMap.source === "canonical" && phases.length === 0)
169
+ warnings.push(`${relative} has no Visual Map phase table`);
170
+ if (visualMap.source === "canonical" && budget !== "simple" && phases.length > 0 && !phases.some((phase) => phase.kind === "execution" && phase.state !== "skipped")) {
171
+ failures.push(`${relative} requires at least one non-skipped execution phase`);
172
+ }
173
+ if (visualMap.source === "legacy" && fs.existsSync(legacyPath)) {
174
+ warnings.push(`${relative} missing; legacy visual_roadmap.md is rewrite input only`);
175
+ }
176
+ else if (visualMap.source === "legacy" && phases.length > 0) {
177
+ warnings.push(`${relative} missing; using legacy task_plan.md visual map fallback`);
178
+ }
179
+ }
180
+ return { failures, warnings };
181
+ }
182
+ export function validateTaskPresetContracts(target, { tasks } = {}) {
183
+ const failures = [];
184
+ const allowedMigrationLevels = new Set([
185
+ "migration-baseline",
186
+ "migration-current-cutover",
187
+ "migration-full-cutover",
188
+ "migration-deferred",
189
+ ]);
190
+ for (const task of tasks || collectTasks(target)) {
191
+ if (!task.taskPreset || task.taskPreset === "none")
192
+ continue;
193
+ let presetPackage = null;
194
+ try {
195
+ presetPackage = readPresetPackage(task.taskPreset, { targetInput: target.projectRoot });
196
+ }
197
+ catch (error) {
198
+ failures.push(`${task.path} unsupported Task Preset: ${task.taskPreset} (${error.message})`);
199
+ continue;
200
+ }
201
+ if (presetPackage?.task?.kind && task.taskKind !== presetPackage.task.kind) {
202
+ failures.push(`${task.path} ${task.taskPreset} preset Task Kind mismatch: expected ${presetPackage.task.kind}, got ${task.taskKind || "(missing)"}`);
203
+ }
204
+ if (String(task.presetVersion || "") !== String(presetPackage.version)) {
205
+ failures.push(`${task.path} ${task.taskPreset} preset missing Preset Version ${presetPackage.version}`);
206
+ }
207
+ if (task.taskPreset !== "lesson-sedimentation" && (presetPackage.evidence?.bundleDir || presetPackage.audit?.evidenceFiles?.length || Object.keys(presetPackage.evidence?.files || {}).length)) {
208
+ if (!task.evidenceBundle)
209
+ failures.push(`${task.path} ${task.taskPreset} preset missing Evidence Bundle`);
210
+ else if (!fs.existsSync(path.join(target.projectRoot, String(task.evidenceBundle).replace(/^TARGET:/, "").replace(/^\/+/, "")))) {
211
+ failures.push(`${task.path} ${task.taskPreset} preset Evidence Bundle missing: ${task.evidenceBundle}`);
212
+ }
213
+ }
214
+ if (task.taskPreset !== "lesson-sedimentation") {
215
+ failures.push(...validateTaskPresetAuditSnapshot(target, task, presetPackage));
216
+ }
217
+ failures.push(...validatePresetResourcesForTask(target, task, presetPackage));
218
+ if (task.taskPreset === "lesson-sedimentation") {
219
+ if (!["standard", "complex"].includes(task.budget))
220
+ failures.push(`${task.path} lesson-sedimentation preset requires Selected budget: standard or complex`);
221
+ if (!task.taskPlanPath)
222
+ failures.push(`${task.path} lesson-sedimentation preset missing task plan`);
223
+ continue;
224
+ }
225
+ if (task.taskPreset !== "legacy-migration") {
226
+ continue;
227
+ }
228
+ if (task.budget !== "complex")
229
+ failures.push(`${task.path} legacy-migration preset requires Selected budget: complex`);
230
+ if (!allowedMigrationLevels.has(task.migrationTargetLevel)) {
231
+ failures.push(`${task.path} legacy-migration preset invalid Migration Target Level: ${task.migrationTargetLevel || "(missing)"}`);
232
+ }
233
+ const achievedLevel = task.migrationAchievedLevel || "";
234
+ if (achievedLevel !== "pending" && !allowedMigrationLevels.has(achievedLevel)) {
235
+ failures.push(`${task.path} legacy-migration preset invalid Migration Achieved Level: ${achievedLevel || "(missing)"}`);
236
+ }
237
+ if (task.evidenceBundle && !task.migrationSnapshot?.evidencePresent) {
238
+ failures.push(`${task.path} legacy-migration preset Evidence Bundle missing: ${task.evidenceBundle}`);
239
+ }
240
+ else if (!task.migrationSnapshot?.sessionPresent) {
241
+ failures.push(`${task.path} legacy-migration preset Evidence Bundle missing session.json`);
242
+ }
243
+ if (achievedLevel === "migration-full-cutover") {
244
+ const snapshot = task.migrationSnapshot || {};
245
+ const blockers = [];
246
+ if (!snapshot.sessionPresent)
247
+ blockers.push("missing session evidence");
248
+ if (snapshot.sessionResult !== "complete")
249
+ blockers.push(`session result is ${snapshot.sessionResult || "(missing)"}`);
250
+ if (snapshot.strictDeferred)
251
+ blockers.push("strictDeferred is present");
252
+ if (snapshot.strictStatus !== "pass")
253
+ blockers.push(`strict status is ${snapshot.strictStatus || "(missing)"}`);
254
+ for (const [field, value] of [
255
+ ["warnings", snapshot.warnings],
256
+ ["taskActions", snapshot.taskActions],
257
+ ["reviewSchemaGaps", snapshot.reviewSchemaGaps],
258
+ ["legacyReferenceGaps", snapshot.legacyReferenceGaps],
259
+ ["legacyResiduals", snapshot.legacyResiduals],
260
+ ]) {
261
+ if (Number(value || 0) !== 0)
262
+ blockers.push(`${field}=${value}`);
263
+ }
264
+ if (snapshot.fullCutoverEligible !== true)
265
+ blockers.push("fullCutoverEligible is not true");
266
+ if (blockers.length) {
267
+ failures.push(`${task.path} migration-full-cutover is not proven: ${blockers.join("; ")}`);
268
+ }
269
+ }
270
+ }
271
+ return { failures, warnings: [] };
272
+ }
273
+ export function validateContextDocs(target, { strict = true } = {}) {
274
+ const failures = [];
275
+ const warnings = [];
276
+ const report = (message) => {
277
+ if (strict)
278
+ failures.push(message);
279
+ else
280
+ warnings.push(`adoption-needed: ${message}`);
281
+ };
282
+ const files = contextDocRoots(target).flatMap((root) => walkFiles(root)).filter((file) => file.endsWith(".md"));
283
+ for (const file of files) {
284
+ if (isArchivedHarnessPath(file))
285
+ continue;
286
+ const relative = toPosix(path.relative(target.projectRoot, file));
287
+ const content = readFileSafe(file);
288
+ if (!/Context Doc Type:\s*\S+/i.test(content) && !/上下文文档类型[::]\s*\S+/.test(content))
289
+ report(`${relative} missing Context Doc Type`);
290
+ if (path.basename(file) === "README.md")
291
+ continue;
292
+ if (!contentHasAny(content, [/Source Evidence/i, "来源证据"]))
293
+ report(`${relative} missing Source Evidence field`);
294
+ if (!/Last Verified:\s*\S+|Last Verified\s*\|/i.test(content) && !/最近验证[::]\s*\S+|最近验证\s*\|/.test(content))
295
+ report(`${relative} missing Last Verified field`);
296
+ if (!/Confidence:\s*(high|medium|low|unknown)|Confidence\s*\|/i.test(content) && !/信心[::]\s*(high|medium|low|unknown|高|中|低|未知)|信心\s*\|/.test(content))
297
+ report(`${relative} missing Confidence field`);
298
+ if (/(^|\/)(?:03-ARCHITECTURE|context\/architecture)\/service-catalog\.md$/.test(relative)) {
299
+ for (const [column, ...aliases] of [
300
+ ["Service / Component", "服务 / 组件"],
301
+ ["Interfaces", "接口"],
302
+ ["Source Evidence", "来源证据"],
303
+ ["Last Verified", "最近验证"],
304
+ ["Confidence", "信心"],
305
+ ]) {
306
+ if (!contentHasAny(content, [column, ...aliases]))
307
+ report(`${relative} service catalog missing column: ${column}`);
308
+ }
309
+ }
310
+ if (/(^|\/)(?:04-DEVELOPMENT|context\/development)\/external-context\/[^/]+\.md$/.test(relative)) {
311
+ for (const [heading, ...aliases] of [
312
+ ["Development Use", "开发用途"],
313
+ ["Do Not Assume", "不要假设"],
314
+ ["Mocks / Stubs", "Mock / Stub", "模拟 / 桩"],
315
+ ]) {
316
+ if (!contentHasAny(content, [heading, ...aliases]))
317
+ report(`${relative} external context missing section: ${heading}`);
318
+ }
319
+ }
320
+ if (/(^|\/)(?:06-INTEGRATIONS|context\/integrations)\/(?:[^/_][^/]*|third-party\/[^/_][^/]*)\.md$/.test(relative)) {
321
+ for (const [heading, ...aliases] of [
322
+ ["Contract Type", "合同类型"],
323
+ ["Auth", "认证"],
324
+ ["Payload", "载荷"],
325
+ ["Errors", "错误"],
326
+ ["Contract Tests", "合同测试"],
327
+ ]) {
328
+ if (!contentHasAny(content, [heading, ...aliases]))
329
+ report(`${relative} integration contract missing section: ${heading}`);
330
+ }
331
+ }
332
+ }
333
+ return { failures, warnings };
334
+ }
335
+ function contextDocRoots(target) {
336
+ if (target.harness?.version === 2) {
337
+ return [
338
+ path.join(target.harness.harnessRoot, "context/architecture"),
339
+ path.join(target.harness.harnessRoot, "context/development"),
340
+ path.join(target.harness.harnessRoot, "context/integrations"),
341
+ ];
342
+ }
343
+ return ["03-ARCHITECTURE", "04-DEVELOPMENT", "06-INTEGRATIONS"].map((root) => path.join(target.docsRoot, root));
344
+ }
345
+ export function buildStatus(targetInput, options = {}) {
346
+ const target = normalizeTarget(targetInput);
347
+ const gitState = summarizeGitState(target);
348
+ const capabilityState = validateCapabilities(target);
349
+ const declaredCapabilities = new Set(capabilityState.registry.capabilities.map((capability) => capability.name));
350
+ const safeAdoptionMode = declaredCapabilities.has(safeAdoptionCapability);
351
+ const shouldRunLegacy = target.harness?.version !== 2 && !options.skipLegacyCheck && (capabilityState.registry.mode === legacyCompatMode || safeAdoptionMode);
352
+ const legacy = shouldRunLegacy ? runCompatibilityCheck(target) : { status: "skipped", code: 0, stdout: "", stderr: "" };
353
+ const contractStrict = Boolean(options.strict) || (capabilityState.registry.mode !== legacyCompatMode && !safeAdoptionMode);
354
+ const taskPlanPaths = listTaskPlanPaths(target);
355
+ const closeoutContent = target.harness?.version === 2 ? "" : readFileSafe(path.join(target.projectRoot, legacyPath(legacyCloseoutFile)));
356
+ const tasks = collectTasks(target, { requireGeneratedScaffoldProvenance: contractStrict, taskPlanPaths, closeoutContent });
357
+ const reviews = validateReviewSchema(target, { strict: contractStrict });
358
+ const visualMaps = validateVisualMaps(target, { taskPlanPaths });
359
+ const planContracts = validatePlanContracts(target, { strict: contractStrict, taskPlanPaths });
360
+ const presetContracts = validateTaskPresetContracts(target, { tasks });
361
+ const contextDocs = validateContextDocs(target, { strict: contractStrict });
362
+ const governanceBoundaries = validateGovernanceTableBoundaries(target);
363
+ const subagentAuthorization = validateSubagentAuthorization(target, { strict: contractStrict });
364
+ const failures = [...capabilityState.failures, ...reviews.failures, ...visualMaps.failures, ...planContracts.failures, ...presetContracts.failures, ...contextDocs.failures, ...governanceBoundaries.failures, ...subagentAuthorization.failures];
365
+ const warnings = [...capabilityState.warnings, ...reviews.warnings, ...visualMaps.warnings, ...planContracts.warnings, ...presetContracts.warnings, ...contextDocs.warnings, ...governanceBoundaries.warnings, ...subagentAuthorization.warnings, ...gitState.warnings];
366
+ if (target.harness?.version !== 2 && hasLegacyHarnessDocs(target) && !options.allowLegacyTarget) {
367
+ failures.push("legacy harness structure is migration input only; run `harness migrate-structure --plan` then `harness migrate-structure --apply`");
368
+ }
369
+ if (legacy.status === "fail") {
370
+ if (options.strictLegacy)
371
+ failures.push("legacy check failed");
372
+ else
373
+ warnings.push(`adoption-needed: legacy check failed: ${(legacy.stderr || legacy.stdout).trim()}`);
374
+ }
375
+ const taskCompletionConsistency = validateTaskCompletionConsistency(tasks);
376
+ failures.push(...taskCompletionConsistency.failures);
377
+ warnings.push(...taskCompletionConsistency.warnings);
378
+ const briefReady = tasks.filter((task) => task.briefSource === "standalone").length;
379
+ const briefMissing = tasks.length - briefReady;
380
+ for (const task of tasks) {
381
+ for (const issue of task.materialIssues || []) {
382
+ if (!String(issue.code || "").startsWith("missing-task-audit") && !String(issue.code || "").startsWith("legacy-"))
383
+ continue;
384
+ const message = `${String(issue.sourcePath || task.path).replace(/^TARGET:/, "")} ${issue.message}`;
385
+ if (contractStrict || options.strictLegacy)
386
+ failures.push(message);
387
+ else
388
+ warnings.push(`adoption-needed: ${message}`);
389
+ }
390
+ if (task.stateSource === "invalid") {
391
+ const message = `${task.path}/progress.md invalid task state: ${task.stateRaw}`;
392
+ if (contractStrict || options.strictLegacy)
393
+ failures.push(message);
394
+ else
395
+ warnings.push(`adoption-needed: ${message}`);
396
+ }
397
+ }
398
+ return buildStatusData(target, {
399
+ capabilityState,
400
+ gitState,
401
+ legacy,
402
+ failures,
403
+ warnings,
404
+ tasks,
405
+ validationMode: "validated",
406
+ });
407
+ }
408
+ function hasLegacyHarnessDocs(target) {
409
+ return [
410
+ path.join(target.projectRoot, legacyPath(legacyPlanningRoot)),
411
+ path.join(target.projectRoot, legacyPath(legacyWalkthroughRoot)),
412
+ path.join(target.projectRoot, legacyPath(legacyLedgerFile)),
413
+ ].some((candidate) => fs.existsSync(candidate));
414
+ }