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,572 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ const context = JSON.parse(fs.readFileSync(process.env.HARNESS_PRESET_CONTEXT, "utf8"));
7
+ const harnessCore = await import(context.runtime?.coreModule || new URL("../../../dist/harness-core.mjs", import.meta.url).href);
8
+ const {
9
+ normalizeTarget,
10
+ collectTasks: collectCoreTasks,
11
+ archiveBlockReason: sharedArchiveBlockReason,
12
+ } = harnessCore;
13
+
14
+ const release = safeRelease(context.inputs.release);
15
+ if (!release) {
16
+ console.error("release-closeout requires inputs.release");
17
+ process.exit(2);
18
+ }
19
+
20
+ const paths = context.paths || {};
21
+ if (!paths.tasksRoot || !paths.governanceRoot) {
22
+ console.error("release-closeout requires structure-aware context.paths from the preset runner");
23
+ process.exit(2);
24
+ }
25
+ const releaseRoot = path.join(context.outputRoot, "release");
26
+ fs.mkdirSync(releaseRoot, { recursive: true });
27
+
28
+ const target = normalizeTarget(context.targetRoot);
29
+ const allTasks = collectCoreTasks(target)
30
+ .map(releaseTaskFromCore)
31
+ .filter((task) => task.id !== context.task.id && task.shortId !== context.task.id && task.preset !== "release-closeout")
32
+ .sort((a, b) => a.id.localeCompare(b.id));
33
+ let selection;
34
+ try {
35
+ selection = selectTasks(allTasks, context.inputs, release);
36
+ } catch (error) {
37
+ console.error(error.message);
38
+ process.exit(2);
39
+ }
40
+
41
+ const tasks = selection.tasks.sort((a, b) => a.id.localeCompare(b.id));
42
+ if (tasks.length === 0) {
43
+ console.error("release-closeout selector matched no tasks");
44
+ process.exit(2);
45
+ }
46
+
47
+ const eligibility = tasks.map((task) => ({ task, reason: archiveBlockReason(task) }));
48
+ const eligibleArchive = eligibility.filter((item) => !item.reason).map((item) => item.task);
49
+ const notEligibleArchive = eligibility.filter((item) => item.reason);
50
+ const governanceRoot = paths.governanceRoot;
51
+ const destinationRoot = `${governanceRoot}/releases/${release}`;
52
+ const releasePackageRef = `${destinationRoot}/INDEX.md`;
53
+
54
+ const aggregate = {
55
+ schemaVersion: "release-closeout-aggregate/v1",
56
+ release,
57
+ generatedAt: new Date().toISOString(),
58
+ selector: selection.selector,
59
+ summary: {
60
+ totalTasks: tasks.length,
61
+ doneTasks: tasks.filter((task) => task.state === "done").length,
62
+ blockedTasks: tasks.filter((task) => task.state === "blocked").length,
63
+ archiveEligibleTasks: eligibleArchive.length,
64
+ },
65
+ matched: tasks.map(taskSummary),
66
+ excluded: selection.excluded.map((task) => ({ ...taskSummary(task), reason: "not-selected" })),
67
+ notEligible: [
68
+ ...selection.missing.map((id) => ({ id, reason: "task-not-found" })),
69
+ ...notEligibleArchive.map(({ task, reason }) => ({ ...taskSummary(task), reason })),
70
+ ],
71
+ tasks: tasks.map((task) => ({
72
+ id: task.id,
73
+ title: task.title,
74
+ state: task.state,
75
+ preset: task.preset || "none",
76
+ deletionState: task.deletionState,
77
+ archiveEligible: eligibleArchive.some((candidate) => candidate.id === task.id),
78
+ archiveBlockedReason: archiveBlockReason(task),
79
+ })),
80
+ };
81
+
82
+ const index = `# Release Closeout Package: ${release}
83
+
84
+ Generated by \`presets/release-closeout\` through the generic preset runner.
85
+
86
+ | Document | Purpose |
87
+ | --- | --- |
88
+ | \`task-aggregate.json\` | Machine-readable task inventory for this release closeout. |
89
+ | \`task-archive-plan.md\` | Preset-owned archive eligibility plan. |
90
+ | \`public-summary.md\` | Redacted public-facing summary. |
91
+ | \`public-redaction-report.json\` | Redaction evidence for public materialization. |
92
+
93
+ ## Summary
94
+
95
+ - Total tasks: ${aggregate.summary.totalTasks}
96
+ - Done tasks: ${aggregate.summary.doneTasks}
97
+ - Blocked tasks: ${aggregate.summary.blockedTasks}
98
+ - Archive eligible tasks: ${aggregate.summary.archiveEligibleTasks}
99
+ `;
100
+
101
+ const archivePlan = `# Release ${release} Task Archive Plan
102
+
103
+ This plan is generated by the release-closeout preset. It does not archive tasks by itself.
104
+ Run the commands from the target project root after reviewing this package.
105
+
106
+ ## Eligible Archive Commands
107
+
108
+ ${eligibleArchive.length ? eligibleArchive.map((task) => `- ${archiveCommand(task, release, releasePackageRef)}`).join("\n") : "- none"}
109
+
110
+ ## Eligible Tasks
111
+
112
+ ${eligibleArchive.length ? eligibleArchive.map((task) => `- ${task.id} - ${task.title}`).join("\n") : "- none"}
113
+
114
+ ## Not Eligible
115
+
116
+ ${aggregate.notEligible.length ? aggregate.notEligible.map((task) => `- ${task.id} - ${task.reason}`).join("\n") : "- none"}
117
+ `;
118
+
119
+ const publicSummaryRaw = `# Release ${release} Public Summary
120
+
121
+ This release closeout aggregates ${aggregate.summary.totalTasks} tasks, including ${aggregate.summary.doneTasks} done tasks and ${aggregate.summary.blockedTasks} blocked tasks.
122
+
123
+ Recent evidence snippets:
124
+
125
+ ${tasks.slice(0, 20).map((task) => `- ${task.id}: ${task.evidenceSnippet || "no evidence snippet"}`).join("\n")}
126
+ `;
127
+ const publicSummary = redactPublic(publicSummaryRaw);
128
+ const redactionReport = {
129
+ schemaVersion: "public-redaction-report/v1",
130
+ status: leaksLocalPath(publicSummary) ? "fail" : "pass",
131
+ rules: ["local-absolute-paths", "file-urls"],
132
+ checkedFiles: ["public-summary.md"],
133
+ findings: leaksLocalPath(publicSummary) ? ["public-summary contains local absolute path"] : [],
134
+ generatedAt: new Date().toISOString(),
135
+ };
136
+ if (redactionReport.status !== "pass") {
137
+ console.error("public redaction failed");
138
+ process.exit(3);
139
+ }
140
+
141
+ write("INDEX.md", index);
142
+ write("task-archive-plan.md", archivePlan);
143
+ write("task-aggregate.json", `${JSON.stringify(aggregate, null, 2)}\n`);
144
+ write("public-summary.md", publicSummary);
145
+ write("public-redaction-report.json", `${JSON.stringify(redactionReport, null, 2)}\n`);
146
+
147
+ fs.writeFileSync(context.materializationManifestPath, `${JSON.stringify({
148
+ schemaVersion: "preset-materialization/v1",
149
+ status: "ok",
150
+ publicRedactionReport: { source: "release/public-redaction-report.json" },
151
+ writes: [
152
+ { source: "release/INDEX.md", destination: `${destinationRoot}/INDEX.md`, type: "text" },
153
+ { source: "release/task-archive-plan.md", destination: `${destinationRoot}/task-archive-plan.md`, type: "text" },
154
+ { source: "release/task-aggregate.json", destination: `${destinationRoot}/task-aggregate.json`, type: "json" },
155
+ { source: "release/public-summary.md", destination: `${destinationRoot}/public-summary.md`, type: "text", visibility: "public" },
156
+ { source: "release/public-redaction-report.json", destination: `${destinationRoot}/public-redaction-report.json`, type: "json" },
157
+ ],
158
+ }, null, 2)}\n`);
159
+
160
+ function write(name, content) {
161
+ fs.writeFileSync(path.join(releaseRoot, name), content.endsWith("\n") ? content : `${content}\n`);
162
+ }
163
+
164
+ function selectTasks(tasks, inputs, release) {
165
+ const hasTaskList = Boolean(inputs.taskList);
166
+ const hasTaskQuery = Boolean(String(inputs.taskQuery || "").trim());
167
+ if (!hasTaskList && !hasTaskQuery) throw new Error("release-closeout requires --task-list or --task-query");
168
+ if (hasTaskList && hasTaskQuery) throw new Error("release-closeout accepts only one selector: --task-list or --task-query");
169
+
170
+ if (hasTaskList) {
171
+ const taskList = inputs.taskList;
172
+ if (!taskList || typeof taskList !== "object" || Array.isArray(taskList)) {
173
+ throw new Error("--task-list must be a JSON object");
174
+ }
175
+ if (taskList.schemaVersion !== "release-closeout-task-list/v1") {
176
+ throw new Error("--task-list schemaVersion must be release-closeout-task-list/v1");
177
+ }
178
+ if (taskList.release && taskList.release !== release) {
179
+ throw new Error(`--task-list release ${taskList.release} does not match --release ${release}`);
180
+ }
181
+ const requestedTaskIds = Array.isArray(taskList.taskIds) ? taskList.taskIds.map(normalizeTaskListId).filter(Boolean) : [];
182
+ if (requestedTaskIds.length === 0) throw new Error("--task-list requires non-empty taskIds");
183
+ const selected = resolveTaskReferences(requestedTaskIds, tasks);
184
+ const selectedIds = new Set(selected.map((task) => task.id));
185
+ return {
186
+ selector: {
187
+ type: "task-list",
188
+ schemaVersion: taskList.schemaVersion,
189
+ release,
190
+ sourcePath: taskList.sourcePath || "",
191
+ taskIds: requestedTaskIds,
192
+ resolvedTaskIds: selected.map((task) => task.id),
193
+ },
194
+ tasks: selected,
195
+ excluded: tasks.filter((task) => !selectedIds.has(task.id)),
196
+ missing: [],
197
+ };
198
+ }
199
+
200
+ const query = String(inputs.taskQuery || "").trim();
201
+ const terms = parseTaskQuery(query);
202
+ const selected = tasks.filter((task) => terms.every((term) => matchesQueryTerm(task, term)));
203
+ const selectedIds = new Set(selected.map((task) => task.id));
204
+ return {
205
+ selector: {
206
+ type: "task-query",
207
+ query,
208
+ terms,
209
+ },
210
+ tasks: selected,
211
+ excluded: tasks.filter((task) => !selectedIds.has(task.id)),
212
+ missing: [],
213
+ };
214
+ }
215
+
216
+ function normalizeTaskListId(value) {
217
+ return String(value || "").trim();
218
+ }
219
+
220
+ function resolveTaskReferences(references, tasks) {
221
+ return references.map((reference) => {
222
+ const canonical = tasks.filter((task) => task.id === reference);
223
+ const candidates = canonical.length > 0 ? canonical : tasks.filter((task) => taskReferenceMatches(task, reference));
224
+ if (candidates.length === 0) {
225
+ throw new Error(`Missing task reference: ${reference}`);
226
+ }
227
+ if (candidates.length > 1) {
228
+ throw new Error(`Ambiguous task reference: ${reference} matched ${candidates.map((task) => task.id).join(", ")}`);
229
+ }
230
+ return candidates[0];
231
+ });
232
+ }
233
+
234
+ function taskReferenceMatches(task, reference) {
235
+ if (/^(TASKS|MODULES|EXTERNAL)\//.test(reference)) return task.id === reference;
236
+ return task.shortId === reference || task.legacyId === reference || task.id.endsWith(`/${reference}`);
237
+ }
238
+
239
+ function parseTaskQuery(query) {
240
+ const rawTerms = query.split(/\s+/).map((item) => item.trim()).filter(Boolean);
241
+ if (rawTerms.length === 0) throw new Error("--task-query requires at least one selector term");
242
+ return rawTerms.map((term) => {
243
+ let match = term.match(/^date:(\d{4}-\d{2}-\d{2})\.\.(\d{4}-\d{2}-\d{2})$/);
244
+ if (match) return { type: "date", from: match[1], to: match[2] };
245
+ match = term.match(/^state:([A-Za-z0-9_-]+)$/);
246
+ if (match) return { type: "state", value: normalizeState(match[1]) };
247
+ match = term.match(/^preset:([A-Za-z0-9._-]+)$/);
248
+ if (match) return { type: "preset", value: match[1].toLowerCase() };
249
+ match = term.match(/^module:([A-Za-z0-9._-]+)$/);
250
+ if (match) return { type: "module", value: match[1].toLowerCase() };
251
+ throw new Error(`Unsupported --task-query term: ${term}`);
252
+ });
253
+ }
254
+
255
+ function matchesQueryTerm(task, term) {
256
+ if (term.type === "date") {
257
+ const date = task.date || "";
258
+ return date >= term.from && date <= term.to;
259
+ }
260
+ if (term.type === "state") return task.state === term.value;
261
+ if (term.type === "preset") return (task.preset || "none") === term.value;
262
+ if (term.type === "module") return (task.module || "").toLowerCase() === term.value;
263
+ return false;
264
+ }
265
+
266
+ function taskSummary(task) {
267
+ return {
268
+ id: task.id,
269
+ title: task.title,
270
+ state: task.state,
271
+ preset: task.preset || "none",
272
+ deletionState: task.deletionState,
273
+ module: task.module || "",
274
+ shortId: task.shortId || "",
275
+ };
276
+ }
277
+
278
+ function archiveBlockReason(task) {
279
+ return sharedArchiveBlockReason(task, { archivedBy: task.reviewConfirmation?.reviewer || "" });
280
+ }
281
+
282
+ function releaseTaskFromCore(task) {
283
+ const shortId = task.shortId || task.id.split("/").at(-1) || "";
284
+ return {
285
+ ...task,
286
+ preset: task.taskPreset || "none",
287
+ date: shortId.match(/^(\d{4}-\d{2}-\d{2})/)?.[1] || "",
288
+ queue: (task.taskQueues || []).includes("blocked") ? "blocked" : "active",
289
+ hasOpenBlockingFindings: (task.risks || []).some((risk) => String(risk.open) !== "no" && (String(risk.blocksRelease) === "yes" || ["P0", "P1", "P2"].includes(String(risk.severity)))),
290
+ materialsIncomplete: task.materialsReady === false,
291
+ evidenceSnippet: "",
292
+ };
293
+ }
294
+
295
+ function archiveCommand(task, release, releasePackageRef) {
296
+ return [
297
+ "harness",
298
+ "task-archive",
299
+ quoteCli(task.id),
300
+ "--reason",
301
+ quoteCli(`release closeout ${release}`),
302
+ "--archived-by",
303
+ quoteCli(task.reviewConfirmation?.reviewer || ""),
304
+ "--archive-field",
305
+ quoteCli(`retention bucket=release:${release}`),
306
+ "--archive-field",
307
+ quoteCli(`release package=${releasePackageRef}`),
308
+ ".",
309
+ ].join(" ");
310
+ }
311
+
312
+ function quoteCli(value) {
313
+ return `"${String(value || "").replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
314
+ }
315
+
316
+ function taskRootsFromContext(targetRoot, paths) {
317
+ const roots = [];
318
+ const seen = new Set();
319
+ const addRoot = (kind, relativeRoot) => {
320
+ if (!relativeRoot) return;
321
+ const root = path.join(targetRoot, relativeRoot);
322
+ const key = path.resolve(root);
323
+ if (seen.has(key)) return;
324
+ seen.add(key);
325
+ roots.push({ kind, root, relativeRoot: toPosix(relativeRoot) });
326
+ };
327
+ addRoot("TASKS", paths.tasksRoot);
328
+ addRoot("MODULES", paths.modulesRoot);
329
+ addRoot("EXTERNAL", paths.externalRoot);
330
+ if (Array.isArray(paths.taskRoots)) {
331
+ for (const relativeRoot of paths.taskRoots) addRoot(inferTaskRootKind(relativeRoot, paths), relativeRoot);
332
+ }
333
+ return roots;
334
+ }
335
+
336
+ function inferTaskRootKind(relativeRoot, paths) {
337
+ const normalized = toPosix(relativeRoot);
338
+ if (normalized === toPosix(paths.tasksRoot || "")) return "TASKS";
339
+ if (normalized === toPosix(paths.modulesRoot || "")) return "MODULES";
340
+ if (normalized === toPosix(paths.externalRoot || "")) return "EXTERNAL";
341
+ return "EXTERNAL";
342
+ }
343
+
344
+ function collectTasks(roots) {
345
+ const tasks = [];
346
+ for (const rootInfo of roots) {
347
+ if (!fs.existsSync(rootInfo.root)) continue;
348
+ const taskPlans = walk(rootInfo.root).filter((file) => path.basename(file) === "task_plan.md");
349
+ for (const taskPlanPath of taskPlans) {
350
+ const taskDir = path.dirname(taskPlanPath);
351
+ const identity = taskIdentity(rootInfo, taskDir);
352
+ if (!identity) continue;
353
+ const taskPlan = read(taskPlanPath);
354
+ const progress = read(path.join(taskDir, "progress.md"));
355
+ const review = read(path.join(taskDir, "review.md"));
356
+ const index = read(path.join(taskDir, "INDEX.md"));
357
+ const budget = parseBudget(taskPlan);
358
+ const state = parseState(progress);
359
+ const tombstone = parseTombstone(taskPlan);
360
+ tasks.push({
361
+ ...identity,
362
+ title: titleFrom(taskPlan, identity.shortId),
363
+ preset: metadataLine(taskPlan, ["Task Preset", "Preset"]).toLowerCase(),
364
+ state,
365
+ deletionState: tombstone.state || "active",
366
+ queue: state === "blocked" ? "blocked" : "active",
367
+ budget,
368
+ hasOpenBlockingFindings: hasOpenBlockingReviewFinding(review),
369
+ materialsIncomplete: budget !== "simple" && hasIncompleteMaterials(taskDir),
370
+ reviewConfirmation: parseReviewConfirmation(index, identity.id),
371
+ evidenceSnippet: progress.split(/\r?\n/).find((line) => /\/Users\/|\/Volumes\/|file:\/\//.test(line)) || "",
372
+ tombstone,
373
+ });
374
+ }
375
+ }
376
+ return tasks;
377
+ }
378
+
379
+ function taskIdentity(rootInfo, taskDir) {
380
+ const relative = toPosix(path.relative(rootInfo.root, taskDir));
381
+ if (!relative || relative.startsWith("..")) return null;
382
+ const segments = relative.split("/").filter(Boolean);
383
+ const shortId = segments.at(-1) || path.basename(taskDir);
384
+ const date = shortId.match(/^(\d{4}-\d{2}-\d{2})/)?.[1] || "";
385
+ if (rootInfo.kind === "TASKS") {
386
+ return {
387
+ id: `TASKS/${relative}`,
388
+ legacyId: shortId,
389
+ shortId,
390
+ date,
391
+ module: "",
392
+ rootKind: "TASKS",
393
+ };
394
+ }
395
+ if (rootInfo.kind === "MODULES") {
396
+ if (segments.length < 3 || segments[1] !== "tasks") return null;
397
+ const moduleKey = segments[0];
398
+ const taskRelative = segments.slice(2).join("/");
399
+ return {
400
+ id: `MODULES/${moduleKey}/${taskRelative}`,
401
+ legacyId: shortId,
402
+ shortId,
403
+ date,
404
+ module: moduleKey,
405
+ rootKind: "MODULES",
406
+ };
407
+ }
408
+ return {
409
+ id: `EXTERNAL/${toPosix(path.basename(rootInfo.root))}/${relative}`,
410
+ legacyId: shortId,
411
+ shortId,
412
+ date,
413
+ module: "",
414
+ rootKind: "EXTERNAL",
415
+ };
416
+ }
417
+
418
+ function toPosix(value) {
419
+ return String(value || "").split(path.sep).join("/");
420
+ }
421
+
422
+ function walk(root) {
423
+ const files = [];
424
+ for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
425
+ const full = path.join(root, entry.name);
426
+ if (entry.isDirectory()) files.push(...walk(full));
427
+ else if (entry.isFile()) files.push(full);
428
+ }
429
+ return files;
430
+ }
431
+
432
+ function read(filePath) {
433
+ try {
434
+ return fs.readFileSync(filePath, "utf8");
435
+ } catch {
436
+ return "";
437
+ }
438
+ }
439
+
440
+ function titleFrom(content, fallback) {
441
+ const match = String(content || "").match(/^#\s+(.+)$/m);
442
+ return match ? match[1].trim() : fallback;
443
+ }
444
+
445
+ function metadataLine(content, labels) {
446
+ const escaped = labels.map((label) => label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
447
+ const match = String(content || "").match(new RegExp(`^(?:${escaped})\\s*[::]\\s*([^\\n]+)`, "im"));
448
+ return match ? match[1].replace(/`/g, "").trim() : "";
449
+ }
450
+
451
+ function parseState(progress) {
452
+ const match = String(progress || "").match(/^##\s*(?:Current Status|Status|状态)\s*[::]?\s*(?:\n\s*)?([^\n]+)/im);
453
+ return normalizeState(match ? match[1] : "");
454
+ }
455
+
456
+ function normalizeState(value) {
457
+ const raw = String(value || "").trim().toLowerCase().replaceAll("-", "_").replaceAll(" ", "_");
458
+ return ["not_started", "planned", "in_progress", "review", "blocked", "done"].includes(raw) ? raw : "unknown";
459
+ }
460
+
461
+ function parseBudget(taskPlan) {
462
+ const match = String(taskPlan || "").match(/Selected budget\s*[::]\s*([A-Za-z0-9_-]+)/i);
463
+ return match ? match[1].trim().toLowerCase() : "standard";
464
+ }
465
+
466
+ function hasOpenBlockingReviewFinding(review) {
467
+ const lines = String(review || "").split(/\r?\n/);
468
+ for (const line of lines) {
469
+ const row = line.trim();
470
+ if (!row.startsWith("|") || /---/.test(row) || /severity|finding/i.test(row)) continue;
471
+ const cells = row.slice(1, -1).split("|").map((cell) => cell.trim().toLowerCase());
472
+ const hasBlockingSeverity = cells.some((cell) => /^p[0-2]\b/.test(cell));
473
+ const openOrBlocking = cells.some((cell) => /^(yes|true|open|是|开放)$/.test(cell));
474
+ if (hasBlockingSeverity && openOrBlocking) return true;
475
+ }
476
+ return /^Open Blocking Findings\s*[::]\s*(yes|true|open|是)/im.test(review);
477
+ }
478
+
479
+ function hasIncompleteMaterials(taskDir) {
480
+ for (const fileName of ["task_plan.md", "progress.md", "review.md", "INDEX.md"]) {
481
+ const content = read(path.join(taskDir, fileName));
482
+ if (/Materials\s*(?:Status|Ready)\s*[::]\s*(incomplete|missing|no|false)/im.test(content)) return true;
483
+ }
484
+ return false;
485
+ }
486
+
487
+ function parseTombstone(content) {
488
+ const match = String(content || "").match(/^##\s*(?:Task Tombstone|任务墓碑)\s*$([\s\S]*?)(?=^##\s+|(?![\s\S]))/im);
489
+ if (!match) return { state: "active", fields: {} };
490
+ const fields = {};
491
+ for (const line of match[1].split(/\r?\n/)) {
492
+ const row = line.trim();
493
+ if (!row.startsWith("|") || /---/.test(row) || /Field\s*\|\s*Value/i.test(row)) continue;
494
+ const cells = row.slice(1, -1).split("|").map((cell) => cell.trim().toLowerCase());
495
+ if (cells.length >= 2) fields[cells[0]] = cells.slice(1).join("|").trim();
496
+ }
497
+ return { state: fields.state || "soft-deleted", fields };
498
+ }
499
+
500
+ function parseReviewConfirmation(content, taskId) {
501
+ const fields = markdownFieldsFromBlock(content, /^##\s*(?:Task Audit Metadata|任务审计元数据)\s*$/im);
502
+ if (fields.size === 0) return { confirmed: false, missingFields: ["Task Audit Metadata"] };
503
+ if (normalizeToken(fields.get("human review status")) !== "confirmed") return { confirmed: false, missingFields: [] };
504
+ const required = ["confirmation id", "confirmed at", "reviewer", "reviewer email", "confirm text", "evidence checked", "review commit sha", "audit status"];
505
+ const missing = required.filter((field) => !isConcreteAuditField(fields.get(field)));
506
+ const confirmText = fields.get("confirm text") || "";
507
+ if (isConcreteAuditField(confirmText) && !taskKeysMatch(confirmText, taskId)) missing.push("confirm text match");
508
+ const auditStatus = fields.get("audit status") || "";
509
+ if (isConcreteAuditField(auditStatus) && normalizeToken(auditStatus) !== "committed") missing.push("audit status committed");
510
+ const commitSha = fields.get("review commit sha") || "";
511
+ if (isConcreteAuditField(commitSha) && !/^[0-9a-f]{7,40}$/i.test(commitSha)) missing.push("review commit sha valid");
512
+ return {
513
+ confirmed: missing.length === 0,
514
+ missingFields: missing,
515
+ confirmationId: fields.get("confirmation id") || "",
516
+ confirmedAt: fields.get("confirmed at") || "",
517
+ reviewer: fields.get("reviewer") || "",
518
+ reviewerEmail: fields.get("reviewer email") || "",
519
+ confirmText,
520
+ evidenceChecked: fields.get("evidence checked") || "",
521
+ commitSha,
522
+ };
523
+ }
524
+
525
+ function markdownFieldsFromBlock(content, headingPattern) {
526
+ const match = String(content || "").match(new RegExp(`${headingPattern.source}([\\s\\S]*?)(?=^##\\s+|(?![\\s\\S]))`, headingPattern.flags));
527
+ const fields = new Map();
528
+ if (!match) return fields;
529
+ for (const line of match[1].split(/\r?\n/)) {
530
+ const row = line.trim();
531
+ if (!row.startsWith("|") || /---/.test(row) || /Field\s*\|\s*Value/i.test(row)) continue;
532
+ const cells = row.slice(1, -1).split("|").map((cell) => cell.trim());
533
+ if (cells.length >= 2) fields.set(cells[0].toLowerCase(), cells.slice(1).join("|").trim());
534
+ }
535
+ return fields;
536
+ }
537
+
538
+ function isConcreteAuditField(value) {
539
+ const raw = String(value || "").replace(/`/g, "").trim();
540
+ return Boolean(raw) && !/^(n\/a|na|none|pending(?:[-_ ].*)?|todo|tbd|\[.*\]|-|—|–|不适用|无|待定|\{\})$/i.test(raw) && !/\{\{[^}]+\}\}/.test(raw);
541
+ }
542
+
543
+ function normalizeToken(value) {
544
+ return String(value || "").trim().toLowerCase().replaceAll("_", "-").replace(/\s+/g, "-");
545
+ }
546
+
547
+ function taskKeysMatch(candidate, expected) {
548
+ const left = String(candidate || "").replace(/`/g, "").trim();
549
+ const right = String(expected || "").replace(/`/g, "").trim();
550
+ return left === right || right.endsWith(`/${left}`);
551
+ }
552
+
553
+ function safeRelease(value) {
554
+ const release = String(value || "").trim();
555
+ return /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(release) ? release : "";
556
+ }
557
+
558
+ function redactPublic(value) {
559
+ return String(value || "")
560
+ .replace(/file:\/\/\/[^\s)"'`<>\]]+/g, "LOCAL_FILE_URL_REDACTED")
561
+ .replaceAll("file://", "LOCAL_FILE_URL_REDACTED")
562
+ .replace(/\/Users\/[^/\s)"'`<>\]]+(?:\/[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED")
563
+ .replace(/\/Volumes\/[^\s)"'`<>\]]+(?:\/[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED")
564
+ .replace(/\/(?:private\/)?tmp\/[^\s)"'`<>\]]+(?:\/[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED")
565
+ .replace(/\/var\/folders\/[^\s)"'`<>\]]+(?:\/[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED")
566
+ .replace(/\/home\/[^/\s)"'`<>\]]+(?:\/[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED")
567
+ .replace(/[A-Za-z]:\\[^\s)"'`<>\]]+(?:\\[^\s)"'`<>\]]*)*/g, "LOCAL_PATH_REDACTED");
568
+ }
569
+
570
+ function leaksLocalPath(value) {
571
+ return /(?:^|[\s"'(])(?:\/Users\/|\/Volumes\/|\/tmp\/|\/private\/tmp\/|\/var\/folders\/|\/home\/|[A-Za-z]:\\|file:\/\/)/.test(String(value || ""));
572
+ }
@@ -0,0 +1,7 @@
1
+ ## Release Closeout Strategy
2
+
3
+ | Phase ID | Depends On | State | Completion | Output | Required Evidence | Evidence Status | Blocking Risk | Owner / Handoff |
4
+ | --- | --- | --- | ---: | --- | --- | --- | --- | --- |
5
+ | RC-PLAN | none | planned | 0 | Release task inventory and archive eligibility plan | `harness preset run release-closeout plan --task {{taskId}} .` | missing | none | coordinator |
6
+ | RC-SCAFFOLD | RC-PLAN | planned | 0 | Version package materialized under governance releases | `harness preset run release-closeout scaffold --task {{taskId}} .` | missing | none | coordinator |
7
+ | RC-CHECK | RC-SCAFFOLD | planned | 0 | Release package validation report | `harness preset run release-closeout check --task {{taskId}} .` | missing | none | coordinator |
@@ -0,0 +1,5 @@
1
+ ## Release Closeout Findings
2
+
3
+ | Severity | Finding | Open | Blocks Release | Disposition | Waiver By |
4
+ | --- | --- | --- | --- | --- | --- |
5
+ | P2 | Release package has not been generated yet. | yes | no | pending `release-closeout scaffold` | n/a |
@@ -0,0 +1,3 @@
1
+ ## Release Closeout Review
2
+
3
+ Review the generated release package, archive plan, and public redaction report before marking this task complete.
@@ -0,0 +1,24 @@
1
+ ## Release Closeout
2
+
3
+ Release Version: {{release}}
4
+ Previous Release: {{previousRelease}}
5
+ Next Release: {{nextRelease}}
6
+ Task Query: {{taskQuery}}
7
+
8
+ ## Release Package Workflow
9
+
10
+ Run these preset entrypoints from the target root. The task scaffold only records the release intent; version package files are generated by the generic preset runner.
11
+
12
+ 1. `harness preset run release-closeout plan --task {{taskId}} .`
13
+ 2. `harness preset run release-closeout scaffold --task {{taskId}} .`
14
+ 3. `harness preset run release-closeout check --task {{taskId}} .`
15
+
16
+ ## Release Package Outputs
17
+
18
+ | Output | Owner |
19
+ | --- | --- |
20
+ | `{{paths.governanceRoot}}/releases/{{release}}/INDEX.md` | `presets/release-closeout` |
21
+ | `{{paths.governanceRoot}}/releases/{{release}}/task-aggregate.json` | `presets/release-closeout` |
22
+ | `{{paths.governanceRoot}}/releases/{{release}}/task-archive-plan.md` | `presets/release-closeout` |
23
+ | `{{paths.governanceRoot}}/releases/{{release}}/public-summary.md` | `presets/release-closeout` |
24
+ | `{{paths.governanceRoot}}/releases/{{release}}/public-redaction-report.json` | `presets/release-closeout` |
@@ -9,7 +9,7 @@ task:
9
9
  entrypoints:
10
10
  newTask:
11
11
  type: template
12
- writes: [coding-agent-harness/planning/tasks/**]
12
+ writes: [{{paths.tasksRoot}}/**]
13
13
  audit: true
14
14
  templates:
15
15
  taskPlanAppend: templates/task_plan.append.md
@@ -27,5 +27,5 @@ audit:
27
27
  evidenceFiles: [preset-audit.json]
28
28
  writeScopes:
29
29
  taskDocs:
30
- path: coding-agent-harness/planning/tasks/**
30
+ path: {{paths.tasksRoot}}/**
31
31
  access: write
@@ -70,23 +70,29 @@ AGENTS.md 只包含两类内容:
70
70
  项目根目录/
71
71
  ├── AGENTS.md ← concise canonical charter + routing index
72
72
  ├── CLAUDE.md ← 轻量 shim,指向 AGENTS.md
73
- └── docs/
74
- ├── Harness-Ledger.md
75
- └── coding-agent-harness/governance/standards/
76
- ├── testing-standard.md
77
- ├── execution-workflow-standard.md
78
- ├── repo-governance-standard.md
79
- ├── ci-cd-standard.md
80
- ├── long-running-task-standard.md
81
- ├── adversarial-review-standard.md
82
- ├── review-routing-standard.md
83
- ├── engineering-standard.md
84
- ├── frontend-standard.md
85
- ├── docs-library-standard.md
86
- ├── harness-ledger-standard.md
87
- ├── regression-ssot-governance.md
88
- ├── walkthrough-standard.md
89
- └── ...(按需扩展)
73
+ └── coding-agent-harness/
74
+ ├── governance/
75
+ │ ├── generated/
76
+ │ │ ├── Harness-Ledger.md
77
+ │ │ └── Closeout-Index.md
78
+ │ └── standards/
79
+ ├── testing-standard.md
80
+ ├── execution-workflow-standard.md
81
+ ├── repo-governance-standard.md
82
+ ├── ci-cd-standard.md
83
+ ├── long-running-task-standard.md
84
+ ├── adversarial-review-standard.md
85
+ ├── review-routing-standard.md
86
+ ├── engineering-standard.md
87
+ ├── frontend-standard.md
88
+ ├── docs-library-standard.md
89
+ │ ├── harness-ledger-standard.md
90
+ │ ├── regression-ssot-governance.md
91
+ │ ├── walkthrough-standard.md
92
+ │ └── ...(按需扩展)
93
+ └── planning/
94
+ ├── tasks/
95
+ └── modules/
90
96
  ```
91
97
 
92
98
  ### 行数控制