coding-agent-harness 1.0.7 → 1.1.0

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 (238) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/CONTRIBUTING.md +9 -5
  3. package/README.md +12 -2
  4. package/README.zh-CN.md +10 -2
  5. package/SKILL.md +14 -3
  6. package/dist/build-dist.mjs +32 -6
  7. package/dist/check-dist-observation.mjs +73 -28
  8. package/dist/check-harness.mjs +0 -1
  9. package/dist/check-import-graph.mjs +44 -27
  10. package/dist/check-lite-forbidden-surfaces.mjs +121 -0
  11. package/dist/check-no-ts-nocheck.mjs +88 -0
  12. package/dist/check-runtime-emit.mjs +10 -3
  13. package/dist/check-type-boundaries.mjs +67 -8
  14. package/dist/commands/dashboard-command.mjs +52 -14
  15. package/dist/commands/migration-command.mjs +18 -8
  16. package/dist/commands/module-command.mjs +142 -0
  17. package/dist/commands/preset-command.mjs +65 -4
  18. package/dist/commands/registry.mjs +483 -0
  19. package/dist/commands/task-command.mjs +111 -53
  20. package/dist/harness.mjs +6 -303
  21. package/dist/lib/capability-registry.mjs +229 -53
  22. package/dist/lib/check-module-parallel.mjs +1 -6
  23. package/dist/lib/check-profiles.mjs +39 -46
  24. package/dist/lib/check-task-contracts.mjs +6 -4
  25. package/dist/lib/command-registry.mjs +248 -0
  26. package/dist/lib/core-shared.mjs +78 -3
  27. package/dist/lib/dashboard-data.mjs +203 -22
  28. package/dist/lib/dashboard-workbench.mjs +245 -21
  29. package/dist/lib/dashboard-writer.mjs +4 -1
  30. package/dist/lib/git-status-summary.mjs +0 -1
  31. package/dist/lib/governance-index-generator.mjs +7 -5
  32. package/dist/lib/governance-sync.mjs +46 -121
  33. package/dist/lib/governance-table-boundary.mjs +1 -14
  34. package/dist/lib/harness-core.mjs +5 -1
  35. package/dist/lib/harness-paths.mjs +115 -1
  36. package/dist/lib/impact-classifier.mjs +420 -0
  37. package/dist/lib/lesson-maintenance.mjs +1 -2
  38. package/dist/lib/markdown-utils.mjs +50 -1
  39. package/dist/lib/migration-planner.mjs +31 -16
  40. package/dist/lib/migration-support.mjs +5 -4
  41. package/dist/lib/module-registry.mjs +296 -0
  42. package/dist/lib/preset-audit-contracts.mjs +24 -1
  43. package/dist/lib/preset-engine.mjs +68 -29
  44. package/dist/lib/preset-registry.mjs +374 -72
  45. package/dist/lib/preset-runner.mjs +560 -0
  46. package/dist/lib/review-confirm-git-gate.mjs +73 -19
  47. package/dist/lib/status-builder.mjs +23 -8
  48. package/dist/lib/structure-migration.mjs +6 -4
  49. package/dist/lib/subagent-authorization-audit.mjs +8 -2
  50. package/dist/lib/task-archive-eligibility.mjs +65 -0
  51. package/dist/lib/task-audit-metadata.mjs +25 -11
  52. package/dist/lib/task-audit-migration.mjs +21 -14
  53. package/dist/lib/task-discovery-contract.mjs +32 -0
  54. package/dist/lib/task-index.mjs +4 -2
  55. package/dist/lib/task-lesson-candidates.mjs +1 -2
  56. package/dist/lib/task-lesson-sedimentation.mjs +310 -9
  57. package/dist/lib/task-lifecycle/create-task-helpers.mjs +6 -3
  58. package/dist/lib/task-lifecycle/phase-sync.mjs +0 -1
  59. package/dist/lib/task-lifecycle/preset-interop.mjs +16 -0
  60. package/dist/lib/task-lifecycle/review-confirm.mjs +34 -2
  61. package/dist/lib/task-lifecycle/review-gates.mjs +12 -5
  62. package/dist/lib/task-lifecycle/review-submission.mjs +1 -2
  63. package/dist/lib/task-lifecycle/scaffold-provenance.mjs +0 -1
  64. package/dist/lib/task-lifecycle/template-files.mjs +2 -5
  65. package/dist/lib/task-lifecycle.mjs +117 -159
  66. package/dist/lib/task-metadata.mjs +10 -5
  67. package/dist/lib/task-preset-contract-drift.mjs +45 -0
  68. package/dist/lib/task-repository.mjs +192 -0
  69. package/dist/lib/task-review-model.mjs +38 -17
  70. package/dist/lib/task-scanner.mjs +75 -23
  71. package/dist/lib/task-template-materials.mjs +131 -0
  72. package/dist/lib/task-tombstone-commands.mjs +187 -18
  73. package/dist/lib/types/check-profiles.js +1 -0
  74. package/dist/lib/types/impact.js +1 -0
  75. package/dist/lib/types/preset.js +1 -0
  76. package/dist/lib/types/task-lifecycle.js +1 -0
  77. package/dist/lib/types/task-scanner.js +1 -0
  78. package/dist/postinstall.mjs +2 -2
  79. package/dist/run-built-tests.mjs +10 -3
  80. package/docs-release/README.md +2 -1
  81. package/docs-release/architecture/document-contract-kernel/README.md +150 -0
  82. package/docs-release/architecture/document-contract-kernel/products/full-skill-overlay.md +29 -0
  83. package/docs-release/architecture/document-contract-kernel/products/lite-forbidden-surfaces.txt +26 -0
  84. package/docs-release/architecture/document-contract-kernel/products/lite-skill-overlay.md +37 -0
  85. package/docs-release/architecture/overview.md +2 -2
  86. package/docs-release/architecture/overview.zh-CN.md +2 -2
  87. package/docs-release/architecture/system-explainer/01-system-overview.md +11 -7
  88. package/docs-release/architecture/system-explainer/02-module-dependency.md +4 -4
  89. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +17 -12
  90. package/docs-release/architecture/system-explainer/05-data-flow.md +6 -6
  91. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +2 -2
  92. package/docs-release/architecture/system-explainer/README.md +1 -1
  93. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +12 -8
  94. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +5 -5
  95. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +19 -11
  96. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +5 -5
  97. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +2 -2
  98. package/docs-release/architecture/system-explainer/en-US/README.md +1 -1
  99. package/docs-release/guides/agent-installation.en-US.md +4 -6
  100. package/docs-release/guides/agent-installation.md +11 -8
  101. package/docs-release/guides/contributing.md +10 -3
  102. package/docs-release/guides/contributing.zh-CN.md +10 -3
  103. package/docs-release/guides/legacy-migration-agent-prompt.md +1 -1
  104. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +1 -1
  105. package/docs-release/guides/migration-playbook.en-US.md +9 -6
  106. package/docs-release/guides/migration-playbook.md +9 -6
  107. package/docs-release/guides/preset-development.md +68 -2
  108. package/docs-release/guides/task-state-machine.en-US.md +8 -8
  109. package/docs-release/guides/task-state-machine.md +7 -7
  110. package/docs-release/guides/typescript-runtime-migration-closeout.md +17 -13
  111. package/package.json +19 -11
  112. package/postinstall.mjs +37 -0
  113. package/presets/legacy-migration/preset.yaml +5 -5
  114. package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
  115. package/presets/lesson-sedimentation/preset.yaml +3 -3
  116. package/presets/module/preset.yaml +2 -2
  117. package/presets/module/templates/execution_strategy.append.md +1 -1
  118. package/presets/module/templates/task_plan.append.md +3 -3
  119. package/presets/release-closeout/checks/check-release-package.mjs +29 -0
  120. package/presets/release-closeout/preset.yaml +100 -0
  121. package/presets/release-closeout/scripts/generate-release-package.mjs +572 -0
  122. package/presets/release-closeout/templates/execution_strategy.append.md +7 -0
  123. package/presets/release-closeout/templates/findings.seed.md +5 -0
  124. package/presets/release-closeout/templates/review.seed.md +3 -0
  125. package/presets/release-closeout/templates/task_plan.append.md +24 -0
  126. package/presets/standard-task/preset.yaml +2 -2
  127. package/references/agents-md-pattern.md +23 -17
  128. package/references/lessons-governance.md +2 -2
  129. package/references/module-parallel-standard.md +3 -6
  130. package/references/pull-request-standard.md +2 -2
  131. package/references/ssot-governance.md +2 -2
  132. package/references/taskr-gap-analysis.md +3 -3
  133. package/run-dist.mjs +34 -0
  134. package/skills/preset-creator/SKILL.md +40 -8
  135. package/skills/preset-creator/references/complex-task-skeleton/brief.md +32 -8
  136. package/skills/preset-creator/references/preset-package-skeleton.md +15 -5
  137. package/skills/preset-creator/references/structure-aware-paths.md +112 -0
  138. package/templates/AGENTS.md.template +28 -26
  139. package/templates/architecture/README.md +2 -2
  140. package/templates/architecture/service-catalog.md +2 -2
  141. package/templates/architecture/services/service-template.md +1 -1
  142. package/templates/dashboard/assets/app-src/00-state.js +5 -1
  143. package/templates/dashboard/assets/app-src/10-router.js +7 -0
  144. package/templates/dashboard/assets/app-src/20-overview.js +8 -8
  145. package/templates/dashboard/assets/app-src/30-tasks.js +132 -40
  146. package/templates/dashboard/assets/app-src/32-task-swimlane.js +314 -0
  147. package/templates/dashboard/assets/app-src/35-task-detail.js +35 -5
  148. package/templates/dashboard/assets/app-src/40-modules.js +257 -41
  149. package/templates/dashboard/assets/app-src/45-review.js +127 -1
  150. package/templates/dashboard/assets/app-src/90-bindings.js +185 -2
  151. package/templates/dashboard/assets/app.css +928 -53
  152. package/templates/dashboard/assets/app.css.manifest.json +2 -0
  153. package/templates/dashboard/assets/app.js +1071 -98
  154. package/templates/dashboard/assets/app.manifest.json +1 -0
  155. package/templates/dashboard/assets/css-src/00-foundation.css +12 -6
  156. package/templates/dashboard/assets/css-src/10-panels-flow.css +2 -2
  157. package/templates/dashboard/assets/css-src/30-task-index.css +21 -13
  158. package/templates/dashboard/assets/css-src/31-archive.css +94 -0
  159. package/templates/dashboard/assets/css-src/32-task-swimlane.css +487 -0
  160. package/templates/dashboard/assets/css-src/35-review-workspace.css +78 -0
  161. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +191 -14
  162. package/templates/dashboard/assets/css-src/50-responsive-overrides.css +23 -0
  163. package/templates/dashboard/assets/i18n.js +166 -2
  164. package/templates/development/README.md +9 -9
  165. package/templates/development/cross-repo-debugging.md +3 -3
  166. package/templates/development/external-context/service-template.md +1 -1
  167. package/templates/development/external-source-packs/README.md +2 -2
  168. package/templates/integrations/README.md +4 -4
  169. package/templates/integrations/api-contract.md +1 -1
  170. package/templates/integrations/event-contract.md +1 -1
  171. package/templates/integrations/third-party/vendor-template.md +1 -1
  172. package/templates/integrations/webhook-contract.md +1 -1
  173. package/templates/ledger/Harness-Ledger.md +1 -1
  174. package/templates/modules/module_brief.md +50 -0
  175. package/templates/modules/module_plan.md +49 -0
  176. package/templates/modules/registry_view.md +9 -0
  177. package/templates/modules/session_prompt_pack.md +55 -0
  178. package/templates/planning/brief.md +32 -8
  179. package/templates/planning/module_brief.md +28 -3
  180. package/templates/planning/module_plan.md +26 -11
  181. package/templates/planning/module_session_prompt.md +11 -2
  182. package/templates/planning/optional/slices/_slice-template/brief.md +28 -0
  183. package/templates/planning/review.md +1 -1
  184. package/templates/planning/visual_map.md +1 -1
  185. package/templates/reference/docs-library-standard.md +7 -7
  186. package/templates/reference/execution-workflow-standard.md +13 -0
  187. package/templates/reference/external-source-intake-standard.md +10 -10
  188. package/templates/reference/pull-request-standard.md +2 -2
  189. package/templates/reference/repo-governance-standard.md +1 -1
  190. package/templates/reference/review-routing-standard.md +4 -0
  191. package/templates/ssot/Module-Registry.md +4 -38
  192. package/templates/walkthrough/walkthrough-template.md +1 -1
  193. package/templates-zh-CN/AGENTS.md.template +27 -25
  194. package/templates-zh-CN/CLAUDE.md.template +1 -1
  195. package/templates-zh-CN/architecture/README.md +2 -2
  196. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  197. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  198. package/templates-zh-CN/development/README.md +9 -9
  199. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  200. package/templates-zh-CN/development/external-context/service-template.md +1 -1
  201. package/templates-zh-CN/development/external-source-packs/README.md +2 -2
  202. package/templates-zh-CN/integrations/README.md +4 -4
  203. package/templates-zh-CN/integrations/api-contract.md +1 -1
  204. package/templates-zh-CN/integrations/event-contract.md +1 -1
  205. package/templates-zh-CN/integrations/third-party/vendor-template.md +1 -1
  206. package/templates-zh-CN/integrations/webhook-contract.md +1 -1
  207. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  208. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  209. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  210. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  211. package/templates-zh-CN/modules/module_brief.md +47 -0
  212. package/templates-zh-CN/modules/module_plan.md +48 -0
  213. package/templates-zh-CN/modules/registry_view.md +9 -0
  214. package/templates-zh-CN/modules/session_prompt_pack.md +50 -0
  215. package/templates-zh-CN/planning/INDEX.md +1 -0
  216. package/templates-zh-CN/planning/brief.md +26 -7
  217. package/templates-zh-CN/planning/module_brief.md +24 -2
  218. package/templates-zh-CN/planning/module_plan.md +35 -29
  219. package/templates-zh-CN/planning/module_session_prompt.md +15 -11
  220. package/templates-zh-CN/planning/optional/slices/_slice-template/brief.md +28 -11
  221. package/templates-zh-CN/planning/review.md +1 -1
  222. package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
  223. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  224. package/templates-zh-CN/reference/docs-library-standard.md +27 -27
  225. package/templates-zh-CN/reference/execution-workflow-standard.md +12 -2
  226. package/templates-zh-CN/reference/external-source-intake-standard.md +10 -10
  227. package/templates-zh-CN/reference/harness-ledger-standard.md +3 -3
  228. package/templates-zh-CN/reference/pull-request-standard.md +1 -1
  229. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  230. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  231. package/templates-zh-CN/reference/review-routing-standard.md +3 -0
  232. package/templates-zh-CN/reference/walkthrough-standard.md +2 -2
  233. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  234. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  235. package/templates-zh-CN/ssot/Delivery-SSoT.md +2 -2
  236. package/templates-zh-CN/ssot/Module-Registry.md +5 -44
  237. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  238. package/templates-zh-CN/walkthrough/walkthrough-template.md +4 -4
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  // Dashboard bundle aggregation stays behavior-first until dashboard domain types are modeled.
3
2
  import fs from "node:fs";
4
3
  import path from "node:path";
@@ -9,7 +8,7 @@ import { readCapabilityRegistry, validateCapabilities } from "./capability-regis
9
8
  import { resolveHarnessPaths } from "./harness-paths.mjs";
10
9
  import { legacyCompatMode, safeAdoptionCapability, } from "./harness-paths.mjs";
11
10
  import { buildStatusData } from "./status-builder.mjs";
12
- import { listTaskPlanPaths, parseTaskState, isActiveTaskState, } from "./task-scanner.mjs";
11
+ import { parseTaskState, isActiveTaskState, createScannerTaskRepository, } from "./task-repository.mjs";
13
12
  import { writeDashboardDirectory, writeDashboardFile } from "./dashboard-writer.mjs";
14
13
  import { listPresetPackageLayers } from "./preset-registry.mjs";
15
14
  import { validateGovernanceTableBoundaries } from "./governance-table-boundary.mjs";
@@ -17,7 +16,7 @@ import { summarizeGitState } from "./git-status-summary.mjs";
17
16
  export function collectMarkdownDocuments(target, options = {}) {
18
17
  const docs = collectDashboardDocumentPaths(target, options);
19
18
  return docs.map((entry, index) => {
20
- const file = typeof entry === "string" ? entry : entry.file;
19
+ const file = entry.file;
21
20
  const content = sanitizeText(readFileSafe(file));
22
21
  const source = prefixedPath(target, file);
23
22
  return {
@@ -76,17 +75,21 @@ function collectDashboardDocumentPaths(target, options = {}) {
76
75
  selected.add(file);
77
76
  }
78
77
  }
79
- const tasksByPlanPath = new Map((options.tasks || []).map((task) => [
80
- path.join(target.projectRoot, String(task.taskPlanPath || "").replace(/^TARGET:/, "")),
78
+ const tasks = options.tasks || createScannerTaskRepository(target).list();
79
+ const tasksByPlanPath = new Map(tasks.map((task) => [
80
+ targetAbsolutePath(target, String(task.taskPlanPath || "")),
81
81
  task,
82
82
  ]));
83
- for (const taskPlanPath of options.taskPlanPaths || listTaskPlanPaths(target)) {
83
+ for (const listedTask of tasks) {
84
+ const taskPlanPath = targetAbsolutePath(target, String(listedTask.taskPlanPath || ""));
85
+ if (!taskPlanPath || !fs.existsSync(taskPlanPath))
86
+ continue;
84
87
  const taskDir = path.dirname(taskPlanPath);
85
88
  const progress = readFileSafe(path.join(taskDir, "progress.md"));
86
89
  const state = parseTaskState(progress);
87
90
  const active = isActiveTaskState(state);
88
- const task = tasksByPlanPath.get(taskPlanPath);
89
- const historicalClosed = !active && task?.closeoutStatus === "closed";
91
+ const taskRef = tasksByPlanPath.get(taskPlanPath);
92
+ const historicalClosed = !active && taskRef?.closeoutStatus === "closed";
90
93
  const documentNames = historicalClosed
91
94
  ? ["brief.md", "walkthrough.md"]
92
95
  : ["brief.md", "task_plan.md", "execution_strategy.md", visualMapFile, legacyVisualRoadmapFile, lessonCandidatesFile, longRunningTaskContractFile, "progress.md", "review.md", "findings.md", "walkthrough.md"];
@@ -98,7 +101,7 @@ function collectDashboardDocumentPaths(target, options = {}) {
98
101
  partial.set(file, {
99
102
  partial: true,
100
103
  partialReason: "historical-closed",
101
- taskId: task?.id || path.basename(taskDir),
104
+ taskId: taskRef?.id || path.basename(taskDir),
102
105
  });
103
106
  }
104
107
  }
@@ -198,7 +201,7 @@ export function collectGraph(status, tables = { tables: [] }, target = null) {
198
201
  };
199
202
  for (const task of status.tasks) {
200
203
  addNode({ id: `task:${task.id}`, type: "task", label: task.title, state: task.state, completion: task.completion });
201
- for (const phase of task.phases || []) {
204
+ for (const phase of dashboardTaskPhases(task)) {
202
205
  const phaseId = `phase:${task.id}:${phase.id}`;
203
206
  addNode({
204
207
  id: phaseId,
@@ -216,12 +219,34 @@ export function collectGraph(status, tables = { tables: [] }, target = null) {
216
219
  addEdge({ from: `phase:${task.id}:${dependency}`, to: phaseId, type: "depends_on" });
217
220
  }
218
221
  }
219
- for (const handoff of task.handoffs || []) {
222
+ for (const handoff of dashboardTaskHandoffs(task)) {
220
223
  const handoffId = `handoff:${handoff.id}`;
221
224
  addNode({ id: handoffId, type: "handoff", label: handoff.summary, state: handoff.state });
222
225
  addEdge({ from: `task:${task.id}`, to: handoffId, type: "handoff" });
223
226
  }
224
227
  }
228
+ for (const module of Array.isArray(status.modules) ? status.modules : []) {
229
+ const key = String(module.key || "");
230
+ if (!key)
231
+ continue;
232
+ const moduleId = `module:${key}`;
233
+ const currentStep = String(module.currentStep || "");
234
+ const state = module.status || "planned";
235
+ addNode({
236
+ id: moduleId,
237
+ type: "module",
238
+ label: String(module.title || key),
239
+ state,
240
+ currentStep,
241
+ ...moduleDocumentPaths(target, key),
242
+ });
243
+ if (currentStep) {
244
+ const stepId = `step:${currentStep}`;
245
+ if (!seenNodes.has(stepId))
246
+ addNode({ id: stepId, type: "step", label: currentStep, state, module: key });
247
+ addEdge({ from: moduleId, to: stepId, type: "current_step" });
248
+ }
249
+ }
225
250
  for (const table of tables.tables || []) {
226
251
  if (table.kind === "module-registry") {
227
252
  for (const row of table.rows) {
@@ -234,7 +259,7 @@ export function collectGraph(status, tables = { tables: [] }, target = null) {
234
259
  addNode({
235
260
  id: moduleId,
236
261
  type: "module",
237
- label: getCell(row.cells, ["Name", "Module", "模块名称", "模块"], key),
262
+ label: getCell(row.cells, ["Title", "Name", "Module", "模块名称", "模块"], key),
238
263
  state: status,
239
264
  currentStep,
240
265
  ...moduleDocumentPaths(target, key),
@@ -271,6 +296,12 @@ export function collectGraph(status, tables = { tables: [] }, target = null) {
271
296
  }
272
297
  return { nodes, edges: edges.filter((edge) => seenNodes.has(edge.from) && seenNodes.has(edge.to)) };
273
298
  }
299
+ function dashboardTaskPhases(task) {
300
+ return Array.isArray(task.phases) ? task.phases : [];
301
+ }
302
+ function dashboardTaskHandoffs(task) {
303
+ return Array.isArray(task.handoffs) ? task.handoffs : [];
304
+ }
274
305
  function moduleKeyFromPlanSource(source, target) {
275
306
  if (!target?.projectRoot || !target?.harness?.modulesRoot) {
276
307
  const moduleMatch = source.match(/(?:MODULES|modules)\/([^/]+)\/module_plan\.md$/);
@@ -285,6 +316,14 @@ function moduleKeyFromPlanSource(source, target) {
285
316
  const legacyMatch = source.match(/(?:MODULES|modules)\/([^/]+)\/module_plan\.md$/);
286
317
  return legacyMatch ? legacyMatch[1] : "";
287
318
  }
319
+ function targetAbsolutePath(target, targetPath) {
320
+ const withoutPrefix = String(targetPath || "").replace(/^TARGET:/, "");
321
+ if (!withoutPrefix)
322
+ return "";
323
+ if (path.isAbsolute(withoutPrefix))
324
+ return withoutPrefix;
325
+ return path.join(target.projectRoot, withoutPrefix.replace(/^\/+/, ""));
326
+ }
288
327
  function moduleDocumentPaths(target, moduleKey) {
289
328
  if (!target?.harness?.modulesRoot || !moduleKey)
290
329
  return {};
@@ -295,6 +334,136 @@ function moduleDocumentPaths(target, moduleKey) {
295
334
  ...(fs.existsSync(modulePlan) ? { modulePlanPath: prefixedPath(target, modulePlan) } : {}),
296
335
  };
297
336
  }
337
+ function collectDashboardModules(status, target) {
338
+ const tasks = Array.isArray(status.tasks) ? status.tasks : [];
339
+ const registered = Array.isArray(status.modules)
340
+ ? status.modules
341
+ : [];
342
+ const modules = new Map();
343
+ for (const module of registered) {
344
+ const key = String(module.key || "").trim();
345
+ if (!key)
346
+ continue;
347
+ modules.set(key, {
348
+ ...module,
349
+ key,
350
+ title: String(module.title || key),
351
+ source: "registry",
352
+ ...moduleDocumentPaths(target, key),
353
+ counts: emptyModuleCounts(),
354
+ tasks: [],
355
+ });
356
+ }
357
+ for (const task of tasks) {
358
+ if (isArchivedDashboardTask(task))
359
+ continue;
360
+ const key = dashboardTaskModuleKey(task);
361
+ if (key === "legacy-unclassified")
362
+ continue;
363
+ if (!modules.has(key)) {
364
+ modules.set(key, {
365
+ key,
366
+ title: key,
367
+ source: "inferred",
368
+ status: String(task.classificationSource || "inferred"),
369
+ ...moduleDocumentPaths(target, key),
370
+ counts: emptyModuleCounts(),
371
+ tasks: [],
372
+ });
373
+ }
374
+ const module = modules.get(key);
375
+ if (!module)
376
+ continue;
377
+ accumulateModuleTask(module, task);
378
+ }
379
+ const unclassifiedTasks = tasks.filter((task) => !isArchivedDashboardTask(task) && dashboardTaskModuleKey(task) === "legacy-unclassified");
380
+ const moduleList = [...modules.values()].sort((left, right) => {
381
+ const leftStatus = String(left.status || "");
382
+ const rightStatus = String(right.status || "");
383
+ if (leftStatus === "in-progress" && rightStatus !== "in-progress")
384
+ return -1;
385
+ if (rightStatus === "in-progress" && leftStatus !== "in-progress")
386
+ return 1;
387
+ return left.key.localeCompare(right.key);
388
+ });
389
+ return {
390
+ modules: moduleList,
391
+ summary: {
392
+ total: moduleList.length,
393
+ registered: registered.length,
394
+ inferred: moduleList.filter((module) => module.source === "inferred").length,
395
+ active: moduleList.filter((module) => Number(module.counts.active || 0) > 0).length,
396
+ risk: moduleList.reduce((sum, module) => sum + Number(module.counts.risk || 0), 0),
397
+ unclassifiedTasks: unclassifiedTasks.length,
398
+ },
399
+ };
400
+ }
401
+ function emptyModuleCounts() {
402
+ return {
403
+ total: 0,
404
+ active: 0,
405
+ in_progress: 0,
406
+ review: 0,
407
+ blocked: 0,
408
+ done: 0,
409
+ planned: 0,
410
+ not_started: 0,
411
+ unknown: 0,
412
+ risk: 0,
413
+ missingDocs: 0,
414
+ };
415
+ }
416
+ function accumulateModuleTask(module, task) {
417
+ const state = String(task.state || "unknown");
418
+ module.counts.total += 1;
419
+ module.counts[state] = (module.counts[state] || 0) + 1;
420
+ if (["in_progress", "review", "blocked", "planned", "not_started"].includes(state))
421
+ module.counts.active += 1;
422
+ if (dashboardTaskHasRisk(task))
423
+ module.counts.risk += 1;
424
+ if (dashboardTaskMissingDocs(task))
425
+ module.counts.missingDocs += 1;
426
+ if (module.tasks.length < 16) {
427
+ module.tasks.push({
428
+ id: task.id,
429
+ shortId: task.shortId,
430
+ title: task.title,
431
+ state,
432
+ completion: task.completion,
433
+ reviewStatus: task.reviewStatus,
434
+ closeoutStatus: task.closeoutStatus,
435
+ lifecycleState: task.lifecycleState,
436
+ taskQueues: task.taskQueues,
437
+ queueReasons: task.queueReasons,
438
+ visualMapStatus: task.visualMapStatus,
439
+ briefSource: task.briefSource,
440
+ path: task.path,
441
+ });
442
+ }
443
+ }
444
+ function dashboardTaskModuleKey(task) {
445
+ return String(task.module || task.inferredModule || "legacy-unclassified");
446
+ }
447
+ function isArchivedDashboardTask(task) {
448
+ const archiveState = String(task.archiveMetadata?.state || "").toLowerCase();
449
+ return task.deletionState === "archived" || archiveState === "archived";
450
+ }
451
+ function dashboardTaskHasRisk(task) {
452
+ if (task.state === "blocked")
453
+ return true;
454
+ if (String(task.reviewStatus || "").includes("blocked"))
455
+ return true;
456
+ if (Array.isArray(task.materialIssues) && task.materialIssues.length > 0)
457
+ return true;
458
+ if (Array.isArray(task.queueReasons) && task.queueReasons.length > 0)
459
+ return true;
460
+ if (String(task.visualMapStatus || "") === "missing")
461
+ return true;
462
+ return false;
463
+ }
464
+ function dashboardTaskMissingDocs(task) {
465
+ return task.briefSource !== "standalone" || String(task.visualMapStatus || "") === "missing";
466
+ }
298
467
  export function categorizeWarning(message) {
299
468
  if (/governance-table-entropy/i.test(message))
300
469
  return "Governance Table Boundary";
@@ -395,11 +564,14 @@ function warningAffectedPaths(message) {
395
564
  return [...new Set(matches.map((item) => item.replace(/[),.;]+$/, "")))];
396
565
  }
397
566
  function summarizeWarnings(warnings) {
398
- const countBy = (field) => warnings.reduce((acc, warning) => {
399
- const key = warning[field] || "unknown";
400
- acc[key] = (acc[key] || 0) + 1;
401
- return acc;
402
- }, {});
567
+ const countBy = (field) => {
568
+ const counts = {};
569
+ for (const warning of warnings) {
570
+ const key = String(warning[field] || "unknown");
571
+ counts[key] = (counts[key] ?? 0) + 1;
572
+ }
573
+ return counts;
574
+ };
403
575
  return {
404
576
  total: warnings.length,
405
577
  byCategory: countBy("category"),
@@ -518,7 +690,7 @@ function warningAction(message) {
518
690
  }
519
691
  export function buildDashboardBundle(targetInput, options = {}) {
520
692
  const target = normalizeTarget(targetInput);
521
- const taskPlanPaths = listTaskPlanPaths(target);
693
+ const tasks = options.tasks || createScannerTaskRepository(target).list();
522
694
  const capabilityState = validateCapabilities(target);
523
695
  const gitState = summarizeGitState(target);
524
696
  const declaredCapabilities = new Set(capabilityState.registry.capabilities.map((capability) => capability.name));
@@ -530,17 +702,18 @@ export function buildDashboardBundle(targetInput, options = {}) {
530
702
  ...options,
531
703
  capabilityState,
532
704
  gitState,
533
- taskPlanPaths,
705
+ tasks,
534
706
  legacy,
535
707
  failures: [...capabilityState.failures, ...governanceBoundaries.failures],
536
708
  warnings: [...capabilityState.warnings, ...legacyWarnings, ...governanceBoundaries.warnings, ...gitState.warnings],
537
709
  });
538
- const documents = { documents: collectMarkdownDocuments(target, { taskPlanPaths, tasks: status.tasks }) };
710
+ const documents = { documents: collectMarkdownDocuments(target, { tasks: status.tasks }) };
539
711
  const tables = collectTables(documents.documents);
540
712
  const graph = collectGraph(status, tables, target);
713
+ const modules = collectDashboardModules(status, target);
541
714
  const adoption = collectAdoption(status);
542
715
  const presetCatalog = collectPresetCatalog(targetInput, target, options);
543
- return sanitizeDeep({ status, tables, documents, graph, adoption, presetCatalog });
716
+ return sanitizeDeep({ status, tables, documents, graph, modules: modules.modules, moduleSummary: modules.summary, adoption, presetCatalog });
544
717
  }
545
718
  function runDashboardCompatibilityCheck(target) {
546
719
  const checkTarget = target.docsOnly ? target.projectRoot : target.input;
@@ -597,7 +770,15 @@ export function writeDashboardFolder(outDir, targetInput, options = {}) {
597
770
  const registry = readCapabilityRegistry(target);
598
771
  const locale = options.localeOverride || registry.locale;
599
772
  const bundle = buildDashboardBundle(targetInput, options);
600
- return writeDashboardDirectory(outDir, bundle, { repoRoot, projectRoot: target.projectRoot, docsRoot: target.docsRoot, locale, workbenchRuntime: options.workbenchRuntime === true, recoverGeneratedDashboard: options.recoverGeneratedDashboard === true });
773
+ return writeDashboardDirectory(outDir, bundle, {
774
+ repoRoot,
775
+ projectRoot: target.projectRoot,
776
+ docsRoot: target.docsRoot,
777
+ locale,
778
+ workbenchRuntime: options.workbenchRuntime === true,
779
+ recoverGeneratedDashboard: options.recoverGeneratedDashboard === true,
780
+ replaceExistingDashboardOutput: options.replaceExistingDashboardOutput === true,
781
+ });
601
782
  }
602
783
  export function writeDashboardSingleFile(outFile, targetInput, options = {}) {
603
784
  const target = normalizeTarget(targetInput);