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,336 @@
1
+ import { appendFileSync, existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+
4
+ import { DEFAULT_RULES } from "../constants.js";
5
+ import { sha256 } from "../utils/hashing.js";
6
+ import { readJsonObjectIfPresent } from "../utils/json.js";
7
+ import { pathMatches, resolveRepoPath, safeFileStem } from "../utils/paths.js";
8
+ import { isRecord } from "../utils/records.js";
9
+ import type { PromotionApplyScope, PromotionCandidateInput, PromotionCandidateRecord, PromotionGates, PromotionKind, PromotionStatus, PromotionTransitionInput } from "./types.js";
10
+ import { PROMOTION_APPLY_SCOPES, PROMOTION_KINDS, PROMOTION_STATUSES } from "./types.js";
11
+
12
+ const SHA256_HEX = /^[a-f0-9]{64}$/i;
13
+ const FORBIDDEN_BODY_KEYS = new Set(["body", "task", "prompt", "output", "content", "message", "text", "rationale", "diff", "patch", "transcript", "rawContext", "rawPrompt"]);
14
+ const STATUS_ORDER: Record<Exclude<PromotionStatus, "rejected" | "blocked">, number> = {
15
+ proposal: 0,
16
+ prepared: 1,
17
+ validated: 2,
18
+ oracle_reviewed: 3,
19
+ approved: 4,
20
+ applied: 5,
21
+ };
22
+
23
+ export const DEFAULT_PROMOTION_GATES: PromotionGates = {
24
+ comsThreadRequired: true,
25
+ sandboxRequired: true,
26
+ validationRequired: true,
27
+ oracleRequired: true,
28
+ humanApprovalRequired: true,
29
+ rollbackRequired: true,
30
+ };
31
+
32
+ const ALLOWED_TRANSITIONS: Record<PromotionStatus, PromotionStatus[]> = {
33
+ proposal: ["prepared", "rejected", "blocked"],
34
+ prepared: ["validated", "rejected", "blocked"],
35
+ validated: ["oracle_reviewed", "rejected", "blocked"],
36
+ oracle_reviewed: ["approved", "rejected", "blocked"],
37
+ approved: ["applied", "rejected", "blocked"],
38
+ applied: [],
39
+ rejected: [],
40
+ blocked: ["proposal"],
41
+ };
42
+
43
+ function hasForbiddenBodyKey(value: unknown): boolean {
44
+ if (!value || typeof value !== "object") return false;
45
+ if (Array.isArray(value)) return value.some(hasForbiddenBodyKey);
46
+ return Object.entries(value as Record<string, unknown>).some(([key, child]) => FORBIDDEN_BODY_KEYS.has(key) || hasForbiddenBodyKey(child));
47
+ }
48
+
49
+ function isBodyFreeRecord(value: unknown): boolean {
50
+ return isRecord(value) && value.bodyStored === false && value.promptBodiesStored === false && value.outputBodiesStored === false && !hasForbiddenBodyKey(value);
51
+ }
52
+
53
+ function isSafeMetaId(value: string | null | undefined): boolean {
54
+ return value === null || value === undefined || (value.length > 0 && safeFileStem(value) === value);
55
+ }
56
+
57
+ function validateArtifactRef(repoRoot: string, ref: string, label: string): string[] {
58
+ const errors: string[] = [];
59
+ if (typeof ref !== "string" || ref.trim().length === 0) return [`${label} must be a non-empty repo-relative ref`];
60
+ if (ref.includes("\0")) errors.push(`${label} contains NUL byte`);
61
+ if (ref.startsWith("/") || ref.includes("\\") || ref.includes("..")) errors.push(`${label} must be safe repo-relative: ${ref}`);
62
+ const resolved = resolveRepoPath(repoRoot, ref);
63
+ errors.push(...resolved.errors.map((error) => `${label}: ${error}`));
64
+ for (const protectedPattern of DEFAULT_RULES.zeroAccessPaths) {
65
+ if (pathMatches(ref, protectedPattern, repoRoot, repoRoot)) errors.push(`${label} references zero-access path: ${protectedPattern}`);
66
+ }
67
+ if (/(^|\/)(node_modules|dist|build|coverage)(\/|$)/.test(ref)) errors.push(`${label} must not reference generated/vendor path: ${ref}`);
68
+ if (/(^|\/)\.env($|[./])|(^|\/)(\.ssh|\.aws)(\/|$)|secret|credential|\.pem$|\.p12$|\.pfx$/i.test(ref)) errors.push(`${label} must not reference secrets: ${ref}`);
69
+ return errors;
70
+ }
71
+
72
+ function requireExistingArtifactRef(repoRoot: string, ref: unknown, label: string): string[] {
73
+ if (typeof ref !== "string") return [`${label} must be a persisted artifact ref`];
74
+ const errors = validateArtifactRef(repoRoot, ref, label);
75
+ if (errors.length === 0 && !existsSync(resolveRepoPath(repoRoot, ref).path)) errors.push(`${label} does not exist: ${ref}`);
76
+ return errors;
77
+ }
78
+
79
+ function normalizeRepoPath(repoRoot: string, ref: string): string {
80
+ return resolveRepoPath(repoRoot, ref).path.replace(/\/+$/g, "");
81
+ }
82
+
83
+ function pathInsideAny(repoRoot: string, child: string, parents: string[]): boolean {
84
+ if (parents.length === 0) return true;
85
+ const normalizedChild = normalizeRepoPath(repoRoot, child);
86
+ return parents.some((parent) => {
87
+ const normalizedParent = normalizeRepoPath(repoRoot, parent);
88
+ return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}/`);
89
+ });
90
+ }
91
+
92
+ function isAtLeast(status: PromotionStatus, minimum: Exclude<PromotionStatus, "rejected" | "blocked">): boolean {
93
+ if (status === "rejected" || status === "blocked") return false;
94
+ return STATUS_ORDER[status] >= STATUS_ORDER[minimum];
95
+ }
96
+
97
+ function mergeGates(input?: Partial<PromotionGates>): PromotionGates {
98
+ return { ...DEFAULT_PROMOTION_GATES, ...(input ?? {}) };
99
+ }
100
+
101
+ export function promotionReportsDir(repoRoot: string): string {
102
+ return join(repoRoot, "reports", "promotions");
103
+ }
104
+
105
+ export function promotionCandidateDir(repoRoot: string, candidateId: string): string {
106
+ return join(promotionReportsDir(repoRoot), safeFileStem(candidateId));
107
+ }
108
+
109
+ export function promotionCandidateRef(candidateId: string, relativePath = "promotion-candidate.json"): string {
110
+ return `reports/promotions/${safeFileStem(candidateId)}/${relativePath}`;
111
+ }
112
+
113
+ function buildCandidateId(input: PromotionCandidateInput): string {
114
+ if (input.candidateId) {
115
+ const safe = safeFileStem(input.candidateId);
116
+ if (safe !== input.candidateId) throw new Error(`Unsafe promotion candidateId: ${input.candidateId}`);
117
+ return safe;
118
+ }
119
+ return `promo_${sha256(`${input.kind}:${input.runId}:${input.sourceRef}:${Date.now()}`).slice(0, 16)}`;
120
+ }
121
+
122
+ export function createPromotionCandidate(input: PromotionCandidateInput): PromotionCandidateRecord {
123
+ const candidateId = buildCandidateId(input);
124
+ const now = new Date().toISOString();
125
+ const changedPaths = [...new Set(input.changedPaths ?? [])].sort();
126
+ const allowedPaths = [...new Set(input.allowedPaths ?? [])].sort();
127
+ const forbiddenPaths = [...new Set(input.forbiddenPaths ?? [])].sort();
128
+ return {
129
+ schema: "zob.promotion-candidate.v1",
130
+ candidateId,
131
+ kind: input.kind,
132
+ runId: input.runId,
133
+ goalId: input.goalId ?? null,
134
+ todoId: input.todoId ?? null,
135
+ status: "proposal",
136
+ sourceRef: input.sourceRef,
137
+ sourceHash: input.sourceHash ?? null,
138
+ preparedArtifactRef: input.preparedArtifactRef ?? null,
139
+ validationRefs: [...new Set(input.validationRefs ?? [])].sort(),
140
+ oracleReviewRef: input.oracleReviewRef ?? null,
141
+ oracleVerdict: input.oracleVerdict ?? null,
142
+ oracleNoShip: input.oracleNoShip ?? null,
143
+ approvalRef: input.approvalRef ?? null,
144
+ rollbackRef: input.rollbackRef ?? null,
145
+ comsThreadRef: input.comsThreadRef ?? null,
146
+ goalRoomMessageRefs: [...new Set(input.goalRoomMessageRefs ?? [])].sort(),
147
+ changedPaths,
148
+ changedPathHashes: changedPaths.map((path) => sha256(path)),
149
+ allowedPaths,
150
+ forbiddenPaths,
151
+ gates: mergeGates(input.gates),
152
+ applyScope: input.applyScope ?? "none",
153
+ applyPerformed: false,
154
+ productionWritesPerformed: false,
155
+ autoApply: false,
156
+ parentOwned: true,
157
+ bodyStored: false,
158
+ promptBodiesStored: false,
159
+ outputBodiesStored: false,
160
+ metadata: input.metadata ?? {},
161
+ createdAt: now,
162
+ updatedAt: now,
163
+ };
164
+ }
165
+
166
+ export function validatePromotionCandidate(repoRoot: string, candidate: unknown): string[] {
167
+ const errors: string[] = [];
168
+ if (!isRecord(candidate)) return ["promotion candidate must be an object"];
169
+ if (!isBodyFreeRecord(candidate)) errors.push("promotion candidate must be body-free/hash-only and must not contain raw body-like keys");
170
+ if (candidate.schema !== "zob.promotion-candidate.v1") errors.push("promotion candidate schema mismatch");
171
+ if (typeof candidate.candidateId !== "string" || safeFileStem(candidate.candidateId) !== candidate.candidateId) errors.push("candidateId must be path-safe");
172
+ if (typeof candidate.runId !== "string" || safeFileStem(candidate.runId) !== candidate.runId) errors.push("runId must be path-safe");
173
+ if (!isSafeMetaId(candidate.goalId as string | null | undefined)) errors.push("goalId must be metadata/path safe when provided");
174
+ if (!isSafeMetaId(candidate.todoId as string | null | undefined)) errors.push("todoId must be metadata/path safe when provided");
175
+ if (typeof candidate.kind !== "string" || !PROMOTION_KINDS.includes(candidate.kind as PromotionKind)) errors.push("invalid promotion kind");
176
+ if (typeof candidate.status !== "string" || !PROMOTION_STATUSES.includes(candidate.status as PromotionStatus)) errors.push("invalid promotion status");
177
+ if (typeof candidate.sourceRef !== "string") errors.push("sourceRef is required");
178
+ else errors.push(...validateArtifactRef(repoRoot, candidate.sourceRef, "sourceRef"));
179
+ if (candidate.sourceHash !== null && candidate.sourceHash !== undefined && !SHA256_HEX.test(String(candidate.sourceHash))) errors.push("sourceHash must be sha256 hex when provided");
180
+ const gates = isRecord(candidate.gates) ? candidate.gates as Partial<PromotionGates> : undefined;
181
+ if (!gates) errors.push("promotion candidate requires gates");
182
+ for (const gateName of ["comsThreadRequired", "sandboxRequired", "validationRequired", "oracleRequired", "humanApprovalRequired", "rollbackRequired"] as const) {
183
+ if (typeof gates?.[gateName] !== "boolean") errors.push(`gate ${gateName} must be boolean`);
184
+ }
185
+ const refs: Array<[unknown, string]> = [
186
+ [candidate.preparedArtifactRef, "preparedArtifactRef"],
187
+ [candidate.oracleReviewRef, "oracleReviewRef"],
188
+ [candidate.approvalRef, "approvalRef"],
189
+ [candidate.rollbackRef, "rollbackRef"],
190
+ [candidate.comsThreadRef, "comsThreadRef"],
191
+ ];
192
+ for (const [ref, label] of refs) {
193
+ if (ref !== null && ref !== undefined) errors.push(...validateArtifactRef(repoRoot, String(ref), label));
194
+ }
195
+ for (const [field, label] of [[candidate.validationRefs, "validationRefs"], [candidate.goalRoomMessageRefs, "goalRoomMessageRefs"], [candidate.changedPaths, "changedPaths"], [candidate.allowedPaths, "allowedPaths"], [candidate.forbiddenPaths, "forbiddenPaths"]] as const) {
196
+ if (!Array.isArray(field) || !field.every((item) => typeof item === "string")) errors.push(`${label} must be a string array`);
197
+ else for (const item of field) errors.push(...validateArtifactRef(repoRoot, item, label));
198
+ }
199
+ if (!Array.isArray(candidate.changedPathHashes) || !candidate.changedPathHashes.every((hash) => typeof hash === "string" && SHA256_HEX.test(hash))) errors.push("changedPathHashes must be sha256 hex array");
200
+ if (Array.isArray(candidate.changedPaths) && Array.isArray(candidate.allowedPaths)) {
201
+ for (const changedPath of candidate.changedPaths.filter((item): item is string => typeof item === "string")) {
202
+ if (!pathInsideAny(repoRoot, changedPath, candidate.allowedPaths.filter((item): item is string => typeof item === "string"))) errors.push(`changed path is outside allowedPaths: ${changedPath}`);
203
+ for (const forbiddenPath of (candidate.forbiddenPaths as unknown[]).filter((item): item is string => typeof item === "string")) {
204
+ if (pathMatches(changedPath, forbiddenPath, repoRoot, repoRoot)) errors.push(`changed path matches forbiddenPaths: ${changedPath}`);
205
+ }
206
+ }
207
+ }
208
+ const status = candidate.status as PromotionStatus;
209
+ if (gates?.comsThreadRequired === true && typeof candidate.comsThreadRef !== "string") errors.push("comsThreadRef is required by gates");
210
+ if (gates?.sandboxRequired === true && isAtLeast(status, "prepared")) errors.push(...requireExistingArtifactRef(repoRoot, candidate.preparedArtifactRef, "preparedArtifactRef"));
211
+ if (gates?.validationRequired === true && isAtLeast(status, "validated")) {
212
+ if (!Array.isArray(candidate.validationRefs) || candidate.validationRefs.length === 0) errors.push("validationRefs are required by validation gate");
213
+ else for (const validationRef of candidate.validationRefs) errors.push(...requireExistingArtifactRef(repoRoot, validationRef, "validationRefs"));
214
+ }
215
+ if (gates?.oracleRequired === true && isAtLeast(status, "oracle_reviewed")) {
216
+ errors.push(...requireExistingArtifactRef(repoRoot, candidate.oracleReviewRef, "oracleReviewRef"));
217
+ if (candidate.oracleVerdict !== "PASS") errors.push("oracle gate requires oracleVerdict=PASS");
218
+ if (candidate.oracleNoShip !== false) errors.push("oracle gate requires oracleNoShip=false");
219
+ }
220
+ if (gates?.comsThreadRequired === true && isAtLeast(status, "approved")) errors.push(...requireExistingArtifactRef(repoRoot, candidate.comsThreadRef, "comsThreadRef"));
221
+ if (gates?.humanApprovalRequired === true && isAtLeast(status, "approved")) errors.push(...requireExistingArtifactRef(repoRoot, candidate.approvalRef, "approvalRef"));
222
+ if (gates?.rollbackRequired === true && status === "applied") errors.push(...requireExistingArtifactRef(repoRoot, candidate.rollbackRef, "rollbackRef"));
223
+ if (!PROMOTION_APPLY_SCOPES.includes(candidate.applyScope as PromotionApplyScope)) errors.push("invalid applyScope");
224
+ if (status === "applied") {
225
+ if (candidate.applyScope !== "quarantine_test_directory") errors.push("applied status is only supported for quarantine_test_directory scope in this runtime slice");
226
+ if (candidate.applyPerformed !== true) errors.push("applied status requires applyPerformed=true");
227
+ } else if (candidate.applyPerformed !== false) {
228
+ errors.push("applyPerformed must remain false until applied status");
229
+ }
230
+ if (candidate.productionWritesPerformed !== false) errors.push("productionWritesPerformed must remain false");
231
+ if (candidate.autoApply !== false) errors.push("autoApply must remain false");
232
+ if (candidate.parentOwned !== true) errors.push("promotion candidate must remain parent-owned");
233
+ return errors;
234
+ }
235
+
236
+ export function transitionAllowed(from: PromotionStatus, to: PromotionStatus): boolean {
237
+ return (ALLOWED_TRANSITIONS[from] ?? []).includes(to);
238
+ }
239
+
240
+ export function advancePromotionCandidate(repoRoot: string, candidate: PromotionCandidateRecord, input: PromotionTransitionInput): PromotionCandidateRecord {
241
+ if (!transitionAllowed(candidate.status, input.toStatus)) throw new Error(`Invalid promotion transition ${candidate.status} -> ${input.toStatus}`);
242
+ const updated: PromotionCandidateRecord = {
243
+ ...candidate,
244
+ status: input.toStatus,
245
+ preparedArtifactRef: input.preparedArtifactRef ?? candidate.preparedArtifactRef,
246
+ validationRefs: [...new Set([...(candidate.validationRefs ?? []), ...(input.validationRefs ?? [])])].sort(),
247
+ oracleReviewRef: input.oracleReviewRef ?? candidate.oracleReviewRef,
248
+ oracleVerdict: input.oracleVerdict ?? candidate.oracleVerdict,
249
+ oracleNoShip: input.oracleNoShip ?? candidate.oracleNoShip,
250
+ approvalRef: input.approvalRef ?? candidate.approvalRef,
251
+ rollbackRef: input.rollbackRef ?? candidate.rollbackRef,
252
+ goalRoomMessageRefs: [...new Set([...(candidate.goalRoomMessageRefs ?? []), ...(input.goalRoomMessageRefs ?? [])])].sort(),
253
+ applyScope: input.applyScope ?? candidate.applyScope,
254
+ applyPerformed: input.applyPerformed ?? (input.toStatus === "applied" ? true : candidate.applyPerformed),
255
+ metadata: { ...candidate.metadata, ...(input.metadata ?? {}) },
256
+ updatedAt: new Date().toISOString(),
257
+ };
258
+ const errors = validatePromotionCandidate(repoRoot, updated);
259
+ if (errors.length > 0) throw new Error(errors.join("; "));
260
+ return updated;
261
+ }
262
+
263
+ export function writePromotionCandidate(repoRoot: string, candidate: PromotionCandidateRecord): string {
264
+ const errors = validatePromotionCandidate(repoRoot, candidate);
265
+ if (errors.length > 0) throw new Error(errors.join("; "));
266
+ const dir = promotionCandidateDir(repoRoot, candidate.candidateId);
267
+ mkdirSync(dir, { recursive: true });
268
+ const relativeRef = promotionCandidateRef(candidate.candidateId);
269
+ writeFileSync(join(dir, "promotion-candidate.json"), JSON.stringify(candidate, null, 2), "utf8");
270
+ appendFileSync(join(dir, "promotion-ledger.jsonl"), `${JSON.stringify({ event: "candidate_written", candidateId: candidate.candidateId, status: candidate.status, applyPerformed: candidate.applyPerformed, productionWritesPerformed: false, autoApply: false, bodyStored: false, promptBodiesStored: false, outputBodiesStored: false, timestamp: new Date().toISOString() })}\n`, "utf8");
271
+ return relativeRef;
272
+ }
273
+
274
+ export function appendPromotionLedger(repoRoot: string, candidateId: string, event: Record<string, unknown>): string {
275
+ const dir = promotionCandidateDir(repoRoot, candidateId);
276
+ mkdirSync(dir, { recursive: true });
277
+ appendFileSync(join(dir, "promotion-ledger.jsonl"), `${JSON.stringify({ ...event, candidateId, bodyStored: false, promptBodiesStored: false, outputBodiesStored: false, timestamp: new Date().toISOString() })}\n`, "utf8");
278
+ return promotionCandidateRef(candidateId, "promotion-ledger.jsonl");
279
+ }
280
+
281
+ function latestPromotionIds(repoRoot: string, limit: number): string[] {
282
+ const root = promotionReportsDir(repoRoot);
283
+ if (!existsSync(root)) return [];
284
+ return readdirSync(root)
285
+ .map((entry) => ({ entry, path: join(root, entry), stat: statSync(join(root, entry)) }))
286
+ .filter((item) => item.stat.isDirectory())
287
+ .sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)
288
+ .slice(0, Math.max(1, limit))
289
+ .map((item) => item.entry);
290
+ }
291
+
292
+ export function summarizePromotionCandidates(repoRoot: string, limit = 10): Record<string, unknown> {
293
+ const latest = latestPromotionIds(repoRoot, limit).flatMap((candidateId) => {
294
+ const candidate = readJsonObjectIfPresent(join(promotionCandidateDir(repoRoot, candidateId), "promotion-candidate.json"));
295
+ if (!candidate) return [];
296
+ const errors = validatePromotionCandidate(repoRoot, candidate);
297
+ return [{
298
+ candidateId: candidate.candidateId,
299
+ kind: candidate.kind,
300
+ status: candidate.status,
301
+ runId: candidate.runId,
302
+ todoId: candidate.todoId,
303
+ changedPaths: Array.isArray(candidate.changedPaths) ? candidate.changedPaths : [],
304
+ validationRefs: Array.isArray(candidate.validationRefs) ? candidate.validationRefs : [],
305
+ oracleReviewRef: candidate.oracleReviewRef,
306
+ approvalRef: candidate.approvalRef,
307
+ applyScope: candidate.applyScope,
308
+ applyPerformed: candidate.applyPerformed,
309
+ productionWritesPerformed: candidate.productionWritesPerformed,
310
+ autoApply: candidate.autoApply,
311
+ valid: errors.length === 0,
312
+ errors,
313
+ artifact: promotionCandidateRef(String(candidate.candidateId)),
314
+ bodyStored: false,
315
+ }];
316
+ });
317
+ return {
318
+ latest,
319
+ blocked: latest.filter((entry) => entry.status === "blocked" || (Array.isArray(entry.errors) && entry.errors.length > 0)),
320
+ awaitingOracle: latest.filter((entry) => entry.status === "validated"),
321
+ awaitingApproval: latest.filter((entry) => entry.status === "oracle_reviewed"),
322
+ eligibleApply: latest.filter((entry) => entry.status === "approved"),
323
+ bodySafety: { summariesBodyFree: !hasForbiddenBodyKey(latest) },
324
+ uiReadyMetadataOnly: true,
325
+ };
326
+ }
327
+
328
+ export function promotionTestWorkspaceRef(candidateId: string, suffix = "applied-test-workspace"): string {
329
+ return promotionCandidateRef(candidateId, suffix);
330
+ }
331
+
332
+ export function assertInsidePromotionWorkspace(repoRoot: string, candidateId: string, path: string): void {
333
+ const root = resolve(promotionCandidateDir(repoRoot, candidateId));
334
+ const resolved = resolve(repoRoot, path);
335
+ if (resolved !== root && !resolved.startsWith(`${root}/`)) throw new Error(`Path must stay inside promotion workspace: ${path}`);
336
+ }
@@ -0,0 +1,127 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import { sha256 } from "../utils/hashing.js";
5
+ import { safeFileStem } from "../utils/paths.js";
6
+ import { isRecord } from "../utils/records.js";
7
+ import { promotionCandidateDir, promotionCandidateRef } from "./candidate.js";
8
+ import type { PromotionCandidateRecord, PromotionComsMessageRef, PromotionComsThreadInput, PromotionComsThreadRecord } from "./types.js";
9
+
10
+ const SHA256_HEX = /^[a-f0-9]{64}$/i;
11
+ const ALLOWED_KINDS = new Set(["STATUS_UPDATE", "FINDING", "RISK", "BLOCKER", "NO_SHIP_ALERT", "CONTEXT_REQUEST", "DELEGATION_REQUEST", "ORACLE_REQUEST", "QUESTION", "ANSWER"]);
12
+ const TERMINAL_BAD_STATUSES = new Set(["stale", "offline", "timeout"]);
13
+ const FORBIDDEN_BODY_KEYS = new Set(["body", "task", "prompt", "output", "content", "message", "text", "rationale", "diff", "patch", "transcript", "rawContext", "rawPrompt"]);
14
+
15
+ function hasForbiddenBodyKey(value: unknown): boolean {
16
+ if (!value || typeof value !== "object") return false;
17
+ if (Array.isArray(value)) return value.some(hasForbiddenBodyKey);
18
+ return Object.entries(value as Record<string, unknown>).some(([key, child]) => FORBIDDEN_BODY_KEYS.has(key) || hasForbiddenBodyKey(child));
19
+ }
20
+
21
+ function safeMeta(value: unknown): boolean {
22
+ return typeof value === "string" && value.length > 0 && safeFileStem(value) === value;
23
+ }
24
+
25
+ export function buildPromotionComsMessageRef(input: Omit<PromotionComsMessageRef, "parentVisible" | "hiddenPeerChat" | "workerToWorkerDirect" | "bodyStored" | "promptBodiesStored" | "outputBodiesStored">): PromotionComsMessageRef {
26
+ return {
27
+ ...input,
28
+ outputHash: input.outputHash ?? null,
29
+ artifactRefs: input.artifactRefs ?? [],
30
+ evidenceRefs: input.evidenceRefs ?? [],
31
+ parentVisible: true,
32
+ hiddenPeerChat: false,
33
+ workerToWorkerDirect: false,
34
+ bodyStored: false,
35
+ promptBodiesStored: false,
36
+ outputBodiesStored: false,
37
+ };
38
+ }
39
+
40
+ export function buildPromotionComsThread(input: PromotionComsThreadInput): PromotionComsThreadRecord {
41
+ const now = new Date().toISOString();
42
+ return {
43
+ schema: "zob.promotion-coms-thread.v1",
44
+ threadId: input.threadId ?? `pthread_${sha256(`${input.candidateId}:${Date.now()}`).slice(0, 16)}`,
45
+ goalId: input.goalId,
46
+ todoId: input.todoId ?? null,
47
+ candidateId: input.candidateId,
48
+ kind: input.kind,
49
+ messageRefs: input.messageRefs ?? [],
50
+ requiredAcks: input.requiredAcks ?? [],
51
+ stalePolicy: input.stalePolicy ?? "stale_blocks_completion",
52
+ parentVisible: true,
53
+ parentOwnedActions: true,
54
+ hiddenPeerChat: false,
55
+ bodyStored: false,
56
+ promptBodiesStored: false,
57
+ outputBodiesStored: false,
58
+ createdAt: now,
59
+ updatedAt: now,
60
+ };
61
+ }
62
+
63
+ export function addPromotionComsMessageRef(thread: PromotionComsThreadRecord, message: PromotionComsMessageRef): PromotionComsThreadRecord {
64
+ return { ...thread, messageRefs: [...thread.messageRefs, message], updatedAt: new Date().toISOString() };
65
+ }
66
+
67
+ export function validatePromotionComsMessageRef(message: unknown): string[] {
68
+ const errors: string[] = [];
69
+ if (!isRecord(message)) return ["promotion coms message ref must be an object"];
70
+ if (hasForbiddenBodyKey(message)) errors.push("promotion coms message ref must not contain raw body-like keys");
71
+ if (!safeMeta(message.msgId)) errors.push("message msgId must be metadata-safe");
72
+ if (typeof message.kind !== "string" || !ALLOWED_KINDS.has(message.kind)) errors.push("message kind is not allowed for promotion coms");
73
+ if (typeof message.sender !== "string" || message.sender.length === 0) errors.push("message sender is required");
74
+ if (typeof message.status !== "string" || !["queued", "acked", "running", "completed", "blocked", "timeout", "stale", "offline"].includes(message.status)) errors.push("message status is invalid");
75
+ if (typeof message.bodyHash !== "string" || !SHA256_HEX.test(message.bodyHash)) errors.push("message bodyHash must be sha256 hex");
76
+ if (message.outputHash !== null && message.outputHash !== undefined && (typeof message.outputHash !== "string" || !SHA256_HEX.test(message.outputHash))) errors.push("message outputHash must be sha256 hex when provided");
77
+ if (!Array.isArray(message.artifactRefs) || !message.artifactRefs.every((ref) => typeof ref === "string")) errors.push("message artifactRefs must be string array");
78
+ if (!Array.isArray(message.evidenceRefs) || !message.evidenceRefs.every((ref) => typeof ref === "string")) errors.push("message evidenceRefs must be string array");
79
+ if (message.status !== "completed" && message.countsAsCompletion === true) errors.push("only completed promotion coms messages may count as completion");
80
+ if (TERMINAL_BAD_STATUSES.has(String(message.status)) && message.countsAsCompletion === true) errors.push("stale/offline/timeout message must not count as completion");
81
+ if (message.parentVisible !== true || message.hiddenPeerChat !== false || message.workerToWorkerDirect !== false) errors.push("promotion coms must be parent-visible and must not be hidden worker-to-worker chat");
82
+ if (message.bodyStored !== false || message.promptBodiesStored !== false || message.outputBodiesStored !== false) errors.push("promotion coms message ref must keep body flags false");
83
+ return errors;
84
+ }
85
+
86
+ export function validatePromotionComsThread(thread: unknown): string[] {
87
+ const errors: string[] = [];
88
+ if (!isRecord(thread)) return ["promotion coms thread must be an object"];
89
+ if (hasForbiddenBodyKey(thread)) errors.push("promotion coms thread must not contain raw body-like keys");
90
+ if (thread.schema !== "zob.promotion-coms-thread.v1") errors.push("promotion coms thread schema mismatch");
91
+ if (!safeMeta(thread.threadId)) errors.push("threadId must be metadata-safe");
92
+ if (!safeMeta(thread.goalId)) errors.push("goalId must be metadata-safe");
93
+ if (thread.todoId !== null && thread.todoId !== undefined && !safeMeta(thread.todoId)) errors.push("todoId must be metadata-safe when provided");
94
+ if (!safeMeta(thread.candidateId)) errors.push("candidateId must be metadata-safe");
95
+ if (thread.stalePolicy !== "stale_blocks_completion") errors.push("stalePolicy must be stale_blocks_completion");
96
+ if (thread.parentVisible !== true || thread.parentOwnedActions !== true || thread.hiddenPeerChat !== false) errors.push("promotion coms thread must be parent-visible and parent-owned");
97
+ if (thread.bodyStored !== false || thread.promptBodiesStored !== false || thread.outputBodiesStored !== false) errors.push("promotion coms thread must keep body flags false");
98
+ if (!Array.isArray(thread.messageRefs)) errors.push("messageRefs must be an array");
99
+ else thread.messageRefs.forEach((message, index) => validatePromotionComsMessageRef(message).forEach((error) => errors.push(`messageRefs[${index}]: ${error}`)));
100
+ if (!Array.isArray(thread.requiredAcks) || !thread.requiredAcks.every((ack) => typeof ack === "string")) errors.push("requiredAcks must be string array");
101
+ const acked = new Set(Array.isArray(thread.messageRefs) ? thread.messageRefs.filter(isRecord).filter((message) => message.status === "acked" || message.status === "completed").map((message) => String(message.msgId)) : []);
102
+ for (const requiredAck of Array.isArray(thread.requiredAcks) ? thread.requiredAcks : []) {
103
+ if (!acked.has(requiredAck)) errors.push(`required ack missing: ${requiredAck}`);
104
+ }
105
+ return errors;
106
+ }
107
+
108
+ export function validatePromotionComsReadiness(candidate: PromotionCandidateRecord, thread: PromotionComsThreadRecord): string[] {
109
+ const errors = validatePromotionComsThread(thread);
110
+ if (thread.candidateId !== candidate.candidateId) errors.push("promotion coms thread candidateId must match candidate");
111
+ if (thread.kind !== candidate.kind) errors.push("promotion coms thread kind must match candidate");
112
+ const messageKinds = new Set(thread.messageRefs.map((message) => message.kind));
113
+ if ((candidate.status === "approved" || candidate.status === "applied") && !messageKinds.has("ORACLE_REQUEST")) errors.push("approved/applied promotion requires ORACLE_REQUEST message ref in coms thread");
114
+ if ((candidate.status === "approved" || candidate.status === "applied") && !thread.messageRefs.some((message) => message.kind === "FINDING" || message.kind === "STATUS_UPDATE")) errors.push("approved/applied promotion requires FINDING or STATUS_UPDATE message ref in coms thread");
115
+ if (thread.messageRefs.some((message) => TERMINAL_BAD_STATUSES.has(message.status) && message.countsAsCompletion === true)) errors.push("stale/offline/timeout coms cannot count as promotion completion");
116
+ if (candidate.status === "applied" && !thread.messageRefs.some((message) => message.status === "completed" && message.countsAsCompletion === true)) errors.push("applied promotion requires a completed parent-visible status message");
117
+ return errors;
118
+ }
119
+
120
+ export function writePromotionComsThread(repoRoot: string, thread: PromotionComsThreadRecord): string {
121
+ const errors = validatePromotionComsThread(thread);
122
+ if (errors.length > 0) throw new Error(errors.join("; "));
123
+ const dir = promotionCandidateDir(repoRoot, thread.candidateId);
124
+ mkdirSync(dir, { recursive: true });
125
+ writeFileSync(join(dir, "promotion-coms-thread.json"), JSON.stringify(thread, null, 2), "utf8");
126
+ return promotionCandidateRef(thread.candidateId, "promotion-coms-thread.json");
127
+ }
@@ -0,0 +1,142 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+
4
+ import { sha256 } from "../utils/hashing.js";
5
+ import { resolveRepoPath, safeFileStem } from "../utils/paths.js";
6
+ import { parseJsonFile } from "../utils/json.js";
7
+ import { isRecord } from "../utils/records.js";
8
+ import { advancePromotionCandidate, createPromotionCandidate, promotionCandidateDir, promotionCandidateRef, validatePromotionCandidate, writePromotionCandidate } from "./candidate.js";
9
+ import type { PromotionCandidateRecord } from "./types.js";
10
+
11
+ export interface DocumentationPromotionDraft {
12
+ targetPath: string;
13
+ draftText: string;
14
+ reason?: string;
15
+ }
16
+
17
+ export interface PrepareDocumentationPromotionInput {
18
+ candidateId?: string;
19
+ runId: string;
20
+ goalId?: string;
21
+ todoId?: string;
22
+ sourceRef: string;
23
+ drafts: DocumentationPromotionDraft[];
24
+ approvalMode?: "proposal_only" | "auto_prepare_patch";
25
+ }
26
+
27
+ const SENSITIVE_DOC_PATTERNS = [/^AGENTS\.md$/, /^docs\//, /^\.pi\/(rules|skills|prompts|agents|output-contracts)\//];
28
+
29
+ function hasForbiddenRawKeys(value: unknown): boolean {
30
+ if (!value || typeof value !== "object") return false;
31
+ if (Array.isArray(value)) return value.some(hasForbiddenRawKeys);
32
+ return Object.entries(value as Record<string, unknown>).some(([key, child]) => ["body", "task", "prompt", "output", "content", "message", "text", "rationale", "diff", "patch", "transcript", "rawContext", "rawPrompt"].includes(key) || hasForbiddenRawKeys(child));
33
+ }
34
+
35
+ function validateTargetPath(repoRoot: string, path: string): string[] {
36
+ const errors: string[] = [];
37
+ const resolved = resolveRepoPath(repoRoot, path);
38
+ errors.push(...resolved.errors.map((error) => `targetPath: ${error}`));
39
+ if (path.includes("\0") || path.startsWith("/") || path.includes("..") || path.includes("\\")) errors.push(`targetPath must be safe repo-relative: ${path}`);
40
+ if (/(^|\/)\.env($|[./])|secret|credential|\.pem$|\.p12$|\.pfx$/i.test(path)) errors.push(`targetPath must not reference secrets: ${path}`);
41
+ if (!SENSITIVE_DOC_PATTERNS.some((pattern) => pattern.test(path))) errors.push(`documentation promotion target must be a docs/guidance path: ${path}`);
42
+ return errors;
43
+ }
44
+
45
+ export function prepareDocumentationPromotion(repoRoot: string, input: PrepareDocumentationPromotionInput): { candidate: PromotionCandidateRecord; preparedArtifactRef: string; draftRefs: string[]; validationRef: string } {
46
+ const targetErrors = input.drafts.flatMap((draft) => validateTargetPath(repoRoot, draft.targetPath));
47
+ if (targetErrors.length > 0) throw new Error(targetErrors.join("; "));
48
+ const candidate = createPromotionCandidate({
49
+ candidateId: input.candidateId,
50
+ kind: "documentation_writeback",
51
+ runId: input.runId,
52
+ goalId: input.goalId,
53
+ todoId: input.todoId,
54
+ sourceRef: input.sourceRef,
55
+ allowedPaths: [...new Set(input.drafts.map((draft) => draft.targetPath.split("/").slice(0, -1).join("/") || draft.targetPath))],
56
+ changedPaths: input.drafts.map((draft) => draft.targetPath),
57
+ comsThreadRef: input.candidateId ? promotionCandidateRef(input.candidateId, "promotion-coms-thread.json") : undefined,
58
+ metadata: { lane: "documentation", approvalMode: input.approvalMode ?? "auto_prepare_patch" },
59
+ });
60
+ const candidateDir = promotionCandidateDir(repoRoot, candidate.candidateId);
61
+ const draftRefs: string[] = [];
62
+ const draftRecords = input.drafts.map((draft) => {
63
+ const draftRef = promotionCandidateRef(candidate.candidateId, `documentation/quarantine/${safeFileStem(draft.targetPath)}.draft.md`);
64
+ const draftPath = join(repoRoot, draftRef);
65
+ mkdirSync(dirname(draftPath), { recursive: true });
66
+ writeFileSync(draftPath, draft.draftText, "utf8");
67
+ draftRefs.push(draftRef);
68
+ return {
69
+ targetPath: draft.targetPath,
70
+ targetPathHash: sha256(draft.targetPath),
71
+ draftRef,
72
+ draftHash: sha256(draft.draftText),
73
+ reasonHash: draft.reason ? sha256(draft.reason) : null,
74
+ bodyStored: false,
75
+ };
76
+ });
77
+ const preparedArtifact = {
78
+ schema: "zob.documentation-promotion-prepared.v1",
79
+ candidateId: candidate.candidateId,
80
+ approvalMode: input.approvalMode ?? "auto_prepare_patch",
81
+ draftRecords,
82
+ changedPaths: candidate.changedPaths,
83
+ patchHash: sha256(JSON.stringify(draftRecords.map((record) => [record.targetPathHash, record.draftHash]))),
84
+ durableApplyPerformed: false,
85
+ autoApply: false,
86
+ bodyStored: false,
87
+ promptBodiesStored: false,
88
+ outputBodiesStored: false,
89
+ };
90
+ const preparedArtifactRef = promotionCandidateRef(candidate.candidateId, "documentation/prepared.json");
91
+ mkdirSync(join(candidateDir, "documentation"), { recursive: true });
92
+ writeFileSync(join(repoRoot, preparedArtifactRef), JSON.stringify(preparedArtifact, null, 2), "utf8");
93
+ const validation = validateDocumentationPromotion(preparedArtifact);
94
+ const validationRef = promotionCandidateRef(candidate.candidateId, "documentation/validation.json");
95
+ writeFileSync(join(repoRoot, validationRef), JSON.stringify({ schema: "zob.documentation-promotion-validation.v1", candidateId: candidate.candidateId, valid: validation.length === 0, errors: validation, bodyStored: false, promptBodiesStored: false, outputBodiesStored: false }, null, 2), "utf8");
96
+ const prepared = advancePromotionCandidate(repoRoot, { ...candidate, comsThreadRef: promotionCandidateRef(candidate.candidateId, "promotion-coms-thread.json") }, { toStatus: "prepared", preparedArtifactRef, validationRefs: validation.length === 0 ? [validationRef] : [], metadata: { draftRefs } });
97
+ writePromotionCandidate(repoRoot, prepared);
98
+ return { candidate: prepared, preparedArtifactRef, draftRefs, validationRef };
99
+ }
100
+
101
+ export function validateDocumentationPromotion(artifact: unknown): string[] {
102
+ const errors: string[] = [];
103
+ if (!isRecord(artifact)) return ["documentation promotion artifact must be an object"];
104
+ if (artifact.schema !== "zob.documentation-promotion-prepared.v1") errors.push("documentation promotion artifact schema mismatch");
105
+ if (hasForbiddenRawKeys(artifact)) errors.push("documentation promotion artifact must not include raw body-like keys");
106
+ if (!Array.isArray(artifact.draftRecords) || artifact.draftRecords.length === 0) errors.push("documentation promotion requires draftRecords");
107
+ if (artifact.durableApplyPerformed !== false || artifact.autoApply !== false) errors.push("documentation promotion must not auto-apply durable docs");
108
+ if (artifact.bodyStored !== false || artifact.promptBodiesStored !== false || artifact.outputBodiesStored !== false) errors.push("documentation promotion artifact must keep body flags false");
109
+ if (typeof artifact.patchHash !== "string" || !/^[a-f0-9]{64}$/i.test(artifact.patchHash)) errors.push("documentation promotion requires patchHash");
110
+ return errors;
111
+ }
112
+
113
+ export function applyDocumentationPromotionInQuarantine(repoRoot: string, candidate: PromotionCandidateRecord): { candidate: PromotionCandidateRecord; appliedRefs: string[]; appliedMetadataRef: string } {
114
+ if (candidate.kind !== "documentation_writeback") throw new Error("candidate kind must be documentation_writeback");
115
+ if (candidate.status !== "approved") throw new Error("documentation quarantine apply requires approved candidate");
116
+ if (!candidate.preparedArtifactRef) throw new Error("documentation quarantine apply requires preparedArtifactRef");
117
+ const prepared = parseJsonFile(join(repoRoot, candidate.preparedArtifactRef));
118
+ if (!isRecord(prepared) || !Array.isArray(prepared.draftRecords)) throw new Error("documentation prepared artifact missing draftRecords");
119
+ const appliedRefs: string[] = [];
120
+ for (const record of prepared.draftRecords.filter(isRecord)) {
121
+ const targetPath = typeof record.targetPath === "string" ? record.targetPath : "unknown.md";
122
+ const draftRef = typeof record.draftRef === "string" ? record.draftRef : undefined;
123
+ if (!draftRef) throw new Error("draftRef missing from documentation prepared artifact");
124
+ const draftText = readFileSync(join(repoRoot, draftRef), "utf8");
125
+ const appliedRef = promotionCandidateRef(candidate.candidateId, `documentation/applied-test-workspace/${safeFileStem(targetPath)}.md`);
126
+ mkdirSync(dirname(join(repoRoot, appliedRef)), { recursive: true });
127
+ writeFileSync(join(repoRoot, appliedRef), draftText, "utf8");
128
+ appliedRefs.push(appliedRef);
129
+ }
130
+ const appliedMetadataRef = promotionCandidateRef(candidate.candidateId, "documentation/applied-test-workspace/apply-metadata.json");
131
+ writeFileSync(join(repoRoot, appliedMetadataRef), JSON.stringify({ schema: "zob.documentation-quarantine-apply.v1", candidateId: candidate.candidateId, appliedRefs, productionWritesPerformed: false, autoApply: false, bodyStored: false, promptBodiesStored: false, outputBodiesStored: false }, null, 2), "utf8");
132
+ const applied = advancePromotionCandidate(repoRoot, candidate, { toStatus: "applied", applyScope: "quarantine_test_directory", applyPerformed: true });
133
+ writePromotionCandidate(repoRoot, applied);
134
+ return { candidate: applied, appliedRefs, appliedMetadataRef };
135
+ }
136
+
137
+ export function validateDocumentationPromotionCandidate(repoRoot: string, candidate: PromotionCandidateRecord): string[] {
138
+ const errors = validatePromotionCandidate(repoRoot, candidate);
139
+ if (candidate.kind !== "documentation_writeback") errors.push("candidate kind must be documentation_writeback");
140
+ if (candidate.autoApply !== false || candidate.productionWritesPerformed !== false) errors.push("documentation candidate must not auto-apply or perform production writes");
141
+ return errors;
142
+ }