@1aboveio/skills 0.10.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 (397) hide show
  1. package/LICENSE +3 -0
  2. package/README.md +90 -0
  3. package/bin/1aboveio-skills.mjs +18 -0
  4. package/package.json +28 -0
  5. package/runtime/skills/distribution/generated/recipes.json +1189 -0
  6. package/runtime/skills/distribution/scripts/bundles.mjs +280 -0
  7. package/runtime/skills/engineering/engineering-runtime/scripts/main-module.mjs +80 -0
  8. package/skills/backend/airflow-dag-develop/LICENSE +3 -0
  9. package/skills/backend/airflow-dag-develop/SKILL.md +111 -0
  10. package/skills/backend/app-debug/LICENSE +3 -0
  11. package/skills/backend/app-debug/SKILL.md +109 -0
  12. package/skills/backend/app-debug/references/common-errors.md +128 -0
  13. package/skills/backend/python-backend/LICENSE +3 -0
  14. package/skills/backend/python-backend/SKILL.md +326 -0
  15. package/skills/cicd-pipeline/cloud-build/LICENSE +3 -0
  16. package/skills/cicd-pipeline/cloud-build/SKILL.md +707 -0
  17. package/skills/cicd-pipeline/cloud-debug/LICENSE +3 -0
  18. package/skills/cicd-pipeline/cloud-debug/SKILL.md +316 -0
  19. package/skills/cicd-pipeline/cloud-debug/references/build-failures.md +238 -0
  20. package/skills/cicd-pipeline/cloud-debug/references/deploy-failures.md +376 -0
  21. package/skills/cicd-pipeline/cloud-debug/references/pipeline-failures.md +378 -0
  22. package/skills/cicd-pipeline/cloud-deploy/LICENSE +3 -0
  23. package/skills/cicd-pipeline/cloud-deploy/SKILL.md +229 -0
  24. package/skills/cicd-pipeline/cloud-deploy/references/config-templates.md +257 -0
  25. package/skills/cicd-pipeline/docker/LICENSE +3 -0
  26. package/skills/cicd-pipeline/docker/SKILL.md +126 -0
  27. package/skills/cicd-pipeline/google-cloud/LICENSE +3 -0
  28. package/skills/cicd-pipeline/google-cloud/SKILL.md +118 -0
  29. package/skills/cicd-pipeline/google-cloud/references/gcs.md +469 -0
  30. package/skills/cicd-pipeline/google-cloud/references/iam.md +451 -0
  31. package/skills/cicd-pipeline/google-cloud/references/project.md +349 -0
  32. package/skills/cicd-pipeline/google-cloud/references/secrets.md +336 -0
  33. package/skills/cicd-pipeline/google-cloud/references/vpc.md +312 -0
  34. package/skills/cicd-pipeline/google-cloud/scripts/create-sa.sh +36 -0
  35. package/skills/cicd-pipeline/google-cloud/scripts/gcp-config.sh +31 -0
  36. package/skills/cicd-pipeline/google-cloud/scripts/grant-iap.sh +41 -0
  37. package/skills/cicd-pipeline/google-cloud/scripts/setup-secrets.sh +48 -0
  38. package/skills/cicd-pipeline/mergify/LICENSE +3 -0
  39. package/skills/cicd-pipeline/mergify/SKILL.md +138 -0
  40. package/skills/cicd-pipeline/mergify/assets/templates/mergify.yml +237 -0
  41. package/skills/cicd-pipeline/mergify/assets/templates/ruleset.json +46 -0
  42. package/skills/cicd-pipeline/mergify/references/branch-protection.md +277 -0
  43. package/skills/cicd-pipeline/mergify/references/configuration.md +183 -0
  44. package/skills/cicd-pipeline/mergify/references/diagnosis.md +73 -0
  45. package/skills/cicd-pipeline/mergify/references/traps.md +78 -0
  46. package/skills/cicd-pipeline/mergify/references/watch-contract.md +218 -0
  47. package/skills/cicd-pipeline/mergify/scripts/audit-core.mjs +131 -0
  48. package/skills/cicd-pipeline/mergify/scripts/audit.mjs +4 -0
  49. package/skills/cicd-pipeline/mergify/scripts/watch-pr-delivery-core.mjs +663 -0
  50. package/skills/cicd-pipeline/mergify/scripts/watch-pr-delivery.mjs +4 -0
  51. package/skills/cicd-pipeline/podman/LICENSE +3 -0
  52. package/skills/cicd-pipeline/podman/SKILL.md +70 -0
  53. package/skills/cicd-pipeline/podman/agents/openai.yaml +4 -0
  54. package/skills/cicd-pipeline/podman/assets/templates/podman-compose-socket-directory.yml +6 -0
  55. package/skills/cicd-pipeline/podman/assets/templates/podman-service-override.conf +3 -0
  56. package/skills/cicd-pipeline/podman/references/compose-compatibility.md +70 -0
  57. package/skills/cicd-pipeline/podman/references/networking-and-ports.md +74 -0
  58. package/skills/cicd-pipeline/podman/references/rootless-services-and-sockets.md +156 -0
  59. package/skills/cicd-pipeline/podman/references/troubleshooting.md +98 -0
  60. package/skills/engineering/e2e-test/LICENSE +3 -0
  61. package/skills/engineering/e2e-test/SKILL.md +156 -0
  62. package/skills/engineering/e2e-test/assets/ci-gates.cloudbuild.yaml +272 -0
  63. package/skills/engineering/e2e-test/assets/ci-gates.github.yml +451 -0
  64. package/skills/engineering/e2e-test/assets/e2e-workflow.yml +282 -0
  65. package/skills/engineering/e2e-test/references/authoring/auth-flows.md +159 -0
  66. package/skills/engineering/e2e-test/references/authoring/playwright-config.md +71 -0
  67. package/skills/engineering/e2e-test/references/authoring/playwright-patterns.md +219 -0
  68. package/skills/engineering/e2e-test/references/authoring/test-skipping.md +44 -0
  69. package/skills/engineering/e2e-test/references/ci-integration.md +121 -0
  70. package/skills/engineering/e2e-test/references/ci-playwright-container.md +280 -0
  71. package/skills/engineering/e2e-test/references/debugging.md +36 -0
  72. package/skills/engineering/e2e-test/references/presentation-sweep.md +131 -0
  73. package/skills/engineering/e2e-test/references/reviewing.md +39 -0
  74. package/skills/engineering/e2e-test/references/route-discovery.md +50 -0
  75. package/skills/engineering/e2e-test/references/route-manifest.md +44 -0
  76. package/skills/engineering/e2e-test/scripts/detect-routes-fastapi.py +290 -0
  77. package/skills/engineering/e2e-test/scripts/detect-routes-nextjs.mjs +200 -0
  78. package/skills/engineering/e2e-test/scripts/post-visual-evidence.mjs +158 -0
  79. package/skills/engineering/e2e-test/scripts/presentation-checks.mjs +171 -0
  80. package/skills/engineering/e2e-test/scripts/presentation-perceivability.mjs +179 -0
  81. package/skills/engineering/e2e-test/scripts/presentation-reachability.mjs +154 -0
  82. package/skills/engineering/e2e-test/scripts/presentation-render-health.mjs +141 -0
  83. package/skills/engineering/e2e-test/scripts/presentation-sweep.mjs +148 -0
  84. package/skills/engineering/e2e-test/scripts/presentation-temporal.mjs +127 -0
  85. package/skills/engineering/e2e-test/scripts/presentation-visual.mjs +84 -0
  86. package/skills/engineering/e2e-test/scripts/project-route-manifest.mjs +75 -0
  87. package/skills/engineering/e2e-test/scripts/validate-manifest.mjs +106 -0
  88. package/skills/engineering/engineering-runtime/LICENSE +3 -0
  89. package/skills/engineering/engineering-runtime/SKILL.md +10 -0
  90. package/skills/engineering/engineering-runtime/agents/openai.yaml +6 -0
  91. package/skills/engineering/engineering-runtime/coherence/workflow.json +553 -0
  92. package/skills/engineering/engineering-runtime/scripts/exact-head-artifact.mjs +131 -0
  93. package/skills/engineering/engineering-runtime/scripts/head-check-set.mjs +398 -0
  94. package/skills/engineering/engineering-runtime/scripts/main-module.mjs +80 -0
  95. package/skills/engineering/engineering-runtime/scripts/mergify-yaml.mjs +11 -0
  96. package/skills/engineering/engineering-runtime/scripts/package-lock.json +43 -0
  97. package/skills/engineering/engineering-runtime/scripts/package.json +10 -0
  98. package/skills/engineering/engineering-runtime/scripts/required-check-plan.mjs +223 -0
  99. package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +576 -0
  100. package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +166 -0
  101. package/skills/engineering/ensure-coverage/LICENSE +3 -0
  102. package/skills/engineering/ensure-coverage/SKILL.md +136 -0
  103. package/skills/engineering/ensure-coverage/evals/evals.json +125 -0
  104. package/skills/engineering/ensure-coverage/references/breadth/coverage-ledger.md +91 -0
  105. package/skills/engineering/ensure-coverage/references/breadth/inventory-contract.md +83 -0
  106. package/skills/engineering/ensure-coverage/references/breadth/surface-baseline.md +44 -0
  107. package/skills/engineering/ensure-coverage/references/breadth/surface-discovery.md +16 -0
  108. package/skills/engineering/ensure-coverage/references/depth/characterization.md +68 -0
  109. package/skills/engineering/ensure-coverage/references/depth/coverage.config.example.json +25 -0
  110. package/skills/engineering/ensure-coverage/references/depth/grading.md +35 -0
  111. package/skills/engineering/ensure-coverage/references/depth/mock-policy.md +87 -0
  112. package/skills/engineering/ensure-coverage/references/depth/test-smells.md +23 -0
  113. package/skills/engineering/ensure-coverage/references/enforcement/ci-contract.md +164 -0
  114. package/skills/engineering/ensure-coverage/references/enforcement/hooks.md +85 -0
  115. package/skills/engineering/ensure-coverage/references/examples/coverage-ledger.md +109 -0
  116. package/skills/engineering/ensure-coverage/references/examples/refund-flow.md +33 -0
  117. package/skills/engineering/ensure-coverage/references/presentation/axis.md +78 -0
  118. package/skills/engineering/ensure-coverage/references/presentation/runner-contract.md +74 -0
  119. package/skills/engineering/ensure-coverage/references/process/audit-mode.md +33 -0
  120. package/skills/engineering/ensure-coverage/references/process/output-template.md +139 -0
  121. package/skills/engineering/ensure-coverage/references/process/review-contract-template.md +119 -0
  122. package/skills/engineering/ensure-coverage/references/process/scope-class.md +178 -0
  123. package/skills/engineering/ensure-coverage/references/process/test-strategy.md +55 -0
  124. package/skills/engineering/ensure-coverage/schemas/coverage-config.schema.json +45 -0
  125. package/skills/engineering/ensure-coverage/schemas/coverage-file.schema.json +93 -0
  126. package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.mjs +178 -0
  127. package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.mjs +101 -0
  128. package/skills/engineering/ensure-coverage/scripts/ci-audit.mjs +358 -0
  129. package/skills/engineering/ensure-coverage/scripts/coverage-checklist.mjs +494 -0
  130. package/skills/engineering/ensure-coverage/scripts/coverage-ledger.mjs +663 -0
  131. package/skills/engineering/ensure-coverage/scripts/design-parity.mjs +591 -0
  132. package/skills/engineering/ensure-coverage/scripts/evidence-block.mjs +367 -0
  133. package/skills/engineering/ensure-coverage/scripts/lint-tests.mjs +269 -0
  134. package/skills/engineering/ensure-coverage/scripts/mock-policy-config.mjs +176 -0
  135. package/skills/engineering/ensure-coverage/scripts/package-lock.json +76 -0
  136. package/skills/engineering/ensure-coverage/scripts/package.json +19 -0
  137. package/skills/engineering/ensure-coverage/scripts/scope-class.mjs +554 -0
  138. package/skills/engineering/harness-runtime/LICENSE +3 -0
  139. package/skills/engineering/harness-runtime/SKILL.md +18 -0
  140. package/skills/engineering/harness-runtime/agents/openai.yaml +6 -0
  141. package/skills/engineering/harness-runtime/bin/discover-models.mjs +4 -0
  142. package/skills/engineering/harness-runtime/bin/model-catalog.mjs +4 -0
  143. package/skills/engineering/harness-runtime/contracts.md +15 -0
  144. package/skills/engineering/harness-runtime/discover-models.mjs +392 -0
  145. package/skills/engineering/harness-runtime/fixtures/native-question-schemas.json +33 -0
  146. package/skills/engineering/harness-runtime/fixtures/question-responses.json +54 -0
  147. package/skills/engineering/harness-runtime/index.mjs +767 -0
  148. package/skills/engineering/harness-runtime/model-catalog.mjs +787 -0
  149. package/skills/engineering/harness-runtime/native-question-contracts.md +37 -0
  150. package/skills/engineering/harness-runtime/references/model-catalog-seed.json +159 -0
  151. package/skills/engineering/harness-runtime/references/model-catalog.md +57 -0
  152. package/skills/engineering/implement-and-pr/LICENSE +3 -0
  153. package/skills/engineering/implement-and-pr/SKILL.md +176 -0
  154. package/skills/engineering/implement-and-pr/references/ci-iteration.md +10 -0
  155. package/skills/engineering/implement-and-pr/references/closeout.md +27 -0
  156. package/skills/engineering/implement-and-pr/references/contract-complete-fix-rounds.md +34 -0
  157. package/skills/engineering/implement-and-pr/references/evidence-rules.md +39 -0
  158. package/skills/engineering/implement-and-pr/references/incremental-plan.md +16 -0
  159. package/skills/engineering/implement-and-pr/references/self-review.md +23 -0
  160. package/skills/engineering/implement-and-pr/references/tdd-mode.md +18 -0
  161. package/skills/engineering/resolve-issues/LICENSE +3 -0
  162. package/skills/engineering/resolve-issues/SKILL.md +167 -0
  163. package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +448 -0
  164. package/skills/engineering/resolve-issues/references/breaker.md +82 -0
  165. package/skills/engineering/resolve-issues/references/deliverables.md +27 -0
  166. package/skills/engineering/resolve-issues/references/delivery.md +108 -0
  167. package/skills/engineering/resolve-issues/references/evidence-lane.md +21 -0
  168. package/skills/engineering/resolve-issues/references/exact-head-ci.md +287 -0
  169. package/skills/engineering/resolve-issues/references/fan-out.md +33 -0
  170. package/skills/engineering/resolve-issues/references/finalization.md +68 -0
  171. package/skills/engineering/resolve-issues/references/guarantees.md +10 -0
  172. package/skills/engineering/resolve-issues/references/high-risk.md +29 -0
  173. package/skills/engineering/resolve-issues/references/incidents/848/README.md +156 -0
  174. package/skills/engineering/resolve-issues/references/intake.md +86 -0
  175. package/skills/engineering/resolve-issues/references/integration-gate.md +53 -0
  176. package/skills/engineering/resolve-issues/references/interference.md +87 -0
  177. package/skills/engineering/resolve-issues/references/loop.md +134 -0
  178. package/skills/engineering/resolve-issues/references/model-catalog.md +9 -0
  179. package/skills/engineering/resolve-issues/references/postmortem.md +27 -0
  180. package/skills/engineering/resolve-issues/references/pre-flight-model-slots.md +41 -0
  181. package/skills/engineering/resolve-issues/references/pre-flight-recording-and-checkout.md +48 -0
  182. package/skills/engineering/resolve-issues/references/pre-flight.md +41 -0
  183. package/skills/engineering/resolve-issues/references/regression-checklist.md +26 -0
  184. package/skills/engineering/resolve-issues/references/run-state.md +288 -0
  185. package/skills/engineering/resolve-issues/references/sandboxed-testing.md +48 -0
  186. package/skills/engineering/resolve-issues/references/spawn-contract.md +96 -0
  187. package/skills/engineering/resolve-issues/references/terminal-evidence-journal.md +40 -0
  188. package/skills/engineering/resolve-issues/references/why.md +653 -0
  189. package/skills/engineering/resolve-issues/schemas/fix-round.schema.json +49 -0
  190. package/skills/engineering/resolve-issues/scripts/combine-and-verify.mjs +721 -0
  191. package/skills/engineering/resolve-issues/scripts/component-candidate.mjs +962 -0
  192. package/skills/engineering/resolve-issues/scripts/contract-revision.mjs +220 -0
  193. package/skills/engineering/resolve-issues/scripts/detect-delivery-mode.mjs +420 -0
  194. package/skills/engineering/resolve-issues/scripts/detect-target-branch.mjs +256 -0
  195. package/skills/engineering/resolve-issues/scripts/detect-workspace-mode.mjs +168 -0
  196. package/skills/engineering/resolve-issues/scripts/discover-models.mjs +9 -0
  197. package/skills/engineering/resolve-issues/scripts/doctrine.mjs +62 -0
  198. package/skills/engineering/resolve-issues/scripts/evidence-lifecycle-contract.mjs +191 -0
  199. package/skills/engineering/resolve-issues/scripts/exact-head-ci.mjs +413 -0
  200. package/skills/engineering/resolve-issues/scripts/exact-head-github-provider.mjs +332 -0
  201. package/skills/engineering/resolve-issues/scripts/finalize.mjs +488 -0
  202. package/skills/engineering/resolve-issues/scripts/fix-rounds.mjs +3307 -0
  203. package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-circular-1001.json +16 -0
  204. package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-valid-sequencing.json +51 -0
  205. package/skills/engineering/resolve-issues/scripts/fixtures/fmm-express-830-component-candidate.json +17 -0
  206. package/skills/engineering/resolve-issues/scripts/fixtures/head-check-set-1081.json +166 -0
  207. package/skills/engineering/resolve-issues/scripts/gate-value-series.mjs +92 -0
  208. package/skills/engineering/resolve-issues/scripts/guide-index.mjs +73 -0
  209. package/skills/engineering/resolve-issues/scripts/head-check-set.mjs +159 -0
  210. package/skills/engineering/resolve-issues/scripts/interference.mjs +427 -0
  211. package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +9 -0
  212. package/skills/engineering/resolve-issues/scripts/next-operations.mjs +419 -0
  213. package/skills/engineering/resolve-issues/scripts/postmortem.mjs +909 -0
  214. package/skills/engineering/resolve-issues/scripts/preflight-questions.mjs +322 -0
  215. package/skills/engineering/resolve-issues/scripts/reconcile-contained-unit-prs.mjs +415 -0
  216. package/skills/engineering/resolve-issues/scripts/release-state-contract.mjs +697 -0
  217. package/skills/engineering/resolve-issues/scripts/report.mjs +494 -0
  218. package/skills/engineering/resolve-issues/scripts/required-check-plan.mjs +172 -0
  219. package/skills/engineering/resolve-issues/scripts/round-metadata.mjs +79 -0
  220. package/skills/engineering/resolve-issues/scripts/run-state-review6-cases.mjs +334 -0
  221. package/skills/engineering/resolve-issues/scripts/run-state.mjs +4784 -0
  222. package/skills/engineering/resolve-issues/scripts/sandbox-selftest.mjs +395 -0
  223. package/skills/engineering/resolve-issues/scripts/spawn-contract.mjs +290 -0
  224. package/skills/engineering/resolve-issues/scripts/terminal-dispositions.mjs +170 -0
  225. package/skills/engineering/resolve-issues/scripts/terminal-evidence-journal.mjs +293 -0
  226. package/skills/engineering/resolve-issues/scripts/unit-kind.mjs +197 -0
  227. package/skills/engineering/resolve-issues/scripts/unit-lifecycle.mjs +127 -0
  228. package/skills/engineering/resolve-issues/scripts/watch-delivery.mjs +893 -0
  229. package/skills/engineering/resolve-issues/scripts/workspaces.mjs +829 -0
  230. package/skills/engineering/resolve-issues/workflows/independent-review.workflow.js +290 -0
  231. package/skills/engineering/resolve-issues/workflows/prior-art-scan.workflow.js +80 -0
  232. package/skills/engineering/resolve-issues/workflows/workflow-smoke.mjs +102 -0
  233. package/skills/engineering/resolve-release/LICENSE +3 -0
  234. package/skills/engineering/resolve-release/SKILL.md +112 -0
  235. package/skills/engineering/resolve-release/references/assembly.md +137 -0
  236. package/skills/engineering/resolve-release/references/auto-when-green.md +56 -0
  237. package/skills/engineering/resolve-release/references/candidate.md +167 -0
  238. package/skills/engineering/resolve-release/references/exposure.md +178 -0
  239. package/skills/engineering/resolve-release/references/handoff.md +24 -0
  240. package/skills/engineering/resolve-release/references/postmortem.md +230 -0
  241. package/skills/engineering/resolve-release/references/preflight.md +207 -0
  242. package/skills/engineering/resolve-release/references/principles.md +94 -0
  243. package/skills/engineering/resolve-release/references/regression-checklist.md +36 -0
  244. package/skills/engineering/resolve-release/references/related-skills.md +12 -0
  245. package/skills/engineering/resolve-release/references/routing.md +149 -0
  246. package/skills/engineering/resolve-release/references/verified-sha-github-flow.md +285 -0
  247. package/skills/engineering/resolve-release/references/versioning.md +202 -0
  248. package/skills/engineering/resolve-release/references/why.md +53 -0
  249. package/skills/engineering/resolve-release/scripts/adapter-completion-artifact.mjs +389 -0
  250. package/skills/engineering/resolve-release/scripts/build-changes.mjs +209 -0
  251. package/skills/engineering/resolve-release/scripts/candidate-hygiene.mjs +407 -0
  252. package/skills/engineering/resolve-release/scripts/candidate-identity.mjs +904 -0
  253. package/skills/engineering/resolve-release/scripts/candidate-traffic.mjs +81 -0
  254. package/skills/engineering/resolve-release/scripts/checked-adapter-loader.mjs +612 -0
  255. package/skills/engineering/resolve-release/scripts/close-attempt.mjs +135 -0
  256. package/skills/engineering/resolve-release/scripts/closeout-release.mjs +161 -0
  257. package/skills/engineering/resolve-release/scripts/doctrine.mjs +106 -0
  258. package/skills/engineering/resolve-release/scripts/durable-processing.mjs +522 -0
  259. package/skills/engineering/resolve-release/scripts/ensure-target-green.mjs +659 -0
  260. package/skills/engineering/resolve-release/scripts/evidence-bundle.mjs +1014 -0
  261. package/skills/engineering/resolve-release/scripts/finalize-release.mjs +526 -0
  262. package/skills/engineering/resolve-release/scripts/fixtures/durable-processing-adapter.mjs +169 -0
  263. package/skills/engineering/resolve-release/scripts/green-gate.mjs +599 -0
  264. package/skills/engineering/resolve-release/scripts/isolated-adapter-evaluator.mjs +752 -0
  265. package/skills/engineering/resolve-release/scripts/metadata-pr-status.mjs +56 -0
  266. package/skills/engineering/resolve-release/scripts/metadata-sync.mjs +1538 -0
  267. package/skills/engineering/resolve-release/scripts/postmortem.mjs +381 -0
  268. package/skills/engineering/resolve-release/scripts/preflight-probes.mjs +498 -0
  269. package/skills/engineering/resolve-release/scripts/production-endpoints.mjs +326 -0
  270. package/skills/engineering/resolve-release/scripts/rc-circuit-breaker.mjs +272 -0
  271. package/skills/engineering/resolve-release/scripts/report.mjs +417 -0
  272. package/skills/engineering/resolve-release/scripts/reprobe-credentials.mjs +114 -0
  273. package/skills/engineering/resolve-release/scripts/revalidate-candidate.mjs +238 -0
  274. package/skills/engineering/resolve-release/scripts/review-packet.mjs +503 -0
  275. package/skills/engineering/resolve-release/scripts/rollback-floor.mjs +263 -0
  276. package/skills/engineering/resolve-release/scripts/version-assert.mjs +339 -0
  277. package/skills/engineering/resolve-release/scripts/version-postmortem.mjs +485 -0
  278. package/skills/engineering/resolve-release/scripts/version.mjs +1199 -0
  279. package/skills/engineering/resolve-release/scripts/watch-candidate-delivery.mjs +449 -0
  280. package/skills/engineering/resolve-release/vendor/ACORN-LICENSE +21 -0
  281. package/skills/engineering/resolve-release/vendor/README.md +60 -0
  282. package/skills/engineering/resolve-release/vendor/acorn.mjs +6233 -0
  283. package/skills/engineering/resolve-release/vendor/quickjs/core/LICENSE +21 -0
  284. package/skills/engineering/resolve-release/vendor/quickjs/core/README.md +341 -0
  285. package/skills/engineering/resolve-release/vendor/quickjs/core/README.template.md +70 -0
  286. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs +2 -0
  287. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs.map +1 -0
  288. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs +7 -0
  289. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs.map +1 -0
  290. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.mts +2033 -0
  291. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.ts +2033 -0
  292. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js +7 -0
  293. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js.map +1 -0
  294. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs +2 -0
  295. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs.map +1 -0
  296. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs +2 -0
  297. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs.map +1 -0
  298. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs +2 -0
  299. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs.map +1 -0
  300. package/skills/engineering/resolve-release/vendor/quickjs/core/package.json +49 -0
  301. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/LICENSE +21 -0
  302. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/README.md +5 -0
  303. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.mts +549 -0
  304. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.ts +549 -0
  305. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js +2 -0
  306. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js.map +1 -0
  307. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs +2 -0
  308. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs.map +1 -0
  309. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/package.json +36 -0
  310. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/LICENSE +47 -0
  311. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/README.md +82 -0
  312. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.d.ts +11 -0
  313. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.mjs +22 -0
  314. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cjs +21 -0
  315. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.cjs +21 -0
  316. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.d.ts +11 -0
  317. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.d.ts +11 -0
  318. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.mjs +25 -0
  319. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.wasm +0 -0
  320. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.mts +85 -0
  321. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.ts +85 -0
  322. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js +2 -0
  323. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js.map +1 -0
  324. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs +2 -0
  325. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs.map +1 -0
  326. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.mts +20 -0
  327. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.ts +20 -0
  328. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js +2 -0
  329. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js.map +1 -0
  330. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs +2 -0
  331. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs.map +1 -0
  332. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/package.json +61 -0
  333. package/skills/engineering/review-pr/LICENSE +3 -0
  334. package/skills/engineering/review-pr/SKILL.md +123 -0
  335. package/skills/engineering/review-pr/references/adversarial-reviewer-prompt.md +42 -0
  336. package/skills/engineering/review-pr/references/code-correctness.md +5 -0
  337. package/skills/engineering/review-pr/references/contract-freshness.md +9 -0
  338. package/skills/engineering/review-pr/references/coordination.md +18 -0
  339. package/skills/engineering/review-pr/references/domain-hazards.md +123 -0
  340. package/skills/engineering/review-pr/references/finding-themes.md +7 -0
  341. package/skills/engineering/review-pr/references/github-posting.md +98 -0
  342. package/skills/engineering/review-pr/references/golden-path-smoke.md +5 -0
  343. package/skills/engineering/review-pr/references/incremental-output.md +16 -0
  344. package/skills/engineering/review-pr/references/inputs-and-discovery.md +31 -0
  345. package/skills/engineering/review-pr/references/output-format.md +99 -0
  346. package/skills/engineering/review-pr/references/over-mock-screen.md +7 -0
  347. package/skills/engineering/review-pr/references/promotion-prs.md +16 -0
  348. package/skills/engineering/review-pr/references/re-review.md +18 -0
  349. package/skills/engineering/review-pr/references/review-method.md +199 -0
  350. package/skills/engineering/review-pr/references/review-mode.md +30 -0
  351. package/skills/engineering/review-pr/references/review-posture.md +53 -0
  352. package/skills/engineering/review-pr/references/round1-depth.md +62 -0
  353. package/skills/engineering/review-pr/references/scripts.md +17 -0
  354. package/skills/engineering/review-pr/references/workflow.md +16 -0
  355. package/skills/engineering/review-pr/schemas/findings.schema.json +282 -0
  356. package/skills/engineering/review-pr/scripts/finding-contract.mjs +285 -0
  357. package/skills/engineering/review-pr/scripts/post-review.mjs +405 -0
  358. package/skills/engineering/review-pr/scripts/pr-context.mjs +207 -0
  359. package/skills/engineering/review-pr/scripts/scan-diff.mjs +365 -0
  360. package/skills/engineering/review-pr/scripts/theme-contract.mjs +57 -0
  361. package/skills/engineering/smoke/LICENSE +3 -0
  362. package/skills/engineering/smoke/SKILL.md +131 -0
  363. package/skills/engineering/smoke/assets/smoke.manifest.example.json +53 -0
  364. package/skills/engineering/smoke/references/manifest.md +192 -0
  365. package/skills/engineering/smoke/scripts/smoke.mjs +713 -0
  366. package/skills/fullstack/better-auth/LICENSE +3 -0
  367. package/skills/fullstack/better-auth/SKILL.md +601 -0
  368. package/skills/fullstack/better-auth/references/feishu-api.md +270 -0
  369. package/skills/fullstack/monorepo/LICENSE +3 -0
  370. package/skills/fullstack/monorepo/SKILL.md +465 -0
  371. package/skills/fullstack/nextjs-fullstack/LICENSE +3 -0
  372. package/skills/fullstack/nextjs-fullstack/SKILL.md +210 -0
  373. package/skills/fullstack/nextjs-fullstack/conventions.md +318 -0
  374. package/skills/fullstack/nextjs-fullstack/frontend-conventions.md +61 -0
  375. package/skills/fullstack/nextjs-fullstack/nextjs16.md +287 -0
  376. package/skills/fullstack/nextjs-fullstack/server-actions.md +409 -0
  377. package/skills/fullstack/prisma-setup/LICENSE +3 -0
  378. package/skills/fullstack/prisma-setup/SKILL.md +180 -0
  379. package/skills/fullstack/prisma-setup/nextjs.md +258 -0
  380. package/skills/fullstack/prisma-setup/turborepo.md +301 -0
  381. package/skills/fullstack/shadcn/LICENSE +3 -0
  382. package/skills/fullstack/shadcn/SKILL.md +119 -0
  383. package/skills/fullstack/shadcn/assets/shadcn-small.png +0 -0
  384. package/skills/fullstack/shadcn/assets/shadcn.png +0 -0
  385. package/skills/fullstack/shadcn/cli.md +411 -0
  386. package/skills/fullstack/shadcn/customization.md +224 -0
  387. package/skills/fullstack/shadcn/evals/evals.json +90 -0
  388. package/skills/fullstack/shadcn/mcp.md +101 -0
  389. package/skills/fullstack/shadcn/rules/base-vs-radix.md +323 -0
  390. package/skills/fullstack/shadcn/rules/component-selection.md +67 -0
  391. package/skills/fullstack/shadcn/rules/composition.md +195 -0
  392. package/skills/fullstack/shadcn/rules/data-table.md +201 -0
  393. package/skills/fullstack/shadcn/rules/forms.md +255 -0
  394. package/skills/fullstack/shadcn/rules/icons.md +103 -0
  395. package/skills/fullstack/shadcn/rules/styling.md +167 -0
  396. package/skills/fullstack/zod-v4/LICENSE +3 -0
  397. package/skills/fullstack/zod-v4/SKILL.md +287 -0
@@ -0,0 +1,962 @@
1
+ import {
2
+ AUTHORIZATION_BOUNDARY_KINDS,
3
+ REOPEN_REASONS,
4
+ classifyRoundMetadata,
5
+ completedReviewRounds,
6
+ deliveryFlow,
7
+ deliveryMode,
8
+ deliveryProvider,
9
+ DELIVERY_MAX_NUDGES,
10
+ validateDeliveryAttempt,
11
+ } from './fix-rounds.mjs'
12
+ import {
13
+ checksForPhase,
14
+ validatePublishOnlyEvidence,
15
+ validateQueueExecutionArtifact,
16
+ validateRequiredCheckExecution,
17
+ validateRequiredCheckPlan,
18
+ } from '../../engineering-runtime/scripts/required-check-plan.mjs'
19
+ import { normalizeChangedSurfaces } from './round-metadata.mjs'
20
+ import { completionBlockers, effectiveUnitKind } from './unit-lifecycle.mjs'
21
+ import { reconciliationErrors } from './reconcile-contained-unit-prs.mjs'
22
+
23
+ export const COMPONENT_CANDIDATE_STATES = ['assembling', 'frozen', 'authorized']
24
+ export const COMPONENT_ROUND_CLASSES = ['unit-discovery', 'integration-fix', 'terminal-authorization']
25
+ export const COMPONENT_ASSEMBLY_RESULTS = ['clean', 'conflict']
26
+ export const COMPONENT_TEST_RESULTS = ['green', 'red', 'not-run']
27
+
28
+ const REOPEN_BOUNDARY_KIND = {
29
+ 'ci-red': 'ci',
30
+ 'head-rewritten': 'head',
31
+ 'contract-revised': 'contract',
32
+ 'integration-finding': 'integration',
33
+ // #941 / epic-876: external bot/human CHANGES_REQUESTED after independent PASS
34
+ 'external-review': 'head',
35
+ }
36
+ const SHA_RE = /^[0-9a-f]{7,64}$/i
37
+ const clone = (value) => structuredClone(value)
38
+ const isObject = (value) => value != null && typeof value === 'object' && !Array.isArray(value)
39
+ const nonEmpty = (value) => typeof value === 'string' && value.trim().length > 0
40
+ const same = (left, right) => JSON.stringify(left) === JSON.stringify(right)
41
+ const unique = (values) => Array.isArray(values) && new Set(values).size === values.length
42
+
43
+ function assertExactObject(value, allowed, label) {
44
+ if (!isObject(value)) throw new Error(`${label} must be an object`)
45
+ const unknown = Object.keys(value).filter((key) => !allowed.includes(key))
46
+ if (unknown.length) throw new Error(`${label} has unknown property ${unknown[0]}`)
47
+ }
48
+
49
+ function assertSha(value, label) {
50
+ if (!nonEmpty(value) || !SHA_RE.test(value)) throw new Error(`${label} must be a 7-64 character hexadecimal SHA`)
51
+ }
52
+
53
+ function componentAndUnits(manifest, componentId) {
54
+ const components = Array.isArray(manifest?.components) ? manifest.components : []
55
+ const component = components.find((entry) => entry?.id === componentId)
56
+ if (!component) throw new Error(`component candidate: no component ${componentId} in this run`)
57
+ if (!Array.isArray(component.units) || !component.units.length || !unique(component.units) || component.units.some((id) => !nonEmpty(id))) {
58
+ throw new Error(`component candidate ${componentId}: units must be a non-empty unique array of unit ids`)
59
+ }
60
+ const byId = new Map((Array.isArray(manifest?.units) ? manifest.units : []).map((unit) => [unit?.id, unit]))
61
+ for (const id of component.units) if (!byId.has(id)) throw new Error(`component candidate ${componentId}: unit ${id} is not in the run roster`)
62
+ return { component, byId }
63
+ }
64
+
65
+ function boundary(value, label) {
66
+ assertExactObject(value, ['kind', 'value'], label)
67
+ if (!AUTHORIZATION_BOUNDARY_KINDS.includes(value.kind)) throw new Error(`${label}.kind must be one of ${AUTHORIZATION_BOUNDARY_KINDS.join('|')}`)
68
+ if (!nonEmpty(value.value)) throw new Error(`${label}.value must be a non-empty string`)
69
+ return clone(value)
70
+ }
71
+
72
+ function typedPreliminaryReview(entry, headSha, label) {
73
+ if (!isObject(entry) || entry.verdict !== 'PASS' || entry.headSha !== headSha) return null
74
+ if (classifyRoundMetadata(entry).legacy) return null
75
+ if (!Number.isInteger(entry.round) || entry.round < 1) throw new Error(`${label}.round must be a positive integer`)
76
+ if (!['code', 'evidence'].includes(entry.reviewSubject)) throw new Error(`${label} needs typed reviewSubject`)
77
+ return {
78
+ round: entry.round,
79
+ verdict: entry.verdict,
80
+ headSha: entry.headSha,
81
+ reviewSubject: entry.reviewSubject,
82
+ authorizationBoundary: boundary(entry.authorizationBoundary, `${label}.authorizationBoundary`),
83
+ }
84
+ }
85
+
86
+ function legacyRoundAuthority(entry, headSha) {
87
+ if (!isObject(entry) || entry.verdict !== 'PASS' || entry.headSha !== headSha) return null
88
+ if (!classifyRoundMetadata(entry).legacy) return null
89
+ const allowed = ['round', 'verdict', 'headSha', 'themeIds', 'scope', 'at', 'evidenceClasses', 'productionMoved']
90
+ if (Object.keys(entry).some((key) => !allowed.includes(key))) return null
91
+ if (!Number.isInteger(entry.round) || entry.round < 1 || typeof entry.productionMoved !== 'boolean') return null
92
+ return entry
93
+ }
94
+
95
+ function deepReviewAuthority(entry, headSha) {
96
+ if (!isObject(entry) || entry.verdict !== 'PASS' || entry.headSha !== headSha) return null
97
+ const hasThemes = Object.prototype.hasOwnProperty.call(entry, 'themes')
98
+ const hasFindings = Object.prototype.hasOwnProperty.call(entry, 'findings')
99
+ const allowed = hasThemes
100
+ ? ['round', 'verdict', 'themes', 'oracleEscalation', 'round1Depth', 'headSha']
101
+ : ['round', 'verdict', 'findings', 'oracleEscalation', 'round1Depth', 'headSha']
102
+ if (Object.keys(entry).some((key) => !allowed.includes(key))) return null
103
+ if (!Number.isInteger(entry.round) || entry.round < 1 || hasThemes === hasFindings) return null
104
+ if (hasThemes && !Array.isArray(entry.themes)) return null
105
+ if (hasFindings && !Array.isArray(entry.findings)) return null
106
+ return entry
107
+ }
108
+
109
+ function legacyPreliminaryReview(unit, headSha) {
110
+ const candidates = []
111
+ for (const [index, entry] of unit.rounds.entries()) {
112
+ const valid = legacyRoundAuthority(entry, headSha)
113
+ if (valid) candidates.push({ entry: valid, source: 'rounds', sourceRank: 0, index })
114
+ }
115
+ for (const [index, entry] of unit.reviewHistory.entries()) {
116
+ const valid = deepReviewAuthority(entry, headSha)
117
+ if (valid) candidates.push({ entry: valid, source: 'reviewHistory', sourceRank: 1, index })
118
+ }
119
+ candidates.sort((left, right) => left.entry.round - right.entry.round
120
+ || left.sourceRank - right.sourceRank
121
+ || left.index - right.index)
122
+ const selected = candidates.at(-1)
123
+ if (!selected) return null
124
+ return {
125
+ round: selected.entry.round,
126
+ verdict: selected.entry.verdict,
127
+ headSha: selected.entry.headSha,
128
+ reviewSubject: 'unknown',
129
+ authorizationBoundary: 'unknown',
130
+ legacy: true,
131
+ source: selected.source,
132
+ }
133
+ }
134
+
135
+ function preliminaryEvidence(unit, componentId) {
136
+ if (unit?.lastVerdict !== 'PASS') throw new Error(`component candidate ${componentId}: unit ${unit?.id} is not preliminarily ready (lastVerdict must be PASS)`)
137
+ assertSha(unit?.pushedSha, `component candidate ${componentId}: unit ${unit?.id}.pushedSha`)
138
+ if (unit.rounds !== undefined && !Array.isArray(unit.rounds)) throw new Error(`component candidate ${componentId}: unit ${unit.id}.rounds must be an array`)
139
+ if (unit.reviewHistory !== undefined && !Array.isArray(unit.reviewHistory)) throw new Error(`component candidate ${componentId}: unit ${unit.id}.reviewHistory must be an array`)
140
+ const rounds = Array.isArray(unit.rounds) ? unit.rounds : []
141
+ const reviewHistory = Array.isArray(unit.reviewHistory) ? unit.reviewHistory : []
142
+ const typed = rounds
143
+ .map((entry, index) => typedPreliminaryReview(entry, unit.pushedSha, `component candidate ${componentId}: unit ${unit.id}.rounds[${index}]`))
144
+ .filter(Boolean)
145
+ .at(-1)
146
+ const preliminaryReview = typed ?? legacyPreliminaryReview({ rounds, reviewHistory }, unit.pushedSha)
147
+ if (!preliminaryReview) {
148
+ throw new Error(`component candidate ${componentId}: unit ${unit?.id} needs a preliminary PASS on its exact pushed head`)
149
+ }
150
+ return {
151
+ unitId: unit.id,
152
+ headSha: unit.pushedSha,
153
+ pr: nonEmpty(unit.pr) ? unit.pr : null,
154
+ preliminaryReview,
155
+ reviewRoundCount: completedReviewRounds(unit),
156
+ }
157
+ }
158
+
159
+ function assertIncludedUnits(component, includedUnits) {
160
+ if (!Array.isArray(includedUnits) || !includedUnits.length || !unique(includedUnits) || includedUnits.some((id) => !nonEmpty(id))) {
161
+ throw new Error(`component candidate ${component.id}: includedUnits must be a non-empty unique array`)
162
+ }
163
+ const members = new Set(component.units)
164
+ for (const id of includedUnits) if (!members.has(id)) throw new Error(`component candidate ${component.id}: included unit ${id} is not owned by this component`)
165
+ const ordered = component.units.filter((id) => includedUnits.includes(id))
166
+ if (!same(ordered, includedUnits)) throw new Error(`component candidate ${component.id}: includedUnits must follow component unit order`)
167
+ }
168
+
169
+ function integratedResult(value, componentId) {
170
+ assertExactObject(value, ['assembly', 'tests', 'conflicts', 'failedTests'], `component candidate ${componentId}.integrated`)
171
+ if (!COMPONENT_ASSEMBLY_RESULTS.includes(value.assembly)) throw new Error(`component candidate ${componentId}: integrated.assembly must be ${COMPONENT_ASSEMBLY_RESULTS.join('|')}`)
172
+ if (!COMPONENT_TEST_RESULTS.includes(value.tests)) throw new Error(`component candidate ${componentId}: integrated.tests must be ${COMPONENT_TEST_RESULTS.join('|')}`)
173
+ if (!Array.isArray(value.conflicts) || value.conflicts.some((conflict) => !isObject(conflict) || !nonEmpty(conflict.unit) || !Array.isArray(conflict.files) || conflict.files.some((file) => !nonEmpty(file)))) {
174
+ throw new Error(`component candidate ${componentId}: integrated.conflicts must be [{ unit, files[] }]`)
175
+ }
176
+ if (!Array.isArray(value.failedTests) || value.failedTests.some((test) => !nonEmpty(test))) throw new Error(`component candidate ${componentId}: integrated.failedTests must be an array of test ids`)
177
+ if (value.assembly === 'clean' && value.conflicts.length) throw new Error(`component candidate ${componentId}: clean assembly cannot carry conflicts`)
178
+ if (value.assembly === 'conflict' && !value.conflicts.length) throw new Error(`component candidate ${componentId}: conflict assembly must name conflicts`)
179
+ if (value.tests === 'green' && value.failedTests.length) throw new Error(`component candidate ${componentId}: green integrated tests cannot carry failures`)
180
+ if (value.tests === 'red' && !value.failedTests.length) throw new Error(`component candidate ${componentId}: red integrated tests must name failed tests`)
181
+ return clone(value)
182
+ }
183
+
184
+ function integrationFix(value, componentId) {
185
+ assertExactObject(value, ['owner', 'reason', 'changedSurfaces'], `component candidate ${componentId}.integrationFix`)
186
+ if (value.owner !== 'integration') throw new Error(`component candidate ${componentId}: integration-fix owner must be "integration"; unit branches retain their passed heads`)
187
+ if (!nonEmpty(value.reason)) throw new Error(`component candidate ${componentId}: integration-fix requires a reason`)
188
+ return { owner: 'integration', reason: value.reason.trim(), changedSurfaces: normalizeChangedSurfaces(value.changedSurfaces) }
189
+ }
190
+
191
+ function candidateBase(existing) {
192
+ if (isObject(existing)) return clone(existing)
193
+ return { state: 'assembling', generation: 1, updates: [], frozen: null, authorizations: [], reopens: [] }
194
+ }
195
+
196
+ function transitionResult(out, componentId) {
197
+ const errors = validateComponentCandidates(out)
198
+ if (errors.length) throw new Error(`component candidate ${componentId}: invalid transition result: ${errors[0]}`)
199
+ return out
200
+ }
201
+
202
+ function positivePr(value) {
203
+ if (Number.isInteger(value) && value > 0) return value
204
+ const match = String(value ?? '').match(/(?:^|#)(\d+)$/)
205
+ return match ? Number(match[1]) : null
206
+ }
207
+
208
+ function initializedDelivery(manifest, component, candidate, authorization) {
209
+ const mode = deliveryMode(manifest)
210
+ const provider = deliveryProvider(manifest)
211
+ if (!['direct', 'queue'].includes(mode)) throw new Error(`component candidate ${component.id}: deliveryMode must be direct|queue`)
212
+ if (mode === 'queue' && !['mergify', 'github'].includes(provider)) {
213
+ throw new Error(`component candidate ${component.id}: queue delivery requires queueProvider mergify|github before terminal authorization`)
214
+ }
215
+ const pr = authorization.ci.pr
216
+ const publishedPr = positivePr(component.pr)
217
+ if (publishedPr != null && publishedPr !== pr) {
218
+ throw new Error(`component candidate ${component.id}: terminal CI PR ${pr} does not match published component PR ${publishedPr}`)
219
+ }
220
+ const prior = isObject(component.delivery) ? component.delivery : null
221
+ if (prior?.outcome === 'merged' || prior?.outcome === 'handed-back') {
222
+ throw new Error(`component candidate ${component.id}: terminal delivery outcome ${prior.outcome} cannot be re-authorized`)
223
+ }
224
+ return {
225
+ pr,
226
+ generation: candidate.generation,
227
+ authorizedHeadSha: authorization.headSha,
228
+ mode,
229
+ provider: mode === 'queue' ? provider : null,
230
+ attempts: Array.isArray(prior?.attempts) ? clone(prior.attempts) : [],
231
+ observations: Array.isArray(prior?.observations) ? clone(prior.observations) : [],
232
+ watch: null,
233
+ queueExecution: null,
234
+ outcome: null,
235
+ outcomeAt: null,
236
+ mergedSha: null,
237
+ mergedAt: null,
238
+ blockedOn: null,
239
+ }
240
+ }
241
+
242
+ function componentDeliveryContext(manifest, componentId, generation, headSha) {
243
+ const { component, byId } = componentAndUnits(manifest, componentId)
244
+ const candidate = candidateBase(component.candidate)
245
+ const authorization = Array.isArray(candidate.authorizations)
246
+ ? candidate.authorizations.findLast((entry) => entry?.generation === generation)
247
+ : null
248
+ if (candidate.state !== 'authorized' || generation !== candidate.generation || authorization?.generation !== generation) {
249
+ throw new Error(`component delivery ${componentId}: generation ${generation} is not the currently authorized component generation ${candidate.generation}`)
250
+ }
251
+ if (headSha !== candidate.frozen?.headSha || authorization.headSha !== headSha) {
252
+ throw new Error(`component delivery ${componentId}: ${headSha} is not the authorized frozen component HEAD ${candidate.frozen?.headSha ?? '(missing)'}`)
253
+ }
254
+ const delivery = component.delivery
255
+ if (!isObject(delivery) || delivery.generation !== generation || delivery.authorizedHeadSha !== headSha) {
256
+ throw new Error(`component delivery ${componentId}: canonical delivery record is stale or missing for authorized generation ${generation}`)
257
+ }
258
+ for (const id of component.units) {
259
+ const unit = byId.get(id)
260
+ if (effectiveUnitKind(unit) !== 'implementation') throw new Error(`component delivery ${componentId}: member ${id} is not an implementation unit`)
261
+ if (unit.status !== 'merge-ready') throw new Error(`component delivery ${componentId}: member ${id} is ${unit.status}, not merge-ready`)
262
+ const blocked = completionBlockers(manifest, unit)
263
+ if (blocked.length) throw new Error(`component delivery ${componentId}: member ${id} has incomplete completion dependencies: ${blocked.join(', ')}`)
264
+ if (unit.delivery !== undefined) throw new Error(`component delivery ${componentId}: member ${id} carries a unit delivery record; shared delivery must be canonical on the component`)
265
+ }
266
+ return { component, candidate, authorization, byId, delivery }
267
+ }
268
+
269
+ function deliveryObservation(value, label) {
270
+ assertExactObject(value, ['at', 'queueState', 'position', 'unmetConditions', 'ack'], label)
271
+ if (!nonEmpty(value.at)) throw new Error(`${label}.at is required`)
272
+ if (!nonEmpty(value.queueState)) throw new Error(`${label}.queueState is required`)
273
+ if (value.unmetConditions !== undefined && (!Array.isArray(value.unmetConditions) || value.unmetConditions.some((condition) => !nonEmpty(condition)))) {
274
+ throw new Error(`${label}.unmetConditions must be an array of non-empty strings`)
275
+ }
276
+ if (value.ack !== undefined && (!isObject(value.ack) || !['reaction', 'payload'].includes(value.ack.kind) || !nonEmpty(value.ack.ref))) {
277
+ throw new Error(`${label}.ack requires {kind: reaction|payload, ref}`)
278
+ }
279
+ return clone(value)
280
+ }
281
+
282
+ export function recordComponentDeliveryAttempt(manifest, componentId, payload) {
283
+ assertExactObject(payload, ['generation', 'headSha', 'attempt'], `record-component-delivery-attempt ${componentId}`)
284
+ if (!Number.isSafeInteger(payload.generation) || payload.generation < 1) throw new Error(`record-component-delivery-attempt ${componentId}.generation must be a positive integer`)
285
+ assertSha(payload.headSha, `record-component-delivery-attempt ${componentId}.headSha`)
286
+ const out = clone(manifest)
287
+ const { component, delivery } = componentDeliveryContext(out, componentId, payload.generation, payload.headSha)
288
+ if (delivery.outcome != null) throw new Error(`component delivery ${componentId} already reached terminal outcome ${delivery.outcome}`)
289
+ const attemptErrors = validateDeliveryAttempt(payload.attempt, delivery.mode, `component delivery ${componentId} attempt`, delivery.provider)
290
+ if (attemptErrors.length) throw new Error(attemptErrors[0])
291
+ if (payload.attempt.headSha !== payload.headSha) throw new Error(`component delivery ${componentId}: attempt must target the authorized component HEAD ${payload.headSha}`)
292
+ if (payload.attempt.pr !== undefined && positivePr(payload.attempt.pr) !== delivery.pr) throw new Error(`component delivery ${componentId}: attempt PR must equal component PR ${delivery.pr}`)
293
+ const sameGeneration = delivery.attempts.filter((attempt) => attempt?.generation === payload.generation)
294
+ if (sameGeneration.some((attempt) => attempt.action === payload.attempt.action)) {
295
+ throw new Error(`component delivery ${componentId}: exactly one ${payload.attempt.action} attempt is allowed for authorized generation ${payload.generation}; duplicate enqueue/merge refused`)
296
+ }
297
+ component.delivery = {
298
+ ...delivery,
299
+ attempts: [...delivery.attempts, {
300
+ generation: payload.generation,
301
+ ...clone(payload.attempt),
302
+ ...(delivery.mode === 'queue' ? { pr: delivery.pr } : {}),
303
+ }],
304
+ }
305
+ return transitionResult(out, componentId)
306
+ }
307
+
308
+ export function recordComponentDeliveryObservation(manifest, componentId, payload) {
309
+ assertExactObject(payload, ['generation', 'headSha', 'observation'], `record-component-delivery-observation ${componentId}`)
310
+ if (!Number.isSafeInteger(payload.generation) || payload.generation < 1) throw new Error(`record-component-delivery-observation ${componentId}.generation must be a positive integer`)
311
+ assertSha(payload.headSha, `record-component-delivery-observation ${componentId}.headSha`)
312
+ const out = clone(manifest)
313
+ const { component, delivery } = componentDeliveryContext(out, componentId, payload.generation, payload.headSha)
314
+ if (delivery.mode !== 'queue') throw new Error(`component delivery ${componentId}: direct delivery has no queue observations`)
315
+ if (delivery.outcome != null) throw new Error(`component delivery ${componentId} already reached terminal outcome ${delivery.outcome}`)
316
+ const observation = deliveryObservation(payload.observation, `component delivery ${componentId} observation`)
317
+ const previous = isObject(delivery.watch) ? delivery.watch : null
318
+ const position = observation.position ?? null
319
+ const unmetConditions = observation.unmetConditions ?? previous?.unmetConditions ?? null
320
+ const conditionKey = (value) => Array.isArray(value) ? value.join('\u0000') : ''
321
+ const moved = !previous || previous.queueState !== observation.queueState || (previous.position ?? null) !== position
322
+ || conditionKey(previous.unmetConditions) !== conditionKey(unmetConditions)
323
+ component.delivery = {
324
+ ...delivery,
325
+ observations: [...delivery.observations, { generation: payload.generation, headSha: payload.headSha, ...observation }],
326
+ watch: {
327
+ firstObservedAt: previous?.firstObservedAt ?? observation.at,
328
+ lastObservedAt: observation.at,
329
+ lastChangeAt: moved ? observation.at : previous.lastChangeAt,
330
+ queueState: observation.queueState,
331
+ position,
332
+ polls: (previous?.polls ?? 0) + 1,
333
+ ...(unmetConditions == null ? {} : { unmetConditions }),
334
+ },
335
+ }
336
+ return transitionResult(out, componentId)
337
+ }
338
+
339
+ export function recordComponentDeliveryNudge(manifest, componentId, payload) {
340
+ assertExactObject(payload, ['generation', 'headSha', 'nudge'], `record-component-delivery-nudge ${componentId}`)
341
+ if (!Number.isSafeInteger(payload.generation) || payload.generation < 1) throw new Error(`record-component-delivery-nudge ${componentId}.generation must be a positive integer`)
342
+ assertSha(payload.headSha, `record-component-delivery-nudge ${componentId}.headSha`)
343
+ assertExactObject(payload.nudge, ['at', 'command', 'ref'], `record-component-delivery-nudge ${componentId}.nudge`)
344
+ const out = clone(manifest)
345
+ const { component, delivery } = componentDeliveryContext(out, componentId, payload.generation, payload.headSha)
346
+ const flow = deliveryFlow(out)
347
+ if (!flow?.supportsNudge) throw new Error(`component delivery ${componentId}: the ${flow?.key ?? 'unresolved'} flow has no queue re-evaluation nudge`)
348
+ if (!isObject(delivery.watch)) throw new Error(`component delivery ${componentId}: record a queue observation before a nudge`)
349
+ const nudges = Array.isArray(delivery.watch.nudges) ? [...delivery.watch.nudges] : []
350
+ if (nudges.length >= DELIVERY_MAX_NUDGES) throw new Error(`component delivery ${componentId}: already spent its ${DELIVERY_MAX_NUDGES} nudge(s)`)
351
+ const at = nonEmpty(payload.nudge.at) ? payload.nudge.at : new Date().toISOString()
352
+ nudges.push({ at, command: nonEmpty(payload.nudge.command) ? payload.nudge.command : flow.ops.nudge, ...(nonEmpty(payload.nudge.ref) ? { ref: payload.nudge.ref } : {}) })
353
+ component.delivery = { ...delivery, watch: { ...delivery.watch, nudges } }
354
+ return transitionResult(out, componentId)
355
+ }
356
+
357
+ function validatedQueueExecution(componentId, delivery, authorization, value) {
358
+ if (value === undefined) return null
359
+ assertExactObject(value, ['artifact', 'evidence'], `component delivery ${componentId}.queueExecution`)
360
+ if (!nonEmpty(value.artifact)) throw new Error(`component delivery ${componentId}.queueExecution.artifact is required`)
361
+ const plan = authorization.ci?.requiredCheckPlan
362
+ const validation = validateQueueExecutionArtifact(plan, value.evidence, {
363
+ expectedRepository: plan?.repository,
364
+ expectedPr: delivery.pr,
365
+ expectedPrHeadSha: delivery.authorizedHeadSha,
366
+ label: `component delivery ${componentId}.queueExecution.evidence`,
367
+ })
368
+ if (!validation.ok) throw new Error(validation.errors[0])
369
+ return clone(value)
370
+ }
371
+
372
+ export function recordComponentDeliveryOutcome(manifest, componentId, payload) {
373
+ assertExactObject(payload, ['generation', 'headSha', 'outcome', 'at', 'mergedSha', 'mergedAt', 'blockedOn', 'queueExecution'], `record-component-delivery-outcome ${componentId}`)
374
+ if (!Number.isSafeInteger(payload.generation) || payload.generation < 1) throw new Error(`record-component-delivery-outcome ${componentId}.generation must be a positive integer`)
375
+ assertSha(payload.headSha, `record-component-delivery-outcome ${componentId}.headSha`)
376
+ if (!['merged', 'handed-back'].includes(payload.outcome)) throw new Error(`record-component-delivery-outcome ${componentId}.outcome must be merged|handed-back`)
377
+ const out = clone(manifest)
378
+ const { component, authorization, delivery, byId } = componentDeliveryContext(out, componentId, payload.generation, payload.headSha)
379
+ if (delivery.outcome != null) throw new Error(`component delivery ${componentId} already reached terminal outcome ${delivery.outcome}`)
380
+ let queueExecution = null
381
+ if (payload.outcome === 'merged') {
382
+ assertSha(payload.mergedSha, `record-component-delivery-outcome ${componentId}.mergedSha`)
383
+ const requiredAction = delivery.mode === 'queue' ? 'enqueue' : 'merge'
384
+ if (!delivery.attempts.some((attempt) => attempt.generation === payload.generation && attempt.action === requiredAction)) {
385
+ throw new Error(`component delivery ${componentId}: merged outcome requires a recorded ${requiredAction} attempt in the authorized generation`)
386
+ }
387
+ const queueProofRequired = checksForPhase(authorization.ci?.requiredCheckPlan, 'queue-head').length > 0
388
+ if (delivery.mode === 'direct' && payload.queueExecution !== undefined) throw new Error(`component delivery ${componentId}: direct delivery cannot carry queue-execution evidence`)
389
+ if (delivery.mode === 'queue' && queueProofRequired && payload.queueExecution === undefined) throw new Error(`component delivery ${componentId}: successful queue delivery requires validated queue-execution evidence`)
390
+ queueExecution = validatedQueueExecution(componentId, delivery, authorization, payload.queueExecution)
391
+ } else {
392
+ if (!nonEmpty(payload.blockedOn)) throw new Error(`record-component-delivery-outcome ${componentId}.blockedOn is required for handed-back`)
393
+ if (payload.queueExecution !== undefined) throw new Error(`component delivery ${componentId}: handed-back cannot claim queue-execution success`)
394
+ }
395
+ component.delivery = {
396
+ ...delivery,
397
+ queueExecution,
398
+ outcome: payload.outcome,
399
+ outcomeAt: nonEmpty(payload.at) ? payload.at : new Date().toISOString(),
400
+ mergedSha: payload.outcome === 'merged' ? payload.mergedSha : null,
401
+ mergedAt: payload.outcome === 'merged' && nonEmpty(payload.mergedAt) ? payload.mergedAt : null,
402
+ blockedOn: payload.outcome === 'handed-back' ? payload.blockedOn : null,
403
+ }
404
+ for (const id of component.units) {
405
+ const unit = byId.get(id)
406
+ unit.disposition = payload.outcome === 'merged' ? 'delivered' : 'handed-back'
407
+ unit.deliveryRef = { componentId, generation: payload.generation }
408
+ }
409
+ return transitionResult(out, componentId)
410
+ }
411
+
412
+ export function recordComponentCandidate(manifest, componentId, payload, { at = new Date().toISOString() } = {}) {
413
+ // targetBaseSha optional (#944 / epic-867): the fetched origin/<targetBranch> tip at
414
+ // this assemble/re-ALIVE. Stored as components[].baseShaAtAssemble so mergeable can
415
+ // refuse when the trunk moved without a re-verify.
416
+ assertExactObject(payload, ['roundClass', 'branch', 'headSha', 'includedUnits', 'integrated', 'integrationFix', 'targetBaseSha'], `record-component-candidate ${componentId}`)
417
+ if (!['unit-discovery', 'integration-fix'].includes(payload.roundClass)) throw new Error(`record-component-candidate ${componentId}: roundClass must be unit-discovery|integration-fix`)
418
+ if (!nonEmpty(payload.branch)) throw new Error(`record-component-candidate ${componentId}: branch is required`)
419
+ assertSha(payload.headSha, `record-component-candidate ${componentId}.headSha`)
420
+ if (payload.targetBaseSha !== undefined) assertSha(payload.targetBaseSha, `record-component-candidate ${componentId}.targetBaseSha`)
421
+
422
+ const out = clone(manifest)
423
+ const { component, byId } = componentAndUnits(out, componentId)
424
+ assertIncludedUnits(component, payload.includedUnits)
425
+ const candidate = candidateBase(component.candidate)
426
+ if (candidate.state !== 'assembling') throw new Error(`component candidate ${componentId} is ${candidate.state}; reopen it before moving the candidate head`)
427
+ const prior = candidate.updates.at(-1)
428
+ if (prior?.headSha === payload.headSha) throw new Error(`component candidate ${componentId}: candidate head did not move`)
429
+ if (prior?.branch && prior.branch !== payload.branch) throw new Error(`component candidate ${componentId}: integration branch cannot move from ${prior.branch} to ${payload.branch}`)
430
+ if (prior) {
431
+ const current = new Set(payload.includedUnits)
432
+ if (prior.includedUnits.some((id) => !current.has(id))) throw new Error(`component candidate ${componentId}: included units cannot shrink; unit ownership/evidence must be preserved`)
433
+ }
434
+
435
+ const unitEvidence = payload.includedUnits.map((id) => preliminaryEvidence(byId.get(id), componentId))
436
+ let fix = null
437
+ if (payload.roundClass === 'integration-fix') {
438
+ fix = integrationFix(payload.integrationFix, componentId)
439
+ if (!prior || !same(prior.includedUnits, payload.includedUnits) || !same(prior.unitEvidence, unitEvidence)) {
440
+ throw new Error(`component candidate ${componentId}: integration-fix must preserve included units and unit heads byte-for-byte; record changed unit evidence as unit-discovery`)
441
+ }
442
+ } else {
443
+ if (payload.integrationFix !== undefined) throw new Error(`component candidate ${componentId}: unit-discovery cannot carry integrationFix`)
444
+ if (prior && same(prior.unitEvidence, unitEvidence)) throw new Error(`component candidate ${componentId}: unit-discovery must add or update unit candidate evidence`)
445
+ }
446
+
447
+ candidate.updates.push({
448
+ roundClass: payload.roundClass,
449
+ generation: candidate.generation,
450
+ branch: payload.branch,
451
+ headSha: payload.headSha,
452
+ includedUnits: clone(payload.includedUnits),
453
+ unitEvidence,
454
+ integrated: integratedResult(payload.integrated, componentId),
455
+ ...(fix ? { integrationFix: fix } : {}),
456
+ at,
457
+ })
458
+ component.candidate = candidate
459
+ if (payload.targetBaseSha !== undefined) {
460
+ component.baseShaAtAssemble = payload.targetBaseSha
461
+ // A re-ALIVE against a new trunk tip invalidates any prior enqueue-base attestation.
462
+ delete component.baseShaAtEnqueue
463
+ }
464
+ return transitionResult(out, componentId)
465
+ }
466
+
467
+ // #944 / epic-867: record the fetched target tip immediately before delivery. Direct
468
+ // delivery owns target integration, so its enqueue/merge attestation must still equal the
469
+ // assembled base. A queue provider owns speculative target integration: it may observe a
470
+ // newer descendant tip, while the live delivery gate proves baseShaAtAssemble remains in
471
+ // that target history and tests the immutable PR head on the synthetic queue branch.
472
+ export function recordComponentTargetBase(manifest, componentId, payload, { at = new Date().toISOString() } = {}) {
473
+ assertExactObject(payload, ['phase', 'baseSha'], `record-component-target-base ${componentId}`)
474
+ if (!['assemble', 'enqueue'].includes(payload.phase)) {
475
+ throw new Error(`record-component-target-base ${componentId}: phase must be assemble|enqueue`)
476
+ }
477
+ assertSha(payload.baseSha, `record-component-target-base ${componentId}.baseSha`)
478
+ const out = clone(manifest)
479
+ const { component } = componentAndUnits(out, componentId)
480
+ if (payload.phase === 'assemble') {
481
+ component.baseShaAtAssemble = payload.baseSha
482
+ delete component.baseShaAtEnqueue
483
+ } else {
484
+ if (!nonEmpty(component.baseShaAtAssemble)) {
485
+ throw new Error(`record-component-target-base ${componentId}: baseShaAtAssemble is required before enqueue attestation — re-assemble/re-ALIVE with targetBaseSha first`)
486
+ }
487
+ const queueOwned = out.deliveryMode === 'queue'
488
+ // Direct delivery must integrate the exact live tip before merge. Queue delivery only
489
+ // records the observed tip here; ancestry is deliberately not trusted from manifest
490
+ // data and is proved live by combine-and-verify before enqueue.
491
+ if (!queueOwned && component.baseShaAtAssemble !== payload.baseSha) {
492
+ throw new Error(`record-component-target-base ${componentId}: target tip ${payload.baseSha} advanced past baseShaAtAssemble ${component.baseShaAtAssemble} — target-drift path: back-merge/reassemble, re-verify, re-ALIVE, then re-attest`)
493
+ }
494
+ component.baseShaAtEnqueue = payload.baseSha
495
+ component.targetIntegrationOwner = queueOwned ? 'queue' : 'source'
496
+ }
497
+ component.targetBaseRecordedAt = at
498
+ return out
499
+ }
500
+
501
+ // Pure policy over live ancestry facts. Direct delivery requires exact-tip integration.
502
+ // Queue delivery permits forward movement only when the recorded assembly base is still an
503
+ // ancestor of the live target; the queue then proves compatibility on its synthetic head.
504
+ // Absent assemble base remains a legacy no-signal shape.
505
+ export function targetBaseDrift({ baseShaAtAssemble, baseShaAtEnqueue, liveTargetSha, deliveryMode = 'direct', assembleBaseIsAncestor = false } = {}) {
506
+ if (!nonEmpty(baseShaAtAssemble)) {
507
+ return { drifted: false, reasons: [] }
508
+ }
509
+ if (!nonEmpty(liveTargetSha)) {
510
+ return { drifted: true, reasons: ['live target SHA is unavailable — target integration ancestry cannot be proved'] }
511
+ }
512
+ if (baseShaAtAssemble === liveTargetSha) {
513
+ // Tip still at assemble base — enqueue attestation optional when tip is unchanged.
514
+ return { drifted: false, reasons: [] }
515
+ }
516
+ if (deliveryMode === 'queue' && assembleBaseIsAncestor) {
517
+ return { drifted: false, reasons: [] }
518
+ }
519
+ // Direct delivery may re-attest only after integrating and re-verifying the new tip.
520
+ if (deliveryMode !== 'queue' && nonEmpty(baseShaAtEnqueue) && baseShaAtEnqueue === liveTargetSha) {
521
+ return { drifted: false, reasons: [] }
522
+ }
523
+ const reason = deliveryMode === 'queue'
524
+ ? `recorded assembly base ${baseShaAtAssemble} is not an ancestor of live target ${liveTargetSha} — queue-owned speculative integration accepts forward target movement, never rewritten or divergent target history`
525
+ : `target trunk moved since assemble (baseShaAtAssemble ${baseShaAtAssemble} → live ${liveTargetSha}) without re-ALIVE / baseShaAtEnqueue matching the new tip — back-merge, re-verify CI/smoke, delta integrated review, then record-component-target-base enqueue (epic-867/#944)`
526
+ return { drifted: true, reasons: [reason] }
527
+ }
528
+
529
+ export function freezeComponentCandidate(manifest, componentId, payload, { at = new Date().toISOString() } = {}) {
530
+ assertExactObject(payload, ['headSha'], `freeze-component-candidate ${componentId}`)
531
+ assertSha(payload.headSha, `freeze-component-candidate ${componentId}.headSha`)
532
+ const out = clone(manifest)
533
+ const { component, byId } = componentAndUnits(out, componentId)
534
+ const candidate = candidateBase(component.candidate)
535
+ if (candidate.state !== 'assembling') throw new Error(`component candidate ${componentId} must be assembling before freeze (got ${candidate.state})`)
536
+ const latest = candidate.updates.at(-1)
537
+ const complete = latest && same(latest.includedUnits, component.units)
538
+ const clean = latest?.integrated?.assembly === 'clean' && latest?.integrated?.tests === 'green'
539
+ && latest.integrated.conflicts.length === 0 && latest.integrated.failedTests.length === 0
540
+ const currentUnitEvidence = complete ? component.units.map((id) => preliminaryEvidence(byId.get(id), componentId)) : []
541
+ const current = complete && same(latest.unitEvidence, currentUnitEvidence)
542
+ if (!latest || latest.headSha !== payload.headSha || !complete || !clean || !current) {
543
+ throw new Error(`component candidate ${componentId}: freeze requires the latest exact head with the complete component, clean assembly, and green integrated tests`)
544
+ }
545
+ candidate.state = 'frozen'
546
+ candidate.frozen = {
547
+ generation: candidate.generation,
548
+ headSha: latest.headSha,
549
+ branch: latest.branch,
550
+ includedUnits: clone(latest.includedUnits),
551
+ unitEvidence: clone(latest.unitEvidence),
552
+ authorizationBoundary: { kind: 'integration', value: latest.headSha },
553
+ at,
554
+ }
555
+ component.candidate = candidate
556
+ return transitionResult(out, componentId)
557
+ }
558
+
559
+ function terminalEvidence(payload, component, candidate, unitById = component.__unitById) {
560
+ assertExactObject(payload, ['headSha', 'ci', 'review', 'smoke'], `authorize-component-candidate ${component.id}`)
561
+ const head = candidate.frozen?.headSha
562
+ assertSha(payload.headSha, `authorize-component-candidate ${component.id}.headSha`)
563
+ if (candidate.state !== 'frozen' || payload.headSha !== head) throw new Error(`component candidate ${component.id}: terminal authorization must target the exact frozen HEAD`)
564
+
565
+ assertExactObject(payload.ci, ['headSha', 'status', 'artifact', 'pr', 'jobs', 'requiredCheckPlan', 'publishOnlyEvidence'], `component candidate ${component.id}.ci`)
566
+ assertExactObject(payload.review, ['headSha', 'verdict', 'reviewSubject', 'highRisk', 'authorizationBoundary', 'checklist', 'scanFindings', 'artifact'], `component candidate ${component.id}.review`)
567
+ assertExactObject(payload.smoke, ['headSha', 'status', 'artifact', 'reason'], `component candidate ${component.id}.smoke`)
568
+ for (const [name, evidence] of [['CI', payload.ci], ['review', payload.review], ['smoke', payload.smoke]]) {
569
+ if (evidence.headSha !== head) throw new Error(`component candidate ${component.id}: terminal ${name} headSha must equal the exact frozen HEAD ${head}`)
570
+ }
571
+ if (payload.ci.status !== 'green') throw new Error(`component candidate ${component.id}: exact-head integrated CI must be green`)
572
+ if (!Number.isInteger(payload.ci.pr) || payload.ci.pr < 1) throw new Error(`component candidate ${component.id}: exact-head integrated CI requires a positive PR number`)
573
+ const plan = validateRequiredCheckPlan(payload.ci.requiredCheckPlan, { expectedHeadSha: head, requireEvidence: true, label: `component candidate ${component.id}.ci.requiredCheckPlan` })
574
+ if (!plan.ok) throw new Error(plan.errors[0])
575
+ const publishOnly = validatePublishOnlyEvidence(payload.ci.requiredCheckPlan, payload.ci.publishOnlyEvidence, { expectedHeadSha: head, expectedRepository: payload.ci.requiredCheckPlan.repository, expectedPr: payload.ci.pr, label: `component candidate ${component.id}.ci.publishOnlyEvidence` })
576
+ if (!publishOnly.ok) throw new Error(publishOnly.errors[0])
577
+ const execution = validateRequiredCheckExecution(payload.ci.requiredCheckPlan, payload.ci.jobs, { expectedHeadSha: head })
578
+ if (!execution.ok) throw new Error(`component candidate ${component.id}: required-check-plan execution invalid: ${execution.errors[0]}`)
579
+ for (const [name, evidence] of [['CI', payload.ci], ['review', payload.review], ['smoke', payload.smoke]]) {
580
+ if (!nonEmpty(evidence.artifact)) throw new Error(`component candidate ${component.id}: terminal ${name} requires an artifact locator`)
581
+ }
582
+ const reviewBoundary = boundary(payload.review.authorizationBoundary, `component candidate ${component.id}.review.authorizationBoundary`)
583
+ if (payload.review.verdict !== 'PASS' || payload.review.reviewSubject !== 'code' || payload.review.checklist !== 'PASS' || payload.review.scanFindings !== 0) {
584
+ throw new Error(`component candidate ${component.id}: terminal integrated review must PASS code on the exact head with checklist PASS and zero scan findings`)
585
+ }
586
+ if (reviewBoundary.kind !== 'integration' || reviewBoundary.value !== head) throw new Error(`component candidate ${component.id}: terminal review authorization boundary must be the component HEAD`)
587
+ const highRisk = candidate.frozen.unitEvidence.some((evidence) => {
588
+ const unit = unitById?.get(evidence.unitId)
589
+ return unit?.highRisk === true
590
+ })
591
+ if (highRisk && payload.review.highRisk !== true) throw new Error(`component candidate ${component.id}: high-risk integrated review is mandatory`)
592
+ const smokeAlive = payload.smoke.status === 'ALIVE'
593
+ const smokeNA = payload.smoke.status === 'not-applicable' && nonEmpty(payload.smoke.reason)
594
+ if (!smokeAlive && !smokeNA) throw new Error(`component candidate ${component.id}: smoke must be ALIVE, or not-applicable with a reason`)
595
+ return { ci: clone(payload.ci), review: { ...clone(payload.review), authorizationBoundary: reviewBoundary }, smoke: clone(payload.smoke) }
596
+ }
597
+
598
+ function legacyTerminalEvidence(payload, component, candidate, unitById = component.__unitById) {
599
+ assertExactObject(payload, ['headSha', 'ci', 'review', 'smoke'], `legacy component authorization ${component.id}`)
600
+ const head = candidate.frozen?.headSha
601
+ assertSha(payload.headSha, `legacy component authorization ${component.id}.headSha`)
602
+ if (candidate.state !== 'frozen' || payload.headSha !== head) throw new Error(`legacy component authorization ${component.id} must target the exact frozen HEAD`)
603
+
604
+ assertExactObject(payload.ci, ['headSha', 'status', 'artifact', 'jobs'], `legacy component authorization ${component.id}.ci`)
605
+ assertExactObject(payload.review, ['headSha', 'verdict', 'reviewSubject', 'highRisk', 'authorizationBoundary', 'checklist', 'scanFindings', 'artifact'], `legacy component authorization ${component.id}.review`)
606
+ assertExactObject(payload.smoke, ['headSha', 'status', 'artifact', 'reason'], `legacy component authorization ${component.id}.smoke`)
607
+ for (const [name, evidence] of [['CI', payload.ci], ['review', payload.review], ['smoke', payload.smoke]]) {
608
+ if (evidence.headSha !== head) throw new Error(`legacy component authorization ${component.id}: ${name} headSha must equal ${head}`)
609
+ if (!nonEmpty(evidence.artifact)) throw new Error(`legacy component authorization ${component.id}: ${name} requires an artifact locator`)
610
+ }
611
+ if (payload.ci.status !== 'green') throw new Error(`legacy component authorization ${component.id}: exact-head CI must be green`)
612
+ const reviewBoundary = boundary(payload.review.authorizationBoundary, `legacy component authorization ${component.id}.review.authorizationBoundary`)
613
+ if (payload.review.verdict !== 'PASS' || payload.review.reviewSubject !== 'code' || payload.review.checklist !== 'PASS' || payload.review.scanFindings !== 0) {
614
+ throw new Error(`legacy component authorization ${component.id}: integrated review must PASS code with checklist PASS and zero scan findings`)
615
+ }
616
+ if (reviewBoundary.kind !== 'integration' || reviewBoundary.value !== head) throw new Error(`legacy component authorization ${component.id}: review boundary must bind the component HEAD`)
617
+ const highRisk = candidate.frozen.unitEvidence.some((evidence) => unitById?.get(evidence.unitId)?.highRisk === true)
618
+ if (highRisk && payload.review.highRisk !== true) throw new Error(`legacy component authorization ${component.id}: high-risk integrated review is mandatory`)
619
+ const smokeAlive = payload.smoke.status === 'ALIVE'
620
+ const smokeNA = payload.smoke.status === 'not-applicable' && nonEmpty(payload.smoke.reason)
621
+ if (!smokeAlive && !smokeNA) throw new Error(`legacy component authorization ${component.id}: smoke must be ALIVE, or not-applicable with a reason`)
622
+ return { ci: clone(payload.ci), review: { ...clone(payload.review), authorizationBoundary: reviewBoundary }, smoke: clone(payload.smoke) }
623
+ }
624
+
625
+ export function authorizeComponentCandidate(manifest, componentId, payload, { at = new Date().toISOString() } = {}) {
626
+ const out = clone(manifest)
627
+ const { component, byId } = componentAndUnits(out, componentId)
628
+ const candidate = candidateBase(component.candidate)
629
+ const evidence = terminalEvidence(payload, component, candidate, byId)
630
+ candidate.state = 'authorized'
631
+ const authorization = {
632
+ roundClass: 'terminal-authorization',
633
+ generation: candidate.generation,
634
+ headSha: payload.headSha,
635
+ authorizationBoundary: clone(evidence.review.authorizationBoundary),
636
+ ...evidence,
637
+ at,
638
+ }
639
+ candidate.authorizations.push(authorization)
640
+ component.candidate = candidate
641
+ component.authorizationBoundary = clone(evidence.review.authorizationBoundary)
642
+ component.delivery = initializedDelivery(out, component, candidate, authorization)
643
+ return transitionResult(out, componentId)
644
+ }
645
+
646
+ export function reopenComponentCandidate(manifest, componentId, payload, { at = new Date().toISOString() } = {}) {
647
+ assertExactObject(payload, ['reopenReason', 'authorizationBoundary'], `reopen-component-candidate ${componentId}`)
648
+ if (!REOPEN_REASONS.includes(payload.reopenReason)) throw new Error(`reopen-component-candidate ${componentId}: reopen reason must be one of ${REOPEN_REASONS.join('|')}`)
649
+ const nextBoundary = boundary(payload.authorizationBoundary, `reopen-component-candidate ${componentId}.authorizationBoundary`)
650
+ if (nextBoundary.kind !== REOPEN_BOUNDARY_KIND[payload.reopenReason]) throw new Error(`reopen-component-candidate ${componentId}: reopen reason ${payload.reopenReason} requires authorizationBoundary.kind ${REOPEN_BOUNDARY_KIND[payload.reopenReason]}`)
651
+
652
+ const out = clone(manifest)
653
+ const { component } = componentAndUnits(out, componentId)
654
+ const candidate = candidateBase(component.candidate)
655
+ if (!['frozen', 'authorized'].includes(candidate.state)) throw new Error(`component candidate ${componentId}: only a frozen or authorized candidate can reopen`)
656
+ if (component.delivery?.outcome === 'merged' || component.delivery?.outcome === 'handed-back') {
657
+ throw new Error(`component candidate ${componentId}: terminal component delivery cannot reopen`)
658
+ }
659
+ const priorBoundary = candidate.state === 'authorized' ? component.authorizationBoundary : candidate.frozen?.authorizationBoundary
660
+ if (!isObject(priorBoundary)) throw new Error(`component candidate ${componentId}: prior authorization boundary is missing`)
661
+ if (same(priorBoundary, nextBoundary)) throw new Error(`component candidate ${componentId}: authorizationBoundary must change when the candidate reopens`)
662
+ candidate.reopens.push({
663
+ fromGeneration: candidate.generation,
664
+ fromHeadSha: candidate.frozen?.headSha ?? null,
665
+ reopenReason: payload.reopenReason,
666
+ priorAuthorizationBoundary: clone(priorBoundary),
667
+ authorizationBoundary: nextBoundary,
668
+ at,
669
+ })
670
+ candidate.generation += 1
671
+ candidate.state = 'assembling'
672
+ candidate.frozen = null
673
+ component.candidate = candidate
674
+ component.authorizationBoundary = nextBoundary
675
+ return transitionResult(out, componentId)
676
+ }
677
+
678
+ export function componentRoundProfile(component) {
679
+ const candidate = isObject(component?.candidate) ? component.candidate : {}
680
+ const updates = Array.isArray(candidate.updates) ? candidate.updates : []
681
+ const authorizations = Array.isArray(candidate.authorizations) ? candidate.authorizations : []
682
+ const unitDiscovery = updates.filter((entry) => entry?.roundClass === 'unit-discovery').length
683
+ const integrationFix = updates.filter((entry) => entry?.roundClass === 'integration-fix').length
684
+ const terminalAuthorization = authorizations.filter((entry) => entry?.roundClass === 'terminal-authorization').length
685
+ return { unitDiscovery, integrationFix, terminalAuthorization, total: unitDiscovery + integrationFix + terminalAuthorization }
686
+ }
687
+
688
+ function captureError(errors, fn) {
689
+ try { fn() } catch (error) { errors.push(error.message) }
690
+ }
691
+
692
+ function validateStoredUnitEvidence(evidence, label, errors) {
693
+ captureError(errors, () => assertExactObject(evidence, ['unitId', 'headSha', 'pr', 'preliminaryReview', 'reviewRoundCount'], label))
694
+ if (!isObject(evidence)) return
695
+ if (!nonEmpty(evidence.unitId)) errors.push(`${label}.unitId must be a non-empty string`)
696
+ captureError(errors, () => assertSha(evidence.headSha, `${label}.headSha`))
697
+ if (evidence.pr !== null && !nonEmpty(evidence.pr)) errors.push(`${label}.pr must be null or a non-empty string`)
698
+ if (!Number.isInteger(evidence.reviewRoundCount) || evidence.reviewRoundCount < 1) errors.push(`${label}.reviewRoundCount must be a positive integer`)
699
+ const review = evidence.preliminaryReview
700
+ const legacy = review?.legacy === true
701
+ const allowed = legacy
702
+ ? ['round', 'verdict', 'headSha', 'reviewSubject', 'authorizationBoundary', 'legacy', 'source']
703
+ : ['round', 'verdict', 'headSha', 'reviewSubject', 'authorizationBoundary']
704
+ captureError(errors, () => assertExactObject(review, allowed, `${label}.preliminaryReview`))
705
+ if (!isObject(review)) return
706
+ if (!Number.isInteger(review.round) || review.round < 1) errors.push(`${label}.preliminaryReview.round must be a positive integer`)
707
+ if (review.verdict !== 'PASS') errors.push(`${label}.preliminaryReview.verdict must be PASS`)
708
+ if (review.headSha !== evidence.headSha) errors.push(`${label}.preliminaryReview must target the evidence head`)
709
+ if (legacy) {
710
+ if (review.reviewSubject !== 'unknown') errors.push(`${label}.preliminaryReview legacy reviewSubject must be unknown`)
711
+ if (review.authorizationBoundary !== 'unknown') errors.push(`${label}.preliminaryReview legacy authorizationBoundary must be unknown`)
712
+ if (!['rounds', 'reviewHistory'].includes(review.source)) errors.push(`${label}.preliminaryReview legacy source must be rounds|reviewHistory`)
713
+ } else {
714
+ if (!['code', 'evidence'].includes(review.reviewSubject)) errors.push(`${label}.preliminaryReview.reviewSubject must be code|evidence`)
715
+ captureError(errors, () => boundary(review.authorizationBoundary, `${label}.preliminaryReview.authorizationBoundary`))
716
+ }
717
+ }
718
+
719
+ function validateStoredAuthorization(manifest, component, candidate, authorization, label, errors, { allowLegacyCiEvidence = false } = {}) {
720
+ captureError(errors, () => assertExactObject(authorization, ['roundClass', 'generation', 'headSha', 'authorizationBoundary', 'ci', 'review', 'smoke', 'at'], label))
721
+ if (!isObject(authorization)) return
722
+ if (authorization.roundClass !== 'terminal-authorization') errors.push(`${label}.roundClass must be terminal-authorization`)
723
+ if (!Number.isInteger(authorization.generation) || authorization.generation < 1 || authorization.generation > candidate.generation) errors.push(`${label}.generation is invalid`)
724
+ const updates = Array.isArray(candidate.updates) ? candidate.updates : []
725
+ const update = updates.filter((entry) => entry?.generation === authorization.generation).at(-1)
726
+ if (!update || update.headSha !== authorization.headSha) errors.push(`${label} must authorize the latest update in its generation`)
727
+ const validationCandidate = {
728
+ ...candidate,
729
+ state: 'frozen',
730
+ frozen: { headSha: authorization.headSha, unitEvidence: clone(update?.unitEvidence ?? []) },
731
+ }
732
+ const unitById = new Map((Array.isArray(manifest?.units) ? manifest.units : []).map((unit) => [unit?.id, unit]))
733
+ const legacyCi = allowLegacyCiEvidence
734
+ && isObject(authorization.ci)
735
+ && Object.keys(authorization.ci).every((key) => ['headSha', 'status', 'artifact', 'jobs'].includes(key))
736
+ && !Object.hasOwn(authorization.ci, 'requiredCheckPlan')
737
+ const validateEvidence = legacyCi ? legacyTerminalEvidence : terminalEvidence
738
+ captureError(errors, () => validateEvidence({
739
+ headSha: authorization.headSha,
740
+ ci: authorization.ci,
741
+ review: authorization.review,
742
+ smoke: authorization.smoke,
743
+ }, component, validationCandidate, unitById))
744
+ if (!same(authorization.authorizationBoundary, authorization.review?.authorizationBoundary)) errors.push(`${label}.authorizationBoundary must equal the terminal review boundary`)
745
+ }
746
+
747
+ function validateStoredComponentDelivery(manifest, component, candidate, index, errors) {
748
+ const delivery = component?.delivery
749
+ const label = `components[${index}] (${component?.id ?? 'unknown'}).delivery`
750
+ const unitById = new Map((Array.isArray(manifest?.units) ? manifest.units : []).map((unit) => [unit?.id, unit]))
751
+ const memberIds = Array.isArray(component?.units) ? component.units : []
752
+
753
+ // Compatibility is absence-based, not schemaVersion-based: #1009 can migrate a valid
754
+ // pre-feature manifest to v2 without inventing delivery facts. Such records retain the
755
+ // legacy unit reader until the component is terminally re-authorized, which creates this
756
+ // canonical record. Once present, member unit delivery duplication is refused below.
757
+ if (delivery === undefined || delivery === null) return
758
+ captureError(errors, () => assertExactObject(delivery, ['pr', 'generation', 'authorizedHeadSha', 'mode', 'provider', 'attempts', 'observations', 'watch', 'queueExecution', 'outcome', 'outcomeAt', 'mergedSha', 'mergedAt', 'blockedOn', 'reconciliations'], label))
759
+ if (!isObject(delivery)) return
760
+ if (!Number.isSafeInteger(delivery.pr) || delivery.pr < 1) errors.push(`${label}.pr must be a positive integer`)
761
+ const publishedPr = positivePr(component.pr)
762
+ if (publishedPr != null && publishedPr !== delivery.pr) errors.push(`${label}.pr must equal the published component PR ${publishedPr}`)
763
+ if (!Number.isSafeInteger(delivery.generation) || delivery.generation < 1) errors.push(`${label}.generation must be a positive integer`)
764
+ captureError(errors, () => assertSha(delivery.authorizedHeadSha, `${label}.authorizedHeadSha`))
765
+ if (!['direct', 'queue'].includes(delivery.mode)) errors.push(`${label}.mode must be direct|queue`)
766
+ if (delivery.mode === 'direct' && delivery.provider !== null) errors.push(`${label}.provider must be null for direct delivery`)
767
+ if (delivery.mode === 'queue' && !['mergify', 'github'].includes(delivery.provider)) errors.push(`${label}.provider must be mergify|github for queue delivery`)
768
+ if (delivery.mode !== deliveryMode(manifest)) errors.push(`${label}.mode must equal the run deliveryMode`)
769
+ if (delivery.provider !== (delivery.mode === 'queue' ? deliveryProvider(manifest) : null)) errors.push(`${label}.provider must equal the run queueProvider`)
770
+
771
+ const authorization = Array.isArray(candidate?.authorizations)
772
+ ? candidate.authorizations.findLast((entry) => entry?.generation === delivery.generation)
773
+ : null
774
+ if (!authorization || authorization.headSha !== delivery.authorizedHeadSha) errors.push(`${label} must bind an existing terminal authorization generation and head`)
775
+ if (candidate?.state === 'authorized' && (candidate.generation !== delivery.generation || candidate.frozen?.headSha !== delivery.authorizedHeadSha)) {
776
+ errors.push(`${label} must bind the currently authorized frozen component generation`)
777
+ }
778
+
779
+ if (!Array.isArray(delivery.attempts)) errors.push(`${label}.attempts must be an array`)
780
+ else {
781
+ const attemptKeys = new Set()
782
+ for (const [attemptIndex, attempt] of delivery.attempts.entries()) {
783
+ const at = `${label}.attempts[${attemptIndex}]`
784
+ if (!isObject(attempt)) { errors.push(`${at} must be an object`); continue }
785
+ if (!Number.isSafeInteger(attempt.generation) || attempt.generation < 1) errors.push(`${at}.generation must be a positive integer`)
786
+ const { generation, ...storedAttempt } = attempt
787
+ errors.push(...validateDeliveryAttempt(storedAttempt, delivery.mode, at, delivery.provider))
788
+ const key = `${generation}:${attempt.action}`
789
+ if (attemptKeys.has(key)) errors.push(`${at} duplicates the ${attempt.action} attempt for generation ${generation}`)
790
+ attemptKeys.add(key)
791
+ const attemptAuthorization = Array.isArray(candidate?.authorizations)
792
+ ? candidate.authorizations.find((entry) => entry?.generation === generation)
793
+ : null
794
+ if (!attemptAuthorization || attempt.headSha !== attemptAuthorization.headSha) errors.push(`${at}.headSha must equal its generation's authorized component HEAD`)
795
+ if (delivery.mode === 'queue' && positivePr(attempt.pr) !== delivery.pr) errors.push(`${at}.pr must equal component delivery PR ${delivery.pr}`)
796
+ }
797
+ }
798
+ if (!Array.isArray(delivery.observations)) errors.push(`${label}.observations must be an array`)
799
+ else for (const [observationIndex, observation] of delivery.observations.entries()) {
800
+ const at = `${label}.observations[${observationIndex}]`
801
+ captureError(errors, () => assertExactObject(observation, ['generation', 'headSha', 'at', 'queueState', 'position', 'unmetConditions', 'ack'], at))
802
+ if (!isObject(observation)) continue
803
+ const observationAuthorization = Array.isArray(candidate?.authorizations)
804
+ ? candidate.authorizations.find((entry) => entry?.generation === observation.generation)
805
+ : null
806
+ if (!observationAuthorization || observation.headSha !== observationAuthorization.headSha) errors.push(`${at}.headSha must equal its generation's authorized component HEAD`)
807
+ captureError(errors, () => deliveryObservation({ at: observation.at, queueState: observation.queueState, ...(observation.position !== undefined ? { position: observation.position } : {}), ...(observation.unmetConditions !== undefined ? { unmetConditions: observation.unmetConditions } : {}), ...(observation.ack !== undefined ? { ack: observation.ack } : {}) }, at))
808
+ }
809
+ if (delivery.mode === 'direct' && (delivery.observations?.length || delivery.watch != null)) errors.push(`${label}: direct delivery cannot carry queue observations or a watch`)
810
+ if (delivery.watch !== null && !isObject(delivery.watch)) errors.push(`${label}.watch must be null or an object`)
811
+ if (isObject(delivery.watch)) {
812
+ const unknownWatch = Object.keys(delivery.watch).filter((key) => !['firstObservedAt', 'lastObservedAt', 'lastChangeAt', 'queueState', 'position', 'polls', 'nudges', 'unmetConditions'].includes(key))
813
+ if (unknownWatch.length) errors.push(`${label}.watch has unknown property ${unknownWatch[0]}`)
814
+ for (const field of ['firstObservedAt', 'lastObservedAt', 'lastChangeAt', 'queueState']) if (!nonEmpty(delivery.watch[field])) errors.push(`${label}.watch.${field} is required`)
815
+ if (!Number.isSafeInteger(delivery.watch.polls) || delivery.watch.polls < 1) errors.push(`${label}.watch.polls must be a positive integer`)
816
+ if (delivery.watch.nudges !== undefined) {
817
+ if (!Array.isArray(delivery.watch.nudges) || delivery.watch.nudges.length > DELIVERY_MAX_NUDGES) errors.push(`${label}.watch.nudges must contain at most ${DELIVERY_MAX_NUDGES} entries`)
818
+ else for (const [nudgeIndex, nudge] of delivery.watch.nudges.entries()) {
819
+ const at = `${label}.watch.nudges[${nudgeIndex}]`
820
+ captureError(errors, () => assertExactObject(nudge, ['at', 'command', 'ref'], at))
821
+ if (!isObject(nudge) || !nonEmpty(nudge.at) || !nonEmpty(nudge.command)) errors.push(`${at} requires at and command`)
822
+ }
823
+ }
824
+ }
825
+ if (delivery.outcome !== null && !['merged', 'handed-back'].includes(delivery.outcome)) errors.push(`${label}.outcome must be null|merged|handed-back`)
826
+ if (delivery.outcome != null && !nonEmpty(delivery.outcomeAt)) errors.push(`${label}.outcomeAt is required for a terminal outcome`)
827
+ if (delivery.outcome === 'merged') {
828
+ captureError(errors, () => assertSha(delivery.mergedSha, `${label}.mergedSha`))
829
+ const requiredAction = delivery.mode === 'queue' ? 'enqueue' : 'merge'
830
+ if (!Array.isArray(delivery.attempts) || !delivery.attempts.some((attempt) => attempt?.generation === delivery.generation && attempt.action === requiredAction)) {
831
+ errors.push(`${label}: merged outcome requires a ${requiredAction} attempt in the authorized generation`)
832
+ }
833
+ const queueRequired = checksForPhase(authorization?.ci?.requiredCheckPlan, 'queue-head').length > 0
834
+ if (delivery.mode === 'direct' && delivery.queueExecution != null) errors.push(`${label}: direct delivery cannot carry queueExecution evidence`)
835
+ if (delivery.mode === 'queue' && queueRequired && !isObject(delivery.queueExecution)) errors.push(`${label}: merged queue delivery requires queueExecution evidence`)
836
+ if (delivery.mode === 'queue' && isObject(delivery.queueExecution)) captureError(errors, () => validatedQueueExecution(component.id, delivery, authorization, delivery.queueExecution))
837
+ }
838
+ if (delivery.outcome === 'handed-back') {
839
+ if (!nonEmpty(delivery.blockedOn)) errors.push(`${label}.blockedOn is required for handed-back`)
840
+ if (delivery.mergedSha != null || delivery.mergedAt != null || delivery.queueExecution != null) errors.push(`${label}: handed-back delivery cannot carry merge or queue-execution success evidence`)
841
+ }
842
+ if (delivery.outcome === null && (delivery.outcomeAt != null || delivery.mergedSha != null || delivery.mergedAt != null || delivery.blockedOn != null || delivery.queueExecution != null)) errors.push(`${label}: nonterminal delivery cannot carry terminal evidence`)
843
+
844
+ for (const id of memberIds) {
845
+ const unit = unitById.get(id)
846
+ if (!unit) continue
847
+ if (unit.delivery !== undefined) errors.push(`${label}: member ${id} must not duplicate canonical attempts or observations in units[].delivery`)
848
+ if (delivery.outcome != null) {
849
+ const expected = delivery.outcome === 'merged' ? 'delivered' : 'handed-back'
850
+ if (unit.disposition !== expected) errors.push(`${label}: terminal outcome must atomically derive disposition ${expected} for member ${id}`)
851
+ if (!same(unit.deliveryRef, { componentId: component.id, generation: delivery.generation })) errors.push(`${label}: member ${id}.deliveryRef must bind this component generation`)
852
+ }
853
+ }
854
+ }
855
+
856
+ function candidateErrors(manifest, component, index, options = {}) {
857
+ const c = component?.candidate
858
+ if (c === undefined || c === null) return []
859
+ const at = `components[${index}] (${component?.id ?? 'unknown'}).candidate`
860
+ const errors = []
861
+ if (!isObject(c)) return [`${at} must be an object`]
862
+ const unknown = Object.keys(c).filter((key) => !['state', 'generation', 'updates', 'frozen', 'authorizations', 'reopens'].includes(key))
863
+ if (unknown.length) errors.push(`${at} has unknown property ${unknown[0]}`)
864
+ if (!COMPONENT_CANDIDATE_STATES.includes(c.state)) errors.push(`${at}.state must be one of ${COMPONENT_CANDIDATE_STATES.join('|')}`)
865
+ if (!Number.isInteger(c.generation) || c.generation < 1) errors.push(`${at}.generation must be a positive integer`)
866
+ for (const key of ['updates', 'authorizations', 'reopens']) if (!Array.isArray(c[key])) errors.push(`${at}.${key} must be an array`)
867
+ const updates = Array.isArray(c.updates) ? c.updates : []
868
+ for (const [i, update] of updates.entries()) {
869
+ const label = `${at}.updates[${i}]`
870
+ captureError(errors, () => assertExactObject(update, ['roundClass', 'generation', 'branch', 'headSha', 'includedUnits', 'unitEvidence', 'integrated', 'integrationFix', 'at'], label))
871
+ if (!isObject(update)) continue
872
+ if (!['unit-discovery', 'integration-fix'].includes(update.roundClass)) errors.push(`${label} has invalid roundClass`)
873
+ if (!Number.isInteger(update.generation) || update.generation < 1 || update.generation > c.generation) errors.push(`${label}.generation is invalid`)
874
+ if (i > 0 && update.generation < updates[i - 1]?.generation) errors.push(`${label}.generation cannot move backwards`)
875
+ if (!nonEmpty(update.branch)) errors.push(`${label}.branch is required`)
876
+ captureError(errors, () => assertSha(update.headSha, `${label}.headSha`))
877
+ captureError(errors, () => assertIncludedUnits(component, update.includedUnits))
878
+ if (!Array.isArray(update.unitEvidence) || update.unitEvidence.length !== update.includedUnits?.length) {
879
+ errors.push(`${label}.unitEvidence must match includedUnits`)
880
+ } else {
881
+ update.unitEvidence.forEach((evidence, evidenceIndex) => validateStoredUnitEvidence(evidence, `${label}.unitEvidence[${evidenceIndex}]`, errors))
882
+ if (!same(update.unitEvidence.map((evidence) => evidence?.unitId), update.includedUnits)) errors.push(`${label}.unitEvidence order must match includedUnits`)
883
+ }
884
+ captureError(errors, () => integratedResult(update.integrated, component.id))
885
+ if (update.roundClass === 'integration-fix') captureError(errors, () => integrationFix(update.integrationFix, component.id))
886
+ else if (update.integrationFix !== undefined) errors.push(`${label}: unit-discovery cannot carry integrationFix`)
887
+ const prior = updates[i - 1]
888
+ if (prior) {
889
+ if (prior.branch !== update.branch) errors.push(`${label}: integration branch cannot move`)
890
+ if (prior.headSha === update.headSha) errors.push(`${label}: candidate head must move`)
891
+ if (prior.includedUnits?.some((id) => !update.includedUnits?.includes(id))) errors.push(`${label}: included units cannot shrink`)
892
+ if (update.roundClass === 'integration-fix' && (!same(prior.includedUnits, update.includedUnits) || !same(prior.unitEvidence, update.unitEvidence))) errors.push(`${label}: integration-fix must preserve unit evidence byte-for-byte`)
893
+ if (update.roundClass === 'unit-discovery' && same(prior.unitEvidence, update.unitEvidence)) errors.push(`${label}: unit-discovery must change unit evidence`)
894
+ }
895
+ }
896
+ const reopens = Array.isArray(c.reopens) ? c.reopens : []
897
+ if (reopens.length !== c.generation - 1) errors.push(`${at}.reopens must contain one typed transition for every completed generation`)
898
+ for (const [i, reopen] of reopens.entries()) {
899
+ const label = `${at}.reopens[${i}]`
900
+ captureError(errors, () => assertExactObject(reopen, ['fromGeneration', 'fromHeadSha', 'reopenReason', 'priorAuthorizationBoundary', 'authorizationBoundary', 'at'], label))
901
+ if (!isObject(reopen)) continue
902
+ if (reopen.fromGeneration !== i + 1) errors.push(`${label}.fromGeneration must preserve generation order`)
903
+ const generationHead = updates.filter((entry) => entry?.generation === reopen.fromGeneration).at(-1)?.headSha
904
+ if (reopen.fromHeadSha !== generationHead) errors.push(`${label}.fromHeadSha must equal that generation's latest candidate head`)
905
+ if (!REOPEN_REASONS.includes(reopen.reopenReason)) errors.push(`${label}.reopenReason is invalid`)
906
+ const priorBoundary = (() => { try { return boundary(reopen.priorAuthorizationBoundary, `${label}.priorAuthorizationBoundary`) } catch (error) { errors.push(error.message); return null } })()
907
+ const nextBoundary = (() => { try { return boundary(reopen.authorizationBoundary, `${label}.authorizationBoundary`) } catch (error) { errors.push(error.message); return null } })()
908
+ if (nextBoundary && nextBoundary.kind !== REOPEN_BOUNDARY_KIND[reopen.reopenReason]) errors.push(`${label}.authorizationBoundary.kind does not match reopenReason`)
909
+ if (priorBoundary && (priorBoundary.kind !== 'integration' || priorBoundary.value !== reopen.fromHeadSha)) errors.push(`${label}.priorAuthorizationBoundary must bind the prior frozen component HEAD`)
910
+ if (priorBoundary && nextBoundary && same(priorBoundary, nextBoundary)) errors.push(`${label}.authorizationBoundary must change`)
911
+ }
912
+ const authorizations = Array.isArray(c.authorizations) ? c.authorizations : []
913
+ for (const [i, authorization] of authorizations.entries()) {
914
+ validateStoredAuthorization(manifest, component, c, authorization, `${at}.authorizations[${i}]`, errors, options)
915
+ if (i > 0 && authorization?.generation <= authorizations[i - 1]?.generation) errors.push(`${at}.authorizations[${i}].generation must increase`)
916
+ }
917
+ if (c.state === 'assembling' && c.frozen != null) errors.push(`${at}: assembling candidate cannot carry a current frozen boundary`)
918
+ if (['frozen', 'authorized'].includes(c.state)) {
919
+ captureError(errors, () => assertExactObject(c.frozen, ['generation', 'headSha', 'branch', 'includedUnits', 'unitEvidence', 'authorizationBoundary', 'at'], `${at}.frozen`))
920
+ if (!isObject(c.frozen)) errors.push(`${at}: ${c.state} candidate requires frozen evidence`)
921
+ else {
922
+ const latest = updates.filter((entry) => entry?.generation === c.generation).at(-1)
923
+ if (!latest || latest.headSha !== c.frozen.headSha) errors.push(`${at}: frozen head must equal the latest update in its generation`)
924
+ if (c.frozen.generation !== c.generation) errors.push(`${at}: frozen generation must equal the current generation`)
925
+ if (c.frozen.branch !== latest?.branch) errors.push(`${at}: frozen branch must equal the latest update branch`)
926
+ if (!same(c.frozen.includedUnits, component.units)) errors.push(`${at}: frozen candidate must include the complete component`)
927
+ if (!same(c.frozen.unitEvidence, latest?.unitEvidence)) errors.push(`${at}: frozen unit evidence must equal the latest update`)
928
+ const byId = new Map((Array.isArray(manifest?.units) ? manifest.units : []).map((unit) => [unit?.id, unit]))
929
+ const currentEvidence = Array.isArray(component.units)
930
+ ? component.units.map((id) => { try { return preliminaryEvidence(byId.get(id), component.id) } catch (error) { errors.push(error.message); return null } })
931
+ : []
932
+ if (!currentEvidence.includes(null) && !same(c.frozen.unitEvidence, currentEvidence)) errors.push(`${at}: frozen unit evidence is stale against the current unit heads`)
933
+ if (latest?.integrated?.assembly !== 'clean' || latest?.integrated?.tests !== 'green' || latest?.integrated?.conflicts?.length || latest?.integrated?.failedTests?.length) errors.push(`${at}: frozen candidate requires clean assembly and green tests`)
934
+ const frozenBoundary = (() => { try { return boundary(c.frozen.authorizationBoundary, `${at}.frozen.authorizationBoundary`) } catch (error) { errors.push(error.message); return null } })()
935
+ if (frozenBoundary && (frozenBoundary.kind !== 'integration' || frozenBoundary.value !== c.frozen.headSha)) errors.push(`${at}: frozen authorization boundary must bind the integration HEAD`)
936
+ }
937
+ }
938
+ if (c.state === 'authorized') {
939
+ const latest = authorizations.at(-1)
940
+ if (!latest || latest.generation !== c.generation || latest.headSha !== c.frozen?.headSha) errors.push(`${at}: authorized candidate requires terminal authorization on the frozen head in this generation`)
941
+ if (!same(component.authorizationBoundary, latest?.authorizationBoundary)) errors.push(`${at}: component authorizationBoundary must equal terminal component HEAD boundary`)
942
+ }
943
+ const roster = new Set((Array.isArray(manifest?.units) ? manifest.units : []).map((unit) => unit?.id))
944
+ for (const id of Array.isArray(component.units) ? component.units : []) if (!roster.has(id)) errors.push(`${at}: component unit ${id} is not in the roster`)
945
+ validateStoredComponentDelivery(manifest, component, c, index, errors)
946
+ return errors
947
+ }
948
+
949
+ export function validateComponentCandidates(manifest, options = {}) {
950
+ if (manifest?.components === undefined || manifest?.components === null) return []
951
+ if (!Array.isArray(manifest.components)) return ['components must be an array']
952
+ const errors = []
953
+ const ids = new Set()
954
+ manifest.components.forEach((component, index) => {
955
+ if (!isObject(component) || !nonEmpty(component.id)) errors.push(`components[${index}] requires a non-empty id`)
956
+ else if (ids.has(component.id)) errors.push(`components[${index}] duplicates component id ${component.id}`)
957
+ else ids.add(component.id)
958
+ errors.push(...candidateErrors(manifest, component, index, options))
959
+ })
960
+ errors.push(...reconciliationErrors(manifest))
961
+ return errors
962
+ }