@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,178 @@
1
+ # Step 4: validate the candidate, then expose it
2
+
3
+ The gate, and the moment users first meet the new code. Previous:
4
+ [candidate.md](candidate.md) · next: [routing.md](routing.md).
5
+
6
+ ### 4 — Validate the candidate, then expose it (skill: smoke)
7
+ Two phases, in this order, and the order is the whole design: **validate at 0% traffic, then shift.** A smoke that runs after traffic has moved is a monitor — the bug is already live when it goes red.
8
+
9
+ **4a — Validate the candidate (no users exposed).** **Read `<skillsRoot>/smoke/SKILL.md` (absolute — same path resolution as step 2) and run the curated E2E happy-path gate against the candidate URL** — not a from-memory smoke:
10
+
11
+ ```bash
12
+ M=smoke.manifest.json; T=<candidateUrl>
13
+ node <smoke>/scripts/smoke.mjs plan --manifest $M --profile prod-preview --target $T # what will run
14
+ # ...drive the planned paths against $T, writing smoke-results.json (smoke/SKILL.md owns how)
15
+ node <smoke>/scripts/smoke.mjs verdict --manifest $M --profile prod-preview --target $T \
16
+ --results smoke-results.json --out smoke-verdict.json
17
+ ```
18
+
19
+ **Select the profile by name — `prod-preview`, the pre-promotion / production / preview-revision profile.** `$T` is only the address to drive against now; the profile decides the path set and the mutation policy. **Run `plan` first and read what it selected.** `verdict` re-derives the expected set from the same profile, so `plan` is where you see what the gate will actually cover — and on production it can cover *nothing*: `prod-preview` is read-only and excludes every `mutates:true` path, and golden paths are typically signup/checkout/purchase, which are all mutating. A gate that selects zero paths proves zero things. `verdict` returns **EMPTY** (exit 1) rather than a vacuous ALIVE in that case, but treat EMPTY as a **manifest problem to fix before releasing**, not a failing candidate: it means this repo has no read-only golden path worth gating on, and production is currently ungated. Add one (a health route, an authenticated GET of the core object) declared in `prod-preview`, rather than routing traffic on an empty verdict.
20
+
21
+ The `prod-preview` profile is **read-only** and enforces [principle 4](../SKILL.md#non-negotiable-principles) mechanically: it excludes **every** `mutates:true` path, tenant or not, because the candidate is wired to the real production database — the old `--no-mutations` flag is now a property of the named profile, not something the caller must remember. And because selection is a pure function of the profile name, passing the **same** `--profile` to the run and to `verdict` makes them select one path set *by construction*: the run/verdict mismatch that free-form `--target`/`--no-mutations` once permitted — and the "check the flags before believing a DEAD" warning it needed — is now unrepresentable. This exercises the real image, real secrets, real infra, at zero user exposure. **DEAD → step 5, pre-exposure path.**
22
+
23
+ **Three not-ALIVE outcomes, and they are about three different things — do not collapse them.** Each names a different defect, so each routes differently, and the routing is the reason the distinction exists:
24
+
25
+ - **DEAD** — the gate ran and the candidate failed it. Evidence about the **candidate**. → step 5's **pre-exposure path**: delete the 0%-traffic revision, file a normal issue, hand the evidence to `app-debug`/`diagnose`.
26
+ - **EMPTY** — the gate ran and selected zero paths. Evidence about the **manifest**: this repo has no read-only golden path, so production is currently ungated. → fix the manifest (a health route, an authenticated GET of the core object) and re-run. `verdict` returns EMPTY (exit 1) rather than a vacuous ALIVE precisely so it cannot be mistaken for a pass.
27
+ - **CANNOT-RUN / BLOCKED** — the gate itself could not produce a verdict. Evidence about the **gate**. Examples: the smoke harness could not authenticate because a secret env var holds the **resource-name string** (`projects/…/secrets/forex-smoke/versions/latest`) instead of the **secret value**, so every request gets 401/403; an external boundary it drives was unavailable; a fixture had drifted; the runner would not start. Under [principle 13](../SKILL.md#non-negotiable-principles) this is a **failure, not an absence** — so it is not a pass and traffic does not move — and it is equally not a DEAD, so nothing here judged the candidate and nothing here licenses deleting it. → **repair the gate** (fix the secret/config, restore the credential, repair the fixture) and **re-run 4a**. If it cannot be restored, the release stops with the candidate unvalidated; the route is still "the gate is the defect," never a pre-exposure discard of a candidate nothing judged.
28
+
29
+ The two mis-routings this prevents sit on either side of it. A cannot-run recorded as DEAD deletes a healthy candidate and sends a diagnosis team after an application bug that does not exist. A cannot-run recorded as a skipped step shifts production traffic on a gate that never asked its question — and the artifact it leaves behind reads like a validated release. **And the block stays scoped** (principle 13): this SHA does not release; the queue or PR lane that carries the fix — to the harness, the fixture, the credential, or the secret — is not paused, and the fix arrives through it like any other unit.
30
+
31
+ **4b — Shift traffic to 100%.** This is the moment users are exposed, so it is **its own authorization** (principle 1; per-step confirm by default, pre-flight item 3) — the promotion approval in step 3 does not carry here.
32
+
33
+ **Re-probe credentials before any 4b action and on resume when `step` is `validate` or `expose`.** The credential named at pre-flight item 3 can expire while the 4a verdict is pending. Run the same probe command pre-flight item 0 runs:
34
+
35
+ ```bash
36
+ node <resolve-release>/scripts/reprobe-credentials.mjs [--command <cmd>]
37
+ ```
38
+
39
+ It fails closed: exit 0 only when the credential is live. A BLOCKED or UNKNOWN result stops here — apply the same remedy as pre-flight item 0 and do not pause, shift, or resume past it. On resume when `step` is `validate` or `expose`, run this probe before any other read.
40
+
41
+ **Where pre-flight item 6 declared durable processing, the shift is a sequence and the fence sits inside it** ([principle 14](../SKILL.md#non-negotiable-principles)). Run these five in order, in **both envelope modes** — `auto-when-green`'s condition (e) authorizes *the exposure*, not a shorter one with the ordering removed:
42
+
43
+ Mechanize steps 1-2 through the validated repository adapter after recording `step: "expose"`:
44
+
45
+ ```bash
46
+ node <resolve-release>/scripts/durable-processing.mjs down \
47
+ --declaration <repository-release-declaration.json> \
48
+ --release-state <run-manifest.json> > durable-processing-down.json
49
+ ```
50
+
51
+ Only `state: "ready-to-shift"` licenses the fence. Its `proof.observed` is the durable-state
52
+ reading, and its `shiftFence` is the generation/fencing identity carried into the shift record.
53
+ `blocked`, `refused`, malformed, or partial output takes the pre-shift unwind and never shifts.
54
+
55
+ 1. **Pause, in the declared order** — outermost first, database admission before worker scheduling, so nothing new enters the pipeline while its far end is still draining. Set `step: "expose"` **before the first pause**, not before the shift: the pause is the first action that changes what production *does*, and a crash after it with the manifest still reading `validate` leaves processing stopped and nothing recording that it is.
56
+ 2. **Prove quiescent** with the read declared at item 6 — in-flight count at zero, queue depth drained, no active scheduler execution — and **record the reading, not the judgment**: the observed value goes in `release.quiescence.proof`, because `quiesced: true` is the assertion again one field deeper and no second reader can tell it from a check that never ran. The pause command's success is **not** this proof; it is the request this proof checks. **Not yet quiescent is a wait, not a pass** — re-read until it drains or the declared bound elapses, and an elapsed bound ends the attempt down the [pre-shift unwind path](routing.md#5--route-on-the-verdict). A proof that **cannot run** is [principle 13](../SKILL.md#non-negotiable-principles): it fails rather than absents, so it licenses no shift, and it takes that same unwind.
57
+ 3. **Fence** — the three assertions below, unchanged and undiluted. They run *after* quiescence because they describe the state the shift is about to act on, and the pause is what stops that state moving underneath them.
58
+ 4. **Shift** to the revision the fence just re-established — never "the newest candidate", and never a handle re-read after the fence.
59
+ 5. **Resume in the exact reverse of step 1's order** — worker scheduling first, database admission last — against the **revision now serving**, confirming each rather than firing and moving on. The reversal is [principle 14](../SKILL.md#non-negotiable-principles)'s rule, not this repo's example: resuming admission before the workers that drain it re-opens the front of a pipeline whose back end is still stopped, which is the queue-growth incident with the traffic shift subtracted.
60
+
61
+ Record the sequence as `release.quiescence` = `{ pausedAt, pauseOrder, proof, resumedAt, resumeOrder, servingRevisionAtResume }` — the two orders **as executed**, so a reader checks the reversal instead of trusting it. Where item 6 declared **no** durable processing, none of this runs and the rest of 4b is the whole step.
62
+
63
+ After the shift's serving-revision proof, drive step 5 against that proven revision and the
64
+ generation/fence from `durable-processing-down.json`:
65
+
66
+ ```bash
67
+ node <resolve-release>/scripts/durable-processing.mjs up \
68
+ --declaration <repository-release-declaration.json> \
69
+ --serving-revision <proven-serving-revision> \
70
+ --incumbent-revision <release.previousRevision> \
71
+ --candidate-revision <release.candidateRevision> \
72
+ --generation <shift-fence-generation> --fence <shift-fence> \
73
+ > durable-processing-up.json
74
+ ```
75
+
76
+ Only `state: "restored"` completes exposure. It proves the named serving revision is exactly
77
+ the declared candidate at 100% with the incumbent at 0%; `ambiguous` stops and pages.
78
+
79
+ **Floor applicability was decided at preflight, not here** (identity-v0.4.1 / #800; #910).
80
+ `release.productionEndpoints` binds the deploy trigger configured production URL to the
81
+ independently observed Cloud Run service URL. A distinct public/edge hostname requires fresh
82
+ read-only `prod-active` ALIVE targeted exactly at that canonical URL; it cannot skip. Cloud
83
+ Run-only is accepted only through the fingerprint-bound reasoned skip emitted when the two reads
84
+ share a hostname. `green-gate.mjs shift` rechecks topology, target, fingerprint, and freshness and
85
+ HOLDs with `e:floor-not-ready` on any absence, mismatch, stale evidence, or hand-authored bypass.
86
+ On resume, run `production-endpoints.mjs resume` before the credential/fence reads and return to
87
+ preflight when endpoint configuration changed.
88
+
89
+ **The authorization is not the last check: re-check the coordinates after it (the fence).** 4a's verdict was minted *before* the wait, and the wait is unbounded — a human confirm can arrive four hours later, and under `auto-when-green` the gate fires whenever it happens to be evaluated. In between, the coordinates move: a newer build takes the `candidate` tag, the incumbent stops holding all the traffic, a second release starts. An authorization is permission to shift **these** coordinates, never permission to shift whatever the coordinates have become. So **after** the confirm and **before** the shift, re-read the live state and compare it against what the manifest recorded:
90
+
91
+ ```bash
92
+ node <resolve-release>/scripts/green-gate.mjs shift \
93
+ --manifest <run-manifest.json> --verdict smoke-verdict.json > fence-verdict.json # 0 = fresh
94
+ gcloud run services describe <svc> --region <r> --flatten='status.traffic[]' \
95
+ --format='value(status.traffic.tag,status.traffic.url,status.traffic.revisionName,status.traffic.percent)' \
96
+ > fence-traffic.txt
97
+ ```
98
+
99
+ **`--flatten` is what makes the second read identity evidence and not just a split.** Unflattened, `value(status.traffic.revisionName,status.traffic.percent)` returns two parallel lists with nothing tying a revision to the tag that points at it — enough to see the percentages, structurally incapable of showing what the candidate URL resolves to. Flattened, each traffic target is one row carrying its `tag`, its `url`, the `revisionName` it currently routes to, and its `percent`, so all three assertions below read off the same observation.
100
+
101
+ Three assertions, each against observed state, none against the manifest alone — and each one **recorded as the value it read**, never as the judgment it produced:
102
+
103
+ 1. **Freshness** — 4a's verdict is still inside `envelope.verdictFreshnessMinutes` (pre-flight item 3). `green-gate.mjs shift` decides it from the verdict's recorded timestamp and holds on `e:stale-verdict`, or on `e:unstamped-verdict` when the artifact carries no timestamp — absent evidence is not fresh evidence. **An expired verdict routes back to 4a, never onward to the shift**: re-run the candidate validation, then re-enter here. This is cheap and it is the point — the candidate is usually fine; what expired is the *evidence*, and re-minting it costs one smoke run. **Re-run the *run*, not just `verdict`** — `smoke.mjs verdict` stamps `generatedAt` when the verdict is minted, not when the paths were driven, so re-reducing the old `smoke-results.json` produces a gate-fresh artifact over observations nobody re-took, and the gate cannot tell. (`verdict`-only is the right move for a *flag* mismatch, where the observations are good and only the selection was wrong.) *Recorded as* `gate` — the gate's own JSON, which carries the age it computed and the window it compared against.
104
+ 2. **Identity** — the flattened row whose `url` is the recorded `release.candidateUrl` (equivalently, whose `tag` is the recorded `release.candidateIdentity.tag`) still names `release.candidateRevision`. **The SHA-keyed tag makes this assertion decisive rather than merely alarming, and does not make it redundant**: step 3 established identity at discovery, and this re-establishes it after an unbounded wait, which is a different question. What changed is the failure it can see — a rival release can no longer take this SHA's tag, so a disagreement here is the tag being deleted, re-pointed by a rebuild, or resolving to a revision that no longer exists, rather than a silent substitution. *Recorded as* `candidateResolvesTo` — **the revision name the URL actually resolved to, copied from the reading**, not a boolean and not the name you expected: an identity check recorded as `true` is the assertion again, one field deeper, and a reader cannot tell it apart from a check that never ran. If it resolves to a different revision — or the candidate revision is absent from the traffic list entirely — **stop**, and read what the disagreement means from the guarantee you are under: with a shared `candidate` tag it is a second release in flight (principle 7) and you are about to shift under another release's identity; with a SHA-keyed tag it is your own handle no longer naming your artifact. Either way what you validated is not what you would expose.
105
+ 3. **Traffic** — the candidate still holds **0%**, and `release.previousRevision` still holds **exactly 100%**. Anything else means the split moved while the authorization was pending, so nothing you validated describes what is serving now. *Recorded as* `candidatePercent` and `previousRevisionPercent`, the two numbers as read.
106
+
107
+ **Any of the three failing ends the attempt without exposure** — do not shift, do not "re-check and proceed anyway." Nobody was exposed, so this is not a failure tier at all: re-establish the coordinates (re-validate at 4a, or resolve the second release in flight) and re-enter 4b under a fresh authorization.
108
+
109
+ **Run the fence in both envelope modes.** Under `auto-when-green` `green-gate.mjs shift`'s exit code *is* the authorization. Under a per-step confirm it is a **check the human's yes does not substitute for** — the human authorized the shift, the fence establishes that the coordinates are still the ones they authorized. A script never authorizes anything (principle 1); it only turns "still true?" from a claim into a comparison.
110
+
111
+ Then set `step: "expose"` *before* issuing the shift — on a **plain shift** it means "a shift was started and may or may not have landed," which is exactly the state a crash here leaves behind. Where the sequence above ran, `step` is **already** at `expose` from before the first pause and means "the exposure sequence started"; the re-set is idempotent, and what separates a paused-but-unshifted production from a landed shift is the resume's **two** reads — the traffic split *and* the processing state — never `step` alone. Shift traffic, then **confirm the live service is the candidate you validated — by re-reading the tuple it attested, not by re-deriving identity from a string**, and **assert its display version matches `envelope.displayVersionPolicy`**:
112
+
113
+ ```bash
114
+ LIVE_TARGET=$(node <resolve-release>/scripts/production-endpoints.mjs live-target \
115
+ --manifest <run-manifest.json>)
116
+ node <resolve-release>/scripts/production-endpoints.mjs verify-live \
117
+ --manifest <run-manifest.json> --target "$LIVE_TARGET"
118
+ curl -fsS "$LIVE_TARGET/health" > serving-health.json
119
+ curl -fsS "$LIVE_TARGET/settings" > serving-settings.json
120
+ <read the served frontend asset version into frontend-asset-version.json>
121
+ node <resolve-release>/scripts/candidate-identity.mjs serving --sha $SHIPPED \
122
+ --identity candidate-identity.json --health serving-health.json # exit 1 = not the candidate
123
+ ```
124
+
125
+ The identity read above is not this step, and neither replaces the other: the attestation says *which* code is serving and nothing about whether it works, while a smoke says the golden paths are alive and nothing about whose build answered — a bare health curl offered as verification passes on any revision that boots, including the old one still serving.
126
+
127
+ **Run `version-assert.mjs` next as the standalone mandatory publication gate** ([versioning §6](versioning.md#6-display-version-is-not-identity-version)). It must receive `serving-health.json`, `serving-settings.json`, and the frontend asset-version JSON. Its own process exit is recorded as `release.versionAssert`; exit 1 or 2 stops the release before step 5. Do not put a shell separator or any smoke/informational follow-on command after it. Only an exit-0 `decision:"accept"` permits the final-tag and GitHub-Release publication path.
128
+
129
+ ```bash
130
+ node <resolve-release>/scripts/version-assert.mjs serving \
131
+ --health serving-health.json \
132
+ --settings serving-settings.json \
133
+ --frontend-asset frontend-asset-version.json \
134
+ --policy "$DISPLAY_VERSION_POLICY" \
135
+ --version "$NEXT_VERSION" \
136
+ --trigger-tag "$RC_TAG" \
137
+ --sha "$SHIPPED"
138
+ ```
139
+
140
+ The display-version assertion is a third check: it confirms the `/health`, `/settings`, and frontend asset version strings operators see match the policy declared at pre-flight, closing the rc-tag case where UI and `/api/health` would otherwise keep showing the attempt tag after the final claim. A mismatch refuses unless the envelope recorded a `--record-mismatch <reason>` exception (see [`scripts/version-assert.mjs --help`](../scripts/version-assert.mjs) and [references/versioning.md](versioning.md#6-display-version-is-not-identity-version)).
141
+
142
+ **`--sha $SHIPPED` is not redundant with the file you are passing.** Without it this check proves the record and the live process agree — a true statement about *whichever release the record describes*, which is worth nothing if a resumed session handed it the previous rc's `candidate-identity.json`. It refuses on `e:identity-sha-mismatch` (the record describes another commit) or `e:identity-guarantee-unbacked` (the record's `guarantee` is contradicted by its own tag or missing digest) **before** it decides anything else — including before it routes to the fallback below, which would otherwise send you off to compare digests against that same wrong record.
143
+
144
+ **Fallback, only where pre-flight item 3 declared no attesting endpoint**: read the serving revision's image and **compare image digests, not revision names** — platforms name revisions from their own counter (`{service}-prod-00042-abc`), which carries no SHA, so a name comparison silently never matches and degrades into no check at all. The comparison is the same script, handed the digest instead of a tuple:
145
+
146
+ ```bash
147
+ # Select the entry HOLDING the traffic, never the first one in the array. The same flattened read
148
+ # as the fence above, so `revisionName` and `percent` come off one row and the selection is a
149
+ # comparison rather than a position.
150
+ SERVING=$(gcloud run services describe <svc> --region <r> --flatten='status.traffic[]' \
151
+ --format='value(status.traffic.revisionName,status.traffic.percent)' \
152
+ | awk -F'\t' '$2 == 100 { print $1 }')
153
+ [ "$(printf '%s' "$SERVING" | grep -c .)" = 1 ] || { echo "no single revision holds 100%: '$SERVING'"; exit 1; }
154
+ SERVING_DIGEST=$(gcloud run revisions describe "$SERVING" --region <r> \
155
+ --format='value(status.imageDigest)')
156
+ node <resolve-release>/scripts/candidate-identity.mjs serving --sha $SHIPPED \
157
+ --identity candidate-identity.json --serving-image-digest "$SERVING_DIGEST" # exit 1 = not the candidate
158
+ ```
159
+
160
+ **Never read `status.traffic[<index>]` — a position is not a routing fact, and reading one inverts this check.** A traffic array is free to list the candidate at 0% first and the incumbent at 100% second, which is exactly the array a **green-but-ineffective shift leaves behind**. The first slot then hands this fallback the *candidate's own* digest, it matches the record it was supposed to be tested against, and `--serving-image-digest` exits 0 while every user is still on the incumbent. That is the failure this step names below in as many words — *"a green shift with traffic still on the old revision is a failed exposure, not a released one"* — made invisible by the read that was supposed to catch it, and it contradicts [principle 5](../SKILL.md#non-negotiable-principles): the loop exits on **observed production state**, and "first in the array" is not a state anything observes. Select by `percent`, and treat "not exactly one revision at 100%" as its own stop: a split means the shift did not take, so there is no serving revision to confirm.
161
+
162
+ **Both sides of that comparison are real, and that is what the declaration bought.** The recorded `release.candidateIdentity.imageDigest` in this branch is the *candidate revision's* configured digest, supplied at step 3 as `--candidate-image-digest` — the substitute pre-flight item 3 requires precisely so this check has something to compare against. Without it the field would be `null` here, and a fallback pointed at a null field is not a weaker check, it is no check wearing one's clothes. Omitting `--serving-image-digest` therefore refuses with **`e:no-attested-tuple`** — "nothing was compared, take the read and re-run" — rather than passing.
163
+
164
+ Use the digest rather than the `:$SHIPPED` tag suffix wherever the platform exposes one: a tag is a mutable pointer a registry can re-point, and the honest cost of this fallback is that both digests describe the artifact a revision was *configured with* rather than the code that is *running*. Then **re-run the smoke against `$LIVE_TARGET` under the `prod-active` profile** (the post-promotion / production / active-revision profile — also read-only; the canonical configured production URL now, never an arbitrary equivalent service URL) and set `step: "verify"`. **The identity read above is not this step, and neither replaces the other**: the attestation says *which* code is serving and nothing about whether it works, while a smoke says the golden paths are alive and nothing about whose build answered — a bare health curl offered as verification passes on any revision that boots, including the old one still serving.
165
+
166
+ **Multi-service releases shift as a set, and a partial shift is its own emergency.** One rc tag can produce one candidate *per service*, so record `candidateRevision`/`candidateUrl`/`previousRevision` **per service**, validate each in 4a, and fence each before its own shift. Shift in dependency order, and if any service fails to shift, **stop and complete the set** — do not debug with production half-shifted. Users are then split across a service boundary, running two versions of a contract that were never validated together; 4a cannot catch it, because it validated each candidate in isolation. Neither failure tier fits (nothing is unhealthy, nothing is un-shipped), so the response is: finish the shift if the remaining service is healthy, or shift the already-moved services **back** to their previous revisions if it is not. Only once the set is consistent do you diagnose.
167
+
168
+ **A green shift with traffic still on the old revision is a failed exposure, not a released one** — and it is its own outcome, distinct from both failure tiers: production is healthy, users were never exposed, the candidate is fine, and only the routing command failed. Do **not** run the rollback policy (there is nothing to restore, and filing an incident for a non-incident is what principle 6 warns teaches people to route around the gate), and do **not** accept step 4a's ALIVE as verification (it described the candidate, not the live service). Diagnose why routing did not take — most often the revision never became `Ready` — then either re-issue the shift or, if the candidate is at fault, go to the pre-exposure path in step 5. A blind retry just fails again more slowly.
169
+
170
+ **And where the sequence's own state is ambiguous, stop and page — do not route it at all** ([principle 14](../SKILL.md#non-negotiable-principles); [principle 6](../SKILL.md#non-negotiable-principles)'s third case). The qualifying states are narrow and worth naming, because each one is a *read* that came back unusable rather than a step that failed: the pause was issued and the quiescence read cannot say whether the workers drained; the shift errored and the traffic split reads as neither the old shape nor the new one; a resume was issued and it cannot be confirmed that scheduling or admission is actually back. **None of these is the pre-exposure discard**, and the pull toward filing it as one is strong precisely because that route is cheap, blameless, and explicitly "routine, not an outage" — but that route *asserts production is untouched*, and here production is the thing nobody can describe. Deleting a revision, resuming a worker, or re-issuing a shift against a state you cannot read is a write against durable data on a guess. Page, hand over the readings you **do** have (the last good quiescence proof, the traffic rows as they actually returned, which resumes were issued and which were confirmed), and let a human establish the state before anything further is issued.
171
+
172
+ **Proof-of-execution:** capture `smoke-results.json`, the ALIVE/DEAD verdict, **the re-check fence's readings**, the serving-revision confirmation, and the post-shift health results (principle 5 — the loop exits on observed state, never a claim; here that state is observed twice, once per phase). The fence is an artifact, not a sentence in the report: record `release.recheckFence` = `{ gate: <green-gate shift JSON>, candidateResolvesTo, candidatePercent, previousRevisionPercent, checkedAt }` in the manifest, written **between** the authorization and the shift. A report that says the coordinates were re-checked, with no recorded reading of what they were, is the prose form of the check this fence replaced. **Where durable processing was declared, `release.quiescence` is owed on the same terms** — the pause order as executed, the quiescence proof's observed value, the resume order, and the revision it was resumed against; a sequence reported as "paused, drained, shifted, resumed" with no reading behind any of it is the same prose failure applied to the one ordering whose cost is durable.
173
+
174
+ **The post-shift half is recorded the same way.** `release.servingConfirmation` = `{ gate: <candidate-identity serving JSON>, candidatePercent, previousRevisionPercent, checkedAt }` — the decision artifact beside the traffic split as read, never `confirmed: true` — and `release.liveVerdict` = `{ state, artifactPath, generatedAt, target }`, the pointer to the prod-active verdict artifact. Both are what step 5's [finalizer](routing.md#5--route-on-the-verdict) validates before it records the terminal closure; a confirmation held in prose is a release that cannot finalize.
175
+
176
+ **Every assertion owes a field here and a grading question in [row 4b](regression-checklist.md).** The mapping is one-to-one and deliberate: freshness → `gate`, identity → `candidateResolvesTo`, traffic → `candidatePercent` + `previousRevisionPercent`, all stamped `checkedAt`. An assertion with no field is the one that quietly degrades back into prose, because nothing in the artifact and nothing in the checklist can tell a performed check from a claimed one — and it degrades exactly where it costs most: a fence recorded complete and green while the candidate URL points at another release's revision is a shift under a stolen identity that every downstream reader will read as verified. So a *fourth* assertion added later gets the same two homes in the same edit, and this file's `recheckFence` shape, the fence's command block, and row 4b move together.
177
+
178
+ If production is unhealthy **after** the shift, that is the post-exposure tier — step 5 rollback, not a candidate deletion.
@@ -0,0 +1,24 @@
1
+ # The handoff report — what a finished release owes its reader
2
+
3
+ Read this when a release reaches a terminal step (`done` / `discard` / `rollback`), or when a stall
4
+ hands back. [SKILL.md](../SKILL.md#definition-of-done-handoff-report) states the bar and names the
5
+ items; this file is each item in full — what to report, in what form, and the cases where the
6
+ report says something even though nothing went wrong.
7
+
8
+ Three items are unconditional, and the reasoning for that is
9
+ [why.md](why.md#why-the-handoff-reports-a-thing-even-when-nothing-went-wrong).
10
+
11
+ ## Every item
12
+
13
+ - **Shipped SHA** and the revision verified live, with **evidence from both phases**: the candidate's ALIVE verdict at 0% traffic; the re-check fence's readings taken between the authorization and the shift (verdict age vs the window, **the revision the candidate URL resolved to**, candidate at 0%, incumbent at 100% — the three the manifest's `recheckFence` records); the serving confirmation (the live service re-attesting `release.candidateIdentity`'s tuple, or the declared image-digest fallback); and health after the shift. **Each verdict says which of its evidence was observed fresh and which was carried over** ([principle 11](../SKILL.md#non-negotiable-principles)) — a verdict that cannot say is not evidence this report may quote, and an environment-sensitive observation is never carried over. On a failure, the tier instead: the **candidate-deletion record** (pre-exposure — revision deleted, the previously-serving revision still at 100%, the diagnosis handoff) or the **rollback record** (post-exposure — what was restored, the restoration evidence, the incident issue, the diagnosis handoff).
14
+ - **Quiescence record** — where [pre-flight item 6](preflight.md#pre-flight-gate-confirm-the-authorization-envelope-mandatory) declared durable processing: the **pause order as executed**, the proof's **observed reading** (the value, never `quiesced: true`), the **resume order as executed**, the revision processing resumed against, and that resume was the exact reverse of pause. On either failure path, which unwind ran and in what order. **Where item 6 declared no durable processing, say that too.** → [why](why.md#why-the-handoff-reports-a-thing-even-when-nothing-went-wrong)
15
+ - **Version identity** — the version, the bump **and why**, the **rc tags minted** and how many attempts they represent, the final tag, the CHANGELOG cut commit, the release-notes link, and plainly whether the version was **reused** (both tag-after-verify shapes) or **burned** (legacy final-tag trigger only), with any `Withdrawn` annotation. On a dev release: unversioned, plus the SHA.
16
+ - **ensure-target-green** — `release.ensureGreen` when tip colour was BLOCKED (or the phase ran): outcome, finalHead, durationMs, failingClasses, repair PRs/issues (tip check-runs only; not #800 floor). Absent on a green no-op is fine; a mid-flight repair with no record is incomplete.
17
+ - **Post-release metadata PR** — `release.metadataPr` after the final claim: the PR number/url from [`metadata-sync.mjs open`](../scripts/metadata-sync.mjs), or an explicit `n/a` with reason. A released attempt without that record is **incomplete** (`report.mjs`; identity-v0.4.1 / identity#678).
18
+ - **Closed issues** — closed on verified-done with the version/SHA/evidence comment, or on a rollback **left open** and cross-linked to the incident issue.
19
+ - **Promotion PR link**, its fast-forward status, and the release-readiness checklist outcome.
20
+ - **Assembly record** — the integration/release branch, per-PR merge order, the **integration CI + combined-diff review** on the assembled tip, and anything bounced back to `resolve-issues` at intake.
21
+ - **Authorization audit** — the confirmed envelope, which step authorized each action, and whether the promotion ran on a human confirm or under `auto-when-green` (if the latter: (a)–(d) held, no carve-out). **Name `envelope.exposureIdentity` on every release**, with any pre-flight item 3 weakening declared.
22
+ - **Release-evidence bundle location** on **every** terminal exit — `<durable-evidence-dir>/<attempt-id>--<terminal-state>.json` (one attempt can leave more than one, e.g. a `blocked-manual` hold and its later `released` close) — and, on the verified path, the **GitHub Release** the `released` bundle was attached to.
23
+ - **Post-mortem** — auto-saved by `report.mjs` at `done`/`discard`/`rollback`; **adjudicate each finding in a line** and file the rendered issue (`--issue`). A **stalled** release reaches no terminal step and auto-saves nothing: run `report.mjs <manifest> --postmortem` by hand. With version + `durableEvidenceDir` recorded it also saves the **version-level aggregate** (`kind: "release-version"`). → [references/postmortem.md](postmortem.md), [why](why.md#why-the-post-mortem-is-adjudicated-not-just-saved)
24
+ - Run manifest reflecting the final state.
@@ -0,0 +1,230 @@
1
+ # The release post-mortem (mandatory, at the terminal step)
2
+
3
+ **It runs on every release and saves itself.** `report.mjs` — which you already run at every
4
+ step transition — computes and persists it the moment the release reaches a terminal step
5
+ (`done` · `discard` · `rollback`). The handoff: [SKILL.md](../SKILL.md#definition-of-done-handoff-report).
6
+
7
+ ```
8
+ report.mjs <manifest> # the checkpoint — and saves the profile at a terminal step
9
+ report.mjs <manifest> --postmortem # print the profile now (the only way to see a STALLED release's)
10
+ report.mjs <manifest> --issue # {title, body} to file against resolve-release
11
+ report.mjs <manifest> --no-save # the opt-out, for a release you do not own
12
+ ```
13
+
14
+ It shares its renderer, its persistence and its **cross-run series** with the delivery
15
+ lane's ([resolve-issues/references/postmortem.md](../../resolve-issues/references/postmortem.md)):
16
+ same `$RESOLVER_POSTMORTEM_DIR`, same `index.jsonl`, tagged `kind: "release"`. What differs
17
+ is the questions.
18
+
19
+ ## It is not a speed pass, and that is the whole design
20
+
21
+ The delivery lane's post-mortem hunts wasted wall-clock, because that is what an epic loses.
22
+ A release does not lose time: measured across the **73 `resolve-release` manifests** on the
23
+ author's machine (2026-06-21 → 2026-08-05), the **median release took 0.4 hours**. Releases
24
+ are fast — the lane is mostly waiting on watchers.
25
+
26
+ What a release loses is **account**: the record of what happened to production. So every
27
+ finder asks a completeness question, and the severities are about how much of a release's
28
+ own history is unreachable from state.
29
+
30
+ **This table is the single source for these numbers.** `postmortem.mjs` deliberately does
31
+ not restate them — not in its comments, not in the strings it renders — because a figure
32
+ quoted in four files is a figure that goes stale in three of them. (That is not a
33
+ hypothetical: the delivery lane's `inFlightCap` default was documented as `2` for nine days
34
+ after it became `4`, and a run read the stale copy and lost five hours to it.) Re-measure
35
+ against the current corpus and update here; the thresholds in the module are derived from
36
+ this table and should be revisited with it.
37
+
38
+ | Finding | Fires when | Measured in the corpus |
39
+ |---|---|---|
40
+ | `step-off-enum` | `step` is not one of the ten release steps | **13/73 (18%)** |
41
+ | `stopped-mid-machine` | last step was non-terminal, no outcome recorded | **11/73** |
42
+ | `no-verification-record` | no `verification`, on a release that reached it | **20/73** |
43
+ | `adhoc-manifest-keys` | keys outside the release schema | **35/73 (48%)**, 125 distinct keys |
44
+ | `long-release` | over 8h — ~20× the median | 4/73 |
45
+
46
+ Sixteen of the seventy-three come back clean.
47
+
48
+ **`step-off-enum` is the loudest, and its point is not the typo.** `step` is the resume key —
49
+ the one field a dead session re-enters at — and real manifests carry `verified-done`,
50
+ `5-verified-done`, `done (all 3 merged into dev)`, `verified-dev-alive-ruleset-held`. A
51
+ resumed session cannot route on any of those: it cannot tell whether the release shipped, is
52
+ mid-deploy, or needs a rollback. And `run-state.mjs validate` **already refuses every one of
53
+ them**. So each occurrence is a release on which validate was never run, which is the
54
+ actionable half — the gate is not missing, it is not being invoked. (Exactly the shape of
55
+ the delivery lane's `runnable` finding: a mechanism that exists and is skipped under load.)
56
+
57
+ ## What is reported rather than faulted
58
+
59
+ **Rollbacks (3/73) and burned versions (0/73).** A rollback that happened and was recorded is
60
+ the machine *working* — the candidate was caught and production was restored. A finder that
61
+ scolded for it would train the reader to ignore the pass. Both appear in the header line
62
+ (`ROLLED BACK`, `version burned`) so they are never invisible.
63
+
64
+ Likewise `discard` and `rollback` are **terminal and successful** steps, not stalls.
65
+
66
+ ## The step
67
+
68
+ 1. **Read it.** `report.mjs` already saved it at the terminal step; `--postmortem` prints it
69
+ again. A release that **stalled** never reaches a terminal step and so never auto-saves —
70
+ run `--postmortem` on it by hand, because "it stalled" is precisely one of the findings.
71
+ 2. **Adjudicate each finding in a line.** Some are legitimate: a long release that was
72
+ waiting on a human is fine and owes no change. An unadjudicated finding is not a result.
73
+ 3. **File the issue** (`--issue`), one per release, findings you adjudicated as legitimate
74
+ dropped with the reason.
75
+ 4. **Report the profile in the handoff**, above the evidence.
76
+
77
+ ## What it can and cannot see
78
+
79
+ The run manifest only — no tree, no live service, no clock. So it can see what the release
80
+ *recorded*, and it cannot see what production actually holds: **a candidate revision it
81
+ reports as recorded may already be deleted, and one it cannot see may still be serving.**
82
+ Where the two disagree, the tree is right and the manifest is the thing to fix. A finding it
83
+ did not raise is not evidence anything was fine.
84
+
85
+ ## Adding a finding
86
+
87
+ Same bar as the delivery lane's: **a finding ships with the measured runs it would have
88
+ caught**, computed from fields the schema already defines. If it needs a new field, the field
89
+ and its reader land in the same change or neither does.
90
+
91
+ ## The version-level pass (#961): the aggregate a single manifest cannot see
92
+
93
+ The run-level pass above profiles ONE manifest's record. It cannot see the shape that made
94
+ Forex `v0.1.12` the incident it was: **nine attempts for one version, eight non-released, and
95
+ the release lane's own tooling — never the application — as the repeated failure owner.** The
96
+ terminal post-mortem that release actually generated reported only `long-release`, because
97
+ that is all one manifest can say. `version-postmortem.mjs` is the aggregate reader: it
98
+ discovers **every durable evidence bundle a version left behind** in the durable evidence dir
99
+ (`<version>-attempt.<n>--<state>.json`, legacy `v<version>-rc.<n>` keys included), orders the
100
+ attempts by the immutable attempt id — **numerically**, so attempt.10 follows attempt.9 — and
101
+ reports what only the series of attempts can say:
102
+
103
+ - attempt and rc-tag counts, terminal-state counts, maximum exposure per attempt, the number
104
+ of traffic mutations (each recorded shift stage, plus each rollback restore — restoring the
105
+ incumbent is a traffic mutation too), the rollback count, and whether the version was
106
+ **reused** (both tag-after-verify shapes — principle 10 makes a failed attempt burn nothing)
107
+ or **burned** (the legacy final-tag shape, or a bundle-recorded withdrawal);
108
+ - candidates **healthy/ready when rolled back reported separately** from the rest — a healthy
109
+ candidate rolled back is the lane doubting itself, an unhealthy one is the lane working, and
110
+ conflating them is how "the tooling cried wolf" reads as "the release failed";
111
+ - a **`repeated-failure-owner` finding** when one failure owner accounts for two or more failed
112
+ attempts (high severity at three or more), with the bundle file for each attempt as evidence.
113
+
114
+ It runs where the run-level pass runs: `report.mjs` computes, prints and saves it at a terminal
115
+ step whenever the manifest names the version and `durableEvidenceDir`, and
116
+ `version-postmortem.mjs --dir <d> --version <v>` runs it standalone. It persists into the
117
+ **same series** (`kind: "release-version"` in the shared `index.jsonl`). It reads the durable
118
+ bundles only — never the manifest, never a live service — and it is **tolerant of schema
119
+ drift**: real bundles were written by different releases months apart, so every signal is read
120
+ defensively and an absent one is reported as absent, never invented. A bundle file that cannot
121
+ be parsed is a finding (`unreadable-version-bundle`), not a silent skip; a dir that cannot be
122
+ read refuses, exactly as `evidence-bundle.mjs` does.
123
+
124
+ ### Failure ownership — the normalized classification (#962 consumes this surface)
125
+
126
+ Every attempt that ended `failed-pre-promotion` or `rolled-back` is assigned exactly one owner
127
+ from **`FAILURE_OWNERS`**: `application`, `configuration`, `platform`, `release-tooling`,
128
+ `gate-environment`. `released` is not a failure; `revoked` and `blocked-manual` are operator
129
+ decisions, not outcomes, and classify neither. **This enum, and the derivation order below,
130
+ are a stable contract** — #962 builds on them; renaming a class or changing a rule's meaning
131
+ is a breaking change, not a refactor. A bundle may **declare** its owner (`failureOwner`,
132
+ top-level or in `disposition`); the declaration wins, because whoever records the failure
133
+ closest to it knows more than any heuristic. Everything else is derived, first match wins:
134
+
135
+ | # | Rule | Owner |
136
+ |---|---|---|
137
+ | 1 | `failureOwner` declared on the bundle or its disposition, naming one of the five | the declared owner |
138
+ | 2 | `candidateVerdict.verdict` is `CANNOT-RUN` — an applicable gate that could not run (principle 13): absent credentials, an unavailable external service, a harness that would not start | `gate-environment` |
139
+ | 3 | live or candidate evidence judged the artifact **unhealthy** (DEAD / non-healthy runtime status) — attributed by the recorded reason: secrets/env/config-shaped → `configuration`; quota/platform/deploy-infrastructure-shaped → `platform`; otherwise → `application` (the observed defect belongs to the artifact until the evidence says otherwise) | `application` / `configuration` / `platform` |
140
+ | 4 | live evidence shows the candidate **healthy** (healthy/* runtime status, ALIVE, probe-succeeded) and the attempt failed anyway — the verifier, the normalization, an adapter, a gate's assumption about a valid platform representation | `release-tooling` |
141
+ | 5 | `candidateVerdict.verdict` ALIVE and the attempt still ended in a failure state — the lane stopped a candidate its own gate judged alive | `release-tooling` |
142
+ | 6 | no verdict evidence at all — the lane ended an attempt it never measured | `release-tooling` |
143
+
144
+ Live evidence wins over the pre-exposure verdict (rule 3 before rule 5) because what
145
+ production showed after the shift is the stronger observation (principle 5). Each
146
+ classification carries its `basis` — the rule and the signals used — so a reader can
147
+ disbelieve it against the bundle itself. **The pass does not adjudicate** (the issue's
148
+ non-goal): the finding says where to look, and a mis-classified owner is corrected by
149
+ adjudicating the bundle, not by editing the table mid-incident.
150
+
151
+ Measured against the real Forex `0.1.12` durable bundles at introduction: 9 attempts, 8
152
+ non-released, 6 rolled back, 7 of 8 failures owned by `release-tooling`, 2 candidates rolled
153
+ back at 100% while healthy/ready — the dominant signal the single-manifest pass missed, now
154
+ the headline.
155
+
156
+ ### Failure phase — where the lane stopped (#962)
157
+
158
+ Beside the owner, every failure bundle records a normalized **phase** from
159
+ **`FAILURE_PHASES`**: `pre-candidate` (the attempt ended before any candidate evidence
160
+ exists — promotion/build/probe; also every failure on the `no-deploy` shape, which never has
161
+ a candidate), `candidate-validation` (a candidate was judged at 0% and traffic never moved),
162
+ `post-exposure` (traffic moved and was restored — a rollback, by construction). Derived from
163
+ the bundle's own evidence; a declared `failurePhase` wins, exactly as a declared owner does.
164
+ Same stability rule as the owner enum: the rc circuit breaker records it per qualifying
165
+ attempt, so renaming a phase is a breaking change.
166
+
167
+ **The close-time stamp (#962's half of this surface).** `evidence-bundle.mjs emit` stamps
168
+ `failureOwner` and `failurePhase` onto a failure bundle at close time — rule 1's "declared"
169
+ extension point, exercised by the mechanism that writes the record. An author-declared owner
170
+ (top-level or in the disposition) always wins over the derived stamp; a non-failure terminal
171
+ state is stamped with neither. The stamp is a pure function of the bundle, so a resumed
172
+ re-emit recomputes the identical record and stays the resume-safe no-op immutability
173
+ promises.
174
+
175
+ ## The rc circuit breaker (#962): holding the loop, not the attempt
176
+
177
+ Every per-attempt gate fails closed, and that is correct — but a loop of correct per-attempt
178
+ refusals can still be wrong: Forex `0.1.12` minted nine immutable RCs on tooling-only
179
+ retries. `rc-circuit-breaker.mjs` is the gate over the loop. The lane runs it at step 3,
180
+ **before `version.mjs rc` mints the next immutable RC** (including a `--retry-from` retry —
181
+ [candidate.md](candidate.md)), and records the decision JSON at `release.circuitBreaker` on
182
+ the manifest on every outcome, proceed included: this post-mortem reports whether the
183
+ breaker fired and how it resolved, and an unrecorded check is one nobody can prove ran.
184
+
185
+ **The count.** Prior **non-released** attempts for the same proposed version whose
186
+ normalized failure owner is `release-tooling` or `gate-environment` — the two lane-side
187
+ owners of the table above, read back through the same `classifyFailureOwner`. Application,
188
+ configuration and platform failures are reported in the decision's per-owner breakdown but
189
+ **never consume the threshold** — an artifact defect has its own route, and counting it here
190
+ would hold healthy lanes for the wrong reason.
191
+
192
+ **The threshold.** Default **2** prior qualifying failures. Configurable: `--threshold <n>`
193
+ beats `$RC_CIRCUIT_BREAKER_THRESHOLD` beats the default, and the decision records which
194
+ source set it — configuration is declared in the record, never silent. What no flag or env
195
+ var can do is **skip the check**: the only way past a hold is the authorization below.
196
+
197
+ **The hold.** Crossing the threshold returns fail-closed `hold-for-release-design-review`
198
+ (CLI exit 1) with the count, the per-owner breakdown, the qualifying attempt ids, their
199
+ phases, and the bundle files as evidence links. The check is read-only: **a hold deletes
200
+ nothing, moves no immutable rc tag, and burns no version.** Exit 2 is a refusal — an
201
+ unreadable durable dir or an unparseable bundle means the history cannot be established, and
202
+ a gate that cannot read the history does not get to mint (the same reading
203
+ `evidence-bundle.mjs`'s mint takes).
204
+
205
+ **Continuing** is a human's explicit, recorded decision — not an env bypass. The
206
+ authorization is a structured JSON record passed as `--authorization <file>`:
207
+
208
+ ```json
209
+ {
210
+ "version": "0.1.12",
211
+ "againstCount": 2,
212
+ "authorizedBy": "<the human>",
213
+ "authorizedAt": "<ISO timestamp>",
214
+ "rationale": "why another RC is preferable to simplifying, removing, or externally repairing the gate",
215
+ "alternativesConsidered": { "simplify": "…", "remove": "…", "externalRepair": "…" }
216
+ }
217
+ ```
218
+
219
+ `alternativesConsidered` is optional; every other field is required. `againstCount` binds
220
+ the record to the current qualifying count: **one authorization covers exactly one
221
+ continuation**, and a further qualifying failure re-holds — a stale record never silently
222
+ carries the next retry. A valid record turns the hold into `authorized-continue` (exit 0),
223
+ echoing the record for the manifest; an invalid or stale one leaves the hold standing and
224
+ names why (`authorizationError`).
225
+
226
+ **In the post-mortem.** The run-level profile reads `release.circuitBreaker`: the summary
227
+ line states `did not fire`, `FIRED (n qualifying) — unresolved`, or `fired —
228
+ authorized-continue by <who>`, and `metrics.circuitBreaker` carries `{fired, resolution}`.
229
+ The metric is absent — not null — when no decision was recorded, because an absent record
230
+ and a breaker that did not fire are different statements.