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
@@ -0,0 +1,420 @@
1
+ import path from "node:path";
2
+ const surfaceOrder = [
3
+ "ts-runtime",
4
+ "type-island",
5
+ "runtime-build",
6
+ "built-test-runner",
7
+ "dashboard-data",
8
+ "template-schema",
9
+ "dashboard-ui",
10
+ "docs-release",
11
+ "preset-package",
12
+ "package-boundary",
13
+ "migration-legacy",
14
+ "multi-repo-contract",
15
+ "ci-pr-evidence",
16
+ "root-contributor-docs",
17
+ "skill-reference-distribution",
18
+ "fixture-golden",
19
+ "gui-submodule",
20
+ "private-local-only",
21
+ "unknown",
22
+ ];
23
+ const runtimeLayerOrder = ["ts-source", "runtime-emit", "built-test", "package-runtime", "installed-target", "not-runtime"];
24
+ const gateOrder = [
25
+ "typecheck",
26
+ "typecheck-guards",
27
+ "import-graph",
28
+ "dist-drift",
29
+ "runtime-emit-contract",
30
+ "dist-build-pipeline",
31
+ "dist-observation",
32
+ "temp-project-cli-smoke",
33
+ "status-json-contract",
34
+ "built-tests",
35
+ "target-project-check",
36
+ "dashboard-smoke",
37
+ "source-package-check",
38
+ "pack-dry-run",
39
+ "docs-leak-link-check",
40
+ "compatibility-policy",
41
+ "maintainer-review",
42
+ "dashboard-data-golden",
43
+ "preset-contract",
44
+ "migration-fixture",
45
+ "workflow-lint",
46
+ "fixture-manifest",
47
+ "gui-check",
48
+ "command-consistency",
49
+ ];
50
+ const compatibilityPolicies = ["legacy-support", "migration-required", "hard-cutover"];
51
+ const surfaceGateMap = {
52
+ "ts-runtime": ["typecheck", "typecheck-guards", "import-graph", "dist-drift"],
53
+ "type-island": ["typecheck", "typecheck-guards", "import-graph"],
54
+ "runtime-build": ["runtime-emit-contract", "dist-build-pipeline", "dist-observation", "dist-drift"],
55
+ "built-test-runner": ["built-tests", "typecheck"],
56
+ "dashboard-data": ["dashboard-data-golden", "status-json-contract", "dashboard-smoke"],
57
+ "template-schema": ["target-project-check", "compatibility-policy"],
58
+ "dashboard-ui": ["dashboard-smoke"],
59
+ "docs-release": ["docs-leak-link-check", "source-package-check"],
60
+ "preset-package": ["preset-contract", "source-package-check", "compatibility-policy"],
61
+ "package-boundary": ["source-package-check", "pack-dry-run", "compatibility-policy"],
62
+ "migration-legacy": ["migration-fixture", "compatibility-policy"],
63
+ "multi-repo-contract": ["target-project-check", "maintainer-review"],
64
+ "ci-pr-evidence": ["workflow-lint", "maintainer-review"],
65
+ "root-contributor-docs": ["docs-leak-link-check", "source-package-check", "command-consistency"],
66
+ "skill-reference-distribution": ["source-package-check", "pack-dry-run"],
67
+ "fixture-golden": ["fixture-manifest", "target-project-check"],
68
+ "gui-submodule": ["gui-check"],
69
+ "private-local-only": ["source-package-check", "maintainer-review"],
70
+ "unknown": ["maintainer-review"],
71
+ };
72
+ const gateReasonMap = {
73
+ typecheck: "TypeScript source changes must satisfy the enforced strict type gate.",
74
+ "typecheck-guards": "Type boundary and no-ts-nocheck guards protect shared type islands and implementation coverage.",
75
+ "import-graph": "Runtime and type island changes can introduce unresolved imports, cycles, or invalid type imports.",
76
+ "dist-drift": "Runtime source or emit changes can make committed/package dist drift from TypeScript source.",
77
+ "runtime-emit-contract": "Runtime build surfaces must prove TypeScript emits package-safe JavaScript.",
78
+ "dist-build-pipeline": "Runtime build changes must prove the dist build pipeline still emits required package files.",
79
+ "dist-observation": "Runtime/package changes need dist observation evidence across source, package, and command surfaces.",
80
+ "temp-project-cli-smoke": "CLI/runtime changes need target-project smoke evidence for the installed command path.",
81
+ "status-json-contract": "Status, scanner, and CLI JSON surfaces need machine-readable contract evidence.",
82
+ "built-tests": "Built tests prove the compiled test runner still exercises the package runtime shape.",
83
+ "target-project-check": "Target project fixtures prove templates, examples, and contracts remain usable.",
84
+ "dashboard-smoke": "Dashboard surfaces need smoke coverage for data and UI consumption.",
85
+ "source-package-check": "Package-facing changes must not publish private or local-only source state.",
86
+ "pack-dry-run": "Package boundary changes need file-list and npm packaging evidence.",
87
+ "docs-leak-link-check": "Public documentation changes need leak and link/path validation.",
88
+ "compatibility-policy": "Contract-changing surfaces must declare legacy-support, migration-required, or hard-cutover.",
89
+ "maintainer-review": "Unknown or high-cost surfaces require maintainer review before contributor verify can pass.",
90
+ "dashboard-data-golden": "Dashboard data contract changes need golden or schema evidence.",
91
+ "preset-contract": "Preset changes need install, resource, and safety contract evidence.",
92
+ "migration-fixture": "Migration and legacy changes need fixture evidence for supported or rejected formats.",
93
+ "workflow-lint": "CI and PR evidence changes need workflow and path-filter validation.",
94
+ "fixture-manifest": "Fixture and golden changes need drift policy evidence.",
95
+ "gui-check": "GUI submodule changes need path-filtered GUI validation or pointer evidence.",
96
+ "command-consistency": "Contributor docs must stay consistent with actual package commands and profiles.",
97
+ };
98
+ export function classifyImpact(changedFiles) {
99
+ const classifiedFiles = changedFiles.map(classifyChangedFile);
100
+ const surfaces = orderedUnique(classifiedFiles.flatMap((file) => file.surfaces), surfaceOrder);
101
+ const runtimeLayers = orderedUnique(classifiedFiles.flatMap((file) => file.runtimeLayers), runtimeLayerOrder);
102
+ const requiredGateIds = orderedUnique(classifiedFiles.flatMap((file) => file.requiredGates), gateOrder);
103
+ const compatibilityRequirements = classifiedFiles.flatMap((file) => buildCompatibilityRequirements(file));
104
+ return {
105
+ changedFiles: classifiedFiles,
106
+ surfaces,
107
+ runtimeLayers,
108
+ requiredGates: requiredGateIds.map((gate) => ({
109
+ id: gate,
110
+ blocking: gate === "maintainer-review" ? "maintainer-review" : "required",
111
+ reason: gateReasonMap[gate],
112
+ surfaces: surfacesForGate(surfaces, gate),
113
+ })),
114
+ unknownSurfaces: classifiedFiles.flatMap((file) => buildUnknownSurface(file)),
115
+ compatibilityPolicyRequired: compatibilityRequirements.length > 0,
116
+ compatibilityPolicyRequirements: compatibilityRequirements,
117
+ };
118
+ }
119
+ function classifyChangedFile(record) {
120
+ const normalizedPath = normalizeChangedPath(record.path);
121
+ const normalizedOldPath = record.oldPath ? normalizeChangedPath(record.oldPath) : undefined;
122
+ const pathClassifications = [
123
+ classifyPath(normalizedPath, record),
124
+ ...(normalizedOldPath && normalizedOldPath !== normalizedPath ? [classifyPath(normalizedOldPath, record)] : []),
125
+ ];
126
+ const surfaces = orderedUnique(pathClassifications.flatMap((classification) => classification.surfaces), surfaceOrder);
127
+ const runtimeLayers = orderedUnique(pathClassifications.flatMap((classification) => classification.runtimeLayers), runtimeLayerOrder);
128
+ const requiredGates = orderedUnique([...surfaces.flatMap((surface) => surfaceGateMap[surface]), ...pathClassifications.flatMap((classification) => classification.requiredGates)], gateOrder);
129
+ return {
130
+ path: normalizedPath,
131
+ status: record.status,
132
+ ...(normalizedOldPath ? { oldPath: normalizedOldPath } : {}),
133
+ surfaces,
134
+ runtimeLayers,
135
+ requiredGates,
136
+ compatibilityPolicyRequired: pathClassifications.some((classification) => classification.compatibilityPolicyRequired),
137
+ };
138
+ }
139
+ function classifyPath(relativePath, record) {
140
+ const surfaces = [];
141
+ const runtimeLayers = [];
142
+ const requiredGates = [];
143
+ let compatibilityPolicyRequired = false;
144
+ const addSurface = (surface) => {
145
+ surfaces.push(surface);
146
+ };
147
+ const addLayer = (layer) => {
148
+ runtimeLayers.push(layer);
149
+ };
150
+ const requirePolicy = () => {
151
+ compatibilityPolicyRequired = true;
152
+ };
153
+ const requireGate = (gate) => {
154
+ requiredGates.push(gate);
155
+ };
156
+ if (!isRepositoryRelativePath(relativePath)) {
157
+ addSurface("unknown");
158
+ addLayer("not-runtime");
159
+ return { surfaces: orderedUnique(surfaces, surfaceOrder), runtimeLayers: orderedUnique(runtimeLayers, runtimeLayerOrder), compatibilityPolicyRequired, requiredGates: orderedUnique(requiredGates, gateOrder) };
160
+ }
161
+ if (isPrivateLocalOnlyPath(relativePath)) {
162
+ addSurface("private-local-only");
163
+ addLayer("not-runtime");
164
+ return { surfaces: orderedUnique(surfaces, surfaceOrder), runtimeLayers: orderedUnique(runtimeLayers, runtimeLayerOrder), compatibilityPolicyRequired, requiredGates: orderedUnique(requiredGates, gateOrder) };
165
+ }
166
+ if (record.isSubmodule || relativePath === "harness-gui" || relativePath.startsWith("harness-gui/")) {
167
+ addSurface("gui-submodule");
168
+ addLayer("not-runtime");
169
+ }
170
+ if (relativePath.startsWith(".github/")) {
171
+ addSurface("ci-pr-evidence");
172
+ addLayer("not-runtime");
173
+ if (relativePath === ".github/pull_request_template.md")
174
+ requirePolicy();
175
+ }
176
+ if (isTypeIslandPath(relativePath)) {
177
+ addSurface("type-island");
178
+ addLayer("ts-source");
179
+ }
180
+ if (isCliContractPath(relativePath)) {
181
+ requireGate("temp-project-cli-smoke");
182
+ requireGate("status-json-contract");
183
+ requirePolicy();
184
+ }
185
+ if (isTsRuntimePath(relativePath)) {
186
+ addSurface("ts-runtime");
187
+ addLayer("ts-source");
188
+ }
189
+ if (isRuntimeBuildPath(relativePath)) {
190
+ addSurface("runtime-build");
191
+ addLayer("runtime-emit");
192
+ if (relativePath === "tsconfig.json")
193
+ addLayer("ts-source");
194
+ if (relativePath.startsWith("dist/")) {
195
+ addSurface("package-boundary");
196
+ addLayer("package-runtime");
197
+ requirePolicy();
198
+ }
199
+ }
200
+ if (isBuiltTestRunnerPath(relativePath)) {
201
+ addSurface("built-test-runner");
202
+ addLayer("built-test");
203
+ if (/^tests\/.*\.mts$/.test(relativePath))
204
+ addLayer("ts-source");
205
+ }
206
+ if (isDashboardDataPath(relativePath)) {
207
+ addSurface("dashboard-data");
208
+ requirePolicy();
209
+ if (relativePath.startsWith("scripts/"))
210
+ addLayer("ts-source");
211
+ }
212
+ if (relativePath.startsWith("templates/dashboard/assets/")) {
213
+ addSurface("dashboard-ui");
214
+ addLayer("installed-target");
215
+ if (relativePath.includes("manifest"))
216
+ requirePolicy();
217
+ }
218
+ else if (relativePath.startsWith("templates/") || relativePath.startsWith("templates-zh-CN/")) {
219
+ addSurface("template-schema");
220
+ addLayer("installed-target");
221
+ requirePolicy();
222
+ }
223
+ if (relativePath.startsWith("docs-release/")) {
224
+ addSurface("docs-release");
225
+ addLayer("not-runtime");
226
+ if (relativePath.includes("parent-control-repository-pattern"))
227
+ addSurface("multi-repo-contract");
228
+ }
229
+ if (isRootContributorDoc(relativePath)) {
230
+ addSurface("root-contributor-docs");
231
+ addLayer("not-runtime");
232
+ }
233
+ if (isPresetPackagePath(relativePath)) {
234
+ addSurface("preset-package");
235
+ addLayer(relativePath.startsWith("scripts/") ? "ts-source" : "installed-target");
236
+ requirePolicy();
237
+ }
238
+ if (isPackageBoundaryPath(relativePath)) {
239
+ addSurface("package-boundary");
240
+ addLayer("package-runtime");
241
+ requirePolicy();
242
+ }
243
+ if (isMigrationPath(relativePath)) {
244
+ addSurface("migration-legacy");
245
+ if (relativePath.startsWith("scripts/"))
246
+ addLayer("ts-source");
247
+ requirePolicy();
248
+ }
249
+ if (isSkillReferenceDistributionPath(relativePath)) {
250
+ addSurface("skill-reference-distribution");
251
+ addLayer("installed-target");
252
+ }
253
+ if (isFixtureGoldenPath(relativePath)) {
254
+ addSurface("fixture-golden");
255
+ addLayer(relativePath.startsWith("examples/") ? "installed-target" : "not-runtime");
256
+ }
257
+ if (surfaces.length === 0) {
258
+ addSurface("unknown");
259
+ addLayer("not-runtime");
260
+ }
261
+ return {
262
+ surfaces: orderedUnique(surfaces, surfaceOrder),
263
+ runtimeLayers: orderedUnique(runtimeLayers, runtimeLayerOrder),
264
+ compatibilityPolicyRequired,
265
+ requiredGates: orderedUnique(requiredGates, gateOrder),
266
+ };
267
+ }
268
+ function buildCompatibilityRequirements(file) {
269
+ if (!file.compatibilityPolicyRequired)
270
+ return [];
271
+ const policySurfaces = file.surfaces.filter((surface) => surfaceRequiresCompatibilityPolicy(surface));
272
+ const surfaces = policySurfaces.length > 0 ? policySurfaces : file.surfaces;
273
+ return surfaces.map((surface) => ({
274
+ path: file.path,
275
+ surface,
276
+ allowedPolicies: compatibilityPolicies,
277
+ reason: compatibilityReason(surface),
278
+ }));
279
+ }
280
+ function buildUnknownSurface(file) {
281
+ if (!file.surfaces.includes("unknown"))
282
+ return [];
283
+ return [
284
+ {
285
+ path: file.path,
286
+ status: file.status,
287
+ reason: "No public impact surface matched this repository-relative path.",
288
+ },
289
+ ];
290
+ }
291
+ function surfacesForGate(surfaces, gate) {
292
+ if ((gate === "temp-project-cli-smoke" || gate === "status-json-contract") && surfaces.includes("ts-runtime")) {
293
+ return ["ts-runtime", ...surfaces.filter((surface) => surface !== "ts-runtime" && surfaceGateMap[surface].includes(gate))];
294
+ }
295
+ return surfaces.filter((surface) => surfaceGateMap[surface].includes(gate));
296
+ }
297
+ function surfaceRequiresCompatibilityPolicy(surface) {
298
+ return [
299
+ "ts-runtime",
300
+ "dashboard-data",
301
+ "template-schema",
302
+ "preset-package",
303
+ "package-boundary",
304
+ "migration-legacy",
305
+ "ci-pr-evidence",
306
+ ].includes(surface);
307
+ }
308
+ function compatibilityReason(surface) {
309
+ switch (surface) {
310
+ case "ts-runtime":
311
+ return "CLI output, exit code, or machine-readable report contract may change.";
312
+ case "dashboard-data":
313
+ return "Dashboard data JSON or public manifest contract may change.";
314
+ case "template-schema":
315
+ return "Task schema, template frontmatter, or target-project layout may change.";
316
+ case "preset-package":
317
+ return "Preset registry, installed resources, or action contract may change.";
318
+ case "package-boundary":
319
+ return "Package file list or installed runtime layout may change.";
320
+ case "migration-legacy":
321
+ return "Migration or legacy adoption behavior may change.";
322
+ case "ci-pr-evidence":
323
+ return "PR evidence or workflow contract may change.";
324
+ default:
325
+ return "Contract-changing surface requires explicit compatibility policy.";
326
+ }
327
+ }
328
+ function normalizeChangedPath(inputPath) {
329
+ const normalized = inputPath.trim().replaceAll("\\", "/").replace(/\/+/g, "/");
330
+ if (normalized === ".")
331
+ return "";
332
+ return normalized.startsWith("./") ? normalized.slice(2) : normalized;
333
+ }
334
+ function isRepositoryRelativePath(relativePath) {
335
+ return relativePath.length > 0 && !relativePath.startsWith("/") && !relativePath.startsWith("../") && !path.isAbsolute(relativePath);
336
+ }
337
+ function isPrivateLocalOnlyPath(relativePath) {
338
+ return (relativePath === "AGENTS.md" ||
339
+ relativePath === "CLAUDE.md" ||
340
+ relativePath === "docs" ||
341
+ relativePath.startsWith("docs/") ||
342
+ relativePath === ".harness-private" ||
343
+ relativePath.startsWith(".harness-private/"));
344
+ }
345
+ function isTypeIslandPath(relativePath) {
346
+ return relativePath.startsWith("scripts/lib/types/") || /^tests\/helpers\/.*types.*\.(mts|ts)$/.test(relativePath);
347
+ }
348
+ function isTsRuntimePath(relativePath) {
349
+ return relativePath.startsWith("scripts/") && relativePath.endsWith(".mts");
350
+ }
351
+ function isCliContractPath(relativePath) {
352
+ return (relativePath === "scripts/harness.mts" ||
353
+ relativePath.startsWith("scripts/commands/") ||
354
+ relativePath === "scripts/lib/types/impact.ts" ||
355
+ relativePath === "scripts/lib/types/task-lifecycle.ts" ||
356
+ relativePath === "scripts/lib/check-profiles.mts" ||
357
+ relativePath === "scripts/lib/status-builder.mts" ||
358
+ relativePath === "scripts/lib/task-index.mts" ||
359
+ relativePath === "scripts/lib/task-lifecycle.mts");
360
+ }
361
+ function isRuntimeBuildPath(relativePath) {
362
+ return (relativePath === "scripts/build-dist.mts" ||
363
+ relativePath === "scripts/check-runtime-emit.mts" ||
364
+ relativePath === "scripts/check-dist-observation.mts" ||
365
+ /^tsconfig(?:\.[A-Za-z-]+)?\.json$/.test(relativePath) ||
366
+ relativePath.startsWith("dist/"));
367
+ }
368
+ function isBuiltTestRunnerPath(relativePath) {
369
+ return relativePath === "scripts/run-built-tests.mts" || relativePath === "tests/run-all.mts" || relativePath === "tsconfig.tests.json" || /^tests\/.*\.mts$/.test(relativePath);
370
+ }
371
+ function isDashboardDataPath(relativePath) {
372
+ return (relativePath === "scripts/lib/dashboard-data.mts" ||
373
+ relativePath === "scripts/lib/check-profiles.mts" ||
374
+ relativePath === "scripts/lib/status-builder.mts" ||
375
+ relativePath === "scripts/lib/status-dashboard-renderer.mts" ||
376
+ relativePath === "scripts/lib/task-scanner.mts" ||
377
+ relativePath === "scripts/lib/task-review-model.mts" ||
378
+ relativePath === "scripts/lib/task-index.mts" ||
379
+ relativePath === "scripts/lib/types/check-profiles.ts" ||
380
+ relativePath === "scripts/lib/types/task-scanner.ts" ||
381
+ relativePath === "scripts/lib/types/impact.ts" ||
382
+ relativePath.startsWith("tests/golden/dashboard/"));
383
+ }
384
+ function isRootContributorDoc(relativePath) {
385
+ return /^README(\.[A-Za-z-]+)?\.md$/.test(relativePath) || relativePath === "CONTRIBUTING.md" || relativePath === "CHANGELOG.md";
386
+ }
387
+ function isPackageBoundaryPath(relativePath) {
388
+ return (relativePath === "package.json" ||
389
+ relativePath === "package-lock.json" ||
390
+ relativePath === "postinstall.mjs" ||
391
+ relativePath === "run-dist.mjs" ||
392
+ relativePath === "LICENSE" ||
393
+ relativePath === "LICENSE-EXCEPTION.md");
394
+ }
395
+ function isPresetPackagePath(relativePath) {
396
+ return (relativePath.startsWith("presets/") ||
397
+ relativePath === "scripts/commands/preset-command.mts" ||
398
+ relativePath === "scripts/lib/core-shared.mts" ||
399
+ relativePath === "scripts/lib/types/preset.ts" ||
400
+ /^scripts\/lib\/preset-.*\.mts$/.test(relativePath));
401
+ }
402
+ function isMigrationPath(relativePath) {
403
+ return relativePath.includes("migration") || relativePath.includes("legacy") || relativePath.includes("hard-cutover");
404
+ }
405
+ function isSkillReferenceDistributionPath(relativePath) {
406
+ return relativePath === "SKILL.md" || relativePath.startsWith("skills/") || relativePath.startsWith("references/");
407
+ }
408
+ function isFixtureGoldenPath(relativePath) {
409
+ return (relativePath.startsWith("examples/") ||
410
+ relativePath.startsWith("fixtures/") ||
411
+ relativePath.startsWith("tests/fixtures/") ||
412
+ relativePath.startsWith("tests/golden/"));
413
+ }
414
+ function orderedUnique(values, order) {
415
+ const present = new Set(values);
416
+ const ordered = order.filter((value) => present.has(value));
417
+ const orderSet = new Set(order);
418
+ const extra = values.filter((value, index) => !orderSet.has(value) && values.indexOf(value) === index);
419
+ return [...ordered, ...extra];
420
+ }
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import { datePrefix, lessonCandidatesFile, normalizeTarget, readFileSafe, slug, toPosix, } from "./core-shared.mjs";
@@ -64,7 +63,7 @@ export function promoteLessonCandidate(targetInput, taskId, candidateId, { dryRu
64
63
  try {
65
64
  fs.mkdirSync(path.dirname(detailPath), { recursive: true });
66
65
  if (!fs.existsSync(detailPath))
67
- fs.writeFileSync(detailPath, renderLessonDetail({ lessonId, candidate: row, task, detailRelative }));
66
+ fs.writeFileSync(detailPath, renderLessonDetail({ lessonId, candidate: row, task }));
68
67
  fs.writeFileSync(candidatePath, markCandidatePromoted(candidateContent, row.id, lessonId));
69
68
  const commit = commitGovernanceSync(governanceContext, [
70
69
  detailRelative,
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  import { sanitizeText, slug } from "./core-shared.mjs";
3
2
  export function markdownTableRows(content) {
4
3
  return content
@@ -89,6 +88,56 @@ export function tableAfterHeading(content, headerPattern) {
89
88
  }
90
89
  return { header, rows: body };
91
90
  }
91
+ export function stripFencedCodeBlocks(content) {
92
+ const lines = String(content || "").split(/\r?\n/);
93
+ const result = [];
94
+ let fence = "";
95
+ for (const line of lines) {
96
+ const match = line.match(/^\s{0,3}(```|~~~)/);
97
+ if (match) {
98
+ if (!fence)
99
+ fence = match[1];
100
+ else if (match[1] === fence)
101
+ fence = "";
102
+ continue;
103
+ }
104
+ if (!fence)
105
+ result.push(line);
106
+ }
107
+ return result.join("\n");
108
+ }
109
+ export function removeHeadingSectionOutsideFences(content, headingPattern) {
110
+ const lines = String(content || "").split(/\r?\n/);
111
+ let fence = "";
112
+ let start = -1;
113
+ let end = lines.length;
114
+ for (let index = 0; index < lines.length; index += 1) {
115
+ const line = lines[index];
116
+ const fenceMatch = line.match(/^\s{0,3}(```|~~~)/);
117
+ if (fenceMatch) {
118
+ if (!fence)
119
+ fence = fenceMatch[1];
120
+ else if (fenceMatch[1] === fence)
121
+ fence = "";
122
+ continue;
123
+ }
124
+ if (fence)
125
+ continue;
126
+ if (start < 0 && headingPattern.test(line.trim())) {
127
+ start = index;
128
+ if (start > 0 && !lines[start - 1].trim())
129
+ start -= 1;
130
+ continue;
131
+ }
132
+ if (start >= 0 && index > start && /^##\s+/.test(line.trim())) {
133
+ end = index;
134
+ break;
135
+ }
136
+ }
137
+ if (start < 0)
138
+ return String(content || "");
139
+ return [...lines.slice(0, start), ...lines.slice(end)].join("\n");
140
+ }
92
141
  export function getColumn(header, name) {
93
142
  return header.findIndex((cell) => cell.toLowerCase() === name.toLowerCase());
94
143
  }
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  import fs from "node:fs";
3
2
  import os from "node:os";
4
3
  import path from "node:path";
@@ -22,7 +21,8 @@ export function buildMigrationPlan(targetInput, { limit = 20 } = {}) {
22
21
  const legacyActions = [];
23
22
  const legacyResiduals = [];
24
23
  const warningGroups = new Map();
25
- const tasksByShortId = new Map(status.tasks.map((task) => [task.shortId, task]));
24
+ const tasks = status.tasks;
25
+ const tasksByShortId = new Map(tasks.map((task) => [task.shortId, task]));
26
26
  function addTaskAction(taskId, actionPath, fileName, actionText) {
27
27
  const existing = taskActionsByTask.get(taskId) || {
28
28
  taskId,
@@ -85,7 +85,7 @@ export function buildMigrationPlan(targetInput, { limit = 20 } = {}) {
85
85
  const legacyVisualOnlyTasks = [];
86
86
  const unknownClassificationTasks = [];
87
87
  const weakBriefTasks = [];
88
- for (const task of status.tasks) {
88
+ for (const task of tasks) {
89
89
  if (task.visualMapStatus === "legacy-only") {
90
90
  legacyVisualOnlyTasks.push({
91
91
  taskId: task.shortId,
@@ -133,7 +133,7 @@ export function buildMigrationPlan(targetInput, { limit = 20 } = {}) {
133
133
  const recommendedCapabilities = recommendedMigrationCapabilities(status, target, registry);
134
134
  const missingExecutionStrategy = taskActions.filter((action) => action.files.includes("execution_strategy.md")).length;
135
135
  const missingVisualMap = taskActions.filter((action) => action.files.includes(visualMapFile)).length;
136
- const cutoverCounters = taskCutoverCounters(status.tasks);
136
+ const cutoverCounters = taskCutoverCounters(tasks);
137
137
  const visualMapActions = taskActions.filter((action) => action.files.includes(visualMapFile)).length;
138
138
  const fullCutoverEligible = status.checkState.status === "pass" &&
139
139
  taskActions.length === 0 &&
@@ -177,7 +177,7 @@ export function buildMigrationPlan(targetInput, { limit = 20 } = {}) {
177
177
  fullCutoverEligible,
178
178
  },
179
179
  recommendedCapabilities,
180
- phases: migrationPhases({ locale, recommendedCapabilities }),
180
+ phases: migrationPhases({ locale, recommendedCapabilities: recommendedCapabilities.map((capability) => ({ ...capability, state: "recommended" })) }),
181
181
  taskActions: taskActions.slice(0, limit),
182
182
  visualMapActions: taskActions.filter((action) => action.files.includes(visualMapFile)).slice(0, limit),
183
183
  legacyVisualOnlyTasks: legacyVisualOnlyTasks.slice(0, limit),
@@ -229,6 +229,7 @@ export function runMigration(targetInput, options = {}) {
229
229
  const strictStatus = buildStatus(targetInput, { strict: true, strictLegacy: true, allowLegacyTarget: true });
230
230
  const finalPlan = buildMigrationPlan(targetInput, { limit: options.limit || 50 });
231
231
  const afterGit = inspectGitStatus(target.projectRoot);
232
+ const finalTarget = normalizeTarget(targetInput);
232
233
  const strictDeferred = strictDeferredFromStatus(strictStatus);
233
234
  const result = options.planOnly
234
235
  ? "plan-only"
@@ -251,7 +252,7 @@ export function runMigration(targetInput, options = {}) {
251
252
  source: options.locale ? "explicit" : localeProbe.mixedLanguageDetected ? "assumed-from-probe" : "probe",
252
253
  probe: localeProbe,
253
254
  },
254
- capabilities: readCapabilityRegistry(target).capabilities,
255
+ capabilities: readCapabilityRegistry(finalTarget).capabilities,
255
256
  baseline: {
256
257
  statusPath: path.join(sessionDir, "baseline-status.json"),
257
258
  migratePlanPath: path.join(sessionDir, "migrate-plan.json"),
@@ -289,7 +290,7 @@ export function runMigration(targetInput, options = {}) {
289
290
  return { ...session, sessionPath, reportPath };
290
291
  }
291
292
  export function verifyMigrationSession(sessionPathInput, { fullCutover = false } = {}) {
292
- const sessionPath = path.resolve(sessionPathInput || "");
293
+ const sessionPath = path.resolve(String(sessionPathInput || ""));
293
294
  if (!sessionPath || !fs.existsSync(sessionPath)) {
294
295
  return { operation: "migrate-verify", status: "fail", failures: [`session file not found: ${sessionPathInput}`], warnings: [] };
295
296
  }
@@ -298,7 +299,7 @@ export function verifyMigrationSession(sessionPathInput, { fullCutover = false }
298
299
  let readError = null;
299
300
  const session = readJsonSafe(sessionPath, null, { onError: (error) => { readError = error; } });
300
301
  if (!session)
301
- return { operation: "migrate-verify", status: "fail", failures: [`invalid session json: ${readError?.message || "unknown parse error"}`], warnings };
302
+ return { operation: "migrate-verify", status: "fail", failures: [`invalid session json: ${errorMessage(readError)}`], warnings };
302
303
  if (session.operation !== "migrate-run")
303
304
  failures.push("session operation is not migrate-run");
304
305
  if (session.schemaVersion !== 1 && session.version !== 1)
@@ -395,19 +396,23 @@ export function verifyMigrationSession(sessionPathInput, { fullCutover = false }
395
396
  else {
396
397
  try {
397
398
  const dashboardBundle = JSON.parse(dataMatch[1]);
399
+ const dashboardStatus = asRecord(dashboardBundle.status);
400
+ const dashboardProject = asRecord(dashboardStatus.project);
401
+ const dashboardCheckState = dashboardStatus.checkState;
402
+ const dashboardAdoption = asRecord(dashboardBundle.adoption);
398
403
  const expectedProjectName = session.target ? path.basename(session.target) : "";
399
- if (dashboardBundle.status?.schemaVersion !== 2)
404
+ if (dashboardStatus.schemaVersion !== 2)
400
405
  failures.push("dashboard bundle missing status schemaVersion 2");
401
- if (expectedProjectName && dashboardBundle.status?.project?.name !== expectedProjectName) {
402
- failures.push(`dashboard bundle project ${dashboardBundle.status?.project?.name || "(none)"} does not match target ${expectedProjectName}`);
406
+ if (expectedProjectName && dashboardProject.name !== expectedProjectName) {
407
+ failures.push(`dashboard bundle project ${dashboardProject.name || "(none)"} does not match target ${expectedProjectName}`);
403
408
  }
404
- if (!dashboardBundle.status?.checkState)
409
+ if (!dashboardCheckState)
405
410
  failures.push("dashboard bundle missing checkState");
406
- if (!Array.isArray(dashboardBundle.adoption?.warnings))
411
+ if (!Array.isArray(dashboardAdoption.warnings))
407
412
  failures.push("dashboard bundle missing adoption warnings array");
408
413
  }
409
414
  catch (error) {
410
- failures.push(`dashboard-data.js contains invalid dashboard JSON: ${error.message}`);
415
+ failures.push(`dashboard-data.js contains invalid dashboard JSON: ${errorMessage(error)}`);
411
416
  }
412
417
  }
413
418
  }
@@ -422,8 +427,9 @@ export function verifyMigrationSession(sessionPathInput, { fullCutover = false }
422
427
  warnings.push(`strict cutover deferred: ${deferred.failureCount} failures`);
423
428
  }
424
429
  }
425
- if (fullCutover)
426
- validateFullCutoverSession(session, failures);
430
+ if (fullCutover) {
431
+ validateFullCutoverSession({ ...session, dashboard: session.dashboard || undefined }, failures);
432
+ }
427
433
  return {
428
434
  operation: "migrate-verify",
429
435
  status: failures.length ? "fail" : "pass",
@@ -437,3 +443,12 @@ export function verifyMigrationSession(sessionPathInput, { fullCutover = false }
437
443
  warnings,
438
444
  };
439
445
  }
446
+ function isRecord(value) {
447
+ return typeof value === "object" && value !== null && !Array.isArray(value);
448
+ }
449
+ function asRecord(value) {
450
+ return isRecord(value) ? value : {};
451
+ }
452
+ function errorMessage(error) {
453
+ return error instanceof Error ? error.message : "unknown parse error";
454
+ }
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  // Migration support scans dynamic target state until migration session domain types are modeled.
3
2
  import fs from "node:fs";
4
3
  import os from "node:os";
@@ -68,11 +67,12 @@ export function ensureSessionDir(projectName, requestedDir = "") {
68
67
  return base;
69
68
  }
70
69
  export function statusCheckSummary(status) {
70
+ const legacy = status.checkState.legacy;
71
71
  return {
72
72
  status: status.checkState.status,
73
73
  failures: status.checkState.failures,
74
74
  warnings: status.checkState.warnings,
75
- legacyStatus: status.checkState.legacy?.status || "skipped",
75
+ legacyStatus: legacy?.status || "skipped",
76
76
  failureDetails: status.checkState.details.failures,
77
77
  warningDetails: status.checkState.details.warnings,
78
78
  };
@@ -162,8 +162,9 @@ export function validateFullCutoverSession(session, failures) {
162
162
  }
163
163
  if (summary.fullCutoverEligible !== true)
164
164
  failures.push("full cutover requires summary.fullCutoverEligible=true");
165
- if ((summary.recommendedCapabilities || []).length) {
166
- failures.push(`full cutover has recommended capabilities: ${summary.recommendedCapabilities.join(", ")}`);
165
+ const recommendedCapabilities = summary.recommendedCapabilities || [];
166
+ if (recommendedCapabilities.length) {
167
+ failures.push(`full cutover has recommended capabilities: ${recommendedCapabilities.join(", ")}`);
167
168
  }
168
169
  if (!session.target || !fs.existsSync(session.target))
169
170
  return;