coding-agent-harness 1.0.2 → 1.0.5

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