coding-agent-harness 1.0.1 → 1.0.4

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 (262) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/CONTRIBUTING.md +98 -0
  3. package/README.en-US.md +14 -0
  4. package/README.md +230 -80
  5. package/README.zh-CN.md +290 -0
  6. package/SKILL.md +132 -198
  7. package/docs-release/README.md +80 -9
  8. package/docs-release/architecture/overview.md +298 -28
  9. package/docs-release/architecture/overview.zh-CN.md +292 -0
  10. package/docs-release/assets/dashboard-overview.png +0 -0
  11. package/docs-release/assets/harness-architecture.svg +163 -0
  12. package/docs-release/assets/harness-workflow.svg +64 -0
  13. package/docs-release/guides/agent-installation.en-US.md +237 -0
  14. package/docs-release/guides/agent-installation.md +149 -27
  15. package/docs-release/guides/contributing.md +100 -0
  16. package/docs-release/guides/contributing.zh-CN.md +99 -0
  17. package/docs-release/guides/document-audience-and-surfaces.en-US.md +113 -0
  18. package/docs-release/guides/document-audience-and-surfaces.md +113 -0
  19. package/docs-release/guides/full-legacy-migration-subagent-strategy.md +334 -0
  20. package/docs-release/guides/full-legacy-migration-subagent-strategy.zh-CN.md +334 -0
  21. package/docs-release/guides/legacy-migration-agent-prompt.md +373 -0
  22. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +350 -0
  23. package/docs-release/guides/migration-playbook.en-US.md +324 -0
  24. package/docs-release/guides/migration-playbook.md +328 -0
  25. package/docs-release/guides/parent-control-repository-pattern.en-US.md +254 -0
  26. package/docs-release/guides/parent-control-repository-pattern.md +254 -0
  27. package/docs-release/guides/preset-development.md +214 -0
  28. package/docs-release/guides/repository-operating-models.en-US.md +197 -0
  29. package/docs-release/guides/repository-operating-models.md +197 -0
  30. package/docs-release/guides/task-state-machine.en-US.md +207 -0
  31. package/docs-release/guides/task-state-machine.md +214 -0
  32. package/docs-release/intl/README.md +15 -0
  33. package/docs-release/intl/de-DE.md +18 -0
  34. package/docs-release/intl/en-US.md +18 -0
  35. package/docs-release/intl/es-ES.md +18 -0
  36. package/docs-release/intl/fr-FR.md +18 -0
  37. package/docs-release/intl/ja-JP.md +18 -0
  38. package/docs-release/intl/ko-KR.md +18 -0
  39. package/docs-release/intl/zh-CN.md +18 -0
  40. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/brief.md +13 -0
  41. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/findings.md +7 -0
  42. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/lesson_candidates.md +24 -0
  43. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +1 -1
  44. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/task_plan.md +4 -2
  45. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/{visual_roadmap.md → visual_map.md} +9 -1
  46. package/package.json +10 -3
  47. package/presets/legacy-migration/checks/preset-check.mjs +3 -0
  48. package/presets/legacy-migration/preset.yaml +134 -0
  49. package/presets/legacy-migration/scripts/plan-work-queue.mjs +4 -0
  50. package/presets/legacy-migration/scripts/scaffold-task-contracts.mjs +4 -0
  51. package/presets/legacy-migration/templates/execution_strategy.append.md +18 -0
  52. package/presets/legacy-migration/templates/findings.seed.md +17 -0
  53. package/presets/legacy-migration/templates/review.seed.md +12 -0
  54. package/presets/legacy-migration/templates/task_plan.append.md +9 -0
  55. package/presets/legacy-migration/templates/visual_map.append.md +12 -0
  56. package/presets/legacy-migration/workbench/dashboard-panels.yaml +2 -0
  57. package/presets/legacy-migration/workbench/migration-queue.schema.json +23 -0
  58. package/presets/lesson-sedimentation/preset.yaml +23 -0
  59. package/presets/lesson-sedimentation/templates/prompt.md +23 -0
  60. package/presets/module/preset.yaml +25 -0
  61. package/presets/module/templates/execution_strategy.append.md +8 -0
  62. package/presets/module/templates/task_plan.append.md +17 -0
  63. package/presets/standard-task/preset.yaml +31 -0
  64. package/presets/standard-task/templates/task_plan.append.md +7 -0
  65. package/references/adversarial-review-standard.md +2 -2
  66. package/references/agents-md-pattern.md +5 -5
  67. package/references/delivery-operating-model-standard.md +3 -3
  68. package/references/docs-directory-standard.md +53 -10
  69. package/references/external-source-intake-standard.md +75 -0
  70. package/references/harness-ledger.md +53 -94
  71. package/references/legacy-12-phase-bootstrap.md +41 -0
  72. package/references/lessons-governance.md +100 -88
  73. package/references/module-parallel-standard.md +14 -14
  74. package/references/planning-loop.md +51 -7
  75. package/references/project-onboarding-audit.md +10 -0
  76. package/references/pull-request-standard.md +118 -0
  77. package/references/repo-governance-standard.md +12 -1
  78. package/references/review-routing-standard.md +7 -1
  79. package/references/ssot-governance.md +67 -59
  80. package/references/taskr-gap-analysis.md +600 -0
  81. package/references/testing-standard.md +50 -0
  82. package/references/walkthrough-closeout.md +10 -9
  83. package/scripts/check-harness.mjs +111 -331
  84. package/scripts/commands/dashboard-command.mjs +67 -0
  85. package/scripts/commands/migration-command.mjs +96 -0
  86. package/scripts/commands/preset-command.mjs +73 -0
  87. package/scripts/commands/task-command.mjs +327 -0
  88. package/scripts/harness.mjs +106 -20
  89. package/scripts/lib/capability-registry.mjs +591 -0
  90. package/scripts/lib/check-module-parallel.mjs +237 -0
  91. package/scripts/lib/check-profiles.mjs +418 -0
  92. package/scripts/lib/check-task-contracts.mjs +47 -0
  93. package/scripts/lib/core-shared.mjs +196 -0
  94. package/scripts/lib/dashboard-data.mjs +412 -0
  95. package/scripts/lib/dashboard-workbench.mjs +257 -0
  96. package/scripts/lib/dashboard-writer.mjs +107 -4
  97. package/scripts/lib/git-status-summary.mjs +46 -0
  98. package/scripts/lib/governance-index-generator.mjs +174 -0
  99. package/scripts/lib/governance-sync.mjs +514 -0
  100. package/scripts/lib/governance-table-boundary.mjs +175 -0
  101. package/scripts/lib/harness-core.mjs +15 -1318
  102. package/scripts/lib/lesson-maintenance.mjs +152 -0
  103. package/scripts/lib/markdown-utils.mjs +158 -0
  104. package/scripts/lib/migration-planner.mjs +478 -0
  105. package/scripts/lib/migration-support.mjs +312 -0
  106. package/scripts/lib/preset-audit-contracts.mjs +37 -0
  107. package/scripts/lib/preset-engine.mjs +497 -0
  108. package/scripts/lib/preset-registry.mjs +627 -0
  109. package/scripts/lib/preset-resource-contracts.mjs +83 -0
  110. package/scripts/lib/review-confirm-git-gate.mjs +248 -0
  111. package/scripts/lib/status-dashboard-renderer.mjs +102 -0
  112. package/scripts/lib/subagent-authorization-audit.mjs +196 -0
  113. package/scripts/lib/task-completion-consistency.mjs +16 -0
  114. package/scripts/lib/task-index.mjs +93 -0
  115. package/scripts/lib/task-lesson-candidates.mjs +242 -0
  116. package/scripts/lib/task-lesson-sedimentation.mjs +326 -0
  117. package/scripts/lib/task-lifecycle/review-confirm.mjs +101 -0
  118. package/scripts/lib/task-lifecycle/review-gates.mjs +70 -0
  119. package/scripts/lib/task-lifecycle/text-utils.mjs +24 -0
  120. package/scripts/lib/task-lifecycle.mjs +649 -0
  121. package/scripts/lib/task-review-model.mjs +469 -0
  122. package/scripts/lib/task-scanner.mjs +576 -0
  123. package/scripts/lib/task-tombstone-commands.mjs +140 -0
  124. package/scripts/postinstall.mjs +14 -0
  125. package/skills/preset-creator/SKILL.md +179 -0
  126. package/skills/preset-creator/references/complex-task-skeleton/README.md +31 -0
  127. package/skills/preset-creator/references/complex-task-skeleton/artifacts/INDEX.md +12 -0
  128. package/skills/preset-creator/references/complex-task-skeleton/brief.md +32 -0
  129. package/skills/preset-creator/references/complex-task-skeleton/execution_strategy.md +71 -0
  130. package/skills/preset-creator/references/complex-task-skeleton/findings.md +24 -0
  131. package/skills/preset-creator/references/complex-task-skeleton/lesson_candidates.md +70 -0
  132. package/skills/preset-creator/references/complex-task-skeleton/long-running-task-contract.md +76 -0
  133. package/skills/preset-creator/references/complex-task-skeleton/progress.md +33 -0
  134. package/skills/preset-creator/references/complex-task-skeleton/references/INDEX.md +13 -0
  135. package/skills/preset-creator/references/complex-task-skeleton/review.md +107 -0
  136. package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +111 -0
  137. package/{templates/planning/visual_roadmap.md → skills/preset-creator/references/complex-task-skeleton/visual_map.md} +24 -2
  138. package/skills/preset-creator/references/preset-package-skeleton.md +296 -0
  139. package/templates/AGENTS.md.template +51 -36
  140. package/templates/architecture/Architecture-SSoT.md +21 -0
  141. package/templates/architecture/README.md +49 -0
  142. package/templates/architecture/critical-flows.md +22 -0
  143. package/templates/architecture/local-repo-context.md +20 -0
  144. package/templates/architecture/service-catalog.md +17 -0
  145. package/templates/architecture/services/service-template.md +31 -0
  146. package/templates/architecture/system-map.md +22 -0
  147. package/templates/dashboard/assets/app-src/00-state.js +42 -0
  148. package/templates/dashboard/assets/app-src/10-router.js +77 -0
  149. package/templates/dashboard/assets/app-src/20-overview.js +241 -0
  150. package/templates/dashboard/assets/app-src/30-tasks.js +409 -0
  151. package/templates/dashboard/assets/app-src/35-task-detail.js +246 -0
  152. package/templates/dashboard/assets/app-src/40-modules.js +58 -0
  153. package/templates/dashboard/assets/app-src/45-review.js +347 -0
  154. package/templates/dashboard/assets/app-src/50-migration.js +183 -0
  155. package/templates/dashboard/assets/app-src/60-shared.js +61 -0
  156. package/templates/dashboard/assets/app-src/90-bindings.js +524 -0
  157. package/templates/dashboard/assets/app.css +3107 -300
  158. package/templates/dashboard/assets/app.css.manifest.json +9 -0
  159. package/templates/dashboard/assets/app.js +2068 -306
  160. package/templates/dashboard/assets/app.manifest.json +12 -0
  161. package/templates/dashboard/assets/css-src/00-foundation.css +342 -0
  162. package/templates/dashboard/assets/css-src/10-panels-flow.css +236 -0
  163. package/templates/dashboard/assets/css-src/20-briefs-controls.css +398 -0
  164. package/templates/dashboard/assets/css-src/30-task-index.css +739 -0
  165. package/templates/dashboard/assets/css-src/35-review-workspace.css +507 -0
  166. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +427 -0
  167. package/templates/dashboard/assets/css-src/50-responsive-overrides.css +551 -0
  168. package/templates/dashboard/assets/i18n.js +531 -44
  169. package/templates/dashboard/assets/mermaid-renderer.js +58 -8
  170. package/templates/development/README.md +52 -0
  171. package/templates/development/codebase-map.md +11 -0
  172. package/templates/development/cross-repo-debugging.md +18 -0
  173. package/templates/development/external-context/service-template.md +33 -0
  174. package/templates/development/external-source-packs/README.md +24 -0
  175. package/templates/development/external-source-packs/digest-template.md +28 -0
  176. package/templates/development/local-setup.md +16 -0
  177. package/templates/development/stubs-and-mocks.md +11 -0
  178. package/templates/integrations/README.md +40 -0
  179. package/templates/integrations/api-contract.md +42 -0
  180. package/templates/integrations/event-contract.md +46 -0
  181. package/templates/integrations/third-party/vendor-template.md +42 -0
  182. package/templates/integrations/webhook-contract.md +41 -0
  183. package/templates/ledger/Harness-Ledger.md +13 -25
  184. package/templates/lessons/lesson-arch-process-change.md +1 -1
  185. package/templates/lessons/lesson-new-doc.md +1 -1
  186. package/templates/lessons/lesson-ref-change.md +1 -1
  187. package/templates/planning/brief.md +32 -0
  188. package/templates/planning/execution_strategy.md +31 -0
  189. package/templates/planning/lesson_candidates.md +70 -0
  190. package/templates/planning/long-running-task-contract.md +7 -0
  191. package/templates/planning/module_brief.md +25 -0
  192. package/templates/planning/module_session_prompt.md +6 -0
  193. package/templates/planning/optional/artifacts/INDEX.md +3 -3
  194. package/templates/planning/optional/references/INDEX.md +3 -3
  195. package/templates/planning/review.md +59 -0
  196. package/templates/planning/task_plan.md +40 -15
  197. package/templates/planning/visual_map.md +50 -0
  198. package/templates/reference/docs-library-standard.md +31 -0
  199. package/templates/reference/execution-workflow-standard.md +5 -2
  200. package/templates/reference/external-source-intake-standard.md +82 -0
  201. package/templates/reference/harness-ledger-standard.md +1 -0
  202. package/templates/reference/pull-request-standard.md +80 -0
  203. package/templates/reference/repo-governance-standard.md +8 -5
  204. package/templates/reference/review-routing-standard.md +6 -0
  205. package/templates/reference/walkthrough-standard.md +3 -1
  206. package/templates/verifier/verifier-output.md +1 -1
  207. package/templates/walkthrough/walkthrough-template.md +2 -2
  208. package/templates-zh-CN/AGENTS.md.template +73 -70
  209. package/templates-zh-CN/architecture/Architecture-SSoT.md +21 -0
  210. package/templates-zh-CN/architecture/README.md +51 -0
  211. package/templates-zh-CN/architecture/critical-flows.md +24 -0
  212. package/templates-zh-CN/architecture/local-repo-context.md +20 -0
  213. package/templates-zh-CN/architecture/service-catalog.md +17 -0
  214. package/templates-zh-CN/architecture/services/service-template.md +31 -0
  215. package/templates-zh-CN/architecture/system-map.md +22 -0
  216. package/templates-zh-CN/development/README.md +54 -0
  217. package/templates-zh-CN/development/codebase-map.md +11 -0
  218. package/templates-zh-CN/development/cross-repo-debugging.md +18 -0
  219. package/templates-zh-CN/development/external-context/service-template.md +33 -0
  220. package/templates-zh-CN/development/external-source-packs/README.md +24 -0
  221. package/templates-zh-CN/development/external-source-packs/digest-template.md +28 -0
  222. package/templates-zh-CN/development/local-setup.md +16 -0
  223. package/templates-zh-CN/development/stubs-and-mocks.md +11 -0
  224. package/templates-zh-CN/integrations/README.md +42 -0
  225. package/templates-zh-CN/integrations/api-contract.md +42 -0
  226. package/templates-zh-CN/integrations/event-contract.md +46 -0
  227. package/templates-zh-CN/integrations/third-party/vendor-template.md +42 -0
  228. package/templates-zh-CN/integrations/webhook-contract.md +41 -0
  229. package/templates-zh-CN/ledger/Harness-Ledger.md +17 -40
  230. package/templates-zh-CN/planning/brief.md +32 -0
  231. package/templates-zh-CN/planning/execution_strategy.md +30 -0
  232. package/templates-zh-CN/planning/lesson_candidates.md +70 -0
  233. package/templates-zh-CN/planning/long-running-task-contract.md +1 -1
  234. package/templates-zh-CN/planning/module_brief.md +25 -0
  235. package/templates-zh-CN/planning/module_plan.md +2 -2
  236. package/templates-zh-CN/planning/module_session_prompt.md +4 -3
  237. package/templates-zh-CN/planning/review.md +59 -1
  238. package/templates-zh-CN/planning/task_plan.md +37 -11
  239. package/templates-zh-CN/planning/{visual_roadmap.md → visual_map.md} +21 -2
  240. package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
  241. package/templates-zh-CN/reference/docs-library-standard.md +36 -1
  242. package/templates-zh-CN/reference/execution-workflow-standard.md +10 -2
  243. package/templates-zh-CN/reference/external-source-intake-standard.md +82 -0
  244. package/templates-zh-CN/reference/harness-ledger-standard.md +7 -4
  245. package/templates-zh-CN/reference/pull-request-standard.md +106 -0
  246. package/templates-zh-CN/reference/repo-governance-standard.md +4 -1
  247. package/templates-zh-CN/reference/review-routing-standard.md +8 -1
  248. package/templates-zh-CN/reference/walkthrough-standard.md +6 -5
  249. package/templates-zh-CN/walkthrough/Closeout-SSoT.md +2 -2
  250. package/templates-zh-CN/walkthrough/walkthrough-template.md +2 -2
  251. package/scripts/smoke-dashboard.mjs +0 -70
  252. package/scripts/test-harness.mjs +0 -483
  253. package/templates/ssot/Feature-SSoT.md +0 -43
  254. package/templates/ssot/Lessons-SSoT.md +0 -44
  255. package/templates-zh-CN/dashboard/assets/app.css +0 -399
  256. package/templates-zh-CN/dashboard/assets/app.js +0 -435
  257. package/templates-zh-CN/dashboard/assets/i18n.js +0 -47
  258. package/templates-zh-CN/dashboard/assets/markdown-reader.js +0 -116
  259. package/templates-zh-CN/dashboard/assets/mermaid-renderer.js +0 -59
  260. package/templates-zh-CN/dashboard/index.html +0 -18
  261. package/templates-zh-CN/ssot/Feature-SSoT.md +0 -49
  262. package/templates-zh-CN/ssot/Lessons-SSoT.md +0 -49
@@ -0,0 +1,497 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import crypto from "node:crypto";
4
+ import { spawnSync } from "node:child_process";
5
+ import { repoRoot, taskContractMarker, toPosix, visualMapFile } from "./core-shared.mjs";
6
+ import { verifyMigrationSession } from "./migration-planner.mjs";
7
+ import { buildPresetAudit, renderPresetTemplate } from "./preset-registry.mjs";
8
+
9
+ export function resolvePresetInputs(preset, { cliArgs = [], fromSession = "", targetInput = "" } = {}) {
10
+ const inputs = {};
11
+ let targetFromInput = "";
12
+ for (const [name, declaration] of Object.entries(preset.inputs || {})) {
13
+ const rawValue = inputValue(declaration, { cliArgs, fromSession });
14
+ if ((rawValue == null || rawValue === "") && declaration.required) {
15
+ throw new Error(`Missing required preset input ${declaration.flag || name}`);
16
+ }
17
+ if (declaration.type === "flag") {
18
+ inputs[name] = rawValue === true;
19
+ continue;
20
+ }
21
+ if (declaration.type === "json-file") {
22
+ if (rawValue == null || rawValue === "") {
23
+ inputs[name] = null;
24
+ continue;
25
+ }
26
+ const filePath = path.resolve(String(rawValue));
27
+ if (!fs.existsSync(filePath)) throw new Error(`Preset input file not found for ${declaration.flag || name}: ${rawValue}`);
28
+ let value;
29
+ try {
30
+ value = JSON.parse(fs.readFileSync(filePath, "utf8"));
31
+ } catch (error) {
32
+ throw new Error(`Invalid preset JSON input ${declaration.flag || name}: ${error.message}`);
33
+ }
34
+ if (declaration.validateOperation && value.operation !== declaration.validateOperation) {
35
+ throw new Error(`${preset.id} preset requires ${declaration.flag || name} operation ${declaration.validateOperation}`);
36
+ }
37
+ if (declaration.rejectPlanOnly && value.planOnly) throw new Error(`${preset.id} preset cannot use plan-only session evidence`);
38
+ if (declaration.requireTarget && (!value.target || !fs.existsSync(value.target))) throw new Error(`Preset input target missing: ${value.target || "(none)"}`);
39
+ if (declaration.targetFromSession) targetFromInput = value.target || targetFromInput;
40
+ inputs[name] = { ...value, sourcePath: filePath };
41
+ continue;
42
+ }
43
+ inputs[name] = rawValue == null || rawValue === "" ? declaration.default || "" : String(rawValue);
44
+ }
45
+ return {
46
+ inputs,
47
+ targetInput: targetFromInput || targetInput,
48
+ };
49
+ }
50
+
51
+ export function evaluateTemplateValues(preset, resolvedInputs, { taskId = "", taskTitle = "", moduleKey = "" } = {}) {
52
+ const computed = computedValues(preset, resolvedInputs);
53
+ const base = {
54
+ inputs: resolvedInputs,
55
+ computed,
56
+ preset: {
57
+ id: preset.id,
58
+ version: String(preset.version),
59
+ source: preset.source,
60
+ },
61
+ task: {
62
+ id: taskId,
63
+ title: taskTitle,
64
+ moduleKey,
65
+ kind: preset.task?.kind || "general",
66
+ },
67
+ };
68
+ const values = {
69
+ preset: preset.id,
70
+ presetVersion: String(preset.version),
71
+ kind: preset.task?.kind || "general",
72
+ ...computed,
73
+ };
74
+ for (const [name, declaration] of Object.entries(preset.templateValues || {})) {
75
+ if (Object.prototype.hasOwnProperty.call(declaration, "from")) {
76
+ values[name] = getPath(base, declaration.from);
77
+ } else if (Object.prototype.hasOwnProperty.call(declaration, "value")) {
78
+ values[name] = declaration.value;
79
+ } else if (Object.prototype.hasOwnProperty.call(declaration, "default")) {
80
+ values[name] = declaration.default;
81
+ }
82
+ }
83
+ return values;
84
+ }
85
+
86
+ export function buildPresetContext(preset, { target, taskDir, taskId, taskTitle, resolvedInputs, evaluatedValues }) {
87
+ const taskRelativeDir = toPosix(path.relative(target.projectRoot, taskDir));
88
+ const evidenceBundle = presetEvidenceBundle(preset, { target, taskDir, evaluatedValues });
89
+ const audit = buildPresetAudit(preset, {
90
+ taskId,
91
+ targetRoot: target.projectRoot,
92
+ entrypoint: "newTask",
93
+ });
94
+ const context = {
95
+ kind: evaluatedValues.kind || preset.task?.kind || "general",
96
+ preset: preset.id,
97
+ presetVersion: String(preset.version),
98
+ presetPackage: preset,
99
+ audit,
100
+ resolvedInputs,
101
+ taskId,
102
+ taskTitle,
103
+ taskRelativeDir,
104
+ values: {
105
+ ...evaluatedValues,
106
+ evidenceBundle,
107
+ },
108
+ migrationTargetLevel: evaluatedValues.migrationTargetLevel || "",
109
+ migrationAchievedLevel: evaluatedValues.migrationAchievedLevel || "",
110
+ evidenceBundle,
111
+ };
112
+ context.evidenceFiles = generateEvidenceFiles(preset, { target, taskDir, context });
113
+ const resources = generateResourceFiles(preset, { context });
114
+ context.resourceFiles = resources.files;
115
+ context.resourceIndexRows = resources.indexRows;
116
+ return context;
117
+ }
118
+
119
+ export function renderPresetTaskTemplate(destination, content, presetContext) {
120
+ if (!presetContext) return content;
121
+ let next = String(content);
122
+ if (destination === "task_plan.md" || destination === "task_plan") {
123
+ next = renderPresetMetadata(next, presetContext);
124
+ }
125
+ const templateKey = {
126
+ task_plan: "taskPlanAppend",
127
+ "task_plan.md": "taskPlanAppend",
128
+ execution_strategy: "executionStrategyAppend",
129
+ "execution_strategy.md": "executionStrategyAppend",
130
+ findings: "findingsSeed",
131
+ "findings.md": "findingsSeed",
132
+ review: "reviewSeed",
133
+ "review.md": "reviewSeed",
134
+ [visualMapFile]: "visualMapAppend",
135
+ }[destination];
136
+ const templatePath = presetContext.presetPackage?.newTaskTemplates?.[templateKey];
137
+ if (templatePath) {
138
+ next = `${next.trimEnd()}\n\n${renderPresetTemplate(presetContext.presetPackage, templatePath, presetContext.values).trimEnd()}\n`;
139
+ }
140
+ if (destination === "task_plan.md" || destination === "task_plan") {
141
+ next = appendPresetRequiredReads(next, presetContext);
142
+ }
143
+ return next;
144
+ }
145
+
146
+ export function renderPresetResourceIndex(content, kind, rows) {
147
+ if (!rows.length) return content;
148
+ const renderedRows = rows.map((row) => kind === "references"
149
+ ? `| ${markdownTableCell(row.id)} | ${markdownTableCell(row.type || "preset")} | ${markdownTableCell(row.path)} | ${markdownTableCell(row.summary)} | ${markdownTableCell(row.usedBy || "coordinator")} |`
150
+ : `| ${markdownTableCell(row.id)} | ${markdownTableCell(row.type || "preset")} | ${markdownTableCell(row.path)} | ${markdownTableCell(row.summary)} | ${markdownTableCell(row.producedBy || "preset")} |`);
151
+ const base = String(content || "").trim() ? String(content || "") : presetIndexSkeleton(kind);
152
+ const lines = base.trimEnd().split(/\r?\n/);
153
+ const separatorIndex = lines.findIndex((line) => /^\|\s*---/.test(line));
154
+ if (separatorIndex >= 0) {
155
+ lines.splice(separatorIndex + 1, 0, ...renderedRows);
156
+ return `${lines.join("\n")}\n`;
157
+ }
158
+ return `${String(content || "").trimEnd()}\n${renderedRows.join("\n")}\n`;
159
+ }
160
+
161
+ export function assertPresetWriteScope(preset, relativePath) {
162
+ const normalized = toPosix(path.normalize(relativePath));
163
+ if (normalized.startsWith("../") || path.isAbsolute(normalized)) {
164
+ throw new Error(`Preset write scope violation for ${relativePath}`);
165
+ }
166
+ if (!preset.writeScopes.some((scope) => matchesScope(scope.path, normalized))) {
167
+ throw new Error(`Preset write scope violation for ${normalized}`);
168
+ }
169
+ }
170
+
171
+ function inputValue(declaration, { cliArgs, fromSession }) {
172
+ if (declaration.flag === "--from-session" && fromSession) return fromSession;
173
+ if (!declaration.flag) return declaration.default;
174
+ const index = cliArgs.indexOf(declaration.flag);
175
+ if (index < 0) return declaration.default;
176
+ if (declaration.type === "flag") return true;
177
+ const value = cliArgs[index + 1];
178
+ if (!value || value.startsWith("--")) return "";
179
+ return value;
180
+ }
181
+
182
+ function computedValues(preset, inputs) {
183
+ const values = {};
184
+ const migrationSession = Object.values(inputs).find((value) => value && typeof value === "object" && value.operation === "migrate-run");
185
+ if (migrationSession) {
186
+ values.migrationTargetLevel = preset.task?.migrationTargetLevel || "migration-baseline";
187
+ values.migrationAchievedLevel = migrationSession.strictDeferred ? "migration-deferred" : migrationSession.result === "complete" ? "migration-full-cutover" : "migration-baseline";
188
+ values.strictDeferred = migrationSession.strictDeferred ? "yes" : "no";
189
+ values.fullCutoverClaimAllowed = values.migrationAchievedLevel === "migration-full-cutover" ? "yes" : "no";
190
+ values.warnings = migrationSession.plan?.summary?.warnings || 0;
191
+ values.taskActions = migrationSession.plan?.summary?.taskActions || 0;
192
+ values.legacyResiduals = migrationSession.plan?.summary?.legacyResiduals || 0;
193
+ values.generatedAt = migrationSession.generatedAt || "";
194
+ }
195
+ return values;
196
+ }
197
+
198
+ function presetEvidenceBundle(preset, { target, taskDir, evaluatedValues }) {
199
+ const bundleDir = String(preset.evidence?.bundleDir || "artifacts/preset").trim();
200
+ const stampSource = evaluatedValues.generatedAt || new Date().toISOString();
201
+ const stamp = String(stampSource).replace(/[^0-9A-Za-z-]+/g, "-").replace(/-+$/g, "");
202
+ const relativeTaskDir = toPosix(path.relative(target.projectRoot, taskDir));
203
+ return toPosix(path.join(relativeTaskDir, bundleDir, stamp || "generated"));
204
+ }
205
+
206
+ function generateEvidenceFiles(preset, { target, context }) {
207
+ const files = [];
208
+ const add = (relativePath, source, content) => {
209
+ assertPresetWriteScope(preset, relativePath);
210
+ files.push({ relativePath, source, content });
211
+ };
212
+ const evidenceFiles = preset.evidence?.files || {};
213
+ for (const [name, declaration] of Object.entries(evidenceFiles)) {
214
+ addEvidenceFile({ name, declaration, preset, target, context, add });
215
+ }
216
+ for (const name of preset.audit.evidenceFiles || []) {
217
+ if (files.some((file) => path.basename(file.relativePath) === name)) continue;
218
+ addAuditFile({ name, preset, context, add });
219
+ }
220
+ return files;
221
+ }
222
+
223
+ function generateResourceFiles(preset, { context }) {
224
+ const files = [];
225
+ const indexRows = { references: [], artifacts: [] };
226
+ const add = (relativePath, source, content) => {
227
+ assertPresetWriteScope(preset, relativePath);
228
+ files.push({ relativePath, source, content });
229
+ };
230
+ for (const resource of Object.values(preset.resources?.references || {})) {
231
+ const relativePath = toPosix(path.join(context.taskRelativeDir, resource.path));
232
+ add(relativePath, resource.source || resource.template, renderResourceContent(preset, resource, context));
233
+ indexRows.references.push(renderReferenceIndexRow(resource, relativePath, context.values));
234
+ }
235
+ for (const resource of Object.values(preset.resources?.artifacts || {})) {
236
+ const relativePath = toPosix(path.join(context.taskRelativeDir, resource.path));
237
+ add(relativePath, resource.source || resource.template, renderResourceContent(preset, resource, context));
238
+ indexRows.artifacts.push(renderArtifactIndexRow(resource, relativePath, context.values));
239
+ }
240
+ return { files, indexRows };
241
+ }
242
+
243
+ function renderResourceContent(preset, resource, context) {
244
+ if (resource.template) return renderPresetTemplate(preset, resource.template, context.values);
245
+ return fs.readFileSync(path.join(preset.directory, resource.source), "utf8");
246
+ }
247
+
248
+ function renderReferenceIndexRow(resource, relativePath, values) {
249
+ return {
250
+ id: resource.index.id,
251
+ type: renderInline(resource.index.type, values),
252
+ path: `TARGET:${relativePath}`,
253
+ summary: renderInline(resource.index.summary, values),
254
+ usedBy: renderInline(resource.index.usedBy, values),
255
+ };
256
+ }
257
+
258
+ function renderArtifactIndexRow(resource, relativePath, values) {
259
+ return {
260
+ id: resource.index.id,
261
+ type: renderInline(resource.index.type, values),
262
+ path: `TARGET:${relativePath}`,
263
+ summary: renderInline(resource.index.summary, values),
264
+ producedBy: renderInline(resource.index.producedBy || "preset", values),
265
+ };
266
+ }
267
+
268
+ function appendPresetRequiredReads(content, context) {
269
+ const requiredReads = context.presetPackage?.context?.requiredReads || [];
270
+ if (!requiredReads.length) return content;
271
+ const rowsById = new Map((context.resourceIndexRows?.references || []).map((row) => [row.id, row]));
272
+ const rows = requiredReads.map((id) => {
273
+ const row = rowsById.get(id);
274
+ return `| ${markdownTableCell(id)} | ${markdownTableCell(row?.path || "references/INDEX.md")} | ${markdownTableCell(row?.summary || "Preset-provided reference")} |`;
275
+ });
276
+ return `${content.trimEnd()}\n\n## Preset Required Reads\n\nOpen \`references/INDEX.md\`, then read these preset-provided references before implementation.\n\n| Reference | Path | Why |\n| --- | --- | --- |\n${rows.join("\n")}\n`;
277
+ }
278
+
279
+ function addEvidenceFile({ name, declaration, preset, target, context, add }) {
280
+ const fileName = declaration.path || `${name}.txt`;
281
+ const relativePath = toPosix(path.join(context.evidenceBundle, fileName));
282
+ const type = declaration.type || "text";
283
+ if (type === "input-json") {
284
+ add(relativePath, declaration.value || "input-json", `${JSON.stringify(getPath({ inputs: context.resolvedInputs }, declaration.value || ""), null, 2)}\n`);
285
+ } else if (type === "json") {
286
+ add(relativePath, declaration.value || "json", `${JSON.stringify(getPath({ inputs: context.resolvedInputs, values: context.values }, declaration.value || ""), null, 2)}\n`);
287
+ } else if (type === "text") {
288
+ add(relativePath, declaration.value || "text", `${String(getPath({ inputs: context.resolvedInputs, values: context.values }, declaration.value || "") || "").trim()}\n`);
289
+ } else if (type === "migration-verify") {
290
+ const session = migrationSession(context);
291
+ add(relativePath, "migrate-verify", `${JSON.stringify(verifyMigrationSession(session.sourcePath, { fullCutover: false }), null, 2)}\n`);
292
+ } else if (type === "migration-ledger") {
293
+ const session = migrationSession(context);
294
+ const verifyResult = verifyMigrationSession(session.sourcePath, { fullCutover: false });
295
+ add(relativePath, "preset-ledger", `${JSON.stringify(migrationLedger({ session, preset, verifyResult }), null, 2)}\n`);
296
+ } else if (type === "preset-manifest") {
297
+ add(relativePath, "preset.yaml", `${JSON.stringify(presetManifestSnapshot(preset), null, 2)}\n`);
298
+ } else if (type === "preset-audit") {
299
+ add(relativePath, "preset-audit", `${JSON.stringify(context.audit, null, 2)}\n`);
300
+ } else if (type === "write-scope") {
301
+ add(relativePath, "preset.yaml", `${JSON.stringify({ preset: preset.id, scopes: preset.writeScopes, entrypointScopes: context.audit.writeScopes }, null, 2)}\n`);
302
+ } else if (type === "dashboard-hash") {
303
+ add(relativePath, "dashboard", `${dashboardHash(migrationSession(context).dashboard?.indexPath || "")}\n`);
304
+ } else if (type === "target-git-status") {
305
+ add(relativePath, "session.git.after", `${JSON.stringify(migrationSession(context).git?.after || {}, null, 2)}\n`);
306
+ } else if (type === "target-commit") {
307
+ add(relativePath, "git", `${targetCommit(target.projectRoot)}\n`);
308
+ } else if (type === "harness-version") {
309
+ add(relativePath, "package.json", `${packageVersion()}\n`);
310
+ } else if (type === "generated-at") {
311
+ add(relativePath, "generated", `${new Date().toISOString()}\n`);
312
+ } else {
313
+ throw new Error(`Unsupported preset evidence type: ${type}`);
314
+ }
315
+ }
316
+
317
+ function addAuditFile({ name, preset, context, add }) {
318
+ const relativePath = toPosix(path.join(context.evidenceBundle, name));
319
+ if (name === "preset-manifest.json") {
320
+ add(relativePath, "preset.yaml", `${JSON.stringify(presetManifestSnapshot(preset), null, 2)}\n`);
321
+ } else if (name === "preset-audit.json") {
322
+ add(relativePath, "preset-audit", `${JSON.stringify(context.audit, null, 2)}\n`);
323
+ } else if (name === "write-scope.json") {
324
+ add(relativePath, "preset.yaml", `${JSON.stringify({ preset: preset.id, scopes: preset.writeScopes, entrypointScopes: context.audit.writeScopes }, null, 2)}\n`);
325
+ } else {
326
+ add(relativePath, "preset-audit", `${JSON.stringify({ preset: preset.id, generatedAt: new Date().toISOString() }, null, 2)}\n`);
327
+ }
328
+ }
329
+
330
+ function renderPresetMetadata(content, context) {
331
+ const metadata = [
332
+ context.kind && context.kind !== "general" ? `Task Kind: ${context.kind}` : "",
333
+ `Task Preset: ${context.preset}`,
334
+ `Preset Version: ${context.presetVersion}`,
335
+ context.migrationTargetLevel ? `Migration Target Level: ${context.migrationTargetLevel}` : "",
336
+ context.migrationAchievedLevel ? `Migration Achieved Level: ${context.migrationAchievedLevel}` : "",
337
+ context.evidenceBundle ? `Evidence Bundle: ${context.evidenceBundle}` : "",
338
+ ...declaredMetadataLines(context),
339
+ ].filter(Boolean).join("\n");
340
+ let next = String(content).replace(new RegExp(`^(${escapeRegExp(taskContractMarker)}\\s*)$`, "im"), `$1\n${metadata}`);
341
+ const outcome = context.presetPackage.task?.defaultOutcome || "";
342
+ if (outcome) {
343
+ next = next
344
+ .replace("[State the outcome this task must deliver in one sentence.]", outcome)
345
+ .replace("[用一句话说明本任务完成后应达到的状态。]", outcome);
346
+ }
347
+ return next;
348
+ }
349
+
350
+ function declaredMetadataLines(context) {
351
+ const base = {
352
+ inputs: context.resolvedInputs || {},
353
+ values: context.values || {},
354
+ preset: {
355
+ id: context.preset,
356
+ version: context.presetVersion,
357
+ },
358
+ task: {
359
+ id: context.taskId,
360
+ title: context.taskTitle,
361
+ kind: context.kind,
362
+ },
363
+ };
364
+ return Object.entries(context.presetPackage?.metadata || {}).map(([name, declaration]) => {
365
+ const label = declaration.label || name;
366
+ let value = "";
367
+ if (Object.prototype.hasOwnProperty.call(declaration, "from")) {
368
+ value = getPath(base, declaration.from);
369
+ } else if (Object.prototype.hasOwnProperty.call(declaration, "value")) {
370
+ value = declaration.value;
371
+ } else if (Object.prototype.hasOwnProperty.call(declaration, "default")) {
372
+ value = declaration.default;
373
+ }
374
+ return value == null || value === "" ? "" : `${label}: ${value}`;
375
+ });
376
+ }
377
+
378
+ function migrationSession(context) {
379
+ const session = Object.values(context.resolvedInputs || {}).find((value) => value && typeof value === "object" && value.operation === "migrate-run");
380
+ if (!session) throw new Error("Preset evidence requires migrate-run session input");
381
+ return session;
382
+ }
383
+
384
+ function migrationLedger({ session, preset, verifyResult }) {
385
+ const summary = session.plan?.summary || {};
386
+ return {
387
+ schemaVersion: "legacy-migration-ledger/v2",
388
+ preset: preset.id,
389
+ presetVersion: preset.version,
390
+ staticDashboardRole: "evidence-snapshot",
391
+ workbenchRole: "human-confirmation-control-plane",
392
+ phases: [
393
+ { id: "baseline", state: verifyResult.status === "pass" ? "done" : "blocked", evidence: ["session.json", "migrate-plan.json", "normal-check.json", "strict-check.json", "migrate-verify.json"] },
394
+ {
395
+ id: "mechanical-scaffold",
396
+ state: "planned",
397
+ automationAllowed: true,
398
+ outputPolicy: "May add missing task contract files and placeholders, but must not mark semantic reconstruction complete.",
399
+ counters: {
400
+ taskActions: Number(summary.taskActions || 0),
401
+ reviewSchemaGaps: Number(summary.reviewSchemaGaps || 0),
402
+ legacyReferenceGaps: Number(summary.legacyReferenceGaps || 0),
403
+ },
404
+ },
405
+ {
406
+ id: "semantic-reconstruction",
407
+ state: "planned",
408
+ automationAllowed: false,
409
+ evidenceLedgerRequired: true,
410
+ requiredEvidenceSources: ["task_plan.md", "progress.md", "review.md", "walkthrough", "Harness-Ledger", "git"],
411
+ completionRule: "Each task needs explicit evidenceSources and reviewState before semantic completion.",
412
+ },
413
+ { id: "cutover-review", state: "planned", humanConfirmationRequired: true, workbenchQueueRequired: true, staticDashboardRole: "evidence-snapshot" },
414
+ ],
415
+ counters: {
416
+ warnings: Number(summary.warnings || 0),
417
+ taskActions: Number(summary.taskActions || 0),
418
+ reviewSchemaGaps: Number(summary.reviewSchemaGaps || 0),
419
+ legacyReferenceGaps: Number(summary.legacyReferenceGaps || 0),
420
+ legacyResiduals: Number(summary.legacyResiduals || 0),
421
+ fullCutoverEligible: summary.fullCutoverEligible === true,
422
+ },
423
+ queue: [],
424
+ };
425
+ }
426
+
427
+ function presetManifestSnapshot(preset) {
428
+ return {
429
+ id: preset.id,
430
+ version: preset.version,
431
+ manifestPath: preset.manifestRelativePath,
432
+ manifestSha256: preset.manifestSha256,
433
+ compatibleBudgets: preset.compatibleBudgets,
434
+ entrypoints: preset.entrypoints,
435
+ audit: preset.audit,
436
+ writeScopes: preset.writeScopes,
437
+ inputs: preset.inputs,
438
+ templateValues: preset.templateValues,
439
+ metadata: preset.metadata,
440
+ resources: preset.resources,
441
+ context: preset.context,
442
+ };
443
+ }
444
+
445
+ function matchesScope(scope, relativePath) {
446
+ const normalizedScope = toPosix(String(scope || ""));
447
+ if (normalizedScope.endsWith("/**")) {
448
+ const prefix = normalizedScope.slice(0, -3);
449
+ return relativePath === prefix || relativePath.startsWith(`${prefix}/`);
450
+ }
451
+ return relativePath === normalizedScope;
452
+ }
453
+
454
+ function dashboardHash(indexPath) {
455
+ if (!indexPath || !fs.existsSync(indexPath)) return "missing";
456
+ return `sha256:${crypto.createHash("sha256").update(fs.readFileSync(indexPath)).digest("hex")}`;
457
+ }
458
+
459
+ function targetCommit(projectRoot) {
460
+ const result = spawnSync("git", ["-C", projectRoot, "rev-parse", "HEAD"], { encoding: "utf8" });
461
+ return result.status === 0 ? result.stdout.trim() : "n/a";
462
+ }
463
+
464
+ function packageVersion() {
465
+ try {
466
+ return JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8")).version || "unknown";
467
+ } catch {
468
+ return "unknown";
469
+ }
470
+ }
471
+
472
+ function getPath(values, key) {
473
+ if (!key) return values;
474
+ return String(key).split(".").reduce((cursor, part) => (cursor && Object.prototype.hasOwnProperty.call(cursor, part) ? cursor[part] : undefined), values);
475
+ }
476
+
477
+ function renderInline(value, values) {
478
+ return String(value || "").replace(/\{\{\s*([A-Za-z0-9_.-]+)\s*\}\}/g, (_match, key) => {
479
+ const result = getPath(values, key);
480
+ return result == null ? "" : String(result);
481
+ });
482
+ }
483
+
484
+ function markdownTableCell(value) {
485
+ return String(value || "").replace(/\r?\n/g, " ").replaceAll("|", "&#124;").trim();
486
+ }
487
+
488
+ function presetIndexSkeleton(kind) {
489
+ if (kind === "references") {
490
+ return "# References Index\n\n| ID | Type | Path | Summary | Used By |\n| --- | --- | --- | --- | --- |\n";
491
+ }
492
+ return "# Artifacts Index\n\n| ID | Type | Path | Summary | Produced By |\n| --- | --- | --- | --- | --- |\n";
493
+ }
494
+
495
+ function escapeRegExp(value) {
496
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
497
+ }