zob-harness 0.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 (356) hide show
  1. package/.pi/adapters/registry.json +103 -0
  2. package/.pi/agents/architecture-cartographer.md +53 -0
  3. package/.pi/agents/chief-vision.md +39 -0
  4. package/.pi/agents/clarifier.md +58 -0
  5. package/.pi/agents/context-steward.md +52 -0
  6. package/.pi/agents/doc-steward.md +34 -0
  7. package/.pi/agents/explore.md +49 -0
  8. package/.pi/agents/factory.md +41 -0
  9. package/.pi/agents/implementer.md +44 -0
  10. package/.pi/agents/librarian.md +32 -0
  11. package/.pi/agents/oracle-merge.md +50 -0
  12. package/.pi/agents/oracle.md +55 -0
  13. package/.pi/agents/pattern-miner.md +53 -0
  14. package/.pi/agents/planner.md +39 -0
  15. package/.pi/agents/project-dna-golden-evaluator.md +32 -0
  16. package/.pi/agents/project-dna-ontology-steward.md +30 -0
  17. package/.pi/agents/project-dna-oracle.md +56 -0
  18. package/.pi/agents/project-dna-orchestrator.md +60 -0
  19. package/.pi/agents/project-dna-query-steward.md +38 -0
  20. package/.pi/agents/project-dna-safety-preflight.md +54 -0
  21. package/.pi/agents/project-dna-test-linker.md +27 -0
  22. package/.pi/agents/qa.md +38 -0
  23. package/.pi/agents/refactor-cartographer.md +28 -0
  24. package/.pi/agents/refactor-mover.md +31 -0
  25. package/.pi/agents/refactor-oracle.md +49 -0
  26. package/.pi/agents/repo-scout.md +60 -0
  27. package/.pi/agents/sample-architect.md +48 -0
  28. package/.pi/agents/specifier.md +57 -0
  29. package/.pi/agents/symbol-range-curator.md +41 -0
  30. package/.pi/agents/synthesis.md +52 -0
  31. package/.pi/agents/temp-agent-creator.md +35 -0
  32. package/.pi/autonomy-policy.json +67 -0
  33. package/.pi/budget-policy.json +54 -0
  34. package/.pi/capabilities/zob-public-runtime-capabilities.json +1700 -0
  35. package/.pi/chains/explore-plan-oracle.json +78 -0
  36. package/.pi/chains/explore-spec-clarify-plan-oracle.json +64 -0
  37. package/.pi/chains/explore-spec-plan-oracle.json +53 -0
  38. package/.pi/chains/spec-clarify-plan-oracle.json +53 -0
  39. package/.pi/chains/spec-factory-oracle.json +42 -0
  40. package/.pi/chains/spec-plan-oracle.json +42 -0
  41. package/.pi/compute-profiles/defaults.json +19 -0
  42. package/.pi/compute-profiles/overrides.json +13 -0
  43. package/.pi/compute-profiles/risk-rules.json +16 -0
  44. package/.pi/daemon-policy.json +80 -0
  45. package/.pi/damage-control-rules.json +45 -0
  46. package/.pi/extensions/zob-child-safety/index.ts +212 -0
  47. package/.pi/extensions/zob-harness/AGENTS.md +28 -0
  48. package/.pi/extensions/zob-harness/index.ts +391 -0
  49. package/.pi/extensions/zob-harness/src/AGENTS.md +25 -0
  50. package/.pi/extensions/zob-harness/src/agents.ts +82 -0
  51. package/.pi/extensions/zob-harness/src/autonomous-runtime.ts +2912 -0
  52. package/.pi/extensions/zob-harness/src/autonomy-readiness.ts +778 -0
  53. package/.pi/extensions/zob-harness/src/budget-policy.ts +308 -0
  54. package/.pi/extensions/zob-harness/src/capabilities.ts +249 -0
  55. package/.pi/extensions/zob-harness/src/child-runner.ts +249 -0
  56. package/.pi/extensions/zob-harness/src/chronicle.ts +262 -0
  57. package/.pi/extensions/zob-harness/src/compute-profile.ts +602 -0
  58. package/.pi/extensions/zob-harness/src/compute-workflow-shape.ts +168 -0
  59. package/.pi/extensions/zob-harness/src/coms-v2/AGENTS.md +16 -0
  60. package/.pi/extensions/zob-harness/src/coms-v2/envelope.ts +121 -0
  61. package/.pi/extensions/zob-harness/src/coms-v2/identity.ts +53 -0
  62. package/.pi/extensions/zob-harness/src/coms-v2/ledger-bridge.ts +67 -0
  63. package/.pi/extensions/zob-harness/src/coms-v2/local-transport.ts +147 -0
  64. package/.pi/extensions/zob-harness/src/coms-v2/pending-replies.ts +80 -0
  65. package/.pi/extensions/zob-harness/src/coms-v2/policy.ts +125 -0
  66. package/.pi/extensions/zob-harness/src/coms-v2/presence.ts +55 -0
  67. package/.pi/extensions/zob-harness/src/coms-v2/registry.ts +113 -0
  68. package/.pi/extensions/zob-harness/src/coms-v2/response-capture.ts +50 -0
  69. package/.pi/extensions/zob-harness/src/coms-v2/transcript-capture.ts +164 -0
  70. package/.pi/extensions/zob-harness/src/coms-v2/types.ts +149 -0
  71. package/.pi/extensions/zob-harness/src/coms-v2/zpeer-profile.ts +140 -0
  72. package/.pi/extensions/zob-harness/src/coms-v2/zpeer.ts +452 -0
  73. package/.pi/extensions/zob-harness/src/constants.ts +108 -0
  74. package/.pi/extensions/zob-harness/src/context-gbrain.ts +465 -0
  75. package/.pi/extensions/zob-harness/src/daemon-policy.ts +223 -0
  76. package/.pi/extensions/zob-harness/src/daemon-readiness.ts +134 -0
  77. package/.pi/extensions/zob-harness/src/daemon-runtime.ts +393 -0
  78. package/.pi/extensions/zob-harness/src/factory/AGENTS.md +24 -0
  79. package/.pi/extensions/zob-harness/src/factory/agentic-plan.ts +65 -0
  80. package/.pi/extensions/zob-harness/src/factory/quarantine.ts +319 -0
  81. package/.pi/extensions/zob-harness/src/factory/run.ts +520 -0
  82. package/.pi/extensions/zob-harness/src/factory/validation.ts +454 -0
  83. package/.pi/extensions/zob-harness/src/factory-selector.ts +318 -0
  84. package/.pi/extensions/zob-harness/src/full-autonomy-test.ts +226 -0
  85. package/.pi/extensions/zob-harness/src/git-ops.ts +868 -0
  86. package/.pi/extensions/zob-harness/src/goal-room.ts +178 -0
  87. package/.pi/extensions/zob-harness/src/goal-runtime.ts +1569 -0
  88. package/.pi/extensions/zob-harness/src/goal-todo-imports.ts +111 -0
  89. package/.pi/extensions/zob-harness/src/goal-todo-types.ts +231 -0
  90. package/.pi/extensions/zob-harness/src/goal-todos.ts +1410 -0
  91. package/.pi/extensions/zob-harness/src/goal.ts +152 -0
  92. package/.pi/extensions/zob-harness/src/governed-requests.ts +436 -0
  93. package/.pi/extensions/zob-harness/src/interactive-autonomy.ts +595 -0
  94. package/.pi/extensions/zob-harness/src/launch-apply.ts +313 -0
  95. package/.pi/extensions/zob-harness/src/merge-queue.ts +290 -0
  96. package/.pi/extensions/zob-harness/src/mission-control.ts +573 -0
  97. package/.pi/extensions/zob-harness/src/model-availability.ts +52 -0
  98. package/.pi/extensions/zob-harness/src/model-routing.ts +429 -0
  99. package/.pi/extensions/zob-harness/src/orchestration/AGENTS.md +23 -0
  100. package/.pi/extensions/zob-harness/src/orchestration/adaptive-delegation.ts +547 -0
  101. package/.pi/extensions/zob-harness/src/orchestration/adaptive-workflow.ts +585 -0
  102. package/.pi/extensions/zob-harness/src/orchestration/lead-plan.ts +192 -0
  103. package/.pi/extensions/zob-harness/src/orchestration/plan.ts +168 -0
  104. package/.pi/extensions/zob-harness/src/orchestration/room.ts +346 -0
  105. package/.pi/extensions/zob-harness/src/orchestration/run.ts +134 -0
  106. package/.pi/extensions/zob-harness/src/orchestration/supervised-readonly.ts +1147 -0
  107. package/.pi/extensions/zob-harness/src/orchestration/widget-readers.ts +132 -0
  108. package/.pi/extensions/zob-harness/src/output-contracts.ts +656 -0
  109. package/.pi/extensions/zob-harness/src/project-dna.ts +533 -0
  110. package/.pi/extensions/zob-harness/src/promotion/AGENTS.md +24 -0
  111. package/.pi/extensions/zob-harness/src/promotion/candidate.ts +336 -0
  112. package/.pi/extensions/zob-harness/src/promotion/coms.ts +127 -0
  113. package/.pi/extensions/zob-harness/src/promotion/documentation.ts +142 -0
  114. package/.pi/extensions/zob-harness/src/promotion/factory.ts +107 -0
  115. package/.pi/extensions/zob-harness/src/promotion/ledger.ts +2 -0
  116. package/.pi/extensions/zob-harness/src/promotion/temp-agent.ts +151 -0
  117. package/.pi/extensions/zob-harness/src/promotion/types.ts +149 -0
  118. package/.pi/extensions/zob-harness/src/promotion/validate.ts +6 -0
  119. package/.pi/extensions/zob-harness/src/promotion/write-lane.ts +162 -0
  120. package/.pi/extensions/zob-harness/src/prompt-packs.ts +239 -0
  121. package/.pi/extensions/zob-harness/src/queue.ts +386 -0
  122. package/.pi/extensions/zob-harness/src/rules.ts +225 -0
  123. package/.pi/extensions/zob-harness/src/runtime/AGENTS.md +26 -0
  124. package/.pi/extensions/zob-harness/src/runtime/adaptive-zmode.ts +116 -0
  125. package/.pi/extensions/zob-harness/src/runtime/auto-compaction.ts +715 -0
  126. package/.pi/extensions/zob-harness/src/runtime/commands.ts +1315 -0
  127. package/.pi/extensions/zob-harness/src/runtime/compaction-policy.ts +516 -0
  128. package/.pi/extensions/zob-harness/src/runtime/delegation-click-markers.ts +141 -0
  129. package/.pi/extensions/zob-harness/src/runtime/delegation-feed.ts +415 -0
  130. package/.pi/extensions/zob-harness/src/runtime/delegation-markdown.ts +97 -0
  131. package/.pi/extensions/zob-harness/src/runtime/delegation-monitor.ts +553 -0
  132. package/.pi/extensions/zob-harness/src/runtime/delegation-mouse.ts +205 -0
  133. package/.pi/extensions/zob-harness/src/runtime/delegation-overlay.ts +434 -0
  134. package/.pi/extensions/zob-harness/src/runtime/events.ts +736 -0
  135. package/.pi/extensions/zob-harness/src/runtime/goal-todo-overlay.ts +214 -0
  136. package/.pi/extensions/zob-harness/src/runtime/mode-intent.ts +144 -0
  137. package/.pi/extensions/zob-harness/src/runtime/plan-capture.ts +270 -0
  138. package/.pi/extensions/zob-harness/src/runtime/state.ts +403 -0
  139. package/.pi/extensions/zob-harness/src/runtime/tools-autonomous.ts +117 -0
  140. package/.pi/extensions/zob-harness/src/runtime/tools-compute.ts +136 -0
  141. package/.pi/extensions/zob-harness/src/runtime/tools-coms.ts +365 -0
  142. package/.pi/extensions/zob-harness/src/runtime/tools-context.ts +70 -0
  143. package/.pi/extensions/zob-harness/src/runtime/tools-delegation.ts +1854 -0
  144. package/.pi/extensions/zob-harness/src/runtime/tools-factory.ts +810 -0
  145. package/.pi/extensions/zob-harness/src/runtime/tools-goal-room.ts +46 -0
  146. package/.pi/extensions/zob-harness/src/runtime/tools-governed-requests.ts +38 -0
  147. package/.pi/extensions/zob-harness/src/runtime/tools-merge-queue.ts +61 -0
  148. package/.pi/extensions/zob-harness/src/runtime/tools-mission-control.ts +77 -0
  149. package/.pi/extensions/zob-harness/src/runtime/tools-orchestration.ts +106 -0
  150. package/.pi/extensions/zob-harness/src/runtime/tools-project-dna.ts +123 -0
  151. package/.pi/extensions/zob-harness/src/runtime/tools-worker-pool.ts +93 -0
  152. package/.pi/extensions/zob-harness/src/runtime/tools-workspace-claims.ts +62 -0
  153. package/.pi/extensions/zob-harness/src/runtime/tools-zcommit.ts +147 -0
  154. package/.pi/extensions/zob-harness/src/runtime/widget.ts +353 -0
  155. package/.pi/extensions/zob-harness/src/runtime/zobHarness.ts +60 -0
  156. package/.pi/extensions/zob-harness/src/safety.ts +338 -0
  157. package/.pi/extensions/zob-harness/src/sandbox.ts +1508 -0
  158. package/.pi/extensions/zob-harness/src/schemas-project-dna.ts +47 -0
  159. package/.pi/extensions/zob-harness/src/schemas.ts +695 -0
  160. package/.pi/extensions/zob-harness/src/telemetry.ts +373 -0
  161. package/.pi/extensions/zob-harness/src/topology/AGENTS.md +22 -0
  162. package/.pi/extensions/zob-harness/src/topology/chains.ts +236 -0
  163. package/.pi/extensions/zob-harness/src/topology/coms.ts +211 -0
  164. package/.pi/extensions/zob-harness/src/topology/orchestration-profiles.ts +204 -0
  165. package/.pi/extensions/zob-harness/src/topology/teams.ts +113 -0
  166. package/.pi/extensions/zob-harness/src/types/core.ts +47 -0
  167. package/.pi/extensions/zob-harness/src/types.ts +939 -0
  168. package/.pi/extensions/zob-harness/src/utils/AGENTS.md +22 -0
  169. package/.pi/extensions/zob-harness/src/utils/formatting.ts +34 -0
  170. package/.pi/extensions/zob-harness/src/utils/hashing.ts +11 -0
  171. package/.pi/extensions/zob-harness/src/utils/json.ts +28 -0
  172. package/.pi/extensions/zob-harness/src/utils/paths.ts +54 -0
  173. package/.pi/extensions/zob-harness/src/utils/records.ts +25 -0
  174. package/.pi/extensions/zob-harness/src/utils/resources.ts +38 -0
  175. package/.pi/extensions/zob-harness/src/worker-pool.ts +672 -0
  176. package/.pi/extensions/zob-harness/src/workspace-claims.ts +297 -0
  177. package/.pi/extensions/zob-switch/index.ts +180 -0
  178. package/.pi/factories/budget-preflight-dry-run/batch-manifest.json +59 -0
  179. package/.pi/factories/budget-preflight-dry-run/factory.json +94 -0
  180. package/.pi/factories/budget-preflight-dry-run/pilot-manifest.json +50 -0
  181. package/.pi/factories/budget-preflight-dry-run/smoke-manifest.json +43 -0
  182. package/.pi/factories/code-review-matrix/batch-manifest.json +61 -0
  183. package/.pi/factories/code-review-matrix/factory.json +163 -0
  184. package/.pi/factories/code-review-matrix/pilot-manifest.json +41 -0
  185. package/.pi/factories/code-review-matrix/smoke-manifest.json +35 -0
  186. package/.pi/factories/factory-forge/batch-manifest.json +56 -0
  187. package/.pi/factories/factory-forge/factory.json +84 -0
  188. package/.pi/factories/factory-forge/pilot-manifest.json +32 -0
  189. package/.pi/factories/factory-forge/smoke-manifest.json +19 -0
  190. package/.pi/factories/opencode-pattern-canonizer/batch-manifest.json +54 -0
  191. package/.pi/factories/opencode-pattern-canonizer/factory.json +86 -0
  192. package/.pi/factories/opencode-pattern-canonizer/pilot-manifest.json +39 -0
  193. package/.pi/factories/opencode-pattern-canonizer/smoke-manifest.json +26 -0
  194. package/.pi/factories/project-dna/README.md +182 -0
  195. package/.pi/factories/project-dna/batch-manifest.json +37 -0
  196. package/.pi/factories/project-dna/example-project-dna-manifest-v2.json +80 -0
  197. package/.pi/factories/project-dna/example-project-dna-manifest.json +58 -0
  198. package/.pi/factories/project-dna/factory.json +131 -0
  199. package/.pi/factories/project-dna/golden-cases-smoke.json +62 -0
  200. package/.pi/factories/project-dna/pi-agentic-ontology.json +88 -0
  201. package/.pi/factories/project-dna/pilot-manifest.json +32 -0
  202. package/.pi/factories/project-dna/schemas/benchmark-suite.schema.json +27 -0
  203. package/.pi/factories/project-dna/schemas/code-knowledge-graph.schema.json +97 -0
  204. package/.pi/factories/project-dna/schemas/context-pack.schema.json +43 -0
  205. package/.pi/factories/project-dna/schemas/golden-case.schema.json +36 -0
  206. package/.pi/factories/project-dna/schemas/manifest-v2.schema.json +128 -0
  207. package/.pi/factories/project-dna/schemas/manifest.schema.json +77 -0
  208. package/.pi/factories/project-dna/schemas/ontology.schema.json +45 -0
  209. package/.pi/factories/project-dna/schemas/project-fingerprint.schema.json +28 -0
  210. package/.pi/factories/project-dna/schemas/query-steward-report.schema.json +52 -0
  211. package/.pi/factories/project-dna/smoke-manifest.json +27 -0
  212. package/.pi/factories/roadmap-smoke-lots/batch-manifest.json +49 -0
  213. package/.pi/factories/roadmap-smoke-lots/factory.json +89 -0
  214. package/.pi/factories/roadmap-smoke-lots/pilot-manifest.json +50 -0
  215. package/.pi/factories/roadmap-smoke-lots/smoke-manifest.json +35 -0
  216. package/.pi/git-policy.json +120 -0
  217. package/.pi/mission-control/zob_coms_transport.json +64 -0
  218. package/.pi/model-catalog.example.json +345 -0
  219. package/.pi/model-economy.example.json +196 -0
  220. package/.pi/model-routing.json +86 -0
  221. package/.pi/orchestrations/adaptive-chief-vision.json +193 -0
  222. package/.pi/orchestrations/ceo-feature-build.json +182 -0
  223. package/.pi/orchestrations/readonly-dynamic-smoke.json +75 -0
  224. package/.pi/output-contracts/agent-event.v1.json +19 -0
  225. package/.pi/output-contracts/base.v1.json +24 -0
  226. package/.pi/output-contracts/brain-lookup.v1.json +21 -0
  227. package/.pi/output-contracts/clarification.v1.json +21 -0
  228. package/.pi/output-contracts/context-pack.v1.json +20 -0
  229. package/.pi/output-contracts/context-request.v1.json +21 -0
  230. package/.pi/output-contracts/context-steward.v1.json +19 -0
  231. package/.pi/output-contracts/context-writeback-proposal.v1.json +18 -0
  232. package/.pi/output-contracts/delegation-request.v1.json +21 -0
  233. package/.pi/output-contracts/explore.v1.json +52 -0
  234. package/.pi/output-contracts/factory.v1.json +48 -0
  235. package/.pi/output-contracts/guidance-steward.v1.json +18 -0
  236. package/.pi/output-contracts/implement.v1.json +40 -0
  237. package/.pi/output-contracts/launch-authorization.v1.json +21 -0
  238. package/.pi/output-contracts/lead-plan.v1.json +22 -0
  239. package/.pi/output-contracts/mission-readiness.v1.json +20 -0
  240. package/.pi/output-contracts/oracle-merge.v1.json +44 -0
  241. package/.pi/output-contracts/oracle-request.v1.json +20 -0
  242. package/.pi/output-contracts/oracle.v1.json +44 -0
  243. package/.pi/output-contracts/orchestration-profile.v1.json +22 -0
  244. package/.pi/output-contracts/plan.v1.json +48 -0
  245. package/.pi/output-contracts/prompt-pack.v1.json +20 -0
  246. package/.pi/output-contracts/qa.v1.json +40 -0
  247. package/.pi/output-contracts/research.v1.json +36 -0
  248. package/.pi/output-contracts/spec.v1.json +22 -0
  249. package/.pi/output-contracts/synthesis.v1.json +44 -0
  250. package/.pi/output-contracts/temp-agent-card.v1.json +23 -0
  251. package/.pi/output-contracts/todo-child-result.v1.json +20 -0
  252. package/.pi/output-contracts/todo-child-result.v2.json +22 -0
  253. package/.pi/output-contracts/todo-claim-validation.v1.json +22 -0
  254. package/.pi/output-contracts/todo-split-request.v1.json +20 -0
  255. package/.pi/prompts/adaptive-workflow.md +63 -0
  256. package/.pi/prompts/autonomous-runtime.md +15 -0
  257. package/.pi/prompts/benchmark-contender.md +15 -0
  258. package/.pi/prompts/benchmark-judge.md +19 -0
  259. package/.pi/prompts/clarify-spec.md +20 -0
  260. package/.pi/prompts/compute-plan.md +36 -0
  261. package/.pi/prompts/compute-preview.md +42 -0
  262. package/.pi/prompts/contract.md +29 -0
  263. package/.pi/prompts/explore.md +13 -0
  264. package/.pi/prompts/factory-run.md +36 -0
  265. package/.pi/prompts/factory.md +20 -0
  266. package/.pi/prompts/implement.md +27 -0
  267. package/.pi/prompts/model-catalog.md +68 -0
  268. package/.pi/prompts/model-economy.md +64 -0
  269. package/.pi/prompts/oracle-merge.md +18 -0
  270. package/.pi/prompts/oracle.md +13 -0
  271. package/.pi/prompts/orchestrator.md +48 -0
  272. package/.pi/prompts/parallel-review.md +21 -0
  273. package/.pi/prompts/plan.md +21 -0
  274. package/.pi/prompts/project-dna.md +90 -0
  275. package/.pi/prompts/refactor-oracle.md +23 -0
  276. package/.pi/prompts/refactor-slice.md +24 -0
  277. package/.pi/prompts/research.md +20 -0
  278. package/.pi/prompts/spec.md +19 -0
  279. package/.pi/prompts/synthesis.md +18 -0
  280. package/.pi/rules/always.md +38 -0
  281. package/.pi/rules/docs.md +32 -0
  282. package/.pi/rules/factory.md +44 -0
  283. package/.pi/rules/oracle.md +34 -0
  284. package/.pi/rules/orchestration.md +44 -0
  285. package/.pi/rules/project.md +34 -0
  286. package/.pi/rules/prompts.md +43 -0
  287. package/.pi/rules/runtime.md +43 -0
  288. package/.pi/rules/sandbox.md +43 -0
  289. package/.pi/settings.json +28 -0
  290. package/.pi/skills/zob-agentic-access/SKILL.md +20 -0
  291. package/.pi/skills/zob-autonomous-runtime/SKILL.md +41 -0
  292. package/.pi/skills/zob-commit/SKILL.md +79 -0
  293. package/.pi/skills/zob-compaction-policy/SKILL.md +92 -0
  294. package/.pi/skills/zob-compute-profile/SKILL.md +108 -0
  295. package/.pi/skills/zob-coms-safety/SKILL.md +54 -0
  296. package/.pi/skills/zob-coms-v2-live/SKILL.md +47 -0
  297. package/.pi/skills/zob-delegation-routing/SKILL.md +82 -0
  298. package/.pi/skills/zob-factory/SKILL.md +28 -0
  299. package/.pi/skills/zob-goal-todo-tree/SKILL.md +279 -0
  300. package/.pi/skills/zob-harness/SKILL.md +68 -0
  301. package/.pi/skills/zob-mission-control-coms/SKILL.md +39 -0
  302. package/.pi/skills/zob-oracle/SKILL.md +21 -0
  303. package/.pi/skills/zob-owner-pool-drill-writer/SKILL.md +244 -0
  304. package/.pi/skills/zob-owner-pool-launcher/SKILL.md +261 -0
  305. package/.pi/skills/zob-project-dna/SKILL.md +275 -0
  306. package/.pi/skills/zob-sandbox/SKILL.md +29 -0
  307. package/.pi/skills/zob-spec/SKILL.md +25 -0
  308. package/.pi/skills/zob-split-refactor/SKILL.md +39 -0
  309. package/.pi/skills/zob-tool-router/SKILL.md +104 -0
  310. package/.pi/teams/zob-core.json +122 -0
  311. package/AGENTS.md +89 -0
  312. package/CONTRIBUTING.md +56 -0
  313. package/LICENSE +21 -0
  314. package/README.md +360 -0
  315. package/SECURITY.md +35 -0
  316. package/SOURCE_INDEX.md +46 -0
  317. package/package.json +135 -0
  318. package/scripts/README.md +57 -0
  319. package/scripts/autonomy/mission-readiness-secret-smoke.mjs +90 -0
  320. package/scripts/compute-profile/plan-workflow.mjs +85 -0
  321. package/scripts/compute-profile/preview.mjs +242 -0
  322. package/scripts/compute-profile/regression-smoke.mjs +38 -0
  323. package/scripts/compute-profile/summarize.mjs +72 -0
  324. package/scripts/compute-profile/validate-policy.mjs +50 -0
  325. package/scripts/compute-profile/validate-preview.mjs +95 -0
  326. package/scripts/compute-profile/validate-workflow.mjs +58 -0
  327. package/scripts/git-ops/commit-policy-smoke.mjs +221 -0
  328. package/scripts/goal-todo/child-goal-ref-smoke.mjs +252 -0
  329. package/scripts/harness-switch/static-smoke.mjs +43 -0
  330. package/scripts/model-catalog/validate-economy.mjs +223 -0
  331. package/scripts/model-catalog/validate.mjs +199 -0
  332. package/scripts/package-surface/validate-script-refs.mjs +190 -0
  333. package/scripts/path-policy/validate-smoke.mjs +103 -0
  334. package/scripts/project-dna/bench-smoke.mjs +217 -0
  335. package/scripts/project-dna/build-capsules.mjs +207 -0
  336. package/scripts/project-dna/build-sample-spec.mjs +140 -0
  337. package/scripts/project-dna/emit-golden-cases.mjs +75 -0
  338. package/scripts/project-dna/emit-ontology.mjs +75 -0
  339. package/scripts/project-dna/generate-sample.mjs +302 -0
  340. package/scripts/project-dna/oracle-review-smoke.mjs +157 -0
  341. package/scripts/project-dna/plan-workflow.mjs +289 -0
  342. package/scripts/project-dna/query-context.mjs +276 -0
  343. package/scripts/project-dna/query-steward.mjs +149 -0
  344. package/scripts/project-dna/scan.mjs +553 -0
  345. package/scripts/project-dna/validate-5of5.mjs +159 -0
  346. package/scripts/project-dna/validate-golden-cases.mjs +78 -0
  347. package/scripts/project-dna/validate-ontology.mjs +97 -0
  348. package/scripts/project-dna/validate-sample-project.mjs +105 -0
  349. package/scripts/project-dna/validate-scaffold.mjs +383 -0
  350. package/scripts/project-dna/validate-scan-artifacts.mjs +187 -0
  351. package/scripts/project-dna/validate-workflow.mjs +166 -0
  352. package/scripts/start-pi.sh +4 -0
  353. package/scripts/worker-pool/static-smoke.mjs +54 -0
  354. package/scripts/zpeer-local-e2e-smoke.mjs +395 -0
  355. package/scripts/zpeer-static-smoke.mjs +129 -0
  356. package/tsconfig.json +12 -0
@@ -0,0 +1,1410 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+
4
+ import type { HarnessRuntimeState } from "./runtime/state.js";
5
+ import type {
6
+ AddGoalTodoInput,
7
+ GoalRoomTodoReducerDecision,
8
+ GoalTodoArtifacts,
9
+ GoalTodoChildGoalStatus,
10
+ GoalTodoClaimRef,
11
+ GoalTodoClaimTargetReadiness,
12
+ GoalTodoClaimValidationConfidence,
13
+ GoalTodoClaimValidationRecommendedAction,
14
+ GoalTodoClaimValidationRef,
15
+ GoalTodoClaimValidationStatus,
16
+ GoalTodoClaimValidationVerdict,
17
+ GoalTodoCommandResult,
18
+ GoalTodoCompletionDiagnostics,
19
+ GoalTodoDelegationRef,
20
+ GoalTodoDelegationStatus,
21
+ GoalTodoEvent,
22
+ GoalTodoEventSource,
23
+ GoalTodoNode,
24
+ GoalTodoOwner,
25
+ GoalTodoPolicy,
26
+ GoalTodoPriority,
27
+ GoalTodoState,
28
+ GoalTodoStatus,
29
+ GoalTodoStatusClaim,
30
+ GoalTodoSummary,
31
+ ResolveGoalTodoAction,
32
+ TodoClaimValidationResult,
33
+ TodoSplitRequest,
34
+ } from "./goal-todo-types.js";
35
+ import { recordZcommitOwnedPaths, type ZcommitChildChangedPathRef } from "./git-ops.js";
36
+ import { sha256 } from "./utils/hashing.js";
37
+ import { isRecord } from "./utils/records.js";
38
+
39
+ export const ZOB_GOAL_TODO_ENTRY_TYPE = "zob-goal-todo";
40
+
41
+ export type {
42
+ AddGoalTodoInput,
43
+ GoalRoomTodoReducerAction,
44
+ GoalRoomTodoReducerDecision,
45
+ GoalTodoArtifacts,
46
+ GoalTodoChildGoalStatus,
47
+ GoalTodoClaimRef,
48
+ GoalTodoClaimTargetReadiness,
49
+ GoalTodoClaimValidationConfidence,
50
+ GoalTodoClaimValidationRecommendedAction,
51
+ GoalTodoClaimValidationRef,
52
+ GoalTodoClaimValidationStatus,
53
+ GoalTodoClaimValidationVerdict,
54
+ GoalTodoCommandResult,
55
+ GoalTodoCompletionDiagnostics,
56
+ GoalTodoDelegationRef,
57
+ GoalTodoDelegationStatus,
58
+ GoalTodoEvent,
59
+ GoalTodoEventSource,
60
+ GoalTodoNode,
61
+ GoalTodoOwner,
62
+ GoalTodoPolicy,
63
+ GoalTodoPriority,
64
+ GoalTodoState,
65
+ GoalTodoStatus,
66
+ GoalTodoStatusClaim,
67
+ GoalTodoSummary,
68
+ ResolveGoalTodoAction,
69
+ TodoClaimValidationResult,
70
+ TodoSplitRequest,
71
+ TodoSplitRequestAction,
72
+ TodoSplitRiskLevel,
73
+ } from "./goal-todo-types.js";
74
+
75
+ const OPEN_REQUIRED_STATUSES = new Set<GoalTodoStatus>(["planned", "ready", "in_progress", "delegated", "claim_returned", "needs_review", "needs_oracle", "needs_user", "blocked"]);
76
+ const ACTIVE_STATUSES = new Set<GoalTodoStatus>(["ready", "in_progress", "delegated", "claim_returned", "needs_review"]);
77
+ const ACTIONABLE_STATUSES = new Set<GoalTodoStatus>(["planned", "ready", "in_progress", "needs_review", "needs_user", "needs_oracle", "blocked"]);
78
+ const VALID_STATUS: readonly GoalTodoStatus[] = ["planned", "ready", "in_progress", "delegated", "claim_returned", "needs_review", "needs_oracle", "needs_user", "blocked", "done", "skipped"];
79
+ const VALID_OWNER: readonly GoalTodoOwner[] = ["agent", "user", "oracle", "subagent", "factory", "orchestration"];
80
+ const VALID_PRIORITY: readonly GoalTodoPriority[] = ["low", "normal", "high", "critical"];
81
+ const VALID_DELEGATION_STATUS: readonly GoalTodoDelegationStatus[] = ["queued", "running", "claim_returned", "accepted", "rejected", "failed"];
82
+ const VALID_CHILD_GOAL_STATUS: readonly GoalTodoChildGoalStatus[] = ["ready_for_oracle", "incomplete", "blocked"];
83
+ const VALID_STATUS_CLAIM: readonly GoalTodoStatusClaim[] = ["done", "incomplete", "blocked"];
84
+ const VALID_TARGET_READINESS: readonly GoalTodoClaimTargetReadiness[] = ["ready_for_parent_acceptance", "needs_parent_review", "blocked"];
85
+ const VALID_VALIDATION_STATUS: readonly GoalTodoClaimValidationStatus[] = ["queued", "running", "passed", "warn", "failed", "blocked"];
86
+ const VALID_VALIDATION_VERDICT: readonly GoalTodoClaimValidationVerdict[] = ["PASS", "WARN", "FAIL"];
87
+ const VALID_VALIDATION_ACTION: readonly GoalTodoClaimValidationRecommendedAction[] = ["accept_claim", "needs_review", "reject_claim", "block"];
88
+ const VALID_VALIDATION_CONFIDENCE: readonly GoalTodoClaimValidationConfidence[] = ["LOW", "MEDIUM", "HIGH"];
89
+ const SHA256_HEX = /^[a-f0-9]{64}$/i;
90
+
91
+ export function defaultGoalTodoPolicy(): GoalTodoPolicy {
92
+ return {
93
+ maxTodoDepth: 6,
94
+ maxDelegationDepth: 4,
95
+ maxChildrenPerTodo: 8,
96
+ maxOpenTodos: 80,
97
+ requireEvidenceForCritical: true,
98
+ parentOwnedClaims: true,
99
+ oracleBeforeGoalComplete: true,
100
+ };
101
+ }
102
+
103
+ export function createGoalTodoState(): GoalTodoState {
104
+ return { nodes: [], policy: defaultGoalTodoPolicy() };
105
+ }
106
+
107
+ function unixSeconds(): number {
108
+ return Math.floor(Date.now() / 1000);
109
+ }
110
+
111
+ function includesString<T extends string>(values: readonly T[], value: unknown): value is T {
112
+ return typeof value === "string" && (values as readonly string[]).includes(value);
113
+ }
114
+
115
+ function stringArray(value: unknown): string[] {
116
+ return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string" && item.trim().length > 0) : [];
117
+ }
118
+
119
+ function numberField(record: Record<string, unknown>, key: string): number | undefined {
120
+ const value = record[key];
121
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
122
+ }
123
+
124
+ function cloneNode(node: GoalTodoNode): GoalTodoNode {
125
+ return {
126
+ ...node,
127
+ acceptanceCriteria: [...node.acceptanceCriteria],
128
+ evidenceRefs: [...node.evidenceRefs],
129
+ validationCommands: [...node.validationCommands],
130
+ delegation: node.delegation ? { ...node.delegation } : undefined,
131
+ claim: node.claim ? { ...node.claim, acceptanceBlockers: [...node.claim.acceptanceBlockers], childChangedPaths: node.claim.childChangedPaths ? node.claim.childChangedPaths.map((ref) => ({ ...ref })) : undefined } : undefined,
132
+ validation: node.validation ? { ...node.validation, evidenceRefs: [...node.validation.evidenceRefs], validationCommands: [...node.validation.validationCommands], blockingIssues: [...node.validation.blockingIssues] } : undefined,
133
+ artifacts: node.artifacts
134
+ ? {
135
+ reports: node.artifacts.reports ? [...node.artifacts.reports] : undefined,
136
+ checkpoints: node.artifacts.checkpoints ? [...node.artifacts.checkpoints] : undefined,
137
+ sentinels: node.artifacts.sentinels ? [...node.artifacts.sentinels] : undefined,
138
+ taskHash: node.artifacts.taskHash,
139
+ outputHash: node.artifacts.outputHash,
140
+ }
141
+ : undefined,
142
+ citations: node.citations ? [...node.citations] : undefined,
143
+ };
144
+ }
145
+
146
+ function clonePolicy(policy: GoalTodoPolicy): GoalTodoPolicy {
147
+ return { ...policy };
148
+ }
149
+
150
+ function normalizePolicy(value: unknown): GoalTodoPolicy | undefined {
151
+ if (!isRecord(value)) return undefined;
152
+ return {
153
+ maxTodoDepth: Math.max(1, Math.trunc(numberField(value, "maxTodoDepth") ?? defaultGoalTodoPolicy().maxTodoDepth)),
154
+ maxDelegationDepth: Math.max(1, Math.trunc(numberField(value, "maxDelegationDepth") ?? defaultGoalTodoPolicy().maxDelegationDepth)),
155
+ maxChildrenPerTodo: Math.max(1, Math.trunc(numberField(value, "maxChildrenPerTodo") ?? defaultGoalTodoPolicy().maxChildrenPerTodo)),
156
+ maxOpenTodos: Math.max(1, Math.trunc(numberField(value, "maxOpenTodos") ?? defaultGoalTodoPolicy().maxOpenTodos)),
157
+ requireEvidenceForCritical: true,
158
+ parentOwnedClaims: true,
159
+ oracleBeforeGoalComplete: true,
160
+ };
161
+ }
162
+
163
+ function normalizeDelegation(value: unknown): GoalTodoDelegationRef | undefined {
164
+ if (!isRecord(value)) return undefined;
165
+ const status = includesString(VALID_DELEGATION_STATUS, value.status) ? value.status : "running";
166
+ return {
167
+ runId: typeof value.runId === "string" ? value.runId : undefined,
168
+ agent: typeof value.agent === "string" ? value.agent : undefined,
169
+ childGoalId: typeof value.childGoalId === "string" ? value.childGoalId : undefined,
170
+ requestId: typeof value.requestId === "string" ? value.requestId : undefined,
171
+ delegationDepth: Math.max(0, Math.trunc(numberField(value, "delegationDepth") ?? 0)),
172
+ status,
173
+ };
174
+ }
175
+
176
+ function normalizeArtifacts(value: unknown): GoalTodoArtifacts | undefined {
177
+ if (!isRecord(value)) return undefined;
178
+ return {
179
+ reports: stringArray(value.reports),
180
+ checkpoints: stringArray(value.checkpoints),
181
+ sentinels: stringArray(value.sentinels),
182
+ taskHash: typeof value.taskHash === "string" ? value.taskHash : undefined,
183
+ outputHash: typeof value.outputHash === "string" ? value.outputHash : undefined,
184
+ };
185
+ }
186
+
187
+ function normalizeChildChangedPathRefs(value: unknown): ZcommitChildChangedPathRef[] {
188
+ if (!Array.isArray(value)) return [];
189
+ return value.flatMap((item) => {
190
+ if (!isRecord(item) || typeof item.path !== "string" || typeof item.pathHash !== "string" || typeof item.status !== "string") return [];
191
+ return [{ path: item.path, pathHash: item.pathHash, status: item.status, contentHash: typeof item.contentHash === "string" ? item.contentHash : undefined }];
192
+ }).slice(0, 100);
193
+ }
194
+
195
+ function normalizeClaim(value: unknown): GoalTodoClaimRef | undefined {
196
+ if (!isRecord(value) || typeof value.claimHash !== "string") return undefined;
197
+ return {
198
+ claimHash: value.claimHash,
199
+ runId: typeof value.runId === "string" ? value.runId : undefined,
200
+ outputHash: typeof value.outputHash === "string" ? value.outputHash : undefined,
201
+ outputContract: typeof value.outputContract === "string" ? value.outputContract : undefined,
202
+ gatePassed: typeof value.gatePassed === "boolean" ? value.gatePassed : undefined,
203
+ childGoalStatus: includesString(VALID_CHILD_GOAL_STATUS, value.childGoalStatus) ? value.childGoalStatus : undefined,
204
+ statusClaim: includesString(VALID_STATUS_CLAIM, value.statusClaim) ? value.statusClaim : undefined,
205
+ targetReadiness: includesString(VALID_TARGET_READINESS, value.targetReadiness) ? value.targetReadiness : undefined,
206
+ acceptanceBlockers: stringArray(value.acceptanceBlockers),
207
+ noShip: typeof value.noShip === "boolean" ? value.noShip : undefined,
208
+ childChangedPaths: normalizeChildChangedPathRefs(value.childChangedPaths),
209
+ returnedAt: Math.trunc(numberField(value, "returnedAt") ?? unixSeconds()),
210
+ };
211
+ }
212
+
213
+ function normalizeValidation(value: unknown): GoalTodoClaimValidationRef | undefined {
214
+ if (!isRecord(value)) return undefined;
215
+ const status = includesString(VALID_VALIDATION_STATUS, value.status) ? value.status : undefined;
216
+ if (!status) return undefined;
217
+ return {
218
+ runId: typeof value.runId === "string" ? value.runId : undefined,
219
+ agent: typeof value.agent === "string" ? value.agent : undefined,
220
+ status,
221
+ verdict: includesString(VALID_VALIDATION_VERDICT, value.verdict) ? value.verdict : undefined,
222
+ recommendedAction: includesString(VALID_VALIDATION_ACTION, value.recommendedAction) ? value.recommendedAction : undefined,
223
+ noShip: typeof value.noShip === "boolean" ? value.noShip : undefined,
224
+ outputHash: typeof value.outputHash === "string" ? value.outputHash : undefined,
225
+ evidenceRefs: stringArray(value.evidenceRefs),
226
+ validationCommands: stringArray(value.validationCommands),
227
+ blockingIssues: stringArray(value.blockingIssues),
228
+ confidence: includesString(VALID_VALIDATION_CONFIDENCE, value.confidence) ? value.confidence : undefined,
229
+ requestedAt: typeof value.requestedAt === "number" ? Math.trunc(value.requestedAt) : undefined,
230
+ validatedAt: typeof value.validatedAt === "number" ? Math.trunc(value.validatedAt) : undefined,
231
+ };
232
+ }
233
+
234
+ function cloneValidation(validation: GoalTodoClaimValidationRef): GoalTodoClaimValidationRef {
235
+ return { ...validation, evidenceRefs: [...validation.evidenceRefs], validationCommands: [...validation.validationCommands], blockingIssues: [...validation.blockingIssues] };
236
+ }
237
+
238
+ function cloneClaim(claim: GoalTodoClaimRef): GoalTodoClaimRef {
239
+ return { ...claim, acceptanceBlockers: [...claim.acceptanceBlockers] };
240
+ }
241
+
242
+ function normalizeNode(value: unknown): GoalTodoNode | undefined {
243
+ if (!isRecord(value)) return undefined;
244
+ if (typeof value.id !== "string" || typeof value.goalId !== "string" || typeof value.title !== "string") return undefined;
245
+ const status = includesString(VALID_STATUS, value.status) ? value.status : "planned";
246
+ const owner = includesString(VALID_OWNER, value.owner) ? value.owner : "agent";
247
+ const priority = includesString(VALID_PRIORITY, value.priority) ? value.priority : "normal";
248
+ const now = unixSeconds();
249
+ return {
250
+ id: value.id,
251
+ goalId: value.goalId,
252
+ parentId: typeof value.parentId === "string" ? value.parentId : undefined,
253
+ path: typeof value.path === "string" && value.path.trim().length > 0 ? value.path : "1",
254
+ depth: Math.max(1, Math.trunc(numberField(value, "depth") ?? 1)),
255
+ title: value.title,
256
+ descriptionHash: typeof value.descriptionHash === "string" ? value.descriptionHash : undefined,
257
+ status,
258
+ owner,
259
+ required: value.required !== false,
260
+ priority,
261
+ acceptanceCriteria: stringArray(value.acceptanceCriteria),
262
+ evidenceRefs: stringArray(value.evidenceRefs),
263
+ validationCommands: stringArray(value.validationCommands),
264
+ delegation: normalizeDelegation(value.delegation),
265
+ claim: normalizeClaim(value.claim),
266
+ validation: normalizeValidation(value.validation),
267
+ artifacts: normalizeArtifacts(value.artifacts),
268
+ contextScopeId: typeof value.contextScopeId === "string" ? value.contextScopeId : undefined,
269
+ contextPackRef: typeof value.contextPackRef === "string" ? value.contextPackRef : undefined,
270
+ citations: stringArray(value.citations),
271
+ freshness: typeof value.freshness === "string" ? value.freshness : undefined,
272
+ blocker: typeof value.blocker === "string" ? value.blocker : undefined,
273
+ skipReason: typeof value.skipReason === "string" ? value.skipReason : undefined,
274
+ reviewNoShip: value.reviewNoShip === true,
275
+ createdAt: Math.trunc(numberField(value, "createdAt") ?? now),
276
+ updatedAt: Math.trunc(numberField(value, "updatedAt") ?? now),
277
+ };
278
+ }
279
+
280
+ function normalizePatch(value: Partial<GoalTodoNode>): Partial<GoalTodoNode> {
281
+ const patch: Partial<GoalTodoNode> = {};
282
+ if ("parentId" in value && (typeof value.parentId === "string" || value.parentId === undefined)) patch.parentId = value.parentId;
283
+ if (typeof value.path === "string") patch.path = value.path;
284
+ if (typeof value.depth === "number" && Number.isFinite(value.depth)) patch.depth = Math.max(1, Math.trunc(value.depth));
285
+ if (typeof value.title === "string" && value.title.trim().length > 0) patch.title = value.title.trim();
286
+ if ("descriptionHash" in value && (typeof value.descriptionHash === "string" || value.descriptionHash === undefined)) patch.descriptionHash = value.descriptionHash;
287
+ if (includesString(VALID_STATUS, value.status)) patch.status = value.status;
288
+ if (includesString(VALID_OWNER, value.owner)) patch.owner = value.owner;
289
+ if (typeof value.required === "boolean") patch.required = value.required;
290
+ if (includesString(VALID_PRIORITY, value.priority)) patch.priority = value.priority;
291
+ if (Array.isArray(value.acceptanceCriteria)) patch.acceptanceCriteria = value.acceptanceCriteria.filter((item): item is string => typeof item === "string");
292
+ if (Array.isArray(value.evidenceRefs)) patch.evidenceRefs = value.evidenceRefs.filter((item): item is string => typeof item === "string");
293
+ if (Array.isArray(value.validationCommands)) patch.validationCommands = value.validationCommands.filter((item): item is string => typeof item === "string");
294
+ if (value.delegation) patch.delegation = { ...value.delegation };
295
+ if (value.claim) patch.claim = cloneClaim(value.claim);
296
+ else if ("claim" in value && value.claim === undefined) patch.claim = undefined;
297
+ if (value.validation) patch.validation = cloneValidation(value.validation);
298
+ else if ("validation" in value && value.validation === undefined) patch.validation = undefined;
299
+ if (value.artifacts) patch.artifacts = { ...value.artifacts };
300
+ if ("contextScopeId" in value && (typeof value.contextScopeId === "string" || value.contextScopeId === undefined)) patch.contextScopeId = value.contextScopeId;
301
+ if ("contextPackRef" in value && (typeof value.contextPackRef === "string" || value.contextPackRef === undefined)) patch.contextPackRef = value.contextPackRef;
302
+ if (Array.isArray(value.citations)) patch.citations = value.citations.filter((item): item is string => typeof item === "string");
303
+ if ("freshness" in value && (typeof value.freshness === "string" || value.freshness === undefined)) patch.freshness = value.freshness;
304
+ if ("blocker" in value && (typeof value.blocker === "string" || value.blocker === undefined)) patch.blocker = value.blocker;
305
+ if ("skipReason" in value && (typeof value.skipReason === "string" || value.skipReason === undefined)) patch.skipReason = value.skipReason;
306
+ if ("reviewNoShip" in value && (typeof value.reviewNoShip === "boolean" || value.reviewNoShip === undefined)) patch.reviewNoShip = value.reviewNoShip;
307
+ return patch;
308
+ }
309
+
310
+ function applyPatchToNode(node: GoalTodoNode, patch: Partial<GoalTodoNode>): GoalTodoNode {
311
+ return {
312
+ ...node,
313
+ ...normalizePatch(patch),
314
+ acceptanceCriteria: patch.acceptanceCriteria ? [...patch.acceptanceCriteria] : [...node.acceptanceCriteria],
315
+ evidenceRefs: patch.evidenceRefs ? [...patch.evidenceRefs] : [...node.evidenceRefs],
316
+ validationCommands: patch.validationCommands ? [...patch.validationCommands] : [...node.validationCommands],
317
+ delegation: patch.delegation ? { ...patch.delegation } : patch.delegation === undefined ? node.delegation ? { ...node.delegation } : undefined : undefined,
318
+ claim: patch.claim ? cloneClaim(patch.claim) : patch.claim === undefined ? node.claim ? cloneClaim(node.claim) : undefined : undefined,
319
+ validation: patch.validation ? cloneValidation(patch.validation) : patch.validation === undefined ? node.validation ? cloneValidation(node.validation) : undefined : undefined,
320
+ artifacts: patch.artifacts ? { ...patch.artifacts } : patch.artifacts === undefined ? node.artifacts ? { ...node.artifacts } : undefined : undefined,
321
+ citations: patch.citations ? [...patch.citations] : node.citations ? [...node.citations] : undefined,
322
+ updatedAt: unixSeconds(),
323
+ };
324
+ }
325
+
326
+ function replaceNode(state: GoalTodoState, node: GoalTodoNode): void {
327
+ const index = state.nodes.findIndex((candidate) => candidate.id === node.id && candidate.goalId === node.goalId);
328
+ if (index >= 0) state.nodes[index] = cloneNode(node);
329
+ else state.nodes.push(cloneNode(node));
330
+ }
331
+
332
+ function removeGoalNodes(state: GoalTodoState, goalId: string): void {
333
+ state.nodes = state.nodes.filter((node) => node.goalId !== goalId);
334
+ if (state.focusTodoId && !state.nodes.some((node) => node.id === state.focusTodoId)) state.focusTodoId = undefined;
335
+ }
336
+
337
+ function applyEvent(state: GoalTodoState, event: GoalTodoEvent): void {
338
+ if (event.kind === "policy_set") {
339
+ state.policy = clonePolicy(event.policy);
340
+ return;
341
+ }
342
+ if (event.kind === "clear_goal_todos") {
343
+ removeGoalNodes(state, event.goalId);
344
+ return;
345
+ }
346
+ if (event.kind === "snapshot") {
347
+ removeGoalNodes(state, event.goalId);
348
+ for (const node of event.nodes) replaceNode(state, node);
349
+ if (event.policy) state.policy = clonePolicy(event.policy);
350
+ state.focusTodoId = event.focusTodoId;
351
+ return;
352
+ }
353
+ if (event.kind === "add") {
354
+ replaceNode(state, event.node);
355
+ return;
356
+ }
357
+ if (event.kind === "patch") {
358
+ const existing = state.nodes.find((node) => node.goalId === event.goalId && node.id === event.todoId);
359
+ if (existing) replaceNode(state, applyPatchToNode(existing, event.patch));
360
+ return;
361
+ }
362
+ if (event.kind === "move") {
363
+ const existing = state.nodes.find((node) => node.goalId === event.goalId && node.id === event.todoId);
364
+ if (existing) replaceNode(state, applyPatchToNode(existing, { parentId: event.parentId }));
365
+ renumberGoalPaths(state, event.goalId);
366
+ return;
367
+ }
368
+ if (event.kind === "split") {
369
+ const parent = state.nodes.find((node) => node.goalId === event.goalId && node.id === event.todoId);
370
+ if (parent) replaceNode(state, applyPatchToNode(parent, { status: "in_progress" }));
371
+ renumberGoalPaths(state, event.goalId);
372
+ return;
373
+ }
374
+ if (event.kind === "delegate_link") {
375
+ const existing = state.nodes.find((node) => node.goalId === event.goalId && node.id === event.todoId);
376
+ if (existing) replaceNode(state, applyPatchToNode(existing, { status: "delegated", owner: "subagent", delegation: event.delegation, blocker: undefined }));
377
+ return;
378
+ }
379
+ if (event.kind === "claim_returned") {
380
+ const existing = state.nodes.find((node) => node.goalId === event.goalId && node.id === event.todoId);
381
+ if (existing) {
382
+ const evidenceRefs = [...new Set([...existing.evidenceRefs, ...event.evidenceRefs])];
383
+ const validationCommands = [...new Set([...existing.validationCommands, ...event.validationCommands])];
384
+ const claim: GoalTodoClaimRef = {
385
+ claimHash: event.claimHash,
386
+ runId: event.runId,
387
+ outputHash: event.outputHash,
388
+ outputContract: event.outputContract,
389
+ gatePassed: event.gatePassed,
390
+ childGoalStatus: event.childGoalStatus,
391
+ statusClaim: event.statusClaim,
392
+ targetReadiness: event.targetReadiness,
393
+ acceptanceBlockers: event.acceptanceBlockers ?? [],
394
+ noShip: event.noShip,
395
+ childChangedPaths: event.childChangedPaths ?? [],
396
+ returnedAt: event.at,
397
+ };
398
+ replaceNode(state, applyPatchToNode(existing, {
399
+ status: "claim_returned",
400
+ evidenceRefs,
401
+ validationCommands,
402
+ delegation: existing.delegation ? { ...existing.delegation, status: "claim_returned" } : undefined,
403
+ claim,
404
+ artifacts: { ...(existing.artifacts ?? {}), outputHash: event.outputHash ?? event.claimHash },
405
+ blocker: event.noShip === true ? "delegated claim returned advisory no_ship=true; parent review required" : undefined,
406
+ reviewNoShip: event.noShip === true,
407
+ }));
408
+ }
409
+ return;
410
+ }
411
+ if (event.kind === "claim_validation_requested") {
412
+ const existing = state.nodes.find((node) => node.goalId === event.goalId && node.id === event.todoId);
413
+ if (existing) replaceNode(state, applyPatchToNode(existing, {
414
+ status: "needs_oracle",
415
+ owner: "oracle",
416
+ validation: cloneValidation(event.validation),
417
+ blocker: undefined,
418
+ reviewNoShip: undefined,
419
+ }));
420
+ return;
421
+ }
422
+ if (event.kind === "claim_validation_returned") {
423
+ const existing = state.nodes.find((node) => node.goalId === event.goalId && node.id === event.todoId);
424
+ if (existing) {
425
+ const evidenceRefs = [...new Set([...existing.evidenceRefs, ...event.evidenceRefs])];
426
+ const validationCommands = [...new Set([...existing.validationCommands, ...event.validationCommands])];
427
+ const passed = event.validation.status === "passed" && event.validation.verdict === "PASS" && event.validation.noShip === false && hasOnlyNoneLike(event.validation.blockingIssues);
428
+ const warned = event.validation.status === "warn" && event.validation.verdict === "WARN" && event.validation.noShip === false && hasOnlyNoneLike(event.validation.blockingIssues);
429
+ replaceNode(state, applyPatchToNode(existing, {
430
+ status: passed ? "claim_returned" : warned ? "needs_review" : "blocked",
431
+ owner: passed ? "subagent" : warned ? "agent" : existing.owner,
432
+ evidenceRefs,
433
+ validationCommands,
434
+ validation: cloneValidation(event.validation),
435
+ blocker: passed ? undefined : event.validation.blockingIssues[0] ?? `claim validation ${event.validation.verdict ?? "blocked"}`,
436
+ reviewNoShip: passed ? undefined : true,
437
+ }));
438
+ }
439
+ return;
440
+ }
441
+ if (event.kind === "claim_accepted") {
442
+ const existing = state.nodes.find((node) => node.goalId === event.goalId && node.id === event.todoId);
443
+ if (existing) replaceNode(state, applyPatchToNode(existing, {
444
+ status: "done",
445
+ evidenceRefs: [...new Set([...existing.evidenceRefs, ...event.evidenceRefs])],
446
+ validationCommands: [...new Set([...existing.validationCommands, ...event.validationCommands])],
447
+ delegation: existing.delegation ? { ...existing.delegation, status: "accepted" } : undefined,
448
+ blocker: undefined,
449
+ reviewNoShip: undefined,
450
+ }));
451
+ return;
452
+ }
453
+ if (event.kind === "claim_rejected") {
454
+ const existing = state.nodes.find((node) => node.goalId === event.goalId && node.id === event.todoId);
455
+ if (existing) replaceNode(state, applyPatchToNode(existing, {
456
+ status: "blocked",
457
+ delegation: existing.delegation ? { ...existing.delegation, status: "rejected" } : undefined,
458
+ blocker: `claim rejected (${event.reasonHash.slice(0, 12)})`,
459
+ reviewNoShip: true,
460
+ }));
461
+ return;
462
+ }
463
+ if (event.kind === "focus") state.focusTodoId = event.todoId;
464
+ }
465
+
466
+ function normalizeEvent(value: unknown): GoalTodoEvent | undefined {
467
+ if (!isRecord(value) || value.version !== 1 || typeof value.kind !== "string" || typeof value.goalId !== "string") return undefined;
468
+ const source = value.source === "tool" || value.source === "runtime" || value.source === "delegation" || value.source === "import" ? value.source : "command";
469
+ const at = Math.trunc(numberField(value, "at") ?? unixSeconds());
470
+ if (value.kind === "policy_set") {
471
+ const policy = normalizePolicy(value.policy);
472
+ return policy ? { version: 1, kind: "policy_set", source, goalId: value.goalId, policy, at } : undefined;
473
+ }
474
+ if (value.kind === "add") {
475
+ const node = normalizeNode(value.node);
476
+ return node ? { version: 1, kind: "add", source, goalId: value.goalId, node, at } : undefined;
477
+ }
478
+ if (value.kind === "patch" && typeof value.todoId === "string" && isRecord(value.patch)) return { version: 1, kind: "patch", source, goalId: value.goalId, todoId: value.todoId, patch: value.patch as Partial<GoalTodoNode>, at };
479
+ if (value.kind === "move" && typeof value.todoId === "string") return { version: 1, kind: "move", source, goalId: value.goalId, todoId: value.todoId, parentId: typeof value.parentId === "string" ? value.parentId : undefined, at };
480
+ if (value.kind === "split" && typeof value.todoId === "string") return { version: 1, kind: "split", source, goalId: value.goalId, todoId: value.todoId, childIds: stringArray(value.childIds), at };
481
+ if (value.kind === "delegate_link" && typeof value.todoId === "string" && typeof value.runId === "string") {
482
+ const delegation = normalizeDelegation(value.delegation);
483
+ return delegation ? { version: 1, kind: "delegate_link", source, goalId: value.goalId, todoId: value.todoId, runId: value.runId, delegation, at } : undefined;
484
+ }
485
+ if (value.kind === "claim_returned" && typeof value.todoId === "string" && typeof value.claimHash === "string") return { version: 1, kind: "claim_returned", source, goalId: value.goalId, todoId: value.todoId, claimHash: value.claimHash, evidenceRefs: stringArray(value.evidenceRefs), validationCommands: stringArray(value.validationCommands), noShip: typeof value.noShip === "boolean" ? value.noShip : undefined, runId: typeof value.runId === "string" ? value.runId : undefined, outputHash: typeof value.outputHash === "string" ? value.outputHash : undefined, outputContract: typeof value.outputContract === "string" ? value.outputContract : undefined, gatePassed: typeof value.gatePassed === "boolean" ? value.gatePassed : undefined, childGoalStatus: includesString(VALID_CHILD_GOAL_STATUS, value.childGoalStatus) ? value.childGoalStatus : undefined, statusClaim: includesString(VALID_STATUS_CLAIM, value.statusClaim) ? value.statusClaim : undefined, targetReadiness: includesString(VALID_TARGET_READINESS, value.targetReadiness) ? value.targetReadiness : undefined, acceptanceBlockers: stringArray(value.acceptanceBlockers), childChangedPaths: normalizeChildChangedPathRefs(value.childChangedPaths), at };
486
+ if (value.kind === "claim_validation_requested" && typeof value.todoId === "string") {
487
+ const validation = normalizeValidation(value.validation);
488
+ return validation ? { version: 1, kind: "claim_validation_requested", source, goalId: value.goalId, todoId: value.todoId, validation, at } : undefined;
489
+ }
490
+ if (value.kind === "claim_validation_returned" && typeof value.todoId === "string") {
491
+ const validation = normalizeValidation(value.validation);
492
+ return validation ? { version: 1, kind: "claim_validation_returned", source, goalId: value.goalId, todoId: value.todoId, validation, evidenceRefs: stringArray(value.evidenceRefs), validationCommands: stringArray(value.validationCommands), noShip: typeof value.noShip === "boolean" ? value.noShip : undefined, at } : undefined;
493
+ }
494
+ if (value.kind === "claim_accepted" && typeof value.todoId === "string") return { version: 1, kind: "claim_accepted", source, goalId: value.goalId, todoId: value.todoId, evidenceRefs: stringArray(value.evidenceRefs), validationCommands: stringArray(value.validationCommands), at };
495
+ if (value.kind === "claim_rejected" && typeof value.todoId === "string" && typeof value.reasonHash === "string") return { version: 1, kind: "claim_rejected", source, goalId: value.goalId, todoId: value.todoId, reasonHash: value.reasonHash, at };
496
+ if (value.kind === "clear_goal_todos") return { version: 1, kind: "clear_goal_todos", source, goalId: value.goalId, at };
497
+ if (value.kind === "focus") return { version: 1, kind: "focus", source, goalId: value.goalId, todoId: typeof value.todoId === "string" ? value.todoId : undefined, at };
498
+ if (value.kind === "snapshot") {
499
+ const nodes = Array.isArray(value.nodes) ? value.nodes.map(normalizeNode).filter((node): node is GoalTodoNode => Boolean(node)) : [];
500
+ return { version: 1, kind: "snapshot", source, goalId: value.goalId, nodes, policy: normalizePolicy(value.policy), focusTodoId: typeof value.focusTodoId === "string" ? value.focusTodoId : undefined, at };
501
+ }
502
+ return undefined;
503
+ }
504
+
505
+ export function restoreGoalTodosFromBranch(entries: Iterable<unknown>): GoalTodoState {
506
+ const state = createGoalTodoState();
507
+ for (const entry of entries) {
508
+ if (!isRecord(entry) || entry.customType !== ZOB_GOAL_TODO_ENTRY_TYPE || !isRecord(entry.data)) continue;
509
+ const event = normalizeEvent(entry.data);
510
+ if (event) applyEvent(state, event);
511
+ }
512
+ return state;
513
+ }
514
+
515
+ function goalRoomMetadata(message: Record<string, unknown>): Record<string, unknown> {
516
+ return isRecord(message.metadata) ? message.metadata : {};
517
+ }
518
+
519
+ function reducerStringArray(value: unknown): string[] {
520
+ return stringArray(value).slice(0, 20);
521
+ }
522
+
523
+ function goalRoomMessageString(message: Record<string, unknown>, key: string): string | undefined {
524
+ const value = message[key];
525
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
526
+ }
527
+
528
+ function metadataString(metadata: Record<string, unknown>, key: string): string | undefined {
529
+ const value = metadata[key];
530
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
531
+ }
532
+
533
+ function baseTodoReducerDecision(message: Record<string, unknown>, reasonCodes: string[] = []): GoalRoomTodoReducerDecision {
534
+ return {
535
+ schema: "zob.todo-event-reducer-decision.v1",
536
+ action: "ignore",
537
+ reasonCodes,
538
+ goalId: goalRoomMessageString(message, "goalId"),
539
+ todoId: goalRoomMessageString(message, "todoId"),
540
+ sourceMsgId: goalRoomMessageString(message, "msgId"),
541
+ sourceKind: goalRoomMessageString(message, "kind"),
542
+ runId: goalRoomMessageString(message, "runId"),
543
+ outputHash: goalRoomMessageString(message, "outputHash"),
544
+ evidenceRefs: reducerStringArray(message.evidenceRefs),
545
+ validationCommands: [],
546
+ acceptanceBlockers: [],
547
+ parentOwnedActions: true,
548
+ directMutationByWorker: false,
549
+ reducerRequiredForTodoMutation: true,
550
+ bodyStored: false,
551
+ promptBodiesStored: false,
552
+ outputBodiesStored: false,
553
+ };
554
+ }
555
+
556
+ export function reduceGoalRoomEventToTodoDecision(message: Record<string, unknown>): GoalRoomTodoReducerDecision {
557
+ const decision = baseTodoReducerDecision(message);
558
+ const kind = goalRoomMessageString(message, "kind");
559
+ const bodyHash = goalRoomMessageString(message, "bodyHash");
560
+ const metadata = goalRoomMetadata(message);
561
+ decision.validationCommands = reducerStringArray(metadata.validationCommands);
562
+ decision.acceptanceBlockers = reducerStringArray(metadata.acceptanceBlockers);
563
+ decision.noShip = typeof metadata.noShip === "boolean" ? metadata.noShip : kind === "NO_SHIP_ALERT" ? true : undefined;
564
+ decision.childGoalStatus = includesString(VALID_CHILD_GOAL_STATUS, metadata.childGoalStatus) ? metadata.childGoalStatus : undefined;
565
+ decision.statusClaim = includesString(VALID_STATUS_CLAIM, metadata.statusClaim) ? metadata.statusClaim : undefined;
566
+ decision.targetReadiness = includesString(VALID_TARGET_READINESS, metadata.targetReadiness) ? metadata.targetReadiness : undefined;
567
+ decision.outputHash = goalRoomMessageString(message, "outputHash") ?? metadataString(metadata, "outputHash");
568
+
569
+ if (message.schema !== "zob.goal-room-message.v1") decision.reasonCodes.push("invalid_schema");
570
+ if (message.parentOwnedActions !== true || message.workerToWorkerDirect !== false || message.hiddenPeerChat !== false) decision.reasonCodes.push("not_parent_owned_visible_event");
571
+ if (message.bodyStored !== false || message.promptBodiesStored !== false || message.outputBodiesStored !== false) decision.reasonCodes.push("not_body_free");
572
+ if (!decision.goalId) decision.reasonCodes.push("missing_goal_id");
573
+ if (!decision.todoId) decision.reasonCodes.push("missing_todo_id");
574
+ if (!bodyHash || !SHA256_HEX.test(bodyHash)) decision.reasonCodes.push("missing_body_hash");
575
+ if (kind && !["TODO_CLAIM", "BLOCKER", "NO_SHIP_ALERT", "ORACLE_REQUEST", "HANDOFF", "DECISION", "STATUS_UPDATE", "ACTION_TAKEN", "ARTIFACT_READY", "FINDING", "RISK"].includes(kind)) decision.reasonCodes.push("unsupported_kind");
576
+ if (decision.reasonCodes.length > 0) return decision;
577
+ const validBodyHash = bodyHash as string;
578
+
579
+ if (kind === "TODO_CLAIM") {
580
+ const artifactRefs = reducerStringArray(message.artifactRefs);
581
+ const evidenceRefs = [...new Set([...decision.evidenceRefs, ...artifactRefs])];
582
+ decision.action = "return_claim";
583
+ decision.claimHash = validBodyHash;
584
+ decision.outputHash = decision.outputHash && SHA256_HEX.test(decision.outputHash) ? decision.outputHash : validBodyHash;
585
+ decision.evidenceRefs = evidenceRefs;
586
+ decision.statusClaim = decision.statusClaim ?? "done";
587
+ decision.childGoalStatus = decision.childGoalStatus ?? "ready_for_oracle";
588
+ decision.targetReadiness = decision.targetReadiness ?? (decision.noShip === true || decision.acceptanceBlockers.length > 0 ? "needs_parent_review" : "ready_for_parent_acceptance");
589
+ if (evidenceRefs.length === 0) decision.acceptanceBlockers = [...new Set([...decision.acceptanceBlockers, "missing_evidence_refs"] )];
590
+ if (decision.acceptanceBlockers.length > 0 && decision.noShip !== true) decision.noShip = true;
591
+ return decision;
592
+ }
593
+
594
+ if (kind === "BLOCKER" || kind === "NO_SHIP_ALERT") {
595
+ decision.action = "block";
596
+ decision.claimHash = validBodyHash;
597
+ decision.noShip = true;
598
+ decision.acceptanceBlockers = [...new Set([...decision.acceptanceBlockers, `${kind.toLowerCase()}_${validBodyHash.slice(0, 12)}`])];
599
+ return decision;
600
+ }
601
+
602
+ if (message.requiresParentAction === true) {
603
+ decision.action = "mark_needs_review";
604
+ decision.claimHash = validBodyHash;
605
+ decision.acceptanceBlockers = [...new Set([...decision.acceptanceBlockers, `parent_action_required_${validBodyHash.slice(0, 12)}`])];
606
+ return decision;
607
+ }
608
+
609
+ decision.reasonCodes.push("status_only_no_todo_mutation");
610
+ return decision;
611
+ }
612
+
613
+ export function applyGoalRoomEventTodoReducer(pi: ExtensionAPI, state: HarnessRuntimeState, message: Record<string, unknown>): { decision: GoalRoomTodoReducerDecision; node?: GoalTodoNode } {
614
+ const decision = reduceGoalRoomEventToTodoDecision(message);
615
+ const activeGoalId = state.runtimeGoal?.goalId;
616
+ if (!activeGoalId || activeGoalId !== decision.goalId || !decision.todoId) return { decision };
617
+ if (decision.action === "return_claim" && decision.claimHash) {
618
+ const node = returnGoalTodoClaim(pi, state, activeGoalId, decision.todoId, {
619
+ claimHash: decision.claimHash,
620
+ evidenceRefs: decision.evidenceRefs,
621
+ validationCommands: decision.validationCommands,
622
+ noShip: decision.noShip,
623
+ runId: decision.runId,
624
+ outputHash: decision.outputHash,
625
+ outputContract: metadataString(goalRoomMetadata(message), "outputContract") ?? "agent-event.v1",
626
+ gatePassed: decision.noShip !== true && decision.acceptanceBlockers.length === 0,
627
+ childGoalStatus: decision.childGoalStatus,
628
+ statusClaim: decision.statusClaim,
629
+ targetReadiness: decision.targetReadiness,
630
+ acceptanceBlockers: decision.acceptanceBlockers,
631
+ }, "runtime");
632
+ return { decision, node: node ? cloneNode(node) : undefined };
633
+ }
634
+ if (decision.action === "block") {
635
+ const node = blockGoalTodo(pi, state, activeGoalId, decision.todoId, `goal-room ${decision.sourceKind ?? "event"} ${decision.claimHash?.slice(0, 12) ?? "hash"} requires parent review`, "runtime");
636
+ return { decision, node: cloneNode(node) };
637
+ }
638
+ if (decision.action === "mark_needs_review") {
639
+ const node = patchGoalTodo(pi, state, activeGoalId, decision.todoId, { status: "needs_review", owner: "agent", blocker: `goal-room event ${decision.claimHash?.slice(0, 12) ?? "hash"} requires parent action`, reviewNoShip: decision.noShip === true }, "runtime");
640
+ return { decision, node: cloneNode(node) };
641
+ }
642
+ return { decision };
643
+ }
644
+
645
+ export function appendGoalTodoEvent(pi: ExtensionAPI, state: HarnessRuntimeState, event: GoalTodoEvent): GoalTodoEvent {
646
+ pi.appendEntry(ZOB_GOAL_TODO_ENTRY_TYPE, event);
647
+ applyEvent(state.goalTodos, event);
648
+ return event;
649
+ }
650
+
651
+ function nextTodoId(): string {
652
+ return `todo_${randomUUID().replace(/-/g, "").slice(0, 12)}`;
653
+ }
654
+
655
+ function goalNodes(todoState: GoalTodoState, goalId: string): GoalTodoNode[] {
656
+ return todoState.nodes.filter((node) => node.goalId === goalId).map(cloneNode);
657
+ }
658
+
659
+ const GOAL_TODO_PATH_PATTERN = /^\d+(?:\.\d+)*$/;
660
+ const DELEGATABLE_GOAL_TODO_STATUSES = new Set<GoalTodoStatus>(["planned", "ready", "in_progress", "needs_review"]);
661
+ const ACTIVE_DELEGATION_STATUSES = new Set<GoalTodoDelegationStatus>(["queued", "running", "claim_returned"]);
662
+ const RECOVERABLE_DELEGATION_STATUSES = new Set<GoalTodoDelegationStatus>(["failed", "rejected"]);
663
+
664
+ function hasActiveGoalTodoDelegation(node: GoalTodoNode): boolean {
665
+ return Boolean(node.delegation && ACTIVE_DELEGATION_STATUSES.has(node.delegation.status));
666
+ }
667
+
668
+ function isRecoverableDelegatedGoalTodoNode(node: GoalTodoNode): boolean {
669
+ return node.status === "delegated" && (!node.delegation || RECOVERABLE_DELEGATION_STATUSES.has(node.delegation.status));
670
+ }
671
+
672
+ function isDelegatableGoalTodoNode(node: GoalTodoNode): boolean {
673
+ return (DELEGATABLE_GOAL_TODO_STATUSES.has(node.status) || isRecoverableDelegatedGoalTodoNode(node)) && !hasActiveGoalTodoDelegation(node);
674
+ }
675
+
676
+ function goalTodoRefHint(nodes: GoalTodoNode[]): string {
677
+ if (nodes.length === 0) return "none";
678
+ return nodes
679
+ .slice()
680
+ .sort((left, right) => left.path.localeCompare(right.path, undefined, { numeric: true }) || left.createdAt - right.createdAt)
681
+ .slice(0, 8)
682
+ .map((node) => `${node.path}=${node.id}`)
683
+ .join(", ");
684
+ }
685
+
686
+ function legacyTodoPathFromRef(ref: string): string | undefined {
687
+ const match = ref.match(/^todo_(\d+(?:\.\d+)*)$/);
688
+ return match?.[1];
689
+ }
690
+
691
+ export function resolveGoalTodoReference(todoState: GoalTodoState, goalId: string | undefined, todoRef: string | undefined, label = "goal TODO ref", options: { requireDelegatable?: boolean } = {}): { node?: GoalTodoNode; matchedBy?: "id" | "path" | "legacy_path"; errors: string[] } {
692
+ if (!todoRef?.trim()) return { errors: [] };
693
+ if (!goalId) return { errors: [`${label} requires an active runtime goal`] };
694
+ const ref = todoRef.trim();
695
+ const allNodes = goalNodes(todoState, goalId);
696
+ const nodes = options.requireDelegatable ? allNodes.filter(isDelegatableGoalTodoNode) : allNodes;
697
+ const exact = nodes.find((node) => node.id === ref);
698
+ if (exact) return { node: exact, matchedBy: "id", errors: [] };
699
+
700
+ const legacyPath = legacyTodoPathFromRef(ref);
701
+ const pathRef = GOAL_TODO_PATH_PATTERN.test(ref) ? ref : legacyPath;
702
+ const inactiveMatch = options.requireDelegatable
703
+ ? allNodes.find((node) => node.id === ref || (pathRef !== undefined && node.path === pathRef))
704
+ : undefined;
705
+ const inactiveNote = inactiveMatch
706
+ ? hasActiveGoalTodoDelegation(inactiveMatch)
707
+ ? ` It matches inactive TODO ${inactiveMatch.path} (${inactiveMatch.status}/${inactiveMatch.delegation?.status}) with active delegated work; do not double-delegate the same leaf. Wait for queued/running work, accept/reject/reopen a returned claim, or split into subtodos for parallel agents/workspaces.`
708
+ : ` It matches inactive TODO ${inactiveMatch.path} (${inactiveMatch.status}${inactiveMatch.delegation ? `/${inactiveMatch.delegation.status}` : ""}); refresh active refs from get_goal_todos, reopen/resolve it first, or split into subtodos before parallel delegation.`
709
+ : "";
710
+ if (pathRef) {
711
+ const matches = nodes.filter((node) => node.path === pathRef);
712
+ if (matches.length === 1) return { node: matches[0], matchedBy: legacyPath ? "legacy_path" : "path", errors: [] };
713
+ if (matches.length > 1) return { errors: [`${label} is ambiguous: ${ref} matched ${matches.length} active TODOs with path ${pathRef}; use a canonical TODO id from get_goal_todos.`] };
714
+ const legacyNote = legacyPath && !inactiveNote ? ` It looks like legacy shorthand for visible TODO path ${legacyPath}, but that path is not active on this goal.` : "";
715
+ return { errors: [`${label} not found: ${ref}.${inactiveNote}${legacyNote} Use a canonical active TODO id from get_goal_todos, or set child_goal.todo_path to a unique active visible path; for parallel work split the parent into subtodos and delegate separate leaves. Active TODO refs: ${goalTodoRefHint(nodes)}`] };
716
+ }
717
+
718
+ return { errors: [`${label} not found: ${ref}.${inactiveNote} Use a canonical active TODO id from get_goal_todos, or set child_goal.todo_path to a unique active visible path; for parallel work split the parent into subtodos and delegate separate leaves. Active TODO refs: ${goalTodoRefHint(nodes)}`] };
719
+ }
720
+
721
+ function childrenOf(todoState: GoalTodoState, goalId: string, parentId: string | undefined): GoalTodoNode[] {
722
+ return goalNodes(todoState, goalId)
723
+ .filter((node) => (node.parentId ?? undefined) === parentId)
724
+ .sort((left, right) => left.path.localeCompare(right.path, undefined, { numeric: true }) || left.createdAt - right.createdAt);
725
+ }
726
+
727
+ function parentPath(todoState: GoalTodoState, goalId: string, parentId: string | undefined): string | undefined {
728
+ if (!parentId) return undefined;
729
+ return todoState.nodes.find((node) => node.goalId === goalId && node.id === parentId)?.path;
730
+ }
731
+
732
+ function nextPathForParent(todoState: GoalTodoState, goalId: string, parentId: string | undefined): string {
733
+ const parent = parentPath(todoState, goalId, parentId);
734
+ const count = childrenOf(todoState, goalId, parentId).length + 1;
735
+ return parent ? `${parent}.${count}` : String(count);
736
+ }
737
+
738
+ function depthForParent(todoState: GoalTodoState, goalId: string, parentId: string | undefined): number {
739
+ if (!parentId) return 1;
740
+ const parent = todoState.nodes.find((node) => node.goalId === goalId && node.id === parentId);
741
+ return (parent?.depth ?? 0) + 1;
742
+ }
743
+
744
+ export function renumberGoalPaths(todoState: GoalTodoState, goalId: string): void {
745
+ const visit = (parentId: string | undefined, prefix: string | undefined, depth: number): void => {
746
+ const children = childrenOf(todoState, goalId, parentId).sort((left, right) => left.createdAt - right.createdAt || left.title.localeCompare(right.title));
747
+ children.forEach((child, index) => {
748
+ const path = prefix ? `${prefix}.${index + 1}` : String(index + 1);
749
+ const existing = todoState.nodes.find((node) => node.goalId === goalId && node.id === child.id);
750
+ if (existing) {
751
+ existing.path = path;
752
+ existing.depth = depth;
753
+ existing.updatedAt = unixSeconds();
754
+ }
755
+ visit(child.id, path, depth + 1);
756
+ });
757
+ };
758
+ visit(undefined, undefined, 1);
759
+ }
760
+
761
+ export function validateGoalTodoGraph(todoState: GoalTodoState, goalId?: string): string[] {
762
+ const errors: string[] = [];
763
+ const nodes = goalId ? goalNodes(todoState, goalId) : todoState.nodes.map(cloneNode);
764
+ const byId = new Map<string, GoalTodoNode>();
765
+ for (const node of nodes) {
766
+ if (byId.has(`${node.goalId}:${node.id}`)) errors.push(`duplicate todo id: ${node.id}`);
767
+ byId.set(`${node.goalId}:${node.id}`, node);
768
+ if (node.depth > todoState.policy.maxTodoDepth) errors.push(`todo ${node.path} exceeds maxTodoDepth=${todoState.policy.maxTodoDepth}`);
769
+ if (node.delegation && node.delegation.delegationDepth > todoState.policy.maxDelegationDepth) errors.push(`todo ${node.path} exceeds maxDelegationDepth=${todoState.policy.maxDelegationDepth}`);
770
+ }
771
+ for (const node of nodes) {
772
+ if (node.parentId && !byId.has(`${node.goalId}:${node.parentId}`)) errors.push(`todo ${node.id} references missing parent ${node.parentId}`);
773
+ const seen = new Set<string>();
774
+ let cursor: GoalTodoNode | undefined = node;
775
+ while (cursor?.parentId) {
776
+ if (seen.has(cursor.id)) {
777
+ errors.push(`todo cycle detected at ${cursor.id}`);
778
+ break;
779
+ }
780
+ seen.add(cursor.id);
781
+ cursor = byId.get(`${cursor.goalId}:${cursor.parentId}`);
782
+ }
783
+ }
784
+ const open = nodes.filter((node) => OPEN_REQUIRED_STATUSES.has(node.status));
785
+ if (open.length > todoState.policy.maxOpenTodos) errors.push(`open todo cap exceeded: ${open.length}/${todoState.policy.maxOpenTodos}`);
786
+ for (const node of nodes) {
787
+ const childCount = nodes.filter((candidate) => candidate.parentId === node.id && candidate.goalId === node.goalId).length;
788
+ if (childCount > todoState.policy.maxChildrenPerTodo) errors.push(`todo ${node.path} exceeds maxChildrenPerTodo=${todoState.policy.maxChildrenPerTodo}`);
789
+ }
790
+ return errors;
791
+ }
792
+
793
+ export function addGoalTodo(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, input: AddGoalTodoInput, source: GoalTodoEventSource = "tool"): GoalTodoNode {
794
+ const title = input.title.trim();
795
+ if (!title) throw new Error("Goal TODO title is required.");
796
+ const depth = depthForParent(state.goalTodos, goalId, input.parentId);
797
+ if (depth > state.goalTodos.policy.maxTodoDepth) throw new Error(`Goal TODO depth exceeds maxTodoDepth=${state.goalTodos.policy.maxTodoDepth}.`);
798
+ if (input.parentId && !state.goalTodos.nodes.some((node) => node.goalId === goalId && node.id === input.parentId)) throw new Error(`Parent TODO not found: ${input.parentId}`);
799
+ const node: GoalTodoNode = {
800
+ id: nextTodoId(),
801
+ goalId,
802
+ parentId: input.parentId,
803
+ path: nextPathForParent(state.goalTodos, goalId, input.parentId),
804
+ depth,
805
+ title,
806
+ descriptionHash: input.descriptionHash,
807
+ status: input.status ?? "planned",
808
+ owner: input.owner ?? "agent",
809
+ required: input.required !== false,
810
+ priority: input.priority ?? "normal",
811
+ acceptanceCriteria: input.acceptanceCriteria ?? [],
812
+ evidenceRefs: input.evidenceRefs ?? [],
813
+ validationCommands: input.validationCommands ?? [],
814
+ createdAt: unixSeconds(),
815
+ updatedAt: unixSeconds(),
816
+ };
817
+ appendGoalTodoEvent(pi, state, { version: 1, kind: "add", source, goalId, node, at: unixSeconds() });
818
+ return cloneNode(node);
819
+ }
820
+
821
+ export function patchGoalTodo(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, patch: Partial<GoalTodoNode>, source: GoalTodoEventSource = "tool"): GoalTodoNode {
822
+ const existing = state.goalTodos.nodes.find((node) => node.goalId === goalId && node.id === todoId);
823
+ if (!existing) throw new Error(`Goal TODO not found: ${todoId}`);
824
+ const next = applyPatchToNode(existing, patch);
825
+ appendGoalTodoEvent(pi, state, { version: 1, kind: "patch", source, goalId, todoId, patch: normalizePatch(patch), at: unixSeconds() });
826
+ return cloneNode(next);
827
+ }
828
+
829
+ function goalTodoNeedsSkipEvidence(node: GoalTodoNode): boolean {
830
+ return node.priority === "critical" || Boolean(node.delegation) || node.owner === "factory" || node.owner === "orchestration";
831
+ }
832
+
833
+ export function completeGoalTodo(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, input: { evidenceRefs?: string[]; validationCommands?: string[]; skipped?: boolean; reason?: string; repoRoot?: string } = {}, source: GoalTodoEventSource = "tool"): GoalTodoNode {
834
+ const existing = state.goalTodos.nodes.find((node) => node.goalId === goalId && node.id === todoId);
835
+ if (!existing) throw new Error(`Goal TODO not found: ${todoId}`);
836
+ const evidenceRefs = [...new Set([...existing.evidenceRefs, ...(input.evidenceRefs ?? [])])];
837
+ const validationCommands = [...new Set([...existing.validationCommands, ...(input.validationCommands ?? [])])];
838
+ const requiredChildren = state.goalTodos.nodes.filter((node) => node.goalId === goalId && node.parentId === todoId && node.required !== false);
839
+ const delegatedCompletionCoveredByChildren = !input.skipped
840
+ && Boolean(existing.delegation && existing.delegation.status !== "accepted")
841
+ && requiredChildren.length > 0
842
+ && requiredChildren.every((node) => node.status === "done" || node.status === "skipped")
843
+ && (evidenceRefs.length > 0 || validationCommands.length > 0 || Boolean(existing.artifacts?.outputHash));
844
+ if (!input.skipped && existing.delegation && existing.delegation.status !== "accepted" && !delegatedCompletionCoveredByChildren) {
845
+ if (existing.status === "claim_returned" || existing.delegation.status === "claim_returned") {
846
+ return acceptGoalTodoClaim(pi, state, goalId, todoId, { evidenceRefs: input.evidenceRefs, validationCommands: input.validationCommands, repoRoot: input.repoRoot }, source);
847
+ }
848
+ throw new Error(`Delegated TODO ${todoId} cannot be marked done directly while delegation status is ${existing.delegation.status}. Wait for claim_returned, then use resolve_goal_todo(action=complete|auto) or accept_goal_todo_claim; or reject/block if evidence/no_ship blocks.`);
849
+ }
850
+ if (input.skipped) {
851
+ const reason = input.reason?.trim();
852
+ if (!reason) throw new Error(`skip for TODO ${todoId} requires explicit reason; no default skip reason is allowed.`);
853
+ if (goalTodoNeedsSkipEvidence(existing) && evidenceRefs.length === 0 && validationCommands.length === 0 && !existing.artifacts?.outputHash) {
854
+ throw new Error(`skip for critical/delegated/factory/orchestration TODO ${todoId} requires evidence_refs or validation_commands.`);
855
+ }
856
+ }
857
+ return patchGoalTodo(pi, state, goalId, todoId, {
858
+ status: input.skipped ? "skipped" : "done",
859
+ evidenceRefs,
860
+ validationCommands,
861
+ skipReason: input.skipped ? input.reason?.trim() : existing.skipReason,
862
+ blocker: undefined,
863
+ reviewNoShip: undefined,
864
+ delegation: existing.delegation ? { ...existing.delegation, status: input.skipped ? "rejected" : delegatedCompletionCoveredByChildren ? "accepted" : existing.delegation.status } : undefined,
865
+ }, source);
866
+ }
867
+
868
+ export function blockGoalTodo(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, reason: string, source: GoalTodoEventSource = "tool"): GoalTodoNode {
869
+ return patchGoalTodo(pi, state, goalId, todoId, { status: "blocked", blocker: reason.trim() || "blocked", reviewNoShip: true }, source);
870
+ }
871
+
872
+ export function splitGoalTodo(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, parentId: string, titles: string[], source: GoalTodoEventSource = "tool"): GoalTodoNode[] {
873
+ const parent = state.goalTodos.nodes.find((node) => node.goalId === goalId && node.id === parentId);
874
+ if (!parent) throw new Error(`Parent TODO not found: ${parentId}`);
875
+ const cleanTitles = titles.map((title) => title.trim()).filter(Boolean);
876
+ if (cleanTitles.length === 0) throw new Error("split_goal_todo requires at least one child title.");
877
+ if (cleanTitles.length + childrenOf(state.goalTodos, goalId, parentId).length > state.goalTodos.policy.maxChildrenPerTodo) throw new Error(`split would exceed maxChildrenPerTodo=${state.goalTodos.policy.maxChildrenPerTodo}`);
878
+ const children = cleanTitles.map((title) => addGoalTodo(pi, state, goalId, { title, parentId, required: true, priority: parent.priority, owner: "agent" }, source));
879
+ appendGoalTodoEvent(pi, state, { version: 1, kind: "split", source, goalId, todoId: parentId, childIds: children.map((child) => child.id), at: unixSeconds() });
880
+ return children;
881
+ }
882
+
883
+ export function linkGoalTodoDelegation(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, input: { runId: string; agent?: string; childGoalId?: string; requestId?: string; delegationDepth?: number }, source: GoalTodoEventSource = "delegation"): GoalTodoNode | undefined {
884
+ const existing = state.goalTodos.nodes.find((node) => node.goalId === goalId && node.id === todoId);
885
+ if (!existing) return undefined;
886
+ const delegation: GoalTodoDelegationRef = {
887
+ runId: input.runId,
888
+ agent: input.agent,
889
+ childGoalId: input.childGoalId,
890
+ requestId: input.requestId,
891
+ delegationDepth: Math.max(0, Math.trunc(input.delegationDepth ?? existing.delegation?.delegationDepth ?? 1)),
892
+ status: "running",
893
+ };
894
+ appendGoalTodoEvent(pi, state, { version: 1, kind: "delegate_link", source, goalId, todoId, runId: input.runId, delegation, at: unixSeconds() });
895
+ return state.goalTodos.nodes.find((node) => node.goalId === goalId && node.id === todoId);
896
+ }
897
+
898
+ export function returnGoalTodoClaim(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, input: { claimText?: string; claimHash?: string; evidenceRefs?: string[]; validationCommands?: string[]; noShip?: boolean; runId?: string; outputHash?: string; outputContract?: string; gatePassed?: boolean; childGoalStatus?: GoalTodoChildGoalStatus; statusClaim?: GoalTodoStatusClaim; targetReadiness?: GoalTodoClaimTargetReadiness; acceptanceBlockers?: string[]; childChangedPaths?: ZcommitChildChangedPathRef[] }, source: GoalTodoEventSource = "delegation"): GoalTodoNode | undefined {
899
+ const existing = state.goalTodos.nodes.find((node) => node.goalId === goalId && node.id === todoId);
900
+ if (!existing) return undefined;
901
+ const claimHash = input.claimHash ?? sha256(input.claimText ?? `${goalId}:${todoId}:${Date.now()}`);
902
+ appendGoalTodoEvent(pi, state, { version: 1, kind: "claim_returned", source, goalId, todoId, claimHash, evidenceRefs: input.evidenceRefs ?? [], validationCommands: input.validationCommands ?? [], noShip: input.noShip, runId: input.runId, outputHash: input.outputHash, outputContract: input.outputContract, gatePassed: input.gatePassed, childGoalStatus: input.childGoalStatus, statusClaim: input.statusClaim, targetReadiness: input.targetReadiness, acceptanceBlockers: input.acceptanceBlockers ?? [], childChangedPaths: input.childChangedPaths ?? [], at: unixSeconds() });
903
+ return state.goalTodos.nodes.find((node) => node.goalId === goalId && node.id === todoId);
904
+ }
905
+
906
+ export function requestGoalTodoClaimValidation(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, input: { runId?: string; agent?: string } = {}, source: GoalTodoEventSource = "delegation"): GoalTodoNode {
907
+ const existing = state.goalTodos.nodes.find((node) => node.goalId === goalId && node.id === todoId);
908
+ if (!existing) throw new Error(`Goal TODO not found: ${todoId}`);
909
+ if (existing.status !== "claim_returned" || existing.delegation?.status !== "claim_returned" || !existing.claim) throw new Error(`TODO ${todoId} has no returned delegated claim to validate.`);
910
+ const validation: GoalTodoClaimValidationRef = {
911
+ runId: input.runId,
912
+ agent: input.agent ?? "oracle",
913
+ status: "running",
914
+ evidenceRefs: [],
915
+ validationCommands: [],
916
+ blockingIssues: [],
917
+ requestedAt: unixSeconds(),
918
+ };
919
+ appendGoalTodoEvent(pi, state, { version: 1, kind: "claim_validation_requested", source, goalId, todoId, validation, at: unixSeconds() });
920
+ const node = state.goalTodos.nodes.find((candidate) => candidate.goalId === goalId && candidate.id === todoId);
921
+ if (!node) throw new Error(`Goal TODO not found: ${todoId}`);
922
+ return cloneNode(node);
923
+ }
924
+
925
+ function validationStatusFromResult(input: TodoClaimValidationResult): GoalTodoClaimValidationStatus {
926
+ if (input.noShip === true || !hasOnlyNoneLike(input.blockingIssues)) return "blocked";
927
+ if (input.verdict === "PASS" && input.noShip === false) return "passed";
928
+ if (input.verdict === "WARN" && input.noShip === false) return "warn";
929
+ return "failed";
930
+ }
931
+
932
+ function hasOnlyNoneLike(items: string[]): boolean {
933
+ return items.length === 0 || items.every((item) => /^(none|no|n\/a|null)$/i.test(item.trim()));
934
+ }
935
+
936
+ export function isGoalTodoClaimReadyForAutoAccept(node: GoalTodoNode): boolean {
937
+ const claim = node.claim;
938
+ const validation = node.validation;
939
+ if (!claim || !validation) return false;
940
+ if (node.status !== "claim_returned" || node.delegation?.status !== "claim_returned") return false;
941
+ if (claim.gatePassed !== true || claim.childGoalStatus !== "ready_for_oracle" || claim.statusClaim !== "done") return false;
942
+ if (claim.noShip === true || validation.noShip === true) return false;
943
+ if (claim.targetReadiness !== "ready_for_parent_acceptance") return false;
944
+ if (!hasOnlyNoneLike(claim.acceptanceBlockers)) return false;
945
+ if (validation.status !== "passed" || validation.verdict !== "PASS" || validation.noShip !== false) return false;
946
+ if (validation.recommendedAction !== "accept_claim") return false;
947
+ if (validation.confidence !== "HIGH" && validation.confidence !== "MEDIUM") return false;
948
+ if (!hasOnlyNoneLike(validation.blockingIssues)) return false;
949
+ return node.evidenceRefs.length > 0 || node.validationCommands.length > 0 || Boolean(node.artifacts?.outputHash);
950
+ }
951
+
952
+ export function recordGoalTodoClaimValidationResult(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, input: { result: TodoClaimValidationResult; runId?: string; agent?: string; outputHash?: string; autoAccept?: boolean; repoRoot?: string }, source: GoalTodoEventSource = "delegation"): GoalTodoNode {
953
+ const existing = state.goalTodos.nodes.find((node) => node.goalId === goalId && node.id === todoId);
954
+ if (!existing) throw new Error(`Goal TODO not found: ${todoId}`);
955
+ if (!existing.claim) throw new Error(`TODO ${todoId} has no returned delegated claim to validate.`);
956
+ if (input.result.todoId && input.result.todoId !== todoId) throw new Error(`claim validation todo_id mismatch: ${input.result.todoId} !== ${todoId}`);
957
+ if (input.result.claimHash && input.result.claimHash !== existing.claim.claimHash) throw new Error(`claim validation claim_hash mismatch for TODO ${todoId}`);
958
+ if (input.autoAccept === true && input.result.claimHash !== existing.claim.claimHash) throw new Error(`claim validation requires exact claim_hash for auto-accept of TODO ${todoId}.`);
959
+ const validation: GoalTodoClaimValidationRef = {
960
+ runId: input.runId ?? existing.validation?.runId,
961
+ agent: input.agent ?? existing.validation?.agent ?? "oracle",
962
+ status: validationStatusFromResult(input.result),
963
+ verdict: input.result.verdict,
964
+ recommendedAction: input.result.recommendedAction,
965
+ noShip: input.result.noShip,
966
+ outputHash: input.outputHash,
967
+ evidenceRefs: input.result.evidenceRefs,
968
+ validationCommands: input.result.validationCommands,
969
+ blockingIssues: input.result.blockingIssues,
970
+ confidence: input.result.confidence,
971
+ requestedAt: existing.validation?.requestedAt,
972
+ validatedAt: unixSeconds(),
973
+ };
974
+ appendGoalTodoEvent(pi, state, { version: 1, kind: "claim_validation_returned", source, goalId, todoId, validation, evidenceRefs: input.result.evidenceRefs, validationCommands: input.result.validationCommands, noShip: input.result.noShip, at: unixSeconds() });
975
+ const node = state.goalTodos.nodes.find((candidate) => candidate.goalId === goalId && candidate.id === todoId);
976
+ if (!node) throw new Error(`Goal TODO not found: ${todoId}`);
977
+ if (input.autoAccept === true && isGoalTodoClaimReadyForAutoAccept(node)) return acceptGoalTodoClaim(pi, state, goalId, todoId, { evidenceRefs: input.result.evidenceRefs, validationCommands: input.result.validationCommands, repoRoot: input.repoRoot }, source);
978
+ return cloneNode(node);
979
+ }
980
+
981
+ export function acceptGoalTodoClaim(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, input: { evidenceRefs?: string[]; validationCommands?: string[]; repoRoot?: string } = {}, source: GoalTodoEventSource = "tool"): GoalTodoNode {
982
+ const existing = state.goalTodos.nodes.find((candidate) => candidate.goalId === goalId && candidate.id === todoId);
983
+ if (!existing) throw new Error(`Goal TODO not found: ${todoId}`);
984
+ if (existing.validation && existing.validation.status !== "passed") throw new Error(`TODO ${todoId} claim validation is ${existing.validation.status}; wait for oracle PASS/no_ship=false or reject/block.`);
985
+ if (existing.status !== "claim_returned" || existing.delegation?.status !== "claim_returned") throw new Error(`TODO ${todoId} has no returned delegated claim to accept.`);
986
+ const changedPaths = existing.claim?.childChangedPaths ?? [];
987
+ if (changedPaths.length > 0 && !input.repoRoot) throw new Error(`repoRoot is required to accept delegated TODO claim ${todoId} with child changed paths.`);
988
+ appendGoalTodoEvent(pi, state, { version: 1, kind: "claim_accepted", source, goalId, todoId, evidenceRefs: input.evidenceRefs ?? [], validationCommands: input.validationCommands ?? [], at: unixSeconds() });
989
+ if (changedPaths.length > 0) recordZcommitOwnedPaths(state.zcommit, input.repoRoot!, changedPaths, "parent_accepted_child_claim");
990
+ const node = state.goalTodos.nodes.find((candidate) => candidate.goalId === goalId && candidate.id === todoId);
991
+ if (!node) throw new Error(`Goal TODO not found: ${todoId}`);
992
+ return cloneNode(node);
993
+ }
994
+
995
+ export function rejectGoalTodoClaim(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, reason: string, source: GoalTodoEventSource = "tool"): GoalTodoNode {
996
+ appendGoalTodoEvent(pi, state, { version: 1, kind: "claim_rejected", source, goalId, todoId, reasonHash: sha256(reason), at: unixSeconds() });
997
+ const node = state.goalTodos.nodes.find((candidate) => candidate.goalId === goalId && candidate.id === todoId);
998
+ if (!node) throw new Error(`Goal TODO not found: ${todoId}`);
999
+ return cloneNode(node);
1000
+ }
1001
+
1002
+ export function nextValidGoalTodoActions(node: GoalTodoNode): ResolveGoalTodoAction[] {
1003
+ if (node.status === "done" || node.status === "skipped") return ["reopen"];
1004
+ if (node.status === "claim_returned" || node.delegation?.status === "claim_returned") return ["accept_claim", "reject_claim", "block", "reopen"];
1005
+ if (node.status === "delegated" || node.delegation?.status === "running" || node.delegation?.status === "queued") return ["block", "reopen"];
1006
+ if (node.status === "blocked") return ["reopen", "skip"];
1007
+ return ["complete", "block", "skip"];
1008
+ }
1009
+
1010
+ export function resolveGoalTodo(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, input: { action: ResolveGoalTodoAction; evidenceRefs?: string[]; validationCommands?: string[]; reason?: string; repoRoot?: string } = { action: "auto" }, source: GoalTodoEventSource = "tool"): GoalTodoNode {
1011
+ const existing = state.goalTodos.nodes.find((candidate) => candidate.goalId === goalId && candidate.id === todoId);
1012
+ if (!existing) throw new Error(`Goal TODO not found: ${todoId}`);
1013
+ const action: ResolveGoalTodoAction = input.action === "auto"
1014
+ ? existing.status === "claim_returned" || existing.delegation?.status === "claim_returned" ? "accept_claim" : "complete"
1015
+ : input.action;
1016
+ if (action === "complete") return completeGoalTodo(pi, state, goalId, todoId, { evidenceRefs: input.evidenceRefs, validationCommands: input.validationCommands, repoRoot: input.repoRoot }, source);
1017
+ if (action === "skip") return completeGoalTodo(pi, state, goalId, todoId, { skipped: true, reason: input.reason, evidenceRefs: input.evidenceRefs, validationCommands: input.validationCommands, repoRoot: input.repoRoot }, source);
1018
+ if (action === "accept_claim") return acceptGoalTodoClaim(pi, state, goalId, todoId, { evidenceRefs: input.evidenceRefs, validationCommands: input.validationCommands, repoRoot: input.repoRoot }, source);
1019
+ if (action === "reject_claim") {
1020
+ if (!input.reason?.trim()) throw new Error("reject_claim requires reason.");
1021
+ return rejectGoalTodoClaim(pi, state, goalId, todoId, input.reason, source);
1022
+ }
1023
+ if (action === "block") {
1024
+ if (!input.reason?.trim()) throw new Error("block requires reason.");
1025
+ return blockGoalTodo(pi, state, goalId, todoId, input.reason, source);
1026
+ }
1027
+ if (action === "reopen") {
1028
+ return patchGoalTodo(pi, state, goalId, todoId, {
1029
+ status: existing.delegation && existing.delegation.status !== "accepted" ? "delegated" : "ready",
1030
+ blocker: undefined,
1031
+ skipReason: undefined,
1032
+ reviewNoShip: undefined,
1033
+ delegation: existing.delegation && existing.delegation.status !== "accepted" ? { ...existing.delegation, status: "running" } : existing.delegation,
1034
+ }, source);
1035
+ }
1036
+ throw new Error(`Unsupported resolve_goal_todo action: ${input.action}`);
1037
+ }
1038
+
1039
+ export function focusGoalTodo(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string | undefined, source: GoalTodoEventSource = "command"): void {
1040
+ if (todoId && !state.goalTodos.nodes.some((node) => node.goalId === goalId && node.id === todoId)) throw new Error(`Goal TODO not found: ${todoId}`);
1041
+ appendGoalTodoEvent(pi, state, { version: 1, kind: "focus", source, goalId, todoId, at: unixSeconds() });
1042
+ }
1043
+
1044
+ export function summarizeGoalTodos(todoState: GoalTodoState, goalId?: string): GoalTodoSummary {
1045
+ const nodes = goalId ? goalNodes(todoState, goalId) : todoState.nodes.map(cloneNode);
1046
+ const done = nodes.filter((node) => node.status === "done").length;
1047
+ const skipped = nodes.filter((node) => node.status === "skipped").length;
1048
+ const open = nodes.filter((node) => OPEN_REQUIRED_STATUSES.has(node.status)).length;
1049
+ const active = nodes.filter((node) => ACTIVE_STATUSES.has(node.status)).length;
1050
+ const blocked = nodes.filter((node) => node.status === "blocked").length;
1051
+ const delegated = nodes.filter((node) => node.status === "delegated" || node.delegation?.status === "running" || node.delegation?.status === "queued").length;
1052
+ const claimReturned = nodes.filter((node) => node.status === "claim_returned").length;
1053
+ const validationQueued = nodes.filter((node) => node.validation?.status === "queued").length;
1054
+ const validationRunning = nodes.filter((node) => node.validation?.status === "running").length;
1055
+ const validationPassed = nodes.filter((node) => node.validation?.status === "passed").length;
1056
+ const validationFailed = nodes.filter((node) => node.validation?.status === "failed" || node.validation?.status === "blocked" || node.validation?.status === "warn").length;
1057
+ const needsUser = nodes.filter((node) => node.status === "needs_user").length;
1058
+ const needsOracle = nodes.filter((node) => node.status === "needs_oracle").length;
1059
+ const nextAgent = nodes.find((node) => node.owner === "agent" && (node.status === "ready" || node.status === "planned" || node.status === "in_progress"));
1060
+ const nextUser = nodes.find((node) => ACTIONABLE_STATUSES.has(node.status) && (node.owner === "user" || node.status === "needs_user"));
1061
+ return {
1062
+ goalId,
1063
+ total: nodes.length,
1064
+ required: nodes.filter((node) => node.required).length,
1065
+ done,
1066
+ skipped,
1067
+ open,
1068
+ active,
1069
+ blocked,
1070
+ delegated,
1071
+ claimReturned,
1072
+ validationQueued,
1073
+ validationRunning,
1074
+ validationPassed,
1075
+ validationFailed,
1076
+ needsUser,
1077
+ needsOracle,
1078
+ nextAgent,
1079
+ nextUser,
1080
+ };
1081
+ }
1082
+
1083
+ export function formatGoalTodoSummary(summary: GoalTodoSummary): string {
1084
+ if (summary.total === 0) return "todos unset";
1085
+ const closed = summary.done + summary.skipped;
1086
+ const validation = summary.validationQueued + summary.validationRunning > 0 ? ` · validation ${summary.validationQueued + summary.validationRunning}` : summary.validationFailed > 0 ? ` · validation_alerts ${summary.validationFailed}` : "";
1087
+ return `todos ${closed}/${summary.total} · open ${summary.open} · active ${summary.active} · blocked ${summary.blocked} · delegated ${summary.delegated} · claims ${summary.claimReturned}${validation}`;
1088
+ }
1089
+
1090
+ export function formatGoalTodoHudLine(todoState: GoalTodoState, goalId: string | undefined): string {
1091
+ if (!goalId) return "todos unset";
1092
+ const summary = summarizeGoalTodos(todoState, goalId);
1093
+ const next = summary.nextAgent ? ` · next agent ${summary.nextAgent.path} ${summary.nextAgent.title}` : summary.nextUser ? ` · next user ${summary.nextUser.path} ${summary.nextUser.title}` : "";
1094
+ return `${formatGoalTodoSummary(summary)}${next}`;
1095
+ }
1096
+
1097
+ export function formatGoalTodoPromptHint(todoState: GoalTodoState, goalId: string | undefined): string {
1098
+ if (!goalId) return "- goal_todos: no active runtime goal";
1099
+ const summary = summarizeGoalTodos(todoState, goalId);
1100
+ if (summary.total === 0) return "- goal_todos: none set; create TODOs for long, multi-step goal work when appropriate";
1101
+ return [
1102
+ `- goal_todos: ${formatGoalTodoSummary(summary)}`,
1103
+ summary.nextAgent ? `- next_agent_todo: ${summary.nextAgent.path} ${summary.nextAgent.title}` : undefined,
1104
+ summary.nextUser ? `- next_user_todo: ${summary.nextUser.path} ${summary.nextUser.title}` : undefined,
1105
+ summary.claimReturned > 0 ? "- delegated_claims: claim_returned TODOs require parent evidence checks or agentic validation; use resolve_goal_todo(action=auto|complete|accept_claim), complete_goal_todo, /goal todo done, or accept_goal_todo_claim to accept returned claims" : undefined,
1106
+ summary.validationQueued + summary.validationRunning > 0 ? `- claim_validation: ${summary.validationQueued + summary.validationRunning} oracle validation(s) queued/running; auto-accept only after PASS/no_ship=false` : undefined,
1107
+ "- completion rule: use resolve_goal_todo for done/skip/claim/block/reopen transitions; required TODOs must be done/skipped with evidence before propose_goal_completion",
1108
+ ].filter((line): line is string => typeof line === "string").join("\n");
1109
+ }
1110
+
1111
+ export function formatGoalTodoTree(todoState: GoalTodoState, goalId: string | undefined): string {
1112
+ if (!goalId) return "No active runtime goal; goal TODOs require a goalId.";
1113
+ const nodes = goalNodes(todoState, goalId);
1114
+ if (nodes.length === 0) return "No goal TODOs set. Use /goal todo add <title>.";
1115
+ const icon = (node: GoalTodoNode): string => {
1116
+ if (node.status === "done") return "✓";
1117
+ if (node.status === "skipped") return "↷";
1118
+ if (node.status === "blocked") return "▲";
1119
+ if (node.status === "delegated") return "⇄";
1120
+ if (node.status === "claim_returned") return "◇";
1121
+ if (node.status === "needs_user") return "?";
1122
+ if (node.status === "needs_oracle") return "◆";
1123
+ if (node.status === "in_progress") return "●";
1124
+ return "○";
1125
+ };
1126
+ const diagnostics = goalTodoCompletionDiagnostics(todoState, goalId);
1127
+ const lines = [formatGoalTodoSummary(summarizeGoalTodos(todoState, goalId)), formatGoalTodoDiagnostics(diagnostics)];
1128
+ const walk = (parentId: string | undefined, indent = ""): void => {
1129
+ const children = childrenOf(todoState, goalId, parentId);
1130
+ children.forEach((node, index) => {
1131
+ const last = index === children.length - 1;
1132
+ const branch = indent ? (last ? "└─" : "├─") : "";
1133
+ const required = node.required ? "req" : "opt";
1134
+ const delegation = node.delegation?.runId ? ` · run ${node.delegation.runId}` : "";
1135
+ const claim = node.claim?.claimHash ? ` · claim ${node.claim.claimHash.slice(0, 12)}` : "";
1136
+ const validation = node.validation ? ` · validation ${node.validation.status}${node.validation.verdict ? `/${node.validation.verdict}` : ""}${node.validation.runId ? ` ${node.validation.runId}` : ""}` : "";
1137
+ const blocker = node.blocker ? ` · blocker ${node.blocker}` : "";
1138
+ lines.push(`${indent}${branch}${icon(node)} ${node.path} ${node.title} [${node.status}/${node.owner}/${required}/${node.priority}] · id ${node.id}${delegation}${claim}${validation}${blocker}`);
1139
+ walk(node.id, `${indent}${last ? " " : "│ "}`);
1140
+ });
1141
+ };
1142
+ walk(undefined);
1143
+ return lines.join("\n");
1144
+ }
1145
+
1146
+ export function goalTodoCompletionBlockers(todoState: GoalTodoState, goalId: string | undefined): string[] {
1147
+ if (!goalId) return [];
1148
+ const nodes = goalNodes(todoState, goalId);
1149
+ if (nodes.length === 0) return [];
1150
+ const blockers: string[] = [];
1151
+ for (const node of nodes) {
1152
+ if (node.required && OPEN_REQUIRED_STATUSES.has(node.status)) blockers.push(`todo ${node.path} '${node.title}' is required and ${node.status}`);
1153
+ if (node.required && node.status === "skipped" && !node.skipReason?.trim()) blockers.push(`todo ${node.path} '${node.title}' skipped without explicit reason`);
1154
+ const evidenceRequired = todoState.policy.requireEvidenceForCritical && (node.priority === "critical" || Boolean(node.delegation) || node.owner === "factory" || node.owner === "orchestration");
1155
+ if (node.status === "done" && evidenceRequired && node.evidenceRefs.length === 0 && node.validationCommands.length === 0 && !node.artifacts?.outputHash) blockers.push(`todo ${node.path} '${node.title}' done without evidence`);
1156
+ if (node.status === "skipped" && evidenceRequired && node.evidenceRefs.length === 0 && node.validationCommands.length === 0 && !node.artifacts?.outputHash) blockers.push(`todo ${node.path} '${node.title}' skipped without evidence`);
1157
+ if (node.status === "done" && node.delegation && node.delegation.status !== "accepted") blockers.push(`todo ${node.path} '${node.title}' delegated claim is not parent-accepted`);
1158
+ if (node.status === "done") {
1159
+ const openChildren = nodes.filter((candidate) => candidate.parentId === node.id && candidate.required && OPEN_REQUIRED_STATUSES.has(candidate.status));
1160
+ if (openChildren.length > 0) blockers.push(`todo ${node.path} '${node.title}' is done but has open required child TODOs`);
1161
+ }
1162
+ }
1163
+ blockers.push(...validateGoalTodoGraph(todoState, goalId));
1164
+ return blockers;
1165
+ }
1166
+
1167
+ export function goalTodoCompletionDiagnostics(todoState: GoalTodoState, goalId: string | undefined): GoalTodoCompletionDiagnostics {
1168
+ const nodes = goalId ? goalNodes(todoState, goalId) : [];
1169
+ const completionBlockers = goalTodoCompletionBlockers(todoState, goalId);
1170
+ const reviewNoShip = nodes.some((node) => node.reviewNoShip === true);
1171
+ const hardNoShip = completionBlockers.length > 0;
1172
+ return {
1173
+ completionReady: completionBlockers.length === 0 && !reviewNoShip,
1174
+ hardNoShip,
1175
+ reviewNoShip,
1176
+ effectiveNoShip: hardNoShip || reviewNoShip,
1177
+ completionBlockers,
1178
+ nextValidActions: Object.fromEntries(nodes.map((node) => [node.id, nextValidGoalTodoActions(node)])),
1179
+ };
1180
+ }
1181
+
1182
+ export function formatGoalTodoDiagnostics(diagnostics: GoalTodoCompletionDiagnostics): string {
1183
+ const blockers = diagnostics.completionBlockers.slice(0, 3).join(" | ") || "none";
1184
+ return `completion_ready=${diagnostics.completionReady} · hard_no_ship=${diagnostics.hardNoShip} · review_no_ship=${diagnostics.reviewNoShip} · effective_no_ship=${diagnostics.effectiveNoShip} · completion_blockers=${blockers}`;
1185
+ }
1186
+
1187
+ export function extractTodoClaimFromText(text: string): { todoId?: string; childGoalStatus?: GoalTodoChildGoalStatus; statusClaim?: GoalTodoStatusClaim; evidenceRefs: string[]; validationCommands: string[]; noShip?: boolean; hasFinalMarker: boolean; subtodoDeltaProposals: string[]; acceptanceBlockers: string[]; targetReadiness?: GoalTodoClaimTargetReadiness; risksBlockers: string[] } {
1188
+ const todoIdMatch = text.match(/todo_id\s*[:=]\s*([^\s]+)/i);
1189
+ const statusMatch = text.match(/child_goal_status\s*[:=]\s*(ready_for_oracle|incomplete|blocked)/i);
1190
+ const statusClaimMatch = text.match(/status_claim\s*[:=]\s*(done|incomplete|blocked)/i);
1191
+ const noShipMatch = text.match(/no_ship\s*[:=]\s*(true|yes|false|no)/i);
1192
+ const targetReadinessRaw = text.match(/target_readiness\s*[:=]\s*(ready_for_parent_acceptance|needs_parent_review|blocked)/i)?.[1]?.toLowerCase();
1193
+ const lines = text.split(/\r?\n/);
1194
+ const collectAfter = (label: RegExp): string[] => {
1195
+ const result: string[] = [];
1196
+ let collecting = false;
1197
+ for (const line of lines) {
1198
+ if (label.test(line)) {
1199
+ collecting = true;
1200
+ const inline = line.split(/[:=]/).slice(1).join(":").trim();
1201
+ if (inline && !/^\s*$/.test(inline)) result.push(inline.replace(/^[-*]\s*/, ""));
1202
+ continue;
1203
+ }
1204
+ if (collecting) {
1205
+ if (/^[A-Za-z_ -]+\s*[:=]/.test(line) && !/^\s*[-*]/.test(line)) break;
1206
+ const item = line.trim().replace(/^[-*]\s*/, "");
1207
+ if (item) result.push(item);
1208
+ }
1209
+ }
1210
+ return result.filter((item) => item.length > 0);
1211
+ };
1212
+ return {
1213
+ todoId: todoIdMatch?.[1]?.trim(),
1214
+ childGoalStatus: statusMatch?.[1]?.toLowerCase() as GoalTodoChildGoalStatus | undefined,
1215
+ statusClaim: statusClaimMatch?.[1]?.toLowerCase() as GoalTodoStatusClaim | undefined,
1216
+ evidenceRefs: collectAfter(/^\s*evidence_refs\s*[:=]/i),
1217
+ validationCommands: collectAfter(/^\s*validation_commands\s*[:=]/i),
1218
+ noShip: noShipMatch ? /^(true|yes)$/i.test(noShipMatch[1]) : undefined,
1219
+ targetReadiness: targetReadinessRaw as GoalTodoClaimTargetReadiness | undefined,
1220
+ acceptanceBlockers: collectAfter(/^\s*acceptance_blockers\s*[:=]/i).filter((item) => !/^(none|n\/a|null)$/i.test(item)),
1221
+ risksBlockers: collectAfter(/^\s*risks_blockers\s*[:=]/i),
1222
+ hasFinalMarker: /FINAL_MARKER\s*:\s*(TODO_CHILD_RESULT_END|TODO_CHILD_RESULT_V2_END)|TODO_CHILD_RESULT_END|TODO_CHILD_RESULT_V2_END/.test(text),
1223
+ subtodoDeltaProposals: collectAfter(/^\s*subtodo_delta_proposals\s*[:=]/i),
1224
+ };
1225
+ }
1226
+
1227
+ function collectLabeledLines(text: string, label: RegExp): string[] {
1228
+ const result: string[] = [];
1229
+ let collecting = false;
1230
+ for (const line of text.split(/\r?\n/)) {
1231
+ if (label.test(line)) {
1232
+ collecting = true;
1233
+ const inline = line.split(/[:=]/).slice(1).join(":").trim();
1234
+ if (inline) result.push(inline.replace(/^[-*]\s*/, ""));
1235
+ continue;
1236
+ }
1237
+ if (collecting) {
1238
+ const labelLine = /^\s*(?:[-*]\s*)?[A-Za-z_ -]+\s*[:=]/.test(line);
1239
+ const indentedBulletItem = /^\s{2,}[-*]\s+/.test(line);
1240
+ if (labelLine && !indentedBulletItem) break;
1241
+ const item = line.trim().replace(/^[-*]\s*/, "");
1242
+ if (item) result.push(item);
1243
+ }
1244
+ }
1245
+ return result.map((item) => item.trim()).filter((item) => item.length > 0 && !/^(none|n\/a|null)$/i.test(item));
1246
+ }
1247
+
1248
+ function extractLabeledScalar(text: string, label: string): string | undefined {
1249
+ const match = text.match(new RegExp(`^\\s*(?:[-*]\\s*)?${label}\\s*[:=]\\s*(.+)$`, "im"));
1250
+ return match?.[1]?.trim();
1251
+ }
1252
+
1253
+ export function extractTodoClaimValidationFromText(text: string): TodoClaimValidationResult {
1254
+ const todoIdMatch = text.match(/todo_id\s*[:=]\s*([^\s]+)/i);
1255
+ const claimHashMatch = text.match(/claim_hash\s*[:=]\s*([a-f0-9]{64})/i);
1256
+ const verdictRaw = extractLabeledScalar(text, "verdict")?.toUpperCase();
1257
+ const actionRaw = extractLabeledScalar(text, "recommended_action")?.toLowerCase().replace(/[ -]/g, "_");
1258
+ const noShipMatch = text.match(/no_ship\s*[:=]\s*(true|yes|false|no)/i);
1259
+ const confidenceRaw = extractLabeledScalar(text, "confidence")?.toUpperCase();
1260
+ return {
1261
+ todoId: todoIdMatch?.[1]?.trim(),
1262
+ claimHash: claimHashMatch?.[1]?.trim(),
1263
+ verdict: verdictRaw === "PASS" || verdictRaw === "WARN" || verdictRaw === "FAIL" ? verdictRaw : undefined,
1264
+ recommendedAction: actionRaw === "accept_claim" || actionRaw === "needs_review" || actionRaw === "reject_claim" || actionRaw === "block" ? actionRaw : undefined,
1265
+ evidenceRefs: collectLabeledLines(text, /^\s*(?:[-*]\s*)?evidence_refs\s*[:=]/i),
1266
+ validationCommands: collectLabeledLines(text, /^\s*(?:[-*]\s*)?validation_commands\s*[:=]/i),
1267
+ blockingIssues: collectLabeledLines(text, /^\s*(?:[-*]\s*)?blocking_issues\s*[:=]/i),
1268
+ noShip: noShipMatch ? /^(true|yes)$/i.test(noShipMatch[1]) : undefined,
1269
+ confidence: confidenceRaw === "LOW" || confidenceRaw === "MEDIUM" || confidenceRaw === "HIGH" ? confidenceRaw : undefined,
1270
+ hasFinalMarker: /FINAL_MARKER\s*:\s*TODO_CLAIM_VALIDATION_END|TODO_CLAIM_VALIDATION_END/.test(text),
1271
+ };
1272
+ }
1273
+
1274
+ export function isActionableTodoClaimValidation(result: TodoClaimValidationResult, todoId: string, claimHash?: string): boolean {
1275
+ return result.todoId === todoId
1276
+ && result.hasFinalMarker
1277
+ && Boolean(result.verdict)
1278
+ && Boolean(result.recommendedAction)
1279
+ && Boolean(result.confidence)
1280
+ && result.noShip !== undefined
1281
+ && (!claimHash || result.claimHash === claimHash);
1282
+ }
1283
+
1284
+ export function extractTodoSplitRequestFromText(text: string): TodoSplitRequest {
1285
+ const todoIdMatch = text.match(/todo_id\s*[:=]\s*([^\s]+)/i);
1286
+ const actionRaw = extractLabeledScalar(text, "recommended_action")?.toLowerCase().replace(/[ -]/g, "_");
1287
+ const riskRaw = extractLabeledScalar(text, "risk_level")?.toLowerCase();
1288
+ const noShipMatch = text.match(/no_ship\s*[:=]\s*(true|yes|false|no)/i);
1289
+ return {
1290
+ todoId: todoIdMatch?.[1]?.trim(),
1291
+ reason: extractLabeledScalar(text, "reason"),
1292
+ recommendedAction: actionRaw === "split" || actionRaw === "replan" || actionRaw === "factory" || actionRaw === "needs_user" || actionRaw === "blocked" ? actionRaw : undefined,
1293
+ proposedSubtodos: collectLabeledLines(text, /^\s*(?:[-*]\s*)?proposed_subtodos\s*[:=]/i),
1294
+ riskLevel: riskRaw === "low" || riskRaw === "medium" || riskRaw === "high" ? riskRaw : undefined,
1295
+ validationPlan: collectLabeledLines(text, /^\s*(?:[-*]\s*)?validation_plan\s*[:=]/i),
1296
+ noShip: noShipMatch ? /^(true|yes)$/i.test(noShipMatch[1]) : undefined,
1297
+ hasFinalMarker: /FINAL_MARKER\s*:\s*TODO_SPLIT_REQUEST_END|TODO_SPLIT_REQUEST_END/.test(text),
1298
+ };
1299
+ }
1300
+
1301
+ export function isActionableTodoSplitRequest(request: TodoSplitRequest, todoId: string): boolean {
1302
+ return request.todoId === todoId
1303
+ && request.hasFinalMarker
1304
+ && request.recommendedAction === "split"
1305
+ && request.noShip !== true
1306
+ && request.proposedSubtodos.length > 0;
1307
+ }
1308
+
1309
+ export function applyTodoSplitRequest(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string, todoId: string, request: TodoSplitRequest, source: GoalTodoEventSource = "delegation"): GoalTodoNode[] {
1310
+ if (!isActionableTodoSplitRequest(request, todoId)) throw new Error(`TODO split request for ${todoId} is not actionable.`);
1311
+ const titles = request.proposedSubtodos.map((title) => title.trim()).filter(Boolean);
1312
+ const children = splitGoalTodo(pi, state, goalId, todoId, titles, source);
1313
+ completeGoalTodo(pi, state, goalId, todoId, { skipped: true, reason: `decomposed into ${children.length} child TODO(s) by TODO_SPLIT_REQUEST`, validationCommands: request.validationPlan }, source);
1314
+ return children;
1315
+ }
1316
+
1317
+ function parseOptionValue(tokens: string[], flag: string): string | undefined {
1318
+ const index = tokens.indexOf(flag);
1319
+ return index >= 0 ? tokens[index + 1] : undefined;
1320
+ }
1321
+
1322
+ function stripKnownOptions(text: string): string {
1323
+ return text
1324
+ .replace(/(^|\s)--parent\s+\S+/g, " ")
1325
+ .replace(/(^|\s)--owner\s+\S+/g, " ")
1326
+ .replace(/(^|\s)--priority\s+\S+/g, " ")
1327
+ .replace(/(^|\s)--optional\b/g, " ")
1328
+ .replace(/(^|\s)--required\b/g, " ")
1329
+ .replace(/(^|\s)--evidence\s+/g, " ")
1330
+ .replace(/\s+/g, " ")
1331
+ .trim();
1332
+ }
1333
+
1334
+ export function handleGoalTodoTextCommand(pi: ExtensionAPI, state: HarnessRuntimeState, goalId: string | undefined, text: string, repoRoot?: string): GoalTodoCommandResult {
1335
+ if (!goalId) return { ok: false, message: "Goal TODOs require an active runtime goal. Use /goal <objective> first." };
1336
+ const trimmed = text.trim();
1337
+ if (!trimmed || trimmed === "list" || trimmed === "tree" || trimmed === "status") return { ok: true, message: formatGoalTodoTree(state.goalTodos, goalId) };
1338
+ if (trimmed === "next") {
1339
+ const summary = summarizeGoalTodos(state.goalTodos, goalId);
1340
+ const next = summary.nextAgent ?? summary.nextUser;
1341
+ return { ok: true, message: next ? `${next.id} ${next.path} ${next.title} [${next.status}/${next.owner}]` : "No next goal TODO found." };
1342
+ }
1343
+ const [command = "", ...rest] = trimmed.split(/\s+/);
1344
+ const body = rest.join(" ").trim();
1345
+ const tokens = rest;
1346
+ try {
1347
+ if (command === "add") {
1348
+ const ownerRaw = parseOptionValue(tokens, "--owner");
1349
+ const priorityRaw = parseOptionValue(tokens, "--priority");
1350
+ const parentId = parseOptionValue(tokens, "--parent");
1351
+ const owner = includesString(VALID_OWNER, ownerRaw) ? ownerRaw : "agent";
1352
+ const priority = includesString(VALID_PRIORITY, priorityRaw) ? priorityRaw : "normal";
1353
+ const required = tokens.includes("--optional") ? false : true;
1354
+ const title = stripKnownOptions(body);
1355
+ const node = addGoalTodo(pi, state, goalId, { title, parentId, owner, priority, required }, "command");
1356
+ return { ok: true, message: `added TODO ${node.id} ${node.path}: ${node.title}`, node };
1357
+ }
1358
+ if (command === "done" || command === "skip") {
1359
+ const todoId = rest[0];
1360
+ if (!todoId) return { ok: false, message: `Usage: /goal todo ${command} <todoId> [evidence/reason]` };
1361
+ const evidence = rest.slice(1).join(" ").replace(/^--evidence\s+/, "").trim();
1362
+ const node = resolveGoalTodo(pi, state, goalId, todoId, { action: command === "skip" ? "skip" : "complete", evidenceRefs: evidence ? [evidence] : [], reason: evidence, repoRoot }, "command");
1363
+ return { ok: true, message: `${command === "skip" ? "skipped" : "done"} TODO ${node.path}: ${node.title}`, node };
1364
+ }
1365
+ if (command === "block" || command === "user") {
1366
+ const todoId = rest[0];
1367
+ const reason = rest.slice(1).join(" ").trim();
1368
+ if (!todoId || !reason) return { ok: false, message: `Usage: /goal todo ${command} <todoId> <reason>` };
1369
+ const node = command === "user"
1370
+ ? patchGoalTodo(pi, state, goalId, todoId, { status: "needs_user", owner: "user", blocker: reason, reviewNoShip: true }, "command")
1371
+ : resolveGoalTodo(pi, state, goalId, todoId, { action: "block", reason, repoRoot }, "command");
1372
+ return { ok: true, message: `updated TODO ${node.path}: ${node.status}`, node };
1373
+ }
1374
+ if (command === "start") {
1375
+ const todoId = rest[0];
1376
+ if (!todoId) return { ok: false, message: "Usage: /goal todo start <todoId>" };
1377
+ const node = patchGoalTodo(pi, state, goalId, todoId, { status: "in_progress", owner: "agent" }, "command");
1378
+ return { ok: true, message: `started TODO ${node.path}: ${node.title}`, node };
1379
+ }
1380
+ if (command === "focus") {
1381
+ const todoId = rest[0];
1382
+ focusGoalTodo(pi, state, goalId, todoId, "command");
1383
+ return { ok: true, message: todoId ? `focused TODO ${todoId}` : "cleared TODO focus" };
1384
+ }
1385
+ if (command === "split") {
1386
+ const todoId = rest[0];
1387
+ const titles = rest.slice(1).join(" ").split(";").map((item) => item.trim()).filter(Boolean);
1388
+ if (!todoId || titles.length === 0) return { ok: false, message: "Usage: /goal todo split <todoId> child A; child B" };
1389
+ const nodes = splitGoalTodo(pi, state, goalId, todoId, titles, "command");
1390
+ return { ok: true, message: `split TODO ${todoId} into ${nodes.length} child TODO(s)` };
1391
+ }
1392
+ if (command === "accept-claim" || command === "accept") {
1393
+ const todoId = rest[0];
1394
+ if (!todoId) return { ok: false, message: `Usage: /goal todo ${command} <todoId> [evidence]` };
1395
+ const evidence = rest.slice(1).join(" ").trim();
1396
+ const node = resolveGoalTodo(pi, state, goalId, todoId, { action: "accept_claim", evidenceRefs: evidence ? [evidence] : [], repoRoot }, "command");
1397
+ return { ok: true, message: `accepted claim for TODO ${node.path}: ${node.title}`, node };
1398
+ }
1399
+ if (command === "reject-claim" || command === "reject") {
1400
+ const todoId = rest[0];
1401
+ const reason = rest.slice(1).join(" ").trim();
1402
+ if (!todoId || !reason) return { ok: false, message: `Usage: /goal todo ${command} <todoId> <reason>` };
1403
+ const node = resolveGoalTodo(pi, state, goalId, todoId, { action: "reject_claim", reason, repoRoot }, "command");
1404
+ return { ok: true, message: `rejected claim for TODO ${node.path}: ${node.title}`, node };
1405
+ }
1406
+ } catch (error) {
1407
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
1408
+ }
1409
+ return { ok: false, message: "Usage: /goal todo [tree|next|add|done|block|skip|user|start|focus|split|accept-claim|reject-claim]; primary API tool is resolve_goal_todo" };
1410
+ }