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,404 @@
1
+ // @ts-nocheck
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { normalizeLocale, normalizeTarget, readJsonSafe, toPosix, } from "./core-shared.mjs";
5
+ import { legacyModuleRoot, legacyPath, v2HarnessRoot, } from "./harness-paths.mjs";
6
+ const legacyMappings = [
7
+ ["03-ARCHITECTURE", `${v2HarnessRoot}/context/architecture`],
8
+ ["04-DEVELOPMENT", `${v2HarnessRoot}/context/development`],
9
+ ["06-INTEGRATIONS", `${v2HarnessRoot}/context/integrations`],
10
+ ["05-TEST-QA", `${v2HarnessRoot}/governance/regression`],
11
+ ["11-REFERENCE", `${v2HarnessRoot}/governance/standards`],
12
+ ["09-PLANNING/TASKS", `${v2HarnessRoot}/planning/tasks`],
13
+ ["09-PLANNING/MODULES", `${v2HarnessRoot}/planning/modules`],
14
+ ["09-PLANNING/Module-Registry.md", `${v2HarnessRoot}/planning/modules/Module-Registry.md`],
15
+ ["10-WALKTHROUGH", `${v2HarnessRoot}/governance/archive/legacy-walkthrough`],
16
+ ["Harness-Ledger.md", `${v2HarnessRoot}/governance/archive/legacy-governance/Harness-Ledger.md`],
17
+ ];
18
+ export function planStructureMigration(targetInput = ".") {
19
+ const target = normalizeTarget(targetInput);
20
+ const legacyDocsRoot = path.join(target.projectRoot, "docs");
21
+ const manifestPath = path.join(target.projectRoot, v2HarnessRoot, "harness.yaml");
22
+ const capabilities = readLegacyCapabilities(target.projectRoot);
23
+ const actions = [];
24
+ if (!fs.existsSync(legacyDocsRoot)) {
25
+ actions.push({
26
+ action: fs.existsSync(manifestPath) ? "already-v2" : "create-v2-manifest",
27
+ source: "",
28
+ destination: toPosix(path.relative(target.projectRoot, manifestPath)),
29
+ });
30
+ }
31
+ for (const [legacyRelative, v2Relative] of legacyMappings) {
32
+ const source = path.join(legacyDocsRoot, legacyRelative);
33
+ if (!fs.existsSync(source))
34
+ continue;
35
+ actions.push({
36
+ action: "move",
37
+ source: toPosix(path.relative(target.projectRoot, source)),
38
+ destination: v2Relative,
39
+ });
40
+ }
41
+ const archiveDestination = `${v2HarnessRoot}/governance/archive/legacy-docs`;
42
+ if (fs.existsSync(legacyDocsRoot)) {
43
+ actions.push({
44
+ action: "archive-source-root",
45
+ source: "docs",
46
+ destination: archiveDestination,
47
+ });
48
+ }
49
+ for (const relative of generatedTemplateDirs()) {
50
+ const legacyEquivalent = relative
51
+ .replace(`${v2HarnessRoot}/planning/tasks`, "09-PLANNING/TASKS")
52
+ .replace(`${v2HarnessRoot}/planning/modules`, "09-PLANNING/MODULES");
53
+ if (fs.existsSync(path.join(target.projectRoot, relative)) || fs.existsSync(path.join(legacyDocsRoot, legacyEquivalent))) {
54
+ actions.push({
55
+ action: "remove-generated-template-dir",
56
+ source: "",
57
+ destination: relative,
58
+ });
59
+ }
60
+ }
61
+ const legacyRegistry = path.join(target.projectRoot, ".harness-capabilities.json");
62
+ if (fs.existsSync(legacyRegistry)) {
63
+ actions.push({
64
+ action: "archive-legacy-registry",
65
+ source: ".harness-capabilities.json",
66
+ destination: `${v2HarnessRoot}/governance/archive/legacy-governance/.harness-capabilities.json`,
67
+ });
68
+ }
69
+ return {
70
+ operation: "migrate-structure",
71
+ target: target.projectRoot,
72
+ mode: fs.existsSync(manifestPath) ? "v2-present" : "legacy-source",
73
+ manifest: toPosix(path.relative(target.projectRoot, manifestPath)),
74
+ capabilities,
75
+ actions,
76
+ summary: {
77
+ actions: actions.length,
78
+ moves: actions.filter((action) => action.action === "move").length,
79
+ willArchiveLegacyDocs: actions.some((action) => action.action === "archive-source-root"),
80
+ canApply: actions.length > 0,
81
+ },
82
+ };
83
+ }
84
+ export function applyStructureMigration(targetInput = ".", { force = false } = {}) {
85
+ const plan = planStructureMigration(targetInput);
86
+ const targetRoot = plan.target;
87
+ const manifestPath = path.join(targetRoot, plan.manifest);
88
+ const applied = [];
89
+ preflightStructureMigration(plan, { force });
90
+ if (!fs.existsSync(manifestPath) || force) {
91
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
92
+ fs.writeFileSync(manifestPath, renderHarnessManifest({ locale: plan.capabilities.locale, capabilities: plan.capabilities.names }));
93
+ applied.push({ action: "write-manifest", destination: plan.manifest });
94
+ }
95
+ for (const action of plan.actions.filter((entry) => entry.action === "move")) {
96
+ const source = path.join(targetRoot, action.source);
97
+ const destination = path.join(targetRoot, action.destination);
98
+ if (!fs.existsSync(source))
99
+ continue;
100
+ if (fs.existsSync(destination) && !force) {
101
+ throw new Error(`Refusing to overwrite existing v2 destination: ${action.destination}`);
102
+ }
103
+ if (fs.existsSync(destination))
104
+ fs.rmSync(destination, { recursive: true, force: true });
105
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
106
+ fs.cpSync(source, destination, { recursive: true });
107
+ applied.push({ action: "copy", source: action.source, destination: action.destination });
108
+ }
109
+ const docsRoot = path.join(targetRoot, "docs");
110
+ if (fs.existsSync(docsRoot)) {
111
+ const archiveRoot = uniqueArchiveRoot(targetRoot);
112
+ fs.mkdirSync(path.dirname(archiveRoot), { recursive: true });
113
+ fs.renameSync(docsRoot, archiveRoot);
114
+ applied.push({ action: "archive-source-root", source: "docs", destination: toPosix(path.relative(targetRoot, archiveRoot)) });
115
+ }
116
+ archiveLegacyCapabilityRegistry(targetRoot, applied);
117
+ normalizeMigratedModuleTasks(targetRoot, applied, { force });
118
+ scaffoldMissingTaskWalkthroughs(targetRoot, applied);
119
+ removeGeneratedTemplateDirectories(targetRoot, applied);
120
+ return {
121
+ ...plan,
122
+ applied: true,
123
+ actionsApplied: applied,
124
+ summary: {
125
+ ...plan.summary,
126
+ applied: applied.length,
127
+ },
128
+ };
129
+ }
130
+ function removeGeneratedTemplateDirectories(targetRoot, applied) {
131
+ for (const relative of generatedTemplateDirs()) {
132
+ const directory = path.join(targetRoot, relative);
133
+ if (!fs.existsSync(directory))
134
+ continue;
135
+ fs.rmSync(directory, { recursive: true, force: true });
136
+ applied.push({
137
+ action: "remove-generated-template-dir",
138
+ destination: relative,
139
+ });
140
+ }
141
+ }
142
+ function generatedTemplateDirs() {
143
+ return [
144
+ `${v2HarnessRoot}/planning/tasks/_task-template`,
145
+ `${v2HarnessRoot}/planning/modules/_task-template`,
146
+ `${v2HarnessRoot}/planning/modules/_module-template`,
147
+ ];
148
+ }
149
+ function preflightStructureMigration(plan, { force = false } = {}) {
150
+ if (force)
151
+ return;
152
+ const conflicts = [];
153
+ for (const action of plan.actions.filter((entry) => entry.action === "move")) {
154
+ const source = path.join(plan.target, action.source);
155
+ const destination = path.join(plan.target, action.destination);
156
+ if (fs.existsSync(source) && fs.existsSync(destination))
157
+ conflicts.push(action.destination);
158
+ }
159
+ conflicts.push(...moduleTaskNormalizationConflicts(plan.target));
160
+ if (conflicts.length) {
161
+ throw new Error(`Refusing to overwrite existing v2 destination(s): ${conflicts.join(", ")}`);
162
+ }
163
+ }
164
+ function moduleTaskNormalizationConflicts(targetRoot) {
165
+ const modulesRoot = path.join(targetRoot, legacyPath(legacyModuleRoot));
166
+ if (!fs.existsSync(modulesRoot))
167
+ return [];
168
+ const conflicts = [];
169
+ for (const moduleName of fs.readdirSync(modulesRoot)) {
170
+ if (moduleName.startsWith("_"))
171
+ continue;
172
+ const moduleDir = path.join(modulesRoot, moduleName);
173
+ if (!fs.statSync(moduleDir).isDirectory())
174
+ continue;
175
+ const legacyTasksRoot = path.join(moduleDir, "TASKS");
176
+ const normalizedTasksRoot = path.join(moduleDir, "tasks");
177
+ if (!hasExactChild(moduleDir, "TASKS") || !hasExactChild(moduleDir, "tasks"))
178
+ continue;
179
+ for (const taskName of fs.readdirSync(legacyTasksRoot)) {
180
+ if (fs.existsSync(path.join(normalizedTasksRoot, taskName))) {
181
+ conflicts.push(`${v2HarnessRoot}/planning/modules/${moduleName}/tasks/${taskName}`);
182
+ }
183
+ }
184
+ }
185
+ return conflicts;
186
+ }
187
+ function hasExactChild(parentDir, childName) {
188
+ return fs.existsSync(parentDir) && fs.readdirSync(parentDir).includes(childName);
189
+ }
190
+ function archiveLegacyCapabilityRegistry(targetRoot, applied) {
191
+ const registry = path.join(targetRoot, ".harness-capabilities.json");
192
+ if (!fs.existsSync(registry))
193
+ return;
194
+ const destination = uniqueArchiveFile(targetRoot, `${v2HarnessRoot}/governance/archive/legacy-governance/.harness-capabilities.json`);
195
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
196
+ fs.renameSync(registry, destination);
197
+ applied.push({
198
+ action: "archive-legacy-registry",
199
+ source: ".harness-capabilities.json",
200
+ destination: toPosix(path.relative(targetRoot, destination)),
201
+ });
202
+ }
203
+ function readLegacyCapabilities(projectRoot) {
204
+ const raw = readJsonSafe(path.join(projectRoot, ".harness-capabilities.json"), null);
205
+ const names = new Set(["core"]);
206
+ let locale = "en-US";
207
+ if (raw) {
208
+ locale = normalizeLocale(raw.locale);
209
+ for (const entry of raw.capabilities || [])
210
+ names.add(typeof entry === "string" ? entry : entry.name);
211
+ }
212
+ return { locale, names: [...names].filter(Boolean) };
213
+ }
214
+ function renderHarnessManifest({ locale, capabilities }) {
215
+ return [
216
+ "version: 2",
217
+ `locale: ${normalizeLocale(locale)}`,
218
+ "capabilities:",
219
+ ...[...new Set(capabilities)].map((capability) => ` - ${capability}`),
220
+ "structure:",
221
+ ` harnessRoot: ${v2HarnessRoot}`,
222
+ ` planningRoot: ${v2HarnessRoot}/planning`,
223
+ ` tasksRoot: ${v2HarnessRoot}/planning/tasks`,
224
+ ` modulesRoot: ${v2HarnessRoot}/planning/modules`,
225
+ ` externalRoot: ${v2HarnessRoot}/planning/external`,
226
+ ` governanceRoot: ${v2HarnessRoot}/governance`,
227
+ ` generatedRoot: ${v2HarnessRoot}/governance/generated`,
228
+ "",
229
+ ].join("\n");
230
+ }
231
+ function uniqueArchiveRoot(targetRoot) {
232
+ const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(".", "-");
233
+ const base = path.join(targetRoot, v2HarnessRoot, "governance/archive/legacy-docs", stamp);
234
+ for (let index = 0; index < 100; index += 1) {
235
+ const candidate = index === 0 ? base : `${base}-${String(index).padStart(2, "0")}`;
236
+ if (!fs.existsSync(candidate))
237
+ return candidate;
238
+ }
239
+ throw new Error("Unable to allocate legacy docs archive directory");
240
+ }
241
+ function uniqueArchiveFile(targetRoot, relativeFile) {
242
+ const parsed = path.parse(path.join(targetRoot, relativeFile));
243
+ const base = path.join(parsed.dir, parsed.name);
244
+ for (let index = 0; index < 100; index += 1) {
245
+ const suffix = index === 0 ? "" : `-${String(index).padStart(2, "0")}`;
246
+ const candidate = `${base}${suffix}${parsed.ext}`;
247
+ if (!fs.existsSync(candidate))
248
+ return candidate;
249
+ }
250
+ throw new Error(`Unable to allocate legacy archive file: ${relativeFile}`);
251
+ }
252
+ function normalizeMigratedModuleTasks(targetRoot, applied, { force = false } = {}) {
253
+ const modulesRoot = path.join(targetRoot, v2HarnessRoot, "planning/modules");
254
+ if (!fs.existsSync(modulesRoot))
255
+ return;
256
+ for (const moduleName of fs.readdirSync(modulesRoot)) {
257
+ if (moduleName.startsWith("_"))
258
+ continue;
259
+ const moduleDir = path.join(modulesRoot, moduleName);
260
+ if (!fs.statSync(moduleDir).isDirectory())
261
+ continue;
262
+ const legacyTasksRoot = path.join(moduleDir, "TASKS");
263
+ if (!fs.existsSync(legacyTasksRoot))
264
+ continue;
265
+ const stagingRoot = uniqueModuleTaskStagingRoot(moduleDir);
266
+ fs.renameSync(legacyTasksRoot, stagingRoot);
267
+ const tasksRoot = path.join(moduleDir, "tasks");
268
+ fs.mkdirSync(tasksRoot, { recursive: true });
269
+ for (const taskName of fs.readdirSync(stagingRoot)) {
270
+ const source = path.join(stagingRoot, taskName);
271
+ const destination = path.join(tasksRoot, taskName);
272
+ if (fs.existsSync(destination) && !force) {
273
+ throw new Error(`Refusing to overwrite existing migrated module task: ${toPosix(path.relative(targetRoot, destination))}`);
274
+ }
275
+ if (fs.existsSync(destination))
276
+ fs.rmSync(destination, { recursive: true, force: true });
277
+ fs.renameSync(source, destination);
278
+ applied.push({
279
+ action: "move",
280
+ source: toPosix(path.relative(targetRoot, source)),
281
+ destination: toPosix(path.relative(targetRoot, destination)),
282
+ });
283
+ }
284
+ fs.rmSync(stagingRoot, { recursive: true, force: true });
285
+ }
286
+ }
287
+ function uniqueModuleTaskStagingRoot(moduleDir) {
288
+ for (let index = 0; index < 100; index += 1) {
289
+ const suffix = index === 0 ? "" : `-${String(index).padStart(2, "0")}`;
290
+ const candidate = path.join(moduleDir, `.TASKS-migration-${process.pid}${suffix}`);
291
+ if (!fs.existsSync(candidate))
292
+ return candidate;
293
+ }
294
+ throw new Error(`Unable to allocate module task staging directory: ${moduleDir}`);
295
+ }
296
+ function scaffoldMissingTaskWalkthroughs(targetRoot, applied) {
297
+ for (const taskDir of migratedTaskDirectories(targetRoot)) {
298
+ const taskId = path.basename(taskDir);
299
+ const index = path.join(taskDir, "INDEX.md");
300
+ if (!fs.existsSync(index)) {
301
+ fs.writeFileSync(index, renderMigratedTaskIndex(taskId));
302
+ applied.push({
303
+ action: "create",
304
+ destination: toPosix(path.relative(targetRoot, index)),
305
+ });
306
+ }
307
+ const walkthrough = path.join(taskDir, "walkthrough.md");
308
+ if (!fs.existsSync(walkthrough)) {
309
+ fs.writeFileSync(walkthrough, "# Walkthrough\n\nPending migrated closeout.\n");
310
+ applied.push({
311
+ action: "create",
312
+ destination: toPosix(path.relative(targetRoot, walkthrough)),
313
+ });
314
+ }
315
+ const visualMap = path.join(taskDir, "visual_map.md");
316
+ if (!fs.existsSync(visualMap)) {
317
+ fs.writeFileSync(visualMap, renderMigratedVisualMap());
318
+ applied.push({
319
+ action: "create",
320
+ destination: toPosix(path.relative(targetRoot, visualMap)),
321
+ });
322
+ }
323
+ }
324
+ }
325
+ function renderMigratedVisualMap() {
326
+ return `# Visual Map
327
+
328
+ Visual Map Contract: v1.0
329
+
330
+ | Phase ID | Kind | Depends On | State | Completion | Output | Required Evidence | Exit Command | Actor | Evidence Status | Blocking Risk | Owner / Handoff |
331
+ | --- | --- | --- | --- | ---: | --- | --- | --- | --- | --- | --- | --- |
332
+ | MIG-01 | execution | none | planned | 0 | migrated task validation | migrated task materials | n/a | agent | present | none | coordinator |
333
+ `;
334
+ }
335
+ function migratedTaskDirectories(targetRoot) {
336
+ const taskDirs = [];
337
+ const tasksRoot = path.join(targetRoot, v2HarnessRoot, "planning/tasks");
338
+ if (fs.existsSync(tasksRoot)) {
339
+ for (const entry of fs.readdirSync(tasksRoot)) {
340
+ const taskDir = path.join(tasksRoot, entry);
341
+ if (fs.existsSync(path.join(taskDir, "task_plan.md")))
342
+ taskDirs.push(taskDir);
343
+ }
344
+ }
345
+ const modulesRoot = path.join(targetRoot, v2HarnessRoot, "planning/modules");
346
+ if (!fs.existsSync(modulesRoot))
347
+ return taskDirs;
348
+ for (const moduleName of fs.readdirSync(modulesRoot)) {
349
+ if (moduleName.startsWith("_"))
350
+ continue;
351
+ const moduleTasksRoot = path.join(modulesRoot, moduleName, "tasks");
352
+ if (!fs.existsSync(moduleTasksRoot))
353
+ continue;
354
+ for (const entry of fs.readdirSync(moduleTasksRoot)) {
355
+ const taskDir = path.join(moduleTasksRoot, entry);
356
+ if (fs.existsSync(path.join(taskDir, "task_plan.md")))
357
+ taskDirs.push(taskDir);
358
+ }
359
+ }
360
+ return taskDirs;
361
+ }
362
+ function renderMigratedTaskIndex(taskId) {
363
+ const today = new Date().toISOString().slice(0, 10);
364
+ return `# ${taskId} - Task Package Index
365
+
366
+ Task Contract: harness-task/v1
367
+
368
+ ## Task Identity
369
+
370
+ | Field | Value |
371
+ | --- | --- |
372
+ | Task ID | \`${taskId}\` |
373
+ | Budget | \`simple\` |
374
+ | Walkthrough Path | \`walkthrough.md\` |
375
+
376
+ ## Task Audit Metadata
377
+
378
+ | Field | Value |
379
+ | --- | --- |
380
+ | Created By | historical-backfill |
381
+ | Created At | ${today} |
382
+ | Command Shape | harness migrate-structure --apply |
383
+ | Budget | simple |
384
+ | Template Source | structure-migration |
385
+ | Task Creator | migration |
386
+ | Task Creator Source | git-unavailable |
387
+ | Human Review Status | not-confirmed |
388
+ | Confirmation ID | n/a |
389
+ | Confirmed At | n/a |
390
+ | Reviewer | n/a |
391
+ | Reviewer Email | n/a |
392
+ | Confirm Text | n/a |
393
+ | Evidence Checked | n/a |
394
+ | Review Commit SHA | n/a |
395
+ | Audit Source | native-index |
396
+ | Audit Status | created |
397
+ | Exception Reason | n/a |
398
+ | Message | v2 structure migration backfill |
399
+ | Migration Status | migrated |
400
+ | Migrated From | docs |
401
+ | Legacy Extra Fields | {} |
402
+ | Migration Notes | task index created during hard-cutover structure migration |
403
+ `;
404
+ }
@@ -0,0 +1,198 @@
1
+ // @ts-nocheck
2
+ import path from "node:path";
3
+ import { readFileSafe, toPosix, walkFiles, isArchivedHarnessPath, } from "./core-shared.mjs";
4
+ import { firstColumn, splitMarkdownRow, } from "./markdown-utils.mjs";
5
+ export function validateSubagentAuthorization(target, { strict = true } = {}) {
6
+ const failures = [];
7
+ const warnings = [];
8
+ const report = (message) => {
9
+ if (strict)
10
+ failures.push(message);
11
+ else
12
+ warnings.push(`adoption-needed: ${message}`);
13
+ };
14
+ const strategyPaths = walkFiles(target.docsRoot)
15
+ .filter((file) => file.endsWith("execution_strategy.md"))
16
+ .filter((file) => !file.includes(`${path.sep}_task-template${path.sep}`))
17
+ .filter((file) => !file.includes(`${path.sep}_optional-structures${path.sep}`))
18
+ .filter((file) => !isArchivedHarnessPath(file));
19
+ for (const strategyPath of strategyPaths) {
20
+ const relative = toPosix(path.relative(target.projectRoot, strategyPath));
21
+ const content = readFileSafe(strategyPath);
22
+ const rows = subagentAuthorizationRows(content);
23
+ for (const row of rows.filter((candidate) => /worker/i.test(candidate.role))) {
24
+ if (!isWorkerAuthorizedStatus(row.status))
25
+ continue;
26
+ const missing = [];
27
+ for (const [label, value] of [
28
+ ["Authorized By", row.authorizedBy],
29
+ ["Authorized At", row.authorizedAt],
30
+ ["Scope", row.scope],
31
+ ["Worktree / Branch", row.worktreeBranch],
32
+ ]) {
33
+ if (!isConcreteAuthorizationValue(value))
34
+ missing.push(label);
35
+ }
36
+ if (missing.length > 0)
37
+ report(`${relative} worker subagent authorization is incomplete: ${missing.join(", ")}`);
38
+ }
39
+ const delegation = subagentDelegationRows(content).find((row) => /worker/i.test(row.question));
40
+ if (delegation && normalizeDecision(delegation.decision) === "ask-user") {
41
+ const userDecision = userAuthorizationRows(content).reverse().find((row) => /worker/i.test(row.gate) && isResolvedWorkerGateState(row.state));
42
+ if (!userDecision) {
43
+ report(`${relative} worker subagent ask-user decision is unresolved: missing User Authorization Decision`);
44
+ }
45
+ else {
46
+ const missing = missingUserDecisionFields(userDecision);
47
+ if (missing.length > 0)
48
+ report(`${relative} worker subagent authorization decision is incomplete: ${missing.join(", ")}`);
49
+ }
50
+ }
51
+ }
52
+ return { failures, warnings };
53
+ }
54
+ function subagentAuthorizationRows(content) {
55
+ const section = markdownSection(content, "Subagent Authorization");
56
+ if (!section)
57
+ return [];
58
+ const lines = section.split(/\r?\n/);
59
+ for (let index = 0; index < lines.length - 1; index += 1) {
60
+ if (!lines[index].trim().startsWith("|"))
61
+ continue;
62
+ const header = splitMarkdownRow(lines[index]);
63
+ const separator = splitMarkdownRow(lines[index + 1]);
64
+ if (!separator.every((cell) => /^:?-{3,}:?$/.test(cell)))
65
+ continue;
66
+ const roleIndex = firstColumn(header, ["Role"]);
67
+ const statusIndex = firstColumn(header, ["Status"]);
68
+ if (roleIndex < 0 || statusIndex < 0)
69
+ continue;
70
+ const indexes = {
71
+ role: roleIndex,
72
+ status: statusIndex,
73
+ authorizedBy: firstColumn(header, ["Authorized By"]),
74
+ authorizedAt: firstColumn(header, ["Authorized At"]),
75
+ scope: firstColumn(header, ["Scope"]),
76
+ worktreeBranch: firstColumn(header, ["Worktree / Branch", "Worktree", "Branch"]),
77
+ };
78
+ return lines
79
+ .slice(index + 2)
80
+ .filter((line) => line.trim().startsWith("|"))
81
+ .map(splitMarkdownRow)
82
+ .filter((row) => row.length === header.length)
83
+ .map((row) => Object.fromEntries(Object.entries(indexes).map(([key, column]) => [key, column >= 0 ? row[column] || "" : ""])));
84
+ }
85
+ return [];
86
+ }
87
+ function subagentDelegationRows(content) {
88
+ const section = markdownSection(content, "Subagent Delegation Decision");
89
+ if (!section)
90
+ return [];
91
+ return parseFirstTable(section, ["Question", "Decision"]).map((row) => ({
92
+ question: row.Question || "",
93
+ decision: row.Decision || "",
94
+ }));
95
+ }
96
+ function userAuthorizationRows(content) {
97
+ return parseMatchingTables(content, ["Gate", "State"]).map((row) => ({
98
+ gate: row.Gate || "",
99
+ state: row.State || "",
100
+ decidedBy: row["Decided By"] || "",
101
+ decidedAt: row["Decided At"] || "",
102
+ scope: row.Scope || "",
103
+ worktreeBranch: row["Worktree / Branch"] || row.Worktree || row.Branch || "",
104
+ }));
105
+ }
106
+ function parseMatchingTables(content, requiredColumns) {
107
+ const lines = String(content || "").split(/\r?\n/);
108
+ const rows = [];
109
+ for (let index = 0; index < lines.length - 1; index += 1) {
110
+ if (!lines[index].trim().startsWith("|"))
111
+ continue;
112
+ const header = splitMarkdownRow(lines[index]);
113
+ const separator = splitMarkdownRow(lines[index + 1]);
114
+ if (!separator.every((cell) => /^:?-{3,}:?$/.test(cell)))
115
+ continue;
116
+ if (requiredColumns.some((column) => firstColumn(header, [column]) < 0))
117
+ continue;
118
+ let rowIndex = index + 2;
119
+ while (rowIndex < lines.length && lines[rowIndex].trim().startsWith("|")) {
120
+ const row = splitMarkdownRow(lines[rowIndex]);
121
+ if (row.length === header.length)
122
+ rows.push(Object.fromEntries(header.map((column, columnIndex) => [column, row[columnIndex] || ""])));
123
+ rowIndex += 1;
124
+ }
125
+ }
126
+ return rows;
127
+ }
128
+ function parseFirstTable(content, requiredColumns) {
129
+ const lines = String(content || "").split(/\r?\n/);
130
+ for (let index = 0; index < lines.length - 1; index += 1) {
131
+ if (!lines[index].trim().startsWith("|"))
132
+ continue;
133
+ const header = splitMarkdownRow(lines[index]);
134
+ const separator = splitMarkdownRow(lines[index + 1]);
135
+ if (!separator.every((cell) => /^:?-{3,}:?$/.test(cell)))
136
+ continue;
137
+ if (requiredColumns.some((column) => firstColumn(header, [column]) < 0))
138
+ continue;
139
+ return lines
140
+ .slice(index + 2)
141
+ .filter((line) => line.trim().startsWith("|"))
142
+ .map(splitMarkdownRow)
143
+ .filter((row) => row.length === header.length)
144
+ .map((row) => Object.fromEntries(header.map((column, columnIndex) => [column, row[columnIndex] || ""])));
145
+ }
146
+ return [];
147
+ }
148
+ function markdownSection(content, heading) {
149
+ const lines = String(content || "").split(/\r?\n/);
150
+ const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
151
+ const start = lines.findIndex((line) => new RegExp(`^##\\s+${escaped}\\s*$`, "i").test(line.trim()));
152
+ if (start < 0)
153
+ return "";
154
+ const end = lines.findIndex((line, index) => index > start && /^##\s+/.test(line));
155
+ return lines.slice(start + 1, end < 0 ? undefined : end).join("\n");
156
+ }
157
+ function isConcreteAuthorizationValue(value) {
158
+ const raw = String(value || "").replace(/`/g, "").trim();
159
+ return Boolean(raw) && !/^\[.*\]$/.test(raw) && !/^(pending|n\/a|na|none|-|—|–|待授权|待定|无)$/i.test(raw);
160
+ }
161
+ function isWorkerAuthorizedStatus(value) {
162
+ const raw = String(value || "")
163
+ .replace(/`/g, "")
164
+ .trim()
165
+ .toLowerCase()
166
+ .replaceAll("_", "-")
167
+ .replace(/\s+/g, "-");
168
+ if (!raw || /^(not-authorized|unauthorized|pending|no|false|未授权|待授权)$/.test(raw))
169
+ return false;
170
+ return /(^|\b)(authorized|used|active|approved)(\b|$)|已授权|已使用/.test(raw);
171
+ }
172
+ function normalizeDecision(value) {
173
+ return String(value || "")
174
+ .replace(/`/g, "")
175
+ .trim()
176
+ .toLowerCase()
177
+ .replaceAll("_", "-")
178
+ .replace(/\s+/g, "-");
179
+ }
180
+ function isResolvedWorkerGateState(value) {
181
+ const raw = normalizeDecision(value);
182
+ return /^(authorized|approved|denied|rejected|not-needed|not-authorized|no|yes|无需|拒绝|已授权)$/.test(raw);
183
+ }
184
+ function missingUserDecisionFields(row) {
185
+ const state = normalizeDecision(row.state);
186
+ const required = state === "authorized" || state === "approved" || state === "yes" || state === "已授权"
187
+ ? [
188
+ ["Decided By", row.decidedBy],
189
+ ["Decided At", row.decidedAt],
190
+ ["Scope", row.scope],
191
+ ["Worktree / Branch", row.worktreeBranch],
192
+ ]
193
+ : [
194
+ ["Decided By", row.decidedBy],
195
+ ["Decided At", row.decidedAt],
196
+ ];
197
+ return required.filter(([, value]) => !isConcreteAuthorizationValue(value)).map(([label]) => label);
198
+ }