@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,183 @@
1
+ # Mergify Configuration
2
+
3
+ The four settings this skill opinionates, and the checklist that puts them in
4
+ place. Governed by [Rule 1](../SKILL.md#rule-1--fetch-the-docs-before-writing-config):
5
+ fetch the schema before writing any of it, and finish with `validate` **and**
6
+ `simulate`.
7
+
8
+ ## The Four Settings
9
+
10
+ ### 1. Enqueue — auto on ready + review-PASS, manual otherwise
11
+
12
+ A PR **auto-enqueues** the moment it is ready, a review has passed, and CI is
13
+ green. Short of that it stays held, and a human enqueues it by hand with
14
+ `@mergifyio queue`.
15
+
16
+ **How:** set `merge_protections_settings.auto_merge_conditions` to that AND-list.
17
+ Its *presence* enables conditional auto-merge; the conditions scope *which* PRs.
18
+ The manual path is not a second setting — `@mergifyio queue` queues a PR
19
+ regardless of `auto_merge_conditions`, and `merge_conditions` gate on CI only
20
+ (never review), so a hand-queued PR still merges on green without a review.
21
+
22
+ ```yaml
23
+ merge_protections_settings:
24
+ reporting_method: check-runs
25
+ post_comment: true
26
+ # AUTO-enqueue only a ready PR whose review passed; everything else waits for a
27
+ # human `@mergifyio queue`. "review PASS" = ≥1 approval and no open change request.
28
+ # `-draft` belongs HERE (don't auto-queue a draft), but stays OUT of
29
+ # queue_conditions/merge_conditions — see the `-draft` note further down.
30
+ auto_merge_conditions:
31
+ - "-draft"
32
+ - "#approved-reviews-by >= 1"
33
+ - "#changes-requested-reviews-by = 0"
34
+ - *gate
35
+ ```
36
+
37
+ Why this is the default and not bare-manual: scoping auto-merge to a *passed
38
+ review* keeps the routine case one-click-free **without** letting an unreviewed
39
+ PR ride in — the failure a review gate exists to stop. It also avoids the mirror
40
+ failure: the retired "auto-queue every non-draft PR" form had no review arm, so a
41
+ green-but-unreviewed PR merged and a reviewer requesting changes could lose the
42
+ race to the queue. The review arm is the point; the manual hatch (above) covers
43
+ the "land it without waiting" case.
44
+
45
+ **To go fully manual instead** (queueing the only entry), omit
46
+ `auto_merge_conditions` entirely and say so in a comment — an omitted key reads as
47
+ an oversight and someone will "fix" it back.
48
+
49
+ Do **not** use `merge_protections_settings.auto_merge: true` or
50
+ `queue_rules[].autoqueue: true` — both are deprecated.
51
+
52
+ Developers enqueue by commenting `@mergifyio queue`, or by ticking the checkbox
53
+ Mergify renders in its queue status comment (`merge_queue.queue_controls_comment`,
54
+ default `true`). Mergify acknowledges a command with a 👍 reaction; **no 👍 means
55
+ the command was not processed**, which is a different problem from a PR that
56
+ failed its conditions.
57
+
58
+ ### 2 & 3. Batch mode, size 3
59
+
60
+ ```yaml
61
+ queue_rules:
62
+ - name: default
63
+ batch_size: 3
64
+ batch_max_wait_time: 30 seconds
65
+ batch_max_failure_resolution_attempts: 10
66
+ merge_queue:
67
+ max_parallel_checks: 3
68
+ ```
69
+
70
+ **The default this skill ships is `batch_size: 3`** — written explicitly, never
71
+ assumed: an unset `batch_size` silently runs the queue at Mergify's built-in
72
+ default of 1, i.e. no batching, so the key being present is the act that turns
73
+ batching on. With it set, Mergify does not simply test "the next three in the
74
+ queue": priority and
75
+ queue order seed a batch, **stacks always stay together**, and similarity (shared
76
+ scopes, then changed directories, then queue time) fills the remaining slots.
77
+ Related PRs enqueued together reliably ride one batch — that is how the queue
78
+ collects a delivery — while unrelated ones may split, deliberately, so one PR's
79
+ failure cannot drag unrelated work into a bisection. When a batch goes red,
80
+ Mergify **bisects** it — splitting and retesting until it isolates the culprit,
81
+ then dequeuing only that PR. Innocent PRs stay in the queue. `max_parallel_checks`
82
+ caps how many splits run at once, so match it to `batch_size`; more is wasted.
83
+
84
+ `batch_max_wait_time` is the knob that decides how eagerly the queue collects a
85
+ batch, and it is a straight latency tax on a **lone** PR — Mergify waits the full
86
+ window before giving up on company. Where PRs arrive one at a time, keep it at
87
+ seconds, not minutes. Where delivery is bursty — an epic landing a stack, a set
88
+ of related PRs expected together — a longer window (a few minutes) buys the batch
89
+ (N PRs, one CI run) at the cost of a slightly slower lone PR. Set it from how PRs
90
+ actually arrive, not from either extreme.
91
+
92
+ **Two hard requirements:**
93
+
94
+ - GitHub branch protection **"Require branches to be up to date before merging"
95
+ must be DISABLED.** Batches cannot merge with it on — the original PRs are not
96
+ up to date by GitHub's reckoning even though the batch was tested against base.
97
+ This is the single most common reason a batched queue never merges anything.
98
+ - Batching disables **in-place checks**. In-place is only active when all three
99
+ hold: `max_parallel_checks == 1`, every queue has `batch_size == 1`, *and*
100
+ every queue CI is single-step (no extra `merge_conditions` — either empty or
101
+ identical to `queue_conditions`). A two-step CI like this repo's `*gate` +
102
+ `*queue_gate` fails the third condition even at `batch_size 1`. Everything
103
+ downstream of that (below) follows from it.
104
+
105
+ Revisit the size from evidence, not taste: if batches go red often enough that
106
+ bisection is the normal path, the changes are less independent than assumed and
107
+ 3 is too wide.
108
+
109
+ ### 4. Enforce head (freshness against base)
110
+
111
+ ```yaml
112
+ queue_rules:
113
+ - name: default
114
+ update_method: rebase # rebase | merge
115
+ ```
116
+
117
+ **Read this before relying on it.** `update_method` governs how a PR is updated
118
+ with its base branch **when the check is done in place** — and in-place checks
119
+ are off whenever `batch_size > 1`. Under `batch_size: 3` the option is inert.
120
+
121
+ That is not a gap. With batching, Mergify builds a **queue branch** of
122
+ `base + batch` and runs CI there, so what gets tested is always fresh against
123
+ base by construction. The PR's own branch simply is not what gets tested.
124
+
125
+ Keep `update_method: rebase` in the config anyway, so dropping to `batch_size: 1`
126
+ does not silently change behaviour — but comment it, or the next reader will
127
+ believe it is doing work it is not.
128
+
129
+ **If you need the PR branch itself kept fresh** (e.g. contributors run local
130
+ checks against it), that is a separate lever:
131
+
132
+ | Want | Use |
133
+ |------|-----|
134
+ | Fresh tree under test | Nothing — the queue branch already gives you this |
135
+ | Rebase the PR branch, on demand | `@mergifyio update` or `@mergifyio rebase` comment |
136
+ | Rebase the PR branch, automatically | a `pull_request_rules` entry with the `update` or `rebase` action |
137
+ | Fresh head at enqueue time | `#commits-behind = 0` in `queue_conditions` (blocks entry rather than fixing it) |
138
+
139
+ Automatic updating fights the queue — every update retriggers the PR's CI — so
140
+ prefer on-demand unless you have a reason.
141
+
142
+ ## Setup Checklist
143
+
144
+ 1. Fetch the schema and the relevant doc pages.
145
+ 2. Copy `assets/templates/mergify.yml` → `.mergify.yml`; replace placeholders.
146
+ 3. Name the real CI check in the `gate` anchor; add the `or` exemption **only**
147
+ if that check is path-filtered, and list the workflow file's own path.
148
+ 4. `mergify config validate`.
149
+ 5. `mergify config simulate <PR-URL>` against a real PR — ideally one that hits
150
+ the exempt path, since that is where configs break.
151
+ 6. **Audit against this skill's default:**
152
+ `node ~/.agents/skills/mergify/scripts/audit.mjs --file .mergify.yml`.
153
+ (Absolute, like the watcher above: you run this from the repo being audited, where a
154
+ CWD-relative `scripts/…` does not resolve.)
155
+ It checks the shape `config validate` can't — enqueue policy (hybrid vs the retired
156
+ indiscriminate auto-queue), `branch_protection_injection_mode: merge`, the
157
+ define-once/assert-many gate anchor, `-draft` misplaced in queue/merge
158
+ conditions, and batch mode actually on (`batch_size: 3` — this skill's
159
+ default; an unset `batch_size` runs at Mergify's built-in 1, so batching
160
+ silently off is a real state). It **abstains** rather than guess, so a `?` means "check by hand," not
161
+ "fine." `--json` for machine output; exits non-zero on any warn (`--allow-warn` to
162
+ downgrade). Runs on the committed `.mergify.yml`, so it fits a pre-merge check.
163
+ 7. Confirm Mergify's app id: `gh api /apps/mergify --jq .id`.
164
+ 8. `gh ruleset list` — if a ruleset already covers this branch, fold these rules
165
+ into it (**PUT**) rather than stacking a second one. Overlapping rulesets are
166
+ additive and the most restrictive wins, which is a confusing way to be blocked.
167
+ 9. Inventory anything that pushes to the protected branch — the `update` rule
168
+ will block it. Add each as a bypass actor (GitHub Actions = `15368`):
169
+ `grep -rnE 'git push|create-pull-request|github-script' .github/workflows/`
170
+ 10. Apply `assets/templates/ruleset.json`. Dry-run with
171
+ `"enforcement": "evaluate"` first if you want to see what it would block —
172
+ read the results from `rule-suites`.
173
+ 11. Prove the allowed path **before** the blocked one: enqueue a PR with
174
+ `@mergifyio queue`, confirm the 👍, and watch `mergify queue show <PR> -v`
175
+ through to merge. A ruleset strict enough to block humans can also block
176
+ Mergify if a bypass id is wrong, and you would rather learn that while you
177
+ can still merge the fix.
178
+ 12. Now prove the blocked path: open a throwaway PR and try to merge it without
179
+ enqueuing, as a non-bypass user. GitHub must refuse, naming the ruleset.
180
+ 13. Decide the escape hatches: keep `priority_rules` (jump the line), and either
181
+ keep or delete the `emergency-merge` rule (skips CI, cannot be limited to
182
+ admins). Keep the `OrganizationAdmin` bypass either way — it is the only way
183
+ back in if Mergify has an outage.
@@ -0,0 +1,73 @@
1
+ # Diagnosing A Stuck PR
2
+
3
+ Two tools, two different questions — and when they disagree, the disagreement is
4
+ itself the diagnosis. For watching a PR through to merge (a different job, with
5
+ its own rules) see [`watch-contract.md`](watch-contract.md).
6
+
7
+ ## Querying PR and Queue Status
8
+
9
+ ### Mergify's own view — why a PR is or isn't moving
10
+
11
+ ```bash
12
+ mergify queue status # all batches + waiting PRs
13
+ mergify queue status --branch main # one branch
14
+ mergify queue status --json # parseable
15
+ mergify queue show <PR> # one PR: position, priority, CI state
16
+ mergify queue show <PR> -v # + full checks table and conditions tree
17
+ mergify queue show <PR> --json
18
+ ```
19
+
20
+ `mergify queue show <PR> -v` is the one to reach for. The conditions tree shows
21
+ which conditions are met and **which single one is blocking** — that is the
22
+ answer to "why is this stuck", and nothing else in the toolchain reports it.
23
+
24
+ Queue states worth recognising:
25
+
26
+ | State | Meaning |
27
+ |-------|---------|
28
+ | `waiting_for_batch` | Queued, not yet picked into a batch |
29
+ | `waiting_for_previous_batches` | Blocked on an earlier batch |
30
+ | `preparing` / `running` | Batch is being set up / running CI |
31
+ | `bisecting` | Batch failed; splitting to find the culprit |
32
+ | `failed` | CI failed for this batch |
33
+ | `waiting_for_merge` | CI passed, waiting on GitHub |
34
+ | `waiting_schedule` | Outside the configured merge schedule |
35
+ | `frozen` | Queue is paused |
36
+
37
+ ### GitHub's view — the raw checks and merge state
38
+
39
+ ```bash
40
+ gh pr checks <PR> # check-run table, exit 1 if any failed
41
+ gh pr checks <PR> --watch # block until they settle
42
+ gh pr view <PR> --json state,isDraft,mergeable,mergeStateStatus,statusCheckRollup
43
+ gh pr view <PR> --json labels --jq '.labels[].name' # queued / dequeued labels
44
+ ```
45
+
46
+ Mergify labels a PR `queued` on entry and `dequeued` on removal
47
+ (`merge_queue.queued_label` / `dequeued_label`). These are **lifecycle** signals,
48
+ not a cheap membership check — what they mean depends on what you saw before
49
+ them, which is why reading them is the watcher's job and not a one-off `gh`
50
+ call. See [`watch-contract.md`](watch-contract.md).
51
+
52
+ ### Which one to ask
53
+
54
+ Use **both, in this order** — they answer different questions and disagreeing
55
+ answers are themselves the diagnosis:
56
+
57
+ 1. `mergify queue show <PR> -v` — is it in the queue, and what condition blocks it?
58
+ 2. `gh pr checks <PR>` — is the underlying CI actually green?
59
+
60
+ A check that is **green in GitHub but unmet in Mergify** means the condition
61
+ names the wrong check. A check that is **absent** (never created, e.g. filtered
62
+ out by the workflow's `paths:`) reads as permanently unmet — see the trap below.
63
+
64
+ ### Pause / resume
65
+
66
+ ```bash
67
+ mergify queue pause --reason "production incident — halting merges"
68
+ mergify queue unpause
69
+ ```
70
+
71
+ Pausing does not cancel running CI; it stops new merges. The reason is visible
72
+ to everyone in `queue status`. Add `--yes-i-am-sure` in scripts.
73
+
@@ -0,0 +1,78 @@
1
+ # Traps
2
+
3
+ These are the ones that cost real time. Each has been hit in production.
4
+
5
+
6
+ **A path-filtered check is *absent*, not failing.** If the workflow has an
7
+ `on.pull_request.paths:` filter, a PR touching none of those paths never creates
8
+ the check — and `check-success = <name>` can never be satisfied. Those PRs sit in
9
+ the queue forever with nothing reporting why. Write the condition as an `or` with
10
+ an explicit exemption (see the template's `gate` anchor), and **include the
11
+ workflow file's own path in the exemption** — otherwise a workflow-only PR takes
12
+ the exempt branch and can merge with the gate red, which is precisely the change
13
+ most able to disable the gate.
14
+
15
+ **Never write `success_conditions: [queue-name = default]`** in a
16
+ `merge_protections` rule to force merges through the queue. Merge-protection
17
+ conditions are injected into the queue's **entry** conditions, while the GitHub
18
+ ruleset requires the resulting check **before** entry — entry waits on a check
19
+ that cannot go green until after entry, and the repository deadlocks with every
20
+ PR parked at "Waiting for queue conditions". `branch_protection_injection_mode:
21
+ merge` does **not** prevent this; it governs GitHub-side injection, not Mergify's
22
+ own merge protections. Queue-only merges are a **GitHub ruleset** job, not a
23
+ Mergify-config job — use the `update` rule, as in
24
+ [Enforcing Queue-Only Merges](branch-protection.md#enforcing-queue-only-merges).
25
+
26
+ **A required status check does not make the queue mandatory.** Once it goes
27
+ green, anyone with write access can merge directly and the queue never sees the
28
+ PR. Only the `update` rule stops that.
29
+
30
+ **`update` locks out your own automation.** Enabling it without adding the
31
+ GitHub Actions app (or whatever pushes to the branch) as a bypass actor turns
32
+ every release/version-bump job red. Inventory pushers before you enable it.
33
+
34
+ **Do not make Mergify the sole bypass actor without a break-glass path.** If
35
+ Mergify is the only actor that can write to the branch and Mergify has an
36
+ outage, nobody can merge the fix either. Keep `OrganizationAdmin` bypass.
37
+
38
+ **Define the gate once, assert it three times.** The queue-entry gate, the
39
+ queue-merge gate and the GitHub-required merge protection must be the same YAML
40
+ anchor. Three hand-written copies drift, and the drift shows up as an
41
+ unmergeable PR, not as an error.
42
+
43
+ **`merge_conditions` re-asserting `queue_conditions` is not redundant.** Mergify
44
+ tests the batch merged with its base — a tree that existed when no individual
45
+ PR's CI ran.
46
+
47
+ **`-draft` is deliberately absent** from `queue_conditions` and
48
+ `merge_conditions`. Under manual enqueue nothing queues a draft by accident, and
49
+ a leftover draft condition presents as "queued, no reason given". Know what the
50
+ absence costs before you re-add it or rely on it: GitHub cannot merge a draft PR,
51
+ so a draft that *is* enqueued rides a whole batch CI run and then fails at the
52
+ merge call, taking the batch's innocent PRs through that failure. Marking a
53
+ queued PR as draft no longer dequeues it either — `@mergifyio dequeue` does.
54
+
55
+ **Stop drafts at *creation*, not just at the queue.** The paragraph above is the
56
+ queue side — it does not stop a PR being *born* a draft, and the queue does not
57
+ filter drafts out. So the guard belongs where PRs are opened. `mergify stack`
58
+ opens each PR per its `--draft` flag, falling back to git config
59
+ `mergify-cli.stack-create-as-draft`, which is `false` when unset and only counts
60
+ the literal string `"true"`. The failure mode is therefore a **global**
61
+ `mergify-cli.stack-create-as-draft=true` inherited into every repo, silently
62
+ drafting every agent-opened PR. Pin it off in the repo so no inherited value can
63
+ flip it:
64
+
65
+ ```bash
66
+ git config mergify-cli.stack-create-as-draft false
67
+ ```
68
+
69
+ An explicit `--draft` on the `mergify stack` invocation still overrides this, so
70
+ also confirm no wrapper or alias passes it. (Deliberate drafting is a separate
71
+ case: `resolve-issues`' integration gate converts per-unit PRs to draft *after*
72
+ opening the combined PR — that is intended and not what this guard is about.)
73
+
74
+ **Under a required Mergify check, set `branch_protection_injection_mode: merge`.**
75
+ The default (`queue`) injects ruleset requirements into entry conditions too, and
76
+ a Mergify check that deliberately fails pre-queue then blocks the enqueue that
77
+ would make it pass.
78
+
@@ -0,0 +1,218 @@
1
+ # Mergify Delivery Watch Contract
2
+
3
+ Use this contract whenever an agent queues or watches a PR through Mergify, whether the work is a full `resolve-issues` delivery or a simple PR. The goal is to avoid the recurring false states where a dequeued PR is reported as healthy, a not-yet-admitted PR is reported as failed, or a stale GitHub check-rollup is treated as Mergify queue truth.
4
+
5
+ ## A Watch Observes A Progression, Not A State
6
+
7
+ Read this before the state machine — it is the premise the rest of the file rests on, and skipping it is how a correct-looking watcher gets built anyway.
8
+
9
+ The queue lifecycle is a **sequence**, and every verdict worth having is a *transition* between its phases, never a property of a single reading:
10
+
11
+ ```
12
+ NOT_YET_QUEUED ──enqueue accepted──▶ QUEUED ──┬──▶ MERGED
13
+ │ ├──▶ DEQUEUED
14
+ └──▶ STALLED (never admitted) └──▶ CLOSED_UNMERGED
15
+ ```
16
+
17
+ The identical observation means opposite things on either side of queue entry:
18
+
19
+ | Reading | Before entry | After entry |
20
+ |---|---|---|
21
+ | active queue membership absent | `NOT_YET_QUEUED` — healthy, keep waiting | **`DEQUEUED`** — terminal, read the reason |
22
+
23
+ So the watcher's memory *is* the verdict. Four pieces of state must survive across polls:
24
+
25
+ 1. **`seenQueued`** — has queue entry ever been observed? Every downstream transition depends on it. Set from the `queued` label **or** `mergify queue show`.
26
+ 2. **A re-read counter** (`missingActiveReads` / `absentStreak`) — one absence is suspicion, two consecutive *readable* absences settle `DEQUEUED`.
27
+ 3. **Readable vs. absent** — a failed `gh`/`mergify` call is *no information* and must not advance that counter.
28
+ 4. **A change clock** (`lastChangeMs`) — the stall bound is *no observable change*, not elapsed time.
29
+
30
+ ### Why a per-poll predicate cannot satisfy this
31
+
32
+ A background-task runner that re-executes a command each tick and reads its exit code is a **stateless predicate evaluator**. There is nowhere to keep items 1–4, so the shape is wrong before the code is. This is not theoretical — pi session `019fae8f` produced four hand-rolled watchers for one PR, and each failed a different one of the four:
33
+
34
+ | Hand-rolled watcher | Failure |
35
+ |---|---|
36
+ | `state ∈ {MERGED, CLOSED}` | A dequeued PR is plain `OPEN`. Dequeue is structurally invisible; the watch runs to its ceiling reporting health. |
37
+ | `.queued // false` | The JSON-shape trap below — never observes entry, so item 1 stays false and the whole dequeue path is dead code. |
38
+ | single absent read → terminal | No item 2. Settled `DEQUEUED` on a PR that was mid-`checking`. |
39
+ | `mergify … \|\| printf '{"queued":false}'` | No item 3. Converts an outage into a false dequeue, *faster* than the stall bound rather than through it. |
40
+
41
+ Two of the four were written *after* the agent had been told to check for dequeue, and one of those reported a false `DEQUEUED` on a healthy PR. Knowing the requirement does not rescue the shape.
42
+
43
+ **Therefore: run [`../scripts/watch-pr-delivery.mjs`](../scripts/watch-pr-delivery.mjs).** It is one long-lived process owning its own poll loop, so items 1–4 live in its locals. Do not wrap it in an outer poller — that reintroduces exactly the shape it exists to replace.
44
+
45
+ ### `phases` — the progression as output
46
+
47
+ The watcher emits `phases`: the ordered list of lifecycle phases actually observed, on both the terminal verdict and every heartbeat. It resolves the one ambiguity the terminal outcome cannot:
48
+
49
+ | terminal | `phases` | meaning | fix |
50
+ |---|---|---|---|
51
+ | `STALLED` | `["NOT_YET_QUEUED","STALLED"]` | never admitted (`reason` is usually `admission-stall`) | enqueue it, or diagnose why admission never happened |
52
+ | `STALLED` | `["NOT_YET_QUEUED","QUEUED","STALLED"]` | admitted, then wedged in the queue (`reason` is usually `stall-bound`) | read the blocking condition |
53
+
54
+ `WAITING` is deliberately not a phase — it is the re-read suspicion state *inside* `QUEUED`, and recording it would oscillate a healthy PR's progression into noise.
55
+
56
+ ## Enqueue Before You Watch
57
+
58
+ The watch observes only — it never posts `@mergifyio queue`. Check membership first, because on a repo configured for manual delivery the answer decides whether a watch is worth starting at all:
59
+
60
+ ```bash
61
+ mergify queue show <PR> --json | jq '{position, queue_rule_name, queued_at}' # membership now
62
+ mergify queue show <PR> --json | jq '.queue_rule.config.autoqueue' # can it self-queue?
63
+ grep -n "auto_merge_conditions" .mergify.yml # or auto-enqueue?
64
+ ```
65
+
66
+ Read `autoqueue` from the **resolved** config Mergify reports, not from `.mergify.yml`. The authorable key is deprecated, so a correct config never contains it and grepping the file always finds nothing — which tells you nothing. `queue show` reports the effective value either way.
67
+
68
+ If the PR is not queued, `autoqueue` is `false`, **and** the repo has no `auto_merge_conditions`, nothing will ever put it in the queue on its own. A watch started there can only end `STALLED` — enqueue first, then watch. A repo whose `.mergify.yml` says *"delivery stays manual"* is exactly this case.
69
+
70
+ A 👍 on the command comment acknowledges the *command*, not queue entry.
71
+
72
+ ## Batch Delivery — Enqueue The Whole Ready Set Together
73
+
74
+ Batch mode is configured at the queue level (`batch_size`, `batch_max_wait_time`), but whether a batch ever *happens* is a delivery decision. Mergify only batches PRs that are **in the queue together**: it holds the gate open for `batch_max_wait_time`, then tests up to `batch_size` PRs as one CI run. So the batching lever in an agent's hands is *enqueue timing and grouping* — not config. (Config's part is one line: write `batch_size: 3` — the skill's default. It must be explicit: an unset `batch_size` silently runs at Mergify's built-in default of 1, i.e. no batching — see [`configuration.md`](configuration.md#2--3-batch-mode-size-3).)
75
+
76
+ **The default delivery pattern is to enqueue the whole ready set in one pass.** When several PRs against the same base are merge-ready — a stack, an epic's units, a burst of related edits — enqueue them all before watching any:
77
+
78
+ - One batch CI run for N PRs instead of N. Serial delivery — enqueue #1, watch it merge, then enqueue #2 — means each PR rides its own batch: N CI runs and N watch cycles for work that could have landed in one. Being in the queue together is never slower; it is usually several CI runs faster.
79
+ - A red batch is safe: Mergify bisects it and dequeues only the culprit; the innocent PRs carry on.
80
+ - **"If possible" is the escape hatch, not the rule.** A lone PR, or a set where the rest still wait on review/CI/conflict, is enqueued as-is — `batch_max_wait_time` bounds the wait for company, and a ready PR is never held back for a straggler.
81
+
82
+ **Mergify groups PRs into a batch by relationship, not queue order.** It does not simply take the next N in the queue: priority and queue order seed a batch, **stacks always stay together**, and similarity (shared scopes, then changed directories, then queue time) fills the remaining slots. Consequences:
83
+
84
+ - Related PRs enqueued together reliably ride one batch — that is the pattern to aim for.
85
+ - Unrelated PRs may still split across batches. That is Mergify's choice and it is correct: it keeps one PR's failure from dragging unrelated work into a bisection. Do not force co-batching (e.g. by inventing scopes) to fight it.
86
+
87
+ **Mechanics.** `@mergifyio queue` is per-PR — it is a comment posted on the PR and takes no PR list — so "together" means posting it on each ready PR in the same pass, then watching: one `watch-pr-delivery.mjs` per PR, launched together (they are cheap at the default interval), or the batch as a whole via `mergify queue status`. Enqueue promptly once a PR is ready: the batch window is the fill time, and a PR entering after it closes rides the *next* batch.
88
+
89
+ While a batch forms, `waiting_for_batch` just means the gate is still open — keep watching, do not read it as stuck. `bisecting` means the batch went red and Mergify is splitting it — keep watching; only the culprit leaves the queue.
90
+
91
+ ## Source Of Truth
92
+
93
+ Use Mergify and GitHub for different facts:
94
+
95
+ 1. `gh pr view <PR> --json state,mergedAt,mergeCommit,labels,url,comments`
96
+ - authoritative for whether the PR merged or closed
97
+ - source for Mergify lifecycle labels: `queued` and `dequeued`
98
+ - `comments` is not optional padding: it is the only source for *when* a `dequeued` label was applied (Mergify's status-comment payload), which is what distinguishes this attempt's dequeue from a stale label left by a previous one. Omit it and that guard silently degrades to "no reliable timestamp" on every poll. It costs no extra request — same call.
99
+ 2. `mergify queue show <PR> --json`
100
+ - authoritative for active queue membership, position, queue rule, CI state, and condition tree
101
+ 3. `gh pr checks <PR>` / check-rollup
102
+ - useful for debugging CI, but not authoritative for queue membership
103
+
104
+ Do not use `Mergify Merge Queue` check-rollup state alone to decide queued/dequeued status. It can be stale, neutral, or in progress while the queue tool says something more precise.
105
+
106
+ ## State Machine
107
+
108
+ A watcher must retain history for the PR it is watching.
109
+
110
+ - `MERGED`: PR state is `MERGED` or `mergedAt` is set. Return the PR merge commit SHA, not the branch head. The caller still owes ancestry verification.
111
+
112
+ On this path Mergify removes `queued` **without ever adding `dequeued`** — so a watcher that waits for a `dequeued` label to decide anything waits forever on the happy path. The label is also a lifecycle mirror that is never cleared on re-enqueue, so its mere presence is not this attempt's outcome; date it against the watch's start before treating it as terminal (see the `#629` guards in the script).
113
+ - `CLOSED_UNMERGED`: PR state is `CLOSED` with no merge. Terminal hand-back.
114
+ - `QUEUED`: `queued` label is present, or `mergify queue show --json` reports active membership. Set `seenQueued = true`.
115
+
116
+ **Do not test for `queued: true` — that field does not exist on a queued PR.** Verified live against PR #1396: a PR *in* the queue returns `position`, `queue_rule_name`, `queued_at`, and `queue_rule` — and no `queued` key at all. Only the *not*-queued answer is literally `{"number": <PR>, "queued": false}`. A watcher keyed on `queued === true` therefore never observes queue entry, so `seenQueued` stays false forever and every downstream transition that depends on it (`DEQUEUED`, the re-read rule) is silently dead. Infer membership the way the bundled script does:
117
+
118
+ ```js
119
+ const queued = parsed.queued === true || Boolean(parsed.queue_rule_name || parsed.queue_rule)
120
+ ```
121
+ - `DEQUEUED`: PR is still open and unmerged, and either:
122
+ - `dequeued` label is present (this-watch timestamp guards apply — see `#629`), or
123
+ - **process-local** membership was observed (`everActive`) and is now absent across two consecutive readable re-reads (`lost-queue-membership`).
124
+ - `NOT_YET_QUEUED`: active queue membership is absent before this process has observed membership. This is not a dequeue. It usually means the queue command has not been processed yet, the PR is waiting for queue conditions, or Mergify status has not refreshed. **`--seen-queued` alone does not leave this state** — that flag is a resume *hint* for callers/phases; it does not prove this process saw membership (epic-824 `#859` first watch false-`DEQUEUED` when the counter started from the seed).
125
+ - `WAITING`: membership was observed, then one readable absence — re-read suspicion, not terminal.
126
+ - `STALLED`: no observable queue/admission state has changed for the configured stall bound. Terminal hand-back, not a merge failure. The bound is **phase-aware**: pre-admission (never observed membership this process) uses the shorter `--admission-stall-minutes` (default 3) and reports `reason: "admission-stall"`; after membership has been observed, the full `--stall-minutes` (default 10) applies and reports `reason: "stall-bound"`. Same terminal outcome and exit code either way — only wall time and `reason` change. A dead admission path (forgotten enqueue, stale Mergify status, unreadable queue API) must not burn the post-entry bound. An `admission-stall` while Mergify is visibly waiting on a green-or-pending admission check is often a **too-short window**, not a broken enqueue — relaunch with the CI-gated 15m pair above rather than treating 60m as the next step.
127
+
128
+ The critical invariant: after a watcher has **observed** queue entry in this process (`everActive`), it must never keep reporting `queued:false` as a routine healthy snapshot. If the PR did not merge, loss of queue membership is a queue exit. Seeding `seenQueued` via `--seen-queued` before the first successful membership poll must not invent that observation.
129
+
130
+ ## Re-read Rule
131
+
132
+ GitHub can briefly expose neither `queued` nor `dequeued` during a label transition. Treat a single missing-label read **after process-local membership** as suspicion, then re-read. Two consecutive missing active-membership reads settle the transition as `DEQUEUED` unless the PR merged in the meantime. Absences before `everActive` stay `NOT_YET_QUEUED`.
133
+
134
+ ## Dequeue Reason
135
+
136
+ Record both fields when the queue gives you a reason:
137
+
138
+ - `rawReason`: the provider's exact string or visible status text
139
+ - `reason`: the routing class used by the caller
140
+
141
+ Suggested routing classes:
142
+
143
+ | raw reason shape | route |
144
+ |---|---|
145
+ | `checks-failed` | fix round, fresh verdict, new enqueue |
146
+ | `pull-request-updated`, `draft-pull-request-changed` | HEAD moved; verdict stale; re-enter delivery after fresh validation |
147
+ | `conflict`, `conflict-with-base-branch` | rebase/evidence refresh, then enqueue |
148
+ | `conditions-unmet`, `frozen`, `manual`, `pr-manually-dequeued`, `checks-timeout` | hand back with the condition/reason quoted |
149
+ | unknown | hand back; fail closed |
150
+
151
+ When the reason is not machine-readable, quote the Mergify status comment, queue show condition leaf, or event-log URL in `rawReason` rather than inventing a reason.
152
+
153
+ ## Watcher Requirements
154
+
155
+ - Run as one background task or one script invocation; do not spend foreground turns polling.
156
+ - Retry transient `gh`, GraphQL, and Mergify CLI failures. EOF, TLS handshake timeouts, and temporary API failures are not dequeue signals — an unreadable read is *no information*, not a "not queued" observation, and must not advance the counter that settles `DEQUEUED`.
157
+ - Bound every subprocess read on its own timeout, in addition to the stall/ceiling bounds below — those are only evaluated after a poll returns, so an unbounded read can hang the watcher past both of them.
158
+ - **Run the bundled script. Do not hand-roll a watcher** — see the progression section above for why the hand-rolled shape cannot hold the contract, whatever care goes into the code. (If you are reading a legacy shell watcher: avoid shell-reserved variable names such as `status`.)
159
+ - Keep enqueue and watch separate. A watch observes only; it does not post `@mergifyio queue`, `@mergifyio dequeue`, or merge.
160
+ - Confirm the PR is queued, or can become queued, *before* launching the watch — see [Enqueue Before You Watch](#enqueue-before-you-watch). A watch that cannot enqueue, on a PR nothing else will enqueue either, can only end `STALLED`.
161
+ - Emit an observation record every poll, not only on the terminal verdict. `NOT_YET_QUEUED` and `QUEUED` are both non-terminal and cover the entire healthy path, so a watcher that speaks only at the end is silent for essentially its whole life — indistinguishable, to the caller trying to decide whether delivery is progressing, from a hung or dead process. Keep the heartbeat off the machine-contract stream: stdout carries exactly the one terminal JSON document callers parse, stderr carries the per-poll records.
162
+ - Bound on stalled state, not total elapsed time alone. `waiting_for_previous_batches` can be slow but healthy if position/state changes.
163
+
164
+ ## Script
165
+
166
+ Use the bundled watcher for simple PR delivery checks:
167
+
168
+ ```bash
169
+ node ~/.agents/skills/mergify/scripts/watch-pr-delivery.mjs \
170
+ --repo <owner>/<repo> \
171
+ --pr 1302
172
+ ```
173
+
174
+ Options:
175
+
176
+ | flag | default | meaning |
177
+ |---|---:|---|
178
+ | `--interval-seconds` | `90` | poll interval |
179
+ | `--stall-minutes` | `10` | post-entry terminal stall bound — no observable delivery-state change for this long *after queue membership has been observed* is `STALLED` with `reason: "stall-bound"`. A *no-change* bound, not elapsed time: any movement in position, label, or queue state resets it, so a slow-but-healthy queue is never cut off by it |
180
+ | `--admission-stall-minutes` | `3` | pre-admission terminal stall bound — no observable change *before this process has observed membership* is `STALLED` with `reason: "admission-stall"`. Shorter on purpose: after enqueue, queue entry should appear quickly, and a dead admission path must not wait out the full post-entry bound. Capped at `--stall-minutes` so a caller that shortens the post-entry bound keeps both phases tight. **When Mergify admission itself waits on CI** (status still "waiting on PR admission gate" / warehouse lane / similar), keep the 3m default for the first watch, then relaunch with **`--admission-stall-minutes 15` and `--stall-minutes` ≥ 15** — the admission bound cannot exceed the post-entry stall. Do **not** jump to `--max-minutes 60` for that; 60 is only the absolute ceiling |
181
+ | `--max-minutes` | `60` | absolute safety ceiling; bounds the watch even while the queue keeps reporting change, so a busy queue whose position keeps moving cannot keep the stall clock resetting forever. **60 minutes is also a hard maximum** — a larger value exits `2` rather than being silently clamped, because a caller that believes it has a four-hour watch and has one hour is worse off than one told plainly to relaunch. A batch takes minutes, so an hour of continuous movement without finishing is already a queue to hand back to a human. The sibling `resolve-issues` watcher enforces the same ceiling; a cap honoured by one of two watchers is not a cap |
182
+ | `--quiet` | off | suppress the per-poll progress heartbeat on stderr |
183
+
184
+ Output streams:
185
+
186
+ | stream | content |
187
+ |---|---|
188
+ | stdout | exactly one pretty-printed terminal JSON verdict, at exit — carrying `phases`, `seenQueued`, and the outcome |
189
+ | stderr | one compact JSON record per poll (`progress: true`), with the live outcome, PR labels, queue membership/position, `phases`, `changed`, and `sinceChangeMs` — the stall clock made visible |
190
+
191
+ Each subprocess read (`gh pr view`, `mergify queue show`) also carries its own bounded
192
+ timeout — a small multiple of `--interval-seconds`, clamped to never exceed the **effective
193
+ phase stall bound** (pre-admission: `--admission-stall-minutes`; post-entry: `--stall-minutes`)
194
+ capped by `--max-minutes`, then divided by the number of reads made per poll (2 here:
195
+ `gh pr view` + `mergify queue show`). The two reads together cannot outlive the bound that will
196
+ actually fire `STALLED` on this poll — so a hung pre-admission poll cannot outlive the short
197
+ admission clock, and a post-entry poll is not permanently clamped to the admission value.
198
+ Those stall/ceiling bounds are only evaluated after a poll returns, and a single hung read
199
+ cannot alone consume more than half of a small stall/ceiling bound. (The sibling watcher makes
200
+ one read per poll, so its clamp divides by 1 and the single-read guarantee is the whole bound
201
+ there.)
202
+
203
+ The `--interval-seconds` default is already set for scale. Two API calls per poll at 90s is
204
+ 80/hour per watched PR — roughly 60 concurrent watchers before an authenticated GitHub token's
205
+ 5,000/hour limit is in range, which comfortably covers an epic-sized `resolve-issues` delivery.
206
+ (At the former 30s default it was 240/hour, or about 20 watchers.) The cost is at most 90s of
207
+ extra merge-detection latency against a queue that takes minutes per batch — a cheap trade.
208
+ Shorten it only for a single foreground watch where that latency actually matters.
209
+
210
+ Exit codes:
211
+
212
+ | code | meaning |
213
+ |---:|---|
214
+ | 0 | merged; JSON includes `outcome:"MERGED"` and `mergedSha` |
215
+ | 4 | terminal but not merged: `DEQUEUED`, `CLOSED_UNMERGED`, or `STALLED` |
216
+ | 2 | usage error |
217
+
218
+ The script emits JSON on stdout. The caller is responsible for recording the delivery attempt/outcome and proving ancestry for `MERGED`.