coding-agent-harness 1.0.8 → 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 (232) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/CONTRIBUTING.md +8 -4
  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 +19 -6
  7. package/dist/check-dist-observation.mjs +57 -29
  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 +7 -7
  12. package/dist/check-runtime-emit.mjs +10 -3
  13. package/dist/check-type-boundaries.mjs +51 -9
  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 +51 -12
  18. package/dist/commands/registry.mjs +483 -0
  19. package/dist/commands/task-command.mjs +109 -52
  20. package/dist/harness.mjs +6 -304
  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 +4 -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 +67 -29
  44. package/dist/lib/preset-registry.mjs +361 -71
  45. package/dist/lib/preset-runner.mjs +292 -26
  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 +3 -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 +116 -160
  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 +36 -17
  70. package/dist/lib/task-scanner.mjs +74 -23
  71. package/dist/lib/task-template-materials.mjs +131 -0
  72. package/dist/lib/task-tombstone-commands.mjs +186 -29
  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 +1 -0
  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 +16 -12
  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 +6 -1
  120. package/presets/release-closeout/preset.yaml +9 -9
  121. package/presets/release-closeout/scripts/generate-release-package.mjs +387 -25
  122. package/presets/release-closeout/templates/task_plan.append.md +5 -5
  123. package/presets/standard-task/preset.yaml +2 -2
  124. package/references/agents-md-pattern.md +23 -17
  125. package/references/lessons-governance.md +2 -2
  126. package/references/module-parallel-standard.md +3 -6
  127. package/references/ssot-governance.md +2 -2
  128. package/references/taskr-gap-analysis.md +3 -3
  129. package/run-dist.mjs +34 -0
  130. package/skills/preset-creator/SKILL.md +40 -8
  131. package/skills/preset-creator/references/complex-task-skeleton/brief.md +32 -8
  132. package/skills/preset-creator/references/preset-package-skeleton.md +15 -5
  133. package/skills/preset-creator/references/structure-aware-paths.md +112 -0
  134. package/templates/AGENTS.md.template +28 -26
  135. package/templates/architecture/README.md +2 -2
  136. package/templates/architecture/service-catalog.md +2 -2
  137. package/templates/architecture/services/service-template.md +1 -1
  138. package/templates/dashboard/assets/app-src/00-state.js +5 -1
  139. package/templates/dashboard/assets/app-src/10-router.js +7 -0
  140. package/templates/dashboard/assets/app-src/20-overview.js +8 -8
  141. package/templates/dashboard/assets/app-src/30-tasks.js +132 -40
  142. package/templates/dashboard/assets/app-src/32-task-swimlane.js +314 -0
  143. package/templates/dashboard/assets/app-src/35-task-detail.js +35 -5
  144. package/templates/dashboard/assets/app-src/40-modules.js +257 -41
  145. package/templates/dashboard/assets/app-src/45-review.js +127 -1
  146. package/templates/dashboard/assets/app-src/90-bindings.js +185 -2
  147. package/templates/dashboard/assets/app.css +928 -53
  148. package/templates/dashboard/assets/app.css.manifest.json +2 -0
  149. package/templates/dashboard/assets/app.js +1071 -98
  150. package/templates/dashboard/assets/app.manifest.json +1 -0
  151. package/templates/dashboard/assets/css-src/00-foundation.css +12 -6
  152. package/templates/dashboard/assets/css-src/10-panels-flow.css +2 -2
  153. package/templates/dashboard/assets/css-src/30-task-index.css +21 -13
  154. package/templates/dashboard/assets/css-src/31-archive.css +94 -0
  155. package/templates/dashboard/assets/css-src/32-task-swimlane.css +487 -0
  156. package/templates/dashboard/assets/css-src/35-review-workspace.css +78 -0
  157. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +191 -14
  158. package/templates/dashboard/assets/css-src/50-responsive-overrides.css +23 -0
  159. package/templates/dashboard/assets/i18n.js +166 -2
  160. package/templates/development/README.md +9 -9
  161. package/templates/development/cross-repo-debugging.md +3 -3
  162. package/templates/development/external-context/service-template.md +1 -1
  163. package/templates/development/external-source-packs/README.md +2 -2
  164. package/templates/integrations/README.md +4 -4
  165. package/templates/integrations/api-contract.md +1 -1
  166. package/templates/integrations/event-contract.md +1 -1
  167. package/templates/integrations/third-party/vendor-template.md +1 -1
  168. package/templates/integrations/webhook-contract.md +1 -1
  169. package/templates/ledger/Harness-Ledger.md +1 -1
  170. package/templates/modules/module_brief.md +50 -0
  171. package/templates/modules/module_plan.md +49 -0
  172. package/templates/modules/registry_view.md +9 -0
  173. package/templates/modules/session_prompt_pack.md +55 -0
  174. package/templates/planning/brief.md +32 -8
  175. package/templates/planning/module_brief.md +28 -3
  176. package/templates/planning/module_plan.md +26 -11
  177. package/templates/planning/module_session_prompt.md +11 -2
  178. package/templates/planning/optional/slices/_slice-template/brief.md +28 -0
  179. package/templates/planning/review.md +1 -1
  180. package/templates/planning/visual_map.md +1 -1
  181. package/templates/reference/docs-library-standard.md +7 -7
  182. package/templates/reference/execution-workflow-standard.md +13 -0
  183. package/templates/reference/external-source-intake-standard.md +10 -10
  184. package/templates/reference/repo-governance-standard.md +1 -1
  185. package/templates/reference/review-routing-standard.md +4 -0
  186. package/templates/ssot/Module-Registry.md +4 -38
  187. package/templates/walkthrough/walkthrough-template.md +1 -1
  188. package/templates-zh-CN/AGENTS.md.template +27 -25
  189. package/templates-zh-CN/CLAUDE.md.template +1 -1
  190. package/templates-zh-CN/architecture/README.md +2 -2
  191. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  192. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  193. package/templates-zh-CN/development/README.md +9 -9
  194. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  195. package/templates-zh-CN/development/external-context/service-template.md +1 -1
  196. package/templates-zh-CN/development/external-source-packs/README.md +2 -2
  197. package/templates-zh-CN/integrations/README.md +4 -4
  198. package/templates-zh-CN/integrations/api-contract.md +1 -1
  199. package/templates-zh-CN/integrations/event-contract.md +1 -1
  200. package/templates-zh-CN/integrations/third-party/vendor-template.md +1 -1
  201. package/templates-zh-CN/integrations/webhook-contract.md +1 -1
  202. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  203. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  204. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  205. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  206. package/templates-zh-CN/modules/module_brief.md +47 -0
  207. package/templates-zh-CN/modules/module_plan.md +48 -0
  208. package/templates-zh-CN/modules/registry_view.md +9 -0
  209. package/templates-zh-CN/modules/session_prompt_pack.md +50 -0
  210. package/templates-zh-CN/planning/INDEX.md +1 -0
  211. package/templates-zh-CN/planning/brief.md +26 -7
  212. package/templates-zh-CN/planning/module_brief.md +24 -2
  213. package/templates-zh-CN/planning/module_plan.md +35 -29
  214. package/templates-zh-CN/planning/module_session_prompt.md +15 -11
  215. package/templates-zh-CN/planning/optional/slices/_slice-template/brief.md +28 -11
  216. package/templates-zh-CN/planning/review.md +1 -1
  217. package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
  218. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  219. package/templates-zh-CN/reference/docs-library-standard.md +27 -27
  220. package/templates-zh-CN/reference/execution-workflow-standard.md +12 -2
  221. package/templates-zh-CN/reference/external-source-intake-standard.md +10 -10
  222. package/templates-zh-CN/reference/harness-ledger-standard.md +3 -3
  223. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  224. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  225. package/templates-zh-CN/reference/review-routing-standard.md +3 -0
  226. package/templates-zh-CN/reference/walkthrough-standard.md +2 -2
  227. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  228. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  229. package/templates-zh-CN/ssot/Delivery-SSoT.md +2 -2
  230. package/templates-zh-CN/ssot/Module-Registry.md +5 -44
  231. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  232. package/templates-zh-CN/walkthrough/walkthrough-template.md +4 -4
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import { normalizeLocale, normalizeTarget, readJsonSafe, toPosix, } from "./core-shared.mjs";
@@ -92,7 +91,7 @@ export function applyStructureMigration(targetInput = ".", { force = false } = {
92
91
  fs.writeFileSync(manifestPath, renderHarnessManifest({ locale: plan.capabilities.locale, capabilities: plan.capabilities.names }));
93
92
  applied.push({ action: "write-manifest", destination: plan.manifest });
94
93
  }
95
- for (const action of plan.actions.filter((entry) => entry.action === "move")) {
94
+ for (const action of plan.actions.filter(isMoveAction)) {
96
95
  const source = path.join(targetRoot, action.source);
97
96
  const destination = path.join(targetRoot, action.destination);
98
97
  if (!fs.existsSync(source))
@@ -150,7 +149,7 @@ function preflightStructureMigration(plan, { force = false } = {}) {
150
149
  if (force)
151
150
  return;
152
151
  const conflicts = [];
153
- for (const action of plan.actions.filter((entry) => entry.action === "move")) {
152
+ for (const action of plan.actions.filter(isMoveAction)) {
154
153
  const source = path.join(plan.target, action.source);
155
154
  const destination = path.join(plan.target, action.destination);
156
155
  if (fs.existsSync(source) && fs.existsSync(destination))
@@ -161,6 +160,9 @@ function preflightStructureMigration(plan, { force = false } = {}) {
161
160
  throw new Error(`Refusing to overwrite existing v2 destination(s): ${conflicts.join(", ")}`);
162
161
  }
163
162
  }
163
+ function isMoveAction(action) {
164
+ return action.action === "move" && typeof action.source === "string";
165
+ }
164
166
  function moduleTaskNormalizationConflicts(targetRoot) {
165
167
  const modulesRoot = path.join(targetRoot, legacyPath(legacyModuleRoot));
166
168
  if (!fs.existsSync(modulesRoot))
@@ -207,7 +209,7 @@ function readLegacyCapabilities(projectRoot) {
207
209
  if (raw) {
208
210
  locale = normalizeLocale(raw.locale);
209
211
  for (const entry of raw.capabilities || [])
210
- names.add(typeof entry === "string" ? entry : entry.name);
212
+ names.add(typeof entry === "string" ? entry : entry.name || "");
211
213
  }
212
214
  return { locale, names: [...names].filter(Boolean) };
213
215
  }
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  import path from "node:path";
3
2
  import { readFileSafe, toPosix, walkFiles, isArchivedHarnessPath, } from "./core-shared.mjs";
4
3
  import { firstColumn, splitMarkdownRow, } from "./markdown-utils.mjs";
@@ -80,7 +79,14 @@ function subagentAuthorizationRows(content) {
80
79
  .filter((line) => line.trim().startsWith("|"))
81
80
  .map(splitMarkdownRow)
82
81
  .filter((row) => row.length === header.length)
83
- .map((row) => Object.fromEntries(Object.entries(indexes).map(([key, column]) => [key, column >= 0 ? row[column] || "" : ""])));
82
+ .map((row) => ({
83
+ role: row[indexes.role] || "",
84
+ status: row[indexes.status] || "",
85
+ authorizedBy: indexes.authorizedBy >= 0 ? row[indexes.authorizedBy] || "" : "",
86
+ authorizedAt: indexes.authorizedAt >= 0 ? row[indexes.authorizedAt] || "" : "",
87
+ scope: indexes.scope >= 0 ? row[indexes.scope] || "" : "",
88
+ worktreeBranch: indexes.worktreeBranch >= 0 ? row[indexes.worktreeBranch] || "" : "",
89
+ }));
84
90
  }
85
91
  return [];
86
92
  }
@@ -0,0 +1,65 @@
1
+ import { isConcreteAuditField } from "./task-audit-metadata.mjs";
2
+ import { normalizeReviewBoolean } from "./task-review-model.mjs";
3
+ export function assessArchiveEligibility(task, { archivedBy = "", now = new Date().toISOString() } = {}) {
4
+ const reason = archiveBlockReason(task, { archivedBy });
5
+ if (reason)
6
+ return { eligible: false, reason, auditFields: {} };
7
+ const confirmation = (task.reviewConfirmation || {});
8
+ const actor = normalizeArchiveActor(archivedBy);
9
+ return {
10
+ eligible: true,
11
+ reason: "",
12
+ auditFields: {
13
+ "Archived By": actor,
14
+ "Archived At": now,
15
+ "Review Confirmed By": String(confirmation.reviewer || ""),
16
+ "Review Confirmed At": String(confirmation.confirmedAt || ""),
17
+ "Review Confirmation ID": String(confirmation.confirmationId || ""),
18
+ "Review Commit SHA": String(confirmation.commitSha || ""),
19
+ },
20
+ };
21
+ }
22
+ export function archiveBlockReason(task, { archivedBy = "" } = {}) {
23
+ if (task.deletionState === "superseded")
24
+ return "superseded tasks cannot be archived";
25
+ if (task.state === "blocked" || (task.taskQueues || []).includes("blocked")) {
26
+ return "blocked tasks cannot be archived without an explicit human waiver";
27
+ }
28
+ const blockingRisks = (task.risks || []).filter((risk) => normalizeReviewBoolean(risk.open) !== "no" && (normalizeReviewBoolean(risk.blocksRelease) === "yes" || ["P0", "P1", "P2"].includes(String(risk.severity))));
29
+ if (blockingRisks.length)
30
+ return "tasks with open blocking review findings cannot be archived without an explicit human waiver";
31
+ if (task.state !== "done")
32
+ return `state:${task.state || "unknown"}`;
33
+ if (task.budget !== "simple" && task.closeoutStatus !== "closed")
34
+ return "tasks must have closed closeout materials before archive";
35
+ if (task.materialsReady === false && task.reviewStatus !== "confirmed") {
36
+ return "tasks with incomplete closeout materials cannot be archived without an explicit human waiver";
37
+ }
38
+ const confirmation = (task.reviewConfirmation || {});
39
+ if (confirmation.confirmed !== true)
40
+ return "Human review confirmation is required before task archive";
41
+ const missingConfirmationFields = [];
42
+ if (!isConcreteAuditField(confirmation.confirmationId))
43
+ missingConfirmationFields.push("Confirmation ID");
44
+ if (!isConcreteAuditField(confirmation.confirmedAt))
45
+ missingConfirmationFields.push("Confirmed At");
46
+ if (!isConcreteAuditField(confirmation.reviewer))
47
+ missingConfirmationFields.push("Reviewer");
48
+ if (!isConcreteAuditField(confirmation.commitSha))
49
+ missingConfirmationFields.push("Review Commit SHA");
50
+ if (missingConfirmationFields.length) {
51
+ return `Human review confirmation is not traceable; missing ${missingConfirmationFields.join(", ")}`;
52
+ }
53
+ if (!normalizeArchiveActor(archivedBy)) {
54
+ return "task archive requires --archived-by <name-or-email> for accountability";
55
+ }
56
+ return "";
57
+ }
58
+ export function normalizeArchiveActor(value) {
59
+ const actor = String(value || "").replace(/\r?\n/g, " ").trim();
60
+ if (!actor)
61
+ return "";
62
+ if (/^(coordinator|agent|unknown|n\/a|na|none|pending|todo|tbd)$/i.test(actor))
63
+ return "";
64
+ return actor;
65
+ }
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  // Dynamic audit metadata parsing stays behavior-first until the metadata domain model PR.
3
2
  import { spawnSync } from "node:child_process";
4
3
  import path from "node:path";
@@ -178,22 +177,24 @@ export function taskAuditMaterialIssues(target, taskDir, audit) {
178
177
  }));
179
178
  }
180
179
  export function scaffoldProvenanceSummaryFromTaskAudit(audit) {
181
- const fields = audit?.fields || new Map();
180
+ const parsed = normalizeTaskAuditLike(audit);
181
+ const fields = parsed.fields;
182
182
  return {
183
183
  required: true,
184
- present: Boolean(audit?.present),
184
+ present: parsed.present,
185
185
  createdBy: normalizeToken(fields.get("created by")),
186
186
  command: fields.get("command shape") || "",
187
187
  createdAt: fields.get("created at") || "",
188
188
  budget: normalizeToken(fields.get("budget")),
189
189
  templateSource: fields.get("template source") || "",
190
190
  exceptionReason: fields.get("exception reason") || "",
191
- issues: audit?.issues || [],
191
+ issues: parsed.issues,
192
192
  };
193
193
  }
194
194
  export function reviewConfirmationFromTaskAudit(audit, { taskKey = "" } = {}) {
195
- const fields = audit?.fields || new Map();
196
- if (!audit?.present)
195
+ const parsed = normalizeTaskAuditLike(audit);
196
+ const fields = parsed.fields;
197
+ if (!parsed.present)
197
198
  return null;
198
199
  const status = normalizeToken(fields.get("human review status"));
199
200
  if (status !== "confirmed")
@@ -204,9 +205,8 @@ export function reviewConfirmationFromTaskAudit(audit, { taskKey = "" } = {}) {
204
205
  const commitSha = fields.get("review commit sha") || "";
205
206
  const auditStatus = fields.get("audit status") || "";
206
207
  const auditSource = fields.get("audit source") || "";
207
- const migratedLegacy = auditSource === "migrated-legacy-review";
208
- const confirmTextMismatch = Boolean(!migratedLegacy && taskKey && isConcreteAuditField(confirmText) && !taskKeysMatch(confirmText, taskKey));
209
- const commitShaInvalid = Boolean(!migratedLegacy && isConcreteAuditField(commitSha) && !/^[0-9a-f]{7,40}$/i.test(commitSha));
208
+ const confirmTextMismatch = Boolean(taskKey && isConcreteAuditField(confirmText) && !taskKeysMatch(confirmText, taskKey));
209
+ const commitShaInvalid = Boolean(isConcreteAuditField(commitSha) && !/^[0-9a-f]{7,40}$/i.test(commitSha));
210
210
  const auditStatusInvalid = Boolean(isConcreteAuditField(auditStatus) && auditStatus.trim().toLowerCase() !== "committed");
211
211
  const invalidFields = [
212
212
  ...(confirmTextMismatch ? ["Confirm Text match"] : []),
@@ -231,7 +231,7 @@ export function reviewConfirmationFromTaskAudit(audit, { taskKey = "" } = {}) {
231
231
  auditStatusInvalid,
232
232
  auditSource,
233
233
  migratedFrom: fields.get("migrated from") || "",
234
- gitAudit: migratedLegacy ? { valid: true, migrated: true } : null,
234
+ gitAudit: null,
235
235
  gitAuditInvalid: false,
236
236
  };
237
237
  }
@@ -250,7 +250,7 @@ export function extractLegacyBlock(content, pattern) {
250
250
  }
251
251
  export function fieldsFromMarkdownBlock(block) {
252
252
  const fields = new Map();
253
- const tableRows = markdownTableRows(block);
253
+ const tableRows = markdownTableRows(String(block || ""));
254
254
  const header = tableRows[0] || [];
255
255
  const fieldIndex = firstColumn(header, ["Field", "字段"]);
256
256
  const valueIndex = firstColumn(header, ["Value", "值"]);
@@ -374,3 +374,17 @@ function isValidDateOnly(value) {
374
374
  const date = new Date(`${raw}T00:00:00.000Z`);
375
375
  return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === raw;
376
376
  }
377
+ function normalizeTaskAuditLike(audit) {
378
+ if (!audit || typeof audit !== "object") {
379
+ return { present: false, fields: new Map(), issues: [], summary: taskAuditSummary(new Map(), []) };
380
+ }
381
+ const record = audit;
382
+ const fields = record.fields instanceof Map ? record.fields : new Map();
383
+ const issues = Array.isArray(record.issues) ? record.issues : [];
384
+ return {
385
+ present: Boolean(record.present),
386
+ fields,
387
+ issues,
388
+ summary: record.summary || taskAuditSummary(fields, issues),
389
+ };
390
+ }
@@ -1,10 +1,19 @@
1
- // @ts-nocheck
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import { normalizeTarget, readFileSafe, toPosix } from "./core-shared.mjs";
5
4
  import { listTaskPlanPaths, taskIdForDirectory } from "./task-scanner.mjs";
6
5
  import { extractLegacyBlock, fieldsFromMarkdownBlock, humanReviewConfirmationHeadingPattern, isConcreteAuditField, legacyExtraFieldsJson, parseTaskAuditMetadata, replaceTaskAuditMetadata, scaffoldProvenanceHeadingPattern, stripLegacyAuditBlocks, taskAuditFieldOrder, } from "./task-audit-metadata.mjs";
7
6
  import { firstColumn, markdownTableRows } from "./markdown-utils.mjs";
7
+ class TaskAuditMigrationError extends Error {
8
+ plan;
9
+ cause;
10
+ constructor(message, { plan, cause } = {}) {
11
+ super(message);
12
+ this.name = "TaskAuditMigrationError";
13
+ this.plan = plan;
14
+ this.cause = cause;
15
+ }
16
+ }
8
17
  const scaffoldFieldMap = new Map([
9
18
  ["created by", "Created By"],
10
19
  ["command", "Command Shape"],
@@ -77,9 +86,7 @@ export function applyTaskAuditIndexMigration(targetInput) {
77
86
  const target = normalizeTarget(targetInput);
78
87
  const plan = planTaskAuditIndexMigration(targetInput);
79
88
  if (plan.failures.length) {
80
- const error = new Error(`Task audit INDEX migration failed during plan: ${plan.failures.map((failure) => `${failure.taskId}: ${failure.failure}`).join("; ")}`);
81
- error.plan = plan;
82
- throw error;
89
+ throw new TaskAuditMigrationError(`Task audit INDEX migration failed during plan: ${plan.failures.map((failure) => `${failure.taskId}: ${failure.failure}`).join("; ")}`, { plan });
83
90
  }
84
91
  const writes = [];
85
92
  for (const action of plan.actions) {
@@ -114,10 +121,7 @@ export function applyTaskAuditIndexMigration(targetInput) {
114
121
  fs.writeFileSync(write.briefPath, write.before.briefContent);
115
122
  fs.writeFileSync(write.reviewPath, write.before.reviewContent);
116
123
  }
117
- const error = new Error(`Task audit INDEX migration apply failed and was rolled back: ${cause.message}`);
118
- error.cause = cause;
119
- error.plan = plan;
120
- throw error;
124
+ throw new TaskAuditMigrationError(`Task audit INDEX migration apply failed and was rolled back: ${errorMessage(cause)}`, { cause, plan });
121
125
  }
122
126
  return { ...plan, result: "applied" };
123
127
  }
@@ -146,7 +150,7 @@ function parseLegacyAudit({ taskId, indexContent, scaffoldBlock, reviewBlock })
146
150
  for (const [legacyKey, canonical] of scaffoldFieldMap) {
147
151
  const value = scaffold.get(legacyKey);
148
152
  if (isConcreteAuditField(value))
149
- fields[canonical] = value;
153
+ fields[canonical] = value || "";
150
154
  }
151
155
  for (const field of requiredScaffold) {
152
156
  if (!isConcreteAuditField(fields[field]))
@@ -169,7 +173,7 @@ function parseLegacyAudit({ taskId, indexContent, scaffoldBlock, reviewBlock })
169
173
  for (const [legacyKey, canonical] of reviewFieldMap) {
170
174
  const value = review.get(legacyKey);
171
175
  if (isConcreteAuditField(value))
172
- fields[canonical] = value;
176
+ fields[canonical] = value || "";
173
177
  }
174
178
  const legacyTaskKey = review.get("task key") || "";
175
179
  if (isConcreteAuditField(legacyTaskKey) && legacyTaskKey !== taskId && !taskId.endsWith(`/${legacyTaskKey}`))
@@ -212,8 +216,8 @@ function parseLegacyAudit({ taskId, indexContent, scaffoldBlock, reviewBlock })
212
216
  }
213
217
  }
214
218
  if (extraEntries.length) {
215
- const mergedExtra = mergeLegacyExtraFields(fields["Legacy Extra Fields"], extraEntries);
216
- if (mergedExtra.failure)
219
+ const mergedExtra = mergeLegacyExtraFields(fields["Legacy Extra Fields"] || "", extraEntries);
220
+ if (mergedExtra.failure !== undefined)
217
221
  failures.push(mergedExtra.failure);
218
222
  else
219
223
  fields["Legacy Extra Fields"] = mergedExtra.json;
@@ -291,7 +295,7 @@ function normalizeExistingAuditFields(fields, taskId, extraEntries) {
291
295
  }
292
296
  function replacePreserving(fields, extraEntries, field, replacement) {
293
297
  if (isConcreteAuditField(fields[field]))
294
- extraEntries.push([`Original ${field}`, fields[field]]);
298
+ extraEntries.push([`Original ${field}`, fields[field] || ""]);
295
299
  fields[field] = replacement;
296
300
  }
297
301
  function parseLegacyReviewFields(body) {
@@ -333,7 +337,7 @@ function concreteReviewConfirmationValues(fields) {
333
337
  "commit sha",
334
338
  "review commit sha",
335
339
  ];
336
- return confirmationKeys.map((key) => fields.get(key)).filter(isConcreteAuditField);
340
+ return confirmationKeys.map((key) => fields.get(key)).filter((value) => isConcreteAuditField(value));
337
341
  }
338
342
  function titleField(value) {
339
343
  return String(value || "")
@@ -353,3 +357,6 @@ function isValidDateOnly(value) {
353
357
  const date = new Date(`${raw}T00:00:00.000Z`);
354
358
  return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === raw;
355
359
  }
360
+ function errorMessage(error) {
361
+ return error instanceof Error ? error.message : String(error);
362
+ }
@@ -0,0 +1,32 @@
1
+ import path from "node:path";
2
+ import fs from "node:fs";
3
+ import { toPosix } from "./core-shared.mjs";
4
+ export function isExcludedTaskPlanPath(file, harnessPaths) {
5
+ const relative = toPosix(path.relative(harnessPaths.projectRoot, file));
6
+ const segments = relative.split("/").filter(Boolean);
7
+ if (segments.includes("_task-template") || segments.includes("_optional-structures"))
8
+ return true;
9
+ if (isTaskLocalOptionalStructure(file, harnessPaths.projectRoot, segments))
10
+ return true;
11
+ const generatedRoot = toPosix(path.relative(harnessPaths.projectRoot, harnessPaths.generatedRoot || ""));
12
+ if (generatedRoot && (relative === `${generatedRoot}/task_plan.md` || relative.startsWith(`${generatedRoot}/`)))
13
+ return true;
14
+ const governanceRoot = toPosix(path.relative(harnessPaths.projectRoot, harnessPaths.governanceRoot || ""));
15
+ return Boolean(governanceRoot && (relative.startsWith(`${governanceRoot}/releases/`) ||
16
+ relative.startsWith(`${governanceRoot}/archive/`) ||
17
+ relative.startsWith(`${governanceRoot}/generated/`)));
18
+ }
19
+ function isTaskLocalOptionalStructure(file, projectRoot, segments) {
20
+ const taskPlanFile = path.basename(file) === "task_plan.md";
21
+ if (!taskPlanFile)
22
+ return false;
23
+ for (const segmentName of ["artifacts", "references"]) {
24
+ const segmentIndex = segments.indexOf(segmentName);
25
+ if (segmentIndex < 1)
26
+ continue;
27
+ const ancestorDir = path.join(projectRoot, ...segments.slice(0, segmentIndex));
28
+ if (fs.existsSync(path.join(ancestorDir, "task_plan.md")))
29
+ return true;
30
+ }
31
+ return false;
32
+ }
@@ -1,13 +1,13 @@
1
- // @ts-nocheck
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import crypto from "node:crypto";
5
4
  import { normalizeTarget, readFileSafe, toPosix, } from "./core-shared.mjs";
6
5
  import { collectTasks } from "./task-scanner.mjs";
7
6
  import { taskScannerVersion } from "./task-review-model.mjs";
7
+ const collectTasksForIndex = collectTasks;
8
8
  export function buildTaskIndex(targetInput) {
9
9
  const target = normalizeTarget(targetInput);
10
- const tasks = collectTasks(target);
10
+ const tasks = collectTasksForIndex(target);
11
11
  assertUniqueTaskKeys(tasks);
12
12
  return {
13
13
  schemaVersion: target.harness.version === 2 ? "task-index/v2" : "task-index/v1",
@@ -56,6 +56,7 @@ export function buildTaskIndex(targetInput) {
56
56
  supersedes: task.supersedes || [],
57
57
  supersededBy: task.supersededBy || "",
58
58
  deletionState: task.deletionState || "active",
59
+ deleteReason: task.deleteReason || "",
59
60
  archiveMetadata: task.archiveMetadata || {},
60
61
  hiddenByDefault: task.hiddenByDefault === true,
61
62
  repairPrompt: task.repairPrompt || "",
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import { tableAfterHeading, firstColumn, } from "./markdown-utils.mjs";
@@ -85,7 +84,7 @@ export function parseLessonCandidateStatus(content) {
85
84
  export function isLessonCandidateDecisionComplete(candidateStatus) {
86
85
  if (!candidateStatus || candidateStatus.issues?.length)
87
86
  return false;
88
- return reviewCompleteLessonCandidateStatuses.has(candidateStatus.status);
87
+ return reviewCompleteLessonCandidateStatuses.has(String(candidateStatus.status || ""));
89
88
  }
90
89
  export function validateLessonCandidateDetailArtifacts(target, taskDir, candidateStatus) {
91
90
  const issues = [];