@1aboveio/skills 0.19.3 → 0.20.1

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 (197) hide show
  1. package/README.md +9 -4
  2. package/package.json +1 -1
  3. package/runtime/skills/distribution/generated/recipes.json +54 -48
  4. package/runtime/skills/distribution/scripts/bundles.mjs +113 -7
  5. package/runtime/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +4 -6
  6. package/runtime/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +3 -36
  7. package/skills/cicd-pipeline/mergify/SKILL.md +1 -1
  8. package/skills/engineering/engineering-runtime/coherence/workflow.json +14 -83
  9. package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +4 -6
  10. package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +3 -36
  11. package/skills/engineering/smoke/SKILL.md +1 -1
  12. package/skills/payment/payment-analysis/SKILL.md +5 -3
  13. package/skills/payment/tld-detection/SKILL.md +163 -0
  14. package/skills/payment/tld-detection/agents/openai.yaml +9 -0
  15. package/skills/payment/tld-detection/evals/evals.json +107 -0
  16. package/skills/payment/tld-detection/references/clean.md +51 -0
  17. package/skills/payment/tld-detection/references/clustering.md +219 -0
  18. package/skills/payment/tld-detection/references/decision.md +85 -0
  19. package/skills/payment/tld-detection/references/report-template.md +169 -0
  20. package/skills/payment/tld-detection/references/terminology.md +69 -0
  21. package/skills/payment/tld-detection/references/workflow.md +78 -0
  22. package/skills/payment/tld-detection/scripts/detect_ladders.py +390 -0
  23. package/skills/payment/tld-detection/tests/test_detect_ladders.py +132 -0
  24. package/skills/engineering/resolve-issues/SKILL.md +0 -168
  25. package/skills/engineering/resolve-issues/agents/openai.yaml +0 -9
  26. package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +0 -499
  27. package/skills/engineering/resolve-issues/references/breaker.md +0 -82
  28. package/skills/engineering/resolve-issues/references/deliverables.md +0 -27
  29. package/skills/engineering/resolve-issues/references/delivery.md +0 -108
  30. package/skills/engineering/resolve-issues/references/evidence-lane.md +0 -21
  31. package/skills/engineering/resolve-issues/references/exact-head-ci.md +0 -287
  32. package/skills/engineering/resolve-issues/references/fan-out.md +0 -33
  33. package/skills/engineering/resolve-issues/references/finalization.md +0 -68
  34. package/skills/engineering/resolve-issues/references/guarantees.md +0 -10
  35. package/skills/engineering/resolve-issues/references/high-risk.md +0 -29
  36. package/skills/engineering/resolve-issues/references/incidents/848/README.md +0 -156
  37. package/skills/engineering/resolve-issues/references/intake.md +0 -86
  38. package/skills/engineering/resolve-issues/references/integration-gate.md +0 -53
  39. package/skills/engineering/resolve-issues/references/interference.md +0 -87
  40. package/skills/engineering/resolve-issues/references/loop.md +0 -134
  41. package/skills/engineering/resolve-issues/references/model-catalog.md +0 -9
  42. package/skills/engineering/resolve-issues/references/postmortem.md +0 -27
  43. package/skills/engineering/resolve-issues/references/pre-flight-model-slots.md +0 -41
  44. package/skills/engineering/resolve-issues/references/pre-flight-recording-and-checkout.md +0 -48
  45. package/skills/engineering/resolve-issues/references/pre-flight.md +0 -41
  46. package/skills/engineering/resolve-issues/references/regression-checklist.md +0 -26
  47. package/skills/engineering/resolve-issues/references/run-state.md +0 -288
  48. package/skills/engineering/resolve-issues/references/sandboxed-testing.md +0 -48
  49. package/skills/engineering/resolve-issues/references/spawn-contract.md +0 -96
  50. package/skills/engineering/resolve-issues/references/terminal-evidence-journal.md +0 -40
  51. package/skills/engineering/resolve-issues/references/why.md +0 -653
  52. package/skills/engineering/resolve-issues/schemas/fix-round.schema.json +0 -49
  53. package/skills/engineering/resolve-issues/scripts/combine-and-verify.mjs +0 -721
  54. package/skills/engineering/resolve-issues/scripts/component-candidate.mjs +0 -962
  55. package/skills/engineering/resolve-issues/scripts/contract-revision.mjs +0 -220
  56. package/skills/engineering/resolve-issues/scripts/detect-delivery-mode.mjs +0 -420
  57. package/skills/engineering/resolve-issues/scripts/detect-target-branch.mjs +0 -256
  58. package/skills/engineering/resolve-issues/scripts/detect-workspace-mode.mjs +0 -168
  59. package/skills/engineering/resolve-issues/scripts/discover-models.mjs +0 -9
  60. package/skills/engineering/resolve-issues/scripts/doctrine.mjs +0 -62
  61. package/skills/engineering/resolve-issues/scripts/evidence-lifecycle-contract.mjs +0 -191
  62. package/skills/engineering/resolve-issues/scripts/exact-head-ci.mjs +0 -413
  63. package/skills/engineering/resolve-issues/scripts/exact-head-github-provider.mjs +0 -332
  64. package/skills/engineering/resolve-issues/scripts/finalize.mjs +0 -488
  65. package/skills/engineering/resolve-issues/scripts/fix-rounds.mjs +0 -3307
  66. package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-circular-1001.json +0 -16
  67. package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-valid-sequencing.json +0 -51
  68. package/skills/engineering/resolve-issues/scripts/fixtures/fmm-express-830-component-candidate.json +0 -17
  69. package/skills/engineering/resolve-issues/scripts/fixtures/head-check-set-1081.json +0 -166
  70. package/skills/engineering/resolve-issues/scripts/gate-value-series.mjs +0 -92
  71. package/skills/engineering/resolve-issues/scripts/guide-index.mjs +0 -73
  72. package/skills/engineering/resolve-issues/scripts/head-check-set.mjs +0 -159
  73. package/skills/engineering/resolve-issues/scripts/interference.mjs +0 -427
  74. package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +0 -9
  75. package/skills/engineering/resolve-issues/scripts/next-operations.mjs +0 -419
  76. package/skills/engineering/resolve-issues/scripts/postmortem.mjs +0 -909
  77. package/skills/engineering/resolve-issues/scripts/preflight-questions.mjs +0 -345
  78. package/skills/engineering/resolve-issues/scripts/reconcile-contained-unit-prs.mjs +0 -415
  79. package/skills/engineering/resolve-issues/scripts/release-state-contract.mjs +0 -697
  80. package/skills/engineering/resolve-issues/scripts/report.mjs +0 -494
  81. package/skills/engineering/resolve-issues/scripts/required-check-plan.mjs +0 -172
  82. package/skills/engineering/resolve-issues/scripts/round-metadata.mjs +0 -79
  83. package/skills/engineering/resolve-issues/scripts/run-state-review6-cases.mjs +0 -334
  84. package/skills/engineering/resolve-issues/scripts/run-state.mjs +0 -4792
  85. package/skills/engineering/resolve-issues/scripts/sandbox-selftest.mjs +0 -395
  86. package/skills/engineering/resolve-issues/scripts/spawn-contract.mjs +0 -290
  87. package/skills/engineering/resolve-issues/scripts/terminal-dispositions.mjs +0 -170
  88. package/skills/engineering/resolve-issues/scripts/terminal-evidence-journal.mjs +0 -293
  89. package/skills/engineering/resolve-issues/scripts/unit-kind.mjs +0 -197
  90. package/skills/engineering/resolve-issues/scripts/unit-lifecycle.mjs +0 -127
  91. package/skills/engineering/resolve-issues/scripts/watch-delivery.mjs +0 -893
  92. package/skills/engineering/resolve-issues/scripts/workspaces.mjs +0 -829
  93. package/skills/engineering/resolve-issues/workflows/independent-review.workflow.js +0 -290
  94. package/skills/engineering/resolve-issues/workflows/prior-art-scan.workflow.js +0 -80
  95. package/skills/engineering/resolve-issues/workflows/workflow-smoke.mjs +0 -102
  96. package/skills/engineering/resolve-release/LICENSE +0 -3
  97. package/skills/engineering/resolve-release/SKILL.md +0 -113
  98. package/skills/engineering/resolve-release/agents/openai.yaml +0 -9
  99. package/skills/engineering/resolve-release/references/assembly.md +0 -137
  100. package/skills/engineering/resolve-release/references/auto-when-green.md +0 -56
  101. package/skills/engineering/resolve-release/references/candidate.md +0 -167
  102. package/skills/engineering/resolve-release/references/exposure.md +0 -178
  103. package/skills/engineering/resolve-release/references/handoff.md +0 -24
  104. package/skills/engineering/resolve-release/references/postmortem.md +0 -230
  105. package/skills/engineering/resolve-release/references/preflight.md +0 -208
  106. package/skills/engineering/resolve-release/references/principles.md +0 -94
  107. package/skills/engineering/resolve-release/references/regression-checklist.md +0 -36
  108. package/skills/engineering/resolve-release/references/related-skills.md +0 -13
  109. package/skills/engineering/resolve-release/references/routing.md +0 -149
  110. package/skills/engineering/resolve-release/references/verified-sha-github-flow.md +0 -285
  111. package/skills/engineering/resolve-release/references/versioning.md +0 -202
  112. package/skills/engineering/resolve-release/references/why.md +0 -53
  113. package/skills/engineering/resolve-release/scripts/adapter-completion-artifact.mjs +0 -389
  114. package/skills/engineering/resolve-release/scripts/build-changes.mjs +0 -209
  115. package/skills/engineering/resolve-release/scripts/candidate-hygiene.mjs +0 -407
  116. package/skills/engineering/resolve-release/scripts/candidate-identity.mjs +0 -904
  117. package/skills/engineering/resolve-release/scripts/candidate-traffic.mjs +0 -81
  118. package/skills/engineering/resolve-release/scripts/checked-adapter-loader.mjs +0 -612
  119. package/skills/engineering/resolve-release/scripts/close-attempt.mjs +0 -135
  120. package/skills/engineering/resolve-release/scripts/closeout-release.mjs +0 -161
  121. package/skills/engineering/resolve-release/scripts/doctrine.mjs +0 -106
  122. package/skills/engineering/resolve-release/scripts/durable-processing.mjs +0 -522
  123. package/skills/engineering/resolve-release/scripts/ensure-target-green.mjs +0 -659
  124. package/skills/engineering/resolve-release/scripts/evidence-bundle.mjs +0 -1014
  125. package/skills/engineering/resolve-release/scripts/finalize-release.mjs +0 -526
  126. package/skills/engineering/resolve-release/scripts/fixtures/durable-processing-adapter.mjs +0 -169
  127. package/skills/engineering/resolve-release/scripts/green-gate.mjs +0 -599
  128. package/skills/engineering/resolve-release/scripts/isolated-adapter-evaluator.mjs +0 -752
  129. package/skills/engineering/resolve-release/scripts/metadata-pr-status.mjs +0 -56
  130. package/skills/engineering/resolve-release/scripts/metadata-sync.mjs +0 -1538
  131. package/skills/engineering/resolve-release/scripts/postmortem.mjs +0 -381
  132. package/skills/engineering/resolve-release/scripts/preflight-probes.mjs +0 -510
  133. package/skills/engineering/resolve-release/scripts/production-endpoints.mjs +0 -326
  134. package/skills/engineering/resolve-release/scripts/rc-circuit-breaker.mjs +0 -272
  135. package/skills/engineering/resolve-release/scripts/report.mjs +0 -417
  136. package/skills/engineering/resolve-release/scripts/reprobe-credentials.mjs +0 -114
  137. package/skills/engineering/resolve-release/scripts/revalidate-candidate.mjs +0 -238
  138. package/skills/engineering/resolve-release/scripts/review-packet.mjs +0 -503
  139. package/skills/engineering/resolve-release/scripts/rollback-floor.mjs +0 -263
  140. package/skills/engineering/resolve-release/scripts/version-assert.mjs +0 -339
  141. package/skills/engineering/resolve-release/scripts/version-postmortem.mjs +0 -485
  142. package/skills/engineering/resolve-release/scripts/version.mjs +0 -1199
  143. package/skills/engineering/resolve-release/scripts/watch-candidate-delivery.mjs +0 -449
  144. package/skills/engineering/resolve-release/vendor/ACORN-LICENSE +0 -21
  145. package/skills/engineering/resolve-release/vendor/README.md +0 -60
  146. package/skills/engineering/resolve-release/vendor/acorn.mjs +0 -6233
  147. package/skills/engineering/resolve-release/vendor/quickjs/core/LICENSE +0 -21
  148. package/skills/engineering/resolve-release/vendor/quickjs/core/README.md +0 -341
  149. package/skills/engineering/resolve-release/vendor/quickjs/core/README.template.md +0 -70
  150. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs +0 -2
  151. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs.map +0 -1
  152. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs +0 -7
  153. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs.map +0 -1
  154. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.mts +0 -2033
  155. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.ts +0 -2033
  156. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js +0 -7
  157. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js.map +0 -1
  158. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs +0 -2
  159. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs.map +0 -1
  160. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs +0 -2
  161. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs.map +0 -1
  162. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs +0 -2
  163. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs.map +0 -1
  164. package/skills/engineering/resolve-release/vendor/quickjs/core/package.json +0 -49
  165. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/LICENSE +0 -21
  166. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/README.md +0 -5
  167. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.mts +0 -549
  168. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.ts +0 -549
  169. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js +0 -2
  170. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js.map +0 -1
  171. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs +0 -2
  172. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs.map +0 -1
  173. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/package.json +0 -36
  174. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/LICENSE +0 -47
  175. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/README.md +0 -82
  176. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.d.ts +0 -11
  177. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.mjs +0 -22
  178. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cjs +0 -21
  179. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.cjs +0 -21
  180. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.d.ts +0 -11
  181. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.d.ts +0 -11
  182. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.mjs +0 -25
  183. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.wasm +0 -0
  184. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.mts +0 -85
  185. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.ts +0 -85
  186. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js +0 -2
  187. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js.map +0 -1
  188. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs +0 -2
  189. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs.map +0 -1
  190. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.mts +0 -20
  191. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.ts +0 -20
  192. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js +0 -2
  193. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js.map +0 -1
  194. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs +0 -2
  195. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs.map +0 -1
  196. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/package.json +0 -61
  197. /package/skills/{engineering/resolve-issues → payment/tld-detection}/LICENSE +0 -0
@@ -1,419 +0,0 @@
1
- // Exact next-operation rendering for resolve-issues. This module composes existing
2
- // state-machine and first-party CLI contracts; it owns no duplicate transition logic.
3
- import { execFileSync } from 'node:child_process'
4
- import { fileURLToPath } from 'node:url'
5
- import path from 'node:path'
6
-
7
- import { deliveryFlow } from './fix-rounds.mjs'
8
- import { terminalDisposition } from './terminal-dispositions.mjs'
9
- import { runIsComplete } from './postmortem.mjs'
10
- import { unitPrReconciliationStatus } from './reconcile-contained-unit-prs.mjs'
11
-
12
- const SCRIPT_PATHS = Object.freeze({
13
- runState: fileURLToPath(new URL('./run-state.mjs', import.meta.url)),
14
- watchDelivery: fileURLToPath(new URL('./watch-delivery.mjs', import.meta.url)),
15
- combineAndVerify: fileURLToPath(new URL('./combine-and-verify.mjs', import.meta.url)),
16
- exactHeadCi: fileURLToPath(new URL('./exact-head-ci.mjs', import.meta.url)),
17
- })
18
-
19
- const object = (value) => value != null && typeof value === 'object' && !Array.isArray(value)
20
- const text = (value) => typeof value === 'string' && value.trim().length > 0
21
- const numberRef = (value) => String(value ?? '').trim().match(/^#?(\d+)$/)?.[1] ?? null
22
-
23
- function tryExec(command, args, cwd) {
24
- try {
25
- return { ok: true, stdout: execFileSync(command, args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() }
26
- } catch {
27
- return { ok: false, stdout: '' }
28
- }
29
- }
30
-
31
- function repositoryFromRemote(remote) {
32
- const match = String(remote ?? '').trim().replace(/\.git$/, '').match(/(?:github\.com[:/])([^/]+\/[^/]+)$/)
33
- return match?.[1] ?? null
34
- }
35
-
36
- export function probeNextOperations(manifest = {}, { cwd = process.cwd() } = {}) {
37
- const top = tryExec('git', ['rev-parse', '--show-toplevel'], cwd)
38
- const repoRoot = top.ok ? path.resolve(top.stdout) : path.resolve(cwd)
39
- const branch = tryExec('git', ['branch', '--show-current'], repoRoot)
40
- const status = tryExec('git', ['status', '--porcelain'], repoRoot)
41
- const unpublished = tryExec('git', ['rev-list', '--max-count=1', 'HEAD', '--not', '--remotes'], repoRoot)
42
- const remote = tryExec('git', ['remote', 'get-url', 'origin'], repoRoot)
43
- const repository = text(manifest.repository) ? manifest.repository.trim() : (remote.ok ? repositoryFromRemote(remote.stdout) : null)
44
- const originalBranchExists = text(manifest.originalBranch)
45
- ? tryExec('git', ['show-ref', '--verify', '--quiet', `refs/heads/${manifest.originalBranch}`], repoRoot).ok
46
- : false
47
- const prStates = {}
48
- const issueStates = {}
49
- for (const unit of Array.isArray(manifest.units) ? manifest.units : []) {
50
- const pr = numberRef(unit?.pr)
51
- const issue = numberRef(unit?.id)
52
- if (repository && pr && (unit?.status === 'merge-ready' || terminalDisposition(unit))) {
53
- const read = tryExec('gh', ['pr', 'view', pr, '--repo', repository, '--json', 'state,mergedAt,mergeCommit,headRefOid,isDraft'], repoRoot)
54
- if (read.ok) try { prStates[unit.id] = JSON.parse(read.stdout) } catch {}
55
- }
56
- if (repository && issue && terminalDisposition(unit)) {
57
- const read = tryExec('gh', ['issue', 'view', issue, '--repo', repository, '--json', 'state'], repoRoot)
58
- if (read.ok) try { issueStates[unit.id] = JSON.parse(read.stdout) } catch {}
59
- }
60
- }
61
- return {
62
- repository,
63
- repoRoot,
64
- currentBranch: branch.ok && branch.stdout ? branch.stdout : null,
65
- clean: status.ok && status.stdout === '',
66
- currentPublished: unpublished.ok && unpublished.stdout === '',
67
- originalBranchExists,
68
- prStates,
69
- issueStates,
70
- }
71
- }
72
-
73
- function operation({ id, kind, argv, cwd, expectedExitCodes, transition, authorizedBy = [], detail = null }) {
74
- return {
75
- id,
76
- kind,
77
- mutates: kind === 'mutation',
78
- argv,
79
- cwd,
80
- expectedExitCodes,
81
- authorizes: { transition },
82
- authorizedBy,
83
- ...(detail ? { detail } : {}),
84
- }
85
- }
86
-
87
- function exactHeadPaths(unit) {
88
- const config = object(unit?.exactHeadCi) ? unit.exactHeadCi : null
89
- if (!text(config?.requestPath) || !text(config?.artifactPath)
90
- || !path.isAbsolute(config.requestPath) || !path.isAbsolute(config.artifactPath)) return null
91
- return { request: config.requestPath, artifact: config.artifactPath }
92
- }
93
-
94
- function verdictPath(manifest, unit, probe) {
95
- if (text(unit.integrationVerdictPath) && path.isAbsolute(unit.integrationVerdictPath)) return unit.integrationVerdictPath
96
- const component = (Array.isArray(manifest.components) ? manifest.components : [])
97
- .find((entry) => Array.isArray(entry?.units) && entry.units.includes(unit.id))
98
- if (text(component?.integrationVerdictPath) && path.isAbsolute(component.integrationVerdictPath)) return component.integrationVerdictPath
99
- const observed = probe?.integrationVerdicts?.[unit.id]
100
- return text(observed) && path.isAbsolute(observed) ? observed : null
101
- }
102
-
103
- export function deriveNextOperations(manifest = {}, {
104
- slug = null,
105
- manifestPath = null,
106
- probe = {},
107
- actions = [],
108
- actionNames = {},
109
- } = {}) {
110
- const m = object(manifest) ? manifest : {}
111
- const runSlug = slug ?? (text(m.runSlug) ? m.runSlug : null)
112
- const file = manifestPath ? path.resolve(manifestPath) : null
113
- const repoRoot = path.resolve(probe.repoRoot || m.workspacePath || process.cwd())
114
- const repository = text(m.repository) ? m.repository.trim() : (text(probe.repository) ? probe.repository.trim() : null)
115
- const units = (Array.isArray(m.units) ? m.units : []).filter(object)
116
- const byId = new Map(units.filter((unit) => text(unit.id)).map((unit) => [unit.id, unit]))
117
- const invocationReady = text(runSlug) && file
118
- const inspections = []
119
- const mutations = []
120
- const blocked = []
121
- const block = (id, reason, transition) => blocked.push({ id, reason, authorizes: { transition } })
122
- const add = (entry) => {
123
- if (!Array.isArray(entry.argv) || entry.argv.some((arg) => typeof arg !== 'string') || !path.isAbsolute(entry.cwd)) {
124
- block(entry.id, 'exact argv/cwd could not be derived from state; no partial operation is emitted', entry.authorizes.transition)
125
- return
126
- }
127
- if (entry.mutates && !invocationReady) {
128
- block(entry.id, 'run slug and resolved manifest path are required before any mutation can be authorized', entry.authorizes.transition)
129
- return
130
- }
131
- ;(entry.mutates ? mutations : inspections).push(entry)
132
- }
133
- const runStateArgv = (...args) => [process.execPath, SCRIPT_PATHS.runState, ...args, ...(file ? ['--file', file] : [])]
134
- const operationExit = (id) => probe?.operationResults?.[id]
135
- const operationPassed = (id, exitCode = 0) => operationExit(id) === exitCode
136
- if (!invocationReady) block('invocation-identity', 'run slug and resolved manifest path are required for run-state-backed operations', 'state -> unchanged')
137
-
138
- for (const action of actions) {
139
- const unit = byId.get(action.unit)
140
- if (!unit) continue
141
- if ([actionNames.PRODUCER_SPAWN, actionNames.REVIEW_SPAWN, actionNames.FIX_ROUND].includes(action.action)) {
142
- const kind = action.action === actionNames.REVIEW_SPAWN ? 'review' : (action.action === actionNames.FIX_ROUND ? 'fix' : 'producer')
143
- add(operation({ id: `spawn-contract:${unit.id}`, kind: 'inspection', cwd: repoRoot, argv: runStateArgv('spawn-contract', runSlug, unit.id, '--kind', kind), expectedExitCodes: [0, 4], transition: `${unit.status ?? 'queued'} -> ${action.action}` }))
144
- continue
145
- }
146
- if (action.action === actionNames.BREAKER_DECISION) {
147
- add(operation({ id: `breaker-diagnosis:${unit.id}`, kind: 'inspection', cwd: repoRoot, argv: runStateArgv('diagnose-breaker', runSlug, unit.id), expectedExitCodes: [0, 1], transition: 'breaker-fired -> breaker-diagnosed' }))
148
- continue
149
- }
150
- if (action.action === actionNames.INTAKE_REENTRY) {
151
- add(operation({ id: `intake-reentry:${unit.id}`, kind: 'mutation', cwd: repoRoot, argv: runStateArgv('record-intake-reentry', runSlug, unit.id), expectedExitCodes: [0], transition: 'breaker-decided -> in-progress', authorizedBy: [{ state: 'units[].breaker.decision' }] }))
152
- continue
153
- }
154
- if (action.action === actionNames.WAIT_ON_DEPENDENCY) {
155
- add(operation({ id: `runnable:${unit.id}`, kind: 'inspection', cwd: repoRoot, argv: runStateArgv('runnable', runSlug, '--json'), expectedExitCodes: [0], transition: 'dependency-wait -> admission-decision' }))
156
- continue
157
- }
158
- if ([actionNames.HUMAN, actionNames.CANNOT_TELL].includes(action.action)) {
159
- block(`current-action:${unit.id}`, action.why, `${unit.status ?? 'unknown'} -> unresolved`)
160
- continue
161
- }
162
- if (![actionNames.DELIVER, actionNames.DELIVERY_WATCH].includes(action.action)) continue
163
-
164
- const component = action.componentId && Array.isArray(m.components)
165
- ? m.components.find((entry) => entry?.id === action.componentId)
166
- : null
167
- if (component?.delivery) {
168
- const delivery = component.delivery
169
- const componentId = component.id
170
- const pr = numberRef(delivery.pr ?? component.pr)
171
- const head = text(delivery.authorizedHeadSha) ? delivery.authorizedHeadSha.trim() : null
172
- const flow = deliveryFlow(m)
173
- const attempts = Array.isArray(delivery.attempts) ? delivery.attempts : []
174
- const observed = probe?.deliveryOutcomes?.[componentId] ?? probe?.deliveryOutcomes?.[unit.id] ?? null
175
- const integration = component.integrationVerdictPath ?? probe?.integrationVerdicts?.[componentId] ?? null
176
- if (!repository) block(`delivery:${componentId}`, 'repository identity is absent from the manifest and Git remote probe', 'component-authorized -> delivery-requested')
177
- if (!m.mergeShippable) block(`delivery-request:${componentId}`, 'mergeShippable authorization is absent/off; no merge or enqueue mutation may be emitted', 'component-authorized -> delivery-requested')
178
- if (!pr) block(`delivery:${componentId}`, 'a concrete component PR number is required', 'component-authorized -> delivery-requested')
179
- if (!head) block(`delivery:${componentId}`, 'components[].delivery.authorizedHeadSha is absent', 'component-authorized -> delivery-requested')
180
- if (!flow) block(`delivery:${componentId}`, 'delivery flow is unresolved; deliveryMode and queueProvider must come from the probe', 'component-authorized -> delivery-requested')
181
-
182
- if (repository && pr && head && flow) {
183
- if (attempts.length) {
184
- const watch = `delivery-watch:${componentId}`
185
- if (operationPassed(watch, 5)) block(watch, 'component delivery watch exited 5 (exact-head CI failed); reopen the component generation and do not record a terminal outcome', 'delivery-attempt-recorded -> fresh-authorization-required')
186
- else if (flow.watchable && operationExit(watch) === undefined && !observed) {
187
- add(operation({ id: watch, kind: 'inspection', cwd: repoRoot, argv: [process.execPath, SCRIPT_PATHS.watchDelivery, '--pr', pr, '--repo', repository, ...(flow.provider ? ['--provider', flow.provider] : []), '--head', head], expectedExitCodes: [0, 4, 5], transition: 'component-delivery-attempt-recorded -> component-delivery-observed' }))
188
- }
189
- } else {
190
- const gate = `delivery-preflight:${componentId}`
191
- const request = `delivery-request:${componentId}`
192
- if (!integration) block(request, 'component integration verdict path is absent; the authorized component cannot be delivered without its ALIVE artifact', 'component-authorized -> delivery-requested')
193
- else {
194
- const gateCommand = flow.mode === 'queue' ? 'enqueue-eligible' : 'mergeable'
195
- if (operationExit(gate) === undefined) add(operation({ id: gate, kind: 'inspection', cwd: repoRoot, argv: [process.execPath, SCRIPT_PATHS.combineAndVerify, gateCommand, '--in', integration, '--manifest', file, '--component', componentId, '--pr', pr], expectedExitCodes: [0, 1], transition: 'component-authorized -> delivery-authorized' }))
196
- if (operationPassed(gate) && operationExit(request) === undefined) {
197
- const argv = flow.mode === 'direct'
198
- ? ['gh', 'pr', 'merge', pr, '--repo', repository, '--squash']
199
- : flow.provider === 'mergify'
200
- ? ['gh', 'pr', 'comment', pr, '--repo', repository, '--body', '@mergifyio queue']
201
- : ['gh', 'pr', 'merge', pr, '--repo', repository, '--auto', '--squash']
202
- add(operation({ id: request, kind: 'mutation', cwd: repoRoot, argv, expectedExitCodes: [0], transition: 'component-authorized -> delivery-requested', authorizedBy: [{ operation: gate, exitCode: 0 }, { state: 'mergeShippable=true' }] }))
203
- } else if (!operationPassed(gate)) block(request, `${gate} ${operationExit(gate) === undefined ? 'has not produced exit 0' : `produced exit ${operationExit(gate)}, not 0`}; delivery authorization is absent`, 'component-authorized -> delivery-requested')
204
- }
205
- const observedAttempt = probe?.deliveryAttempts?.[componentId] ?? probe?.deliveryAttempts?.[unit.id]
206
- if (operationPassed(gate) && operationPassed(request) && object(observedAttempt)) {
207
- const payload = { generation: delivery.generation, headSha: head, attempt: observedAttempt }
208
- add(operation({ id: `record-component-delivery-attempt:${componentId}`, kind: 'mutation', cwd: repoRoot,
209
- argv: runStateArgv('record-component-delivery-attempt', runSlug, componentId, JSON.stringify(payload)), expectedExitCodes: [0], transition: 'delivery-requested -> component-delivery-attempt-recorded',
210
- authorizedBy: [{ operation: gate, exitCode: 0 }, { operation: request, exitCode: 0 }, { probe: `deliveryAttempts.${componentId}` }],
211
- }))
212
- } else block(`record-component-delivery-attempt:${componentId}`, 'delivery preflight and provider request have not both produced exit 0 with one observed provider payload', 'delivery-requested -> component-delivery-attempt-recorded')
213
- }
214
- }
215
-
216
- if (observed?.outcome === 'merged' && text(observed.mergedSha) && repository && pr && text(m.targetBranch)) {
217
- const proof = `delivery-proof:${componentId}`
218
- const queueArtifactReady = flow?.mode !== 'queue' || (text(observed.queueExecutionArtifactPath) && integration)
219
- const proofArgv = flow?.mode === 'queue'
220
- ? (queueArtifactReady ? [process.execPath, SCRIPT_PATHS.combineAndVerify, 'queue-delivered', '--in', integration, '--artifact', observed.queueExecutionArtifactPath, '--sha', observed.mergedSha, '--target', m.targetBranch, '--pr', pr] : null)
221
- : [process.execPath, SCRIPT_PATHS.combineAndVerify, 'delivered', '--sha', observed.mergedSha, '--target', m.targetBranch, '--pr', pr]
222
- if (!proofArgv) block(proof, 'queue delivery completion requires the integration verdict and queue-execution artifact path; direct ancestry proof cannot substitute for synthetic queue evidence', 'component-delivery-observed -> component-delivery-proved')
223
- else if (operationExit(proof) === undefined) add(operation({ id: proof, kind: 'inspection', cwd: repoRoot, argv: proofArgv, expectedExitCodes: [0, 1], transition: 'component-delivery-observed -> component-delivery-proved' }))
224
- if (attempts.length && operationPassed(proof)) {
225
- const queueExecution = object(observed.queueExecution)
226
- ? observed.queueExecution
227
- : text(observed.queueExecutionArtifactPath) && object(observed.queueExecutionArtifact)
228
- ? { artifact: observed.queueExecutionArtifactPath, evidence: observed.queueExecutionArtifact }
229
- : null
230
- const payload = {
231
- generation: delivery.generation,
232
- headSha: head,
233
- outcome: 'merged',
234
- mergedSha: observed.mergedSha,
235
- ...(observed.mergedAt ? { mergedAt: observed.mergedAt } : {}),
236
- ...(queueExecution ? { queueExecution } : {}),
237
- }
238
- add(operation({ id: `record-component-delivery-outcome:${componentId}`, kind: 'mutation', cwd: repoRoot,
239
- argv: runStateArgv('record-component-delivery-outcome', runSlug, componentId, JSON.stringify(payload)), expectedExitCodes: [0], transition: 'component-authorized -> delivered', authorizedBy: [{ operation: proof, exitCode: 0 }],
240
- }))
241
- } else if (!attempts.length) block(`record-component-delivery-outcome:${componentId}`, 'canonical component delivery requires its one provider attempt before merged outcome recording', 'component-authorized -> delivered')
242
- else block(`record-component-delivery-outcome:${componentId}`, `${proof} has not produced exit 0; ancestry/queue evidence authorization is absent`, 'component-authorized -> delivered')
243
- } else if (observed?.outcome === 'handed-back' && text(observed.blockedOn)) {
244
- const watch = `delivery-watch:${componentId}`
245
- const payload = { generation: delivery.generation, headSha: head, outcome: 'handed-back', blockedOn: observed.blockedOn }
246
- if (operationPassed(watch, 4)) add(operation({ id: `record-component-delivery-outcome:${componentId}`, kind: 'mutation', cwd: repoRoot,
247
- argv: runStateArgv('record-component-delivery-outcome', runSlug, componentId, JSON.stringify(payload)), expectedExitCodes: [0], transition: 'component-authorized -> handed-back', authorizedBy: [{ operation: watch, exitCode: 4 }],
248
- }))
249
- else block(`record-component-delivery-outcome:${componentId}`, `${watch} has not produced exit 4; hand-back authorization is absent`, 'component-authorized -> handed-back')
250
- }
251
- continue
252
- }
253
-
254
- const pr = numberRef(unit.pr)
255
- const head = text(unit.pushedSha) ? unit.pushedSha.trim() : null
256
- const flow = deliveryFlow(m)
257
- const exact = exactHeadPaths(unit)
258
- const attempts = Array.isArray(unit.delivery?.attempts) ? unit.delivery.attempts : []
259
- const observed = probe?.deliveryOutcomes?.[unit.id] ?? (() => {
260
- const state = probe?.prStates?.[unit.id]
261
- const oid = state?.mergeCommit?.oid
262
- return (state?.state === 'MERGED' || state?.mergedAt) && text(oid)
263
- ? { outcome: 'merged', mergedSha: oid, ...(state.mergedAt ? { mergedAt: state.mergedAt } : {}) }
264
- : null
265
- })()
266
- if (!repository) block(`delivery:${unit.id}`, 'repository identity is absent from the manifest and Git remote probe', 'merge-ready -> delivery-requested')
267
- if (!m.mergeShippable) block(`delivery-request:${unit.id}`, 'mergeShippable authorization is absent/off; no merge or enqueue mutation may be emitted', 'merge-ready -> delivery-requested')
268
- if (!pr) block(`delivery:${unit.id}`, 'a concrete PR number is required', 'merge-ready -> delivery-requested')
269
- if (!head) block(`delivery:${unit.id}`, 'units[].pushedSha is absent; exact HEAD delivery is required', 'merge-ready -> delivery-requested')
270
- if (!flow) block(`delivery:${unit.id}`, 'delivery flow is unresolved; deliveryMode and queueProvider must come from the probe', 'merge-ready -> delivery-requested')
271
-
272
- if (repository && pr && head && flow) {
273
- if (attempts.length) {
274
- const watch = `delivery-watch:${unit.id}`
275
- if (operationPassed(watch, 5)) block(watch, 'delivery watch exited 5 (exact-head CI failed); re-enter the fix/review loop and do not record a terminal outcome', 'delivery-attempt-recorded -> fresh-verdict-required')
276
- else if (flow.watchable && operationExit(watch) === undefined && !observed) {
277
- add(operation({ id: watch, kind: 'inspection', cwd: repoRoot, argv: [process.execPath, SCRIPT_PATHS.watchDelivery, '--pr', pr, '--repo', repository, ...(flow.provider ? ['--provider', flow.provider] : []), '--head', head], expectedExitCodes: [0, 4, 5], transition: 'delivery-attempt-recorded -> delivery-observed' }))
278
- }
279
- } else if (!exact) {
280
- block(`exact-head-ci:${unit.id}`, 'exact-head CI request/artifact paths are absent; delivery cannot start without the #889 adapter request', 'reviewed-head -> exact-head-ci-recorded')
281
- block(`delivery-request:${unit.id}`, 'exact-head CI authorization is absent', 'merge-ready -> delivery-requested')
282
- } else {
283
- const exactRun = `exact-head-ci:${unit.id}`
284
- const exactValidate = `exact-head-ci-validate:${unit.id}`
285
- const exactRunPassed = operationPassed(exactRun)
286
- const exactValidatePassed = exactRunPassed && operationPassed(exactValidate)
287
- if (operationExit(exactRun) === undefined) {
288
- add(operation({ id: exactRun, kind: 'mutation', cwd: repoRoot, argv: [process.execPath, SCRIPT_PATHS.exactHeadCi, 'run', '--input', exact.request, '--out', exact.artifact], expectedExitCodes: [0, 1], transition: 'reviewed-head -> exact-head-ci-recorded', authorizedBy: [{ state: `units[${unit.id}].exactHeadCi` }, { state: `units[${unit.id}].pushedSha=${head}` }] }))
289
- block(exactValidate, `${exactRun} has not produced exit 0`, 'exact-head-ci-recorded -> exact-head-ci-proved')
290
- } else if (!exactRunPassed) {
291
- block(exactValidate, `${exactRun} produced exit ${operationExit(exactRun)}, not 0`, 'exact-head-ci-recorded -> exact-head-ci-proved')
292
- } else if (operationExit(exactValidate) === undefined) {
293
- add(operation({ id: exactValidate, kind: 'inspection', cwd: repoRoot, argv: [process.execPath, SCRIPT_PATHS.exactHeadCi, 'validate', exact.artifact, '--head', head], expectedExitCodes: [0, 1], transition: 'exact-head-ci-recorded -> exact-head-ci-proved' }))
294
- } else if (!exactValidatePassed) {
295
- block(exactValidate, `${exactValidate} produced exit ${operationExit(exactValidate)}, not 0`, 'exact-head-ci-recorded -> exact-head-ci-proved')
296
- }
297
-
298
- const integration = verdictPath(m, unit, probe)
299
- const gate = `delivery-preflight:${unit.id}`
300
- const request = `delivery-request:${unit.id}`
301
- if (!m.mergeShippable) block(request, 'mergeShippable authorization is absent/off; hand back merge-ready and do not enqueue or merge', 'merge-ready -> delivery-requested')
302
- else if (!exactValidatePassed) block(request, `${exactRun} and ${exactValidate} have not both produced exit 0; exact-head authorization is absent`, 'merge-ready -> delivery-requested')
303
- else if (!integration) block(request, 'integration verdict path is absent; combine-and-verify mergeable must authorize delivery first', 'merge-ready -> delivery-requested')
304
- else {
305
- if (operationExit(gate) === undefined) add(operation({ id: gate, kind: 'inspection', cwd: repoRoot, argv: [process.execPath, SCRIPT_PATHS.combineAndVerify, 'mergeable', '--in', integration, '--manifest', file, '--pr', pr], expectedExitCodes: [0, 1], transition: 'merge-ready -> delivery-authorized' }))
306
- if (operationPassed(gate) && operationExit(request) === undefined) {
307
- const argv = flow.mode === 'direct'
308
- ? ['gh', 'pr', 'merge', pr, '--repo', repository, '--squash']
309
- : flow.provider === 'mergify'
310
- ? ['gh', 'pr', 'comment', pr, '--repo', repository, '--body', '@mergifyio queue']
311
- : ['gh', 'pr', 'merge', pr, '--repo', repository, '--auto', '--squash']
312
- add(operation({ id: request, kind: 'mutation', cwd: repoRoot, argv, expectedExitCodes: [0], transition: 'merge-ready -> delivery-requested', authorizedBy: [{ operation: gate, exitCode: 0 }, { state: 'mergeShippable=true' }] }))
313
- } else if (!operationPassed(gate)) block(request, `${gate} ${operationExit(gate) === undefined ? 'has not produced exit 0' : `produced exit ${operationExit(gate)}, not 0`}; delivery authorization is absent`, 'merge-ready -> delivery-requested')
314
- else if (!operationPassed(request)) block(request, `${request} produced exit ${operationExit(request)}, not 0`, 'merge-ready -> delivery-requested')
315
- }
316
-
317
- const observedAttempt = probe?.deliveryAttempts?.[unit.id]
318
- const requestPassed = operationPassed(gate) && operationPassed(request)
319
- if (requestPassed && object(observedAttempt)) add(operation({ id: `record-delivery-attempt:${unit.id}`, kind: 'mutation', cwd: repoRoot,
320
- argv: runStateArgv('record-delivery-attempt', runSlug, unit.id, JSON.stringify(observedAttempt)), expectedExitCodes: [0], transition: 'delivery-requested -> delivery-attempt-recorded',
321
- authorizedBy: [{ operation: gate, exitCode: 0 }, { operation: request, exitCode: 0 }, { probe: `deliveryAttempts.${unit.id}` }],
322
- }))
323
- else block(`record-delivery-attempt:${unit.id}`, `${gate} and ${request} have not both produced exit 0 with an observed provider payload; no attempt record is authorized`, 'delivery-requested -> delivery-attempt-recorded')
324
- }
325
- }
326
-
327
- if (observed?.outcome === 'merged' && text(observed.mergedSha) && repository && pr && text(m.targetBranch)) {
328
- const proof = `delivery-proof:${unit.id}`
329
- if (operationExit(proof) === undefined) add(operation({ id: proof, kind: 'inspection', cwd: repoRoot, argv: [process.execPath, SCRIPT_PATHS.combineAndVerify, 'delivered', '--sha', observed.mergedSha, '--target', m.targetBranch, '--pr', pr], expectedExitCodes: [0, 1], transition: 'delivery-observed -> delivery-proved' }))
330
- if (Array.isArray(unit.delivery?.attempts) && unit.delivery.attempts.length) {
331
- const payload = { outcome: 'merged', mergedSha: observed.mergedSha, ...(observed.mergedAt ? { mergedAt: observed.mergedAt } : {}) }
332
- if (operationPassed(proof)) add(operation({ id: `record-delivery-outcome:${unit.id}`, kind: 'mutation', cwd: repoRoot, argv: runStateArgv('record-delivery-outcome', runSlug, unit.id, JSON.stringify(payload)), expectedExitCodes: [0], transition: 'merge-ready -> delivered', authorizedBy: [{ operation: proof, exitCode: 0 }] }))
333
- else block(`record-delivery-outcome:${unit.id}`, `${proof} has not produced exit 0; ancestry authorization is absent`, 'merge-ready -> delivered')
334
- } else block(`record-delivery-outcome:${unit.id}`, 'typed delivery state requires a recorded attempt before merged outcome recording; do not invent an enqueue for an auto-merge observation', 'merge-ready -> delivered')
335
- } else if (observed?.outcome === 'handed-back' && text(observed.blockedOn)) {
336
- const payload = { outcome: 'handed-back', blockedOn: observed.blockedOn }
337
- const watch = `delivery-watch:${unit.id}`
338
- if (operationPassed(watch, 4)) add(operation({ id: `record-delivery-outcome:${unit.id}`, kind: 'mutation', cwd: repoRoot, argv: runStateArgv('record-delivery-outcome', runSlug, unit.id, JSON.stringify(payload)), expectedExitCodes: [0], transition: 'merge-ready -> handed-back', authorizedBy: [{ operation: watch, exitCode: 4 }] }))
339
- else block(`record-delivery-outcome:${unit.id}`, `${watch} has not produced exit 4; hand-back authorization is absent`, 'merge-ready -> handed-back')
340
- }
341
- }
342
-
343
- for (const unit of units) {
344
- if (unit.disposition === 'descoped' && terminalDisposition(unit) !== 'descoped') {
345
- if (unit.scopeDecision?.issueDisposition === 'closed-not-planned') block(`issue-disposition:${unit.id}`, 'scopeDecision is not a valid human descope; issue closure is not authorized', 'descoped -> issue-closed-not-planned')
346
- if (unit.scopeDecision?.prDisposition === 'closed-unmerged') block(`pr-disposition:${unit.id}`, 'scopeDecision is not a valid human descope; PR closure is not authorized', 'descoped -> pr-closed-unmerged')
347
- continue
348
- }
349
- if (terminalDisposition(unit) !== 'descoped' || !repository) continue
350
- const issue = numberRef(unit.id)
351
- const pr = numberRef(unit.pr)
352
- if (issue) add(operation({ id: `issue-disposition-check:${unit.id}`, kind: 'inspection', cwd: repoRoot, argv: ['gh', 'issue', 'view', issue, '--repo', repository, '--json', 'state'], expectedExitCodes: [0], transition: `${terminalDisposition(unit)} -> issue-disposition-observed` }))
353
- if (unit.scopeDecision?.issueDisposition === 'closed-not-planned' && issue && probe?.issueStates?.[unit.id]?.state === 'OPEN' && unit.scopeDecision?.decidedBy === 'human') {
354
- add(operation({ id: `issue-disposition:${unit.id}`, kind: 'mutation', cwd: repoRoot, argv: ['gh', 'issue', 'close', issue, '--repo', repository, '--reason', 'not planned'], expectedExitCodes: [0], transition: 'descoped -> issue-closed-not-planned', authorizedBy: [{ state: 'scopeDecision.decidedBy=human' }] }))
355
- } else if (unit.scopeDecision?.issueDisposition === 'closed-not-planned' && issue && probe?.issueStates?.[unit.id]?.state === 'OPEN') {
356
- block(`issue-disposition:${unit.id}`, 'scopeDecision.decidedBy is not human; issue closure is not authorized', 'descoped -> issue-closed-not-planned')
357
- }
358
- if (pr) add(operation({ id: `pr-disposition-check:${unit.id}`, kind: 'inspection', cwd: repoRoot, argv: ['gh', 'pr', 'view', pr, '--repo', repository, '--json', 'state'], expectedExitCodes: [0], transition: `${terminalDisposition(unit)} -> pr-disposition-observed` }))
359
- if (unit.scopeDecision?.prDisposition === 'closed-unmerged' && pr && probe?.prStates?.[unit.id]?.state === 'OPEN' && unit.scopeDecision?.decidedBy === 'human') {
360
- add(operation({ id: `pr-disposition:${unit.id}`, kind: 'mutation', cwd: repoRoot, argv: ['gh', 'pr', 'close', pr, '--repo', repository], expectedExitCodes: [0], transition: 'descoped -> pr-closed-unmerged', authorizedBy: [{ state: 'scopeDecision.decidedBy=human' }] }))
361
- } else if (unit.scopeDecision?.prDisposition === 'closed-unmerged' && pr && probe?.prStates?.[unit.id]?.state === 'OPEN') {
362
- block(`pr-disposition:${unit.id}`, 'scopeDecision.decidedBy is not human; PR closure is not authorized', 'descoped -> pr-closed-unmerged')
363
- }
364
- }
365
-
366
- if (runIsComplete(m)) {
367
- const reconciliation = unitPrReconciliationStatus(m)
368
- if (reconciliation.state === 'invalid') {
369
- block('reconcile-contained-unit-prs', reconciliation.reason, 'run-terminal -> unit-prs-reconciled')
370
- block('finalize', 'contained unit PR reconciliation state is invalid', 'run-terminal -> finalized')
371
- } else if (reconciliation.required && !reconciliation.complete) {
372
- add(operation({
373
- id: 'reconcile-contained-unit-prs',
374
- kind: 'mutation',
375
- cwd: repoRoot,
376
- argv: runStateArgv('reconcile-contained-unit-prs', runSlug),
377
- expectedExitCodes: [0, 1],
378
- transition: 'run-terminal -> unit-prs-reconciled',
379
- authorizedBy: [{ state: 'canonical combined component delivery is merged' }],
380
- detail: 'freshly verify exact unit heads, target ancestry, and the live component merge before closing superseded unit PRs',
381
- }))
382
- block('finalize', 'delivery-linked unit PRs require terminal reconciliation observations first', 'run-terminal -> finalized')
383
- } else {
384
- add(operation({
385
- id: 'finalize',
386
- kind: 'mutation',
387
- cwd: repoRoot,
388
- argv: runStateArgv('finalize', runSlug),
389
- expectedExitCodes: [0, 1],
390
- transition: 'run-terminal -> finalized',
391
- authorizedBy: [{ state: 'all units have typed terminal dispositions' }, ...(reconciliation.required ? [{ state: 'delivery-linked unit PRs have terminal reconciliation observations' }] : [])],
392
- detail: 'finalize owns and validates live terminal state, original-branch restoration, and workspace cleanup',
393
- }))
394
- }
395
- }
396
-
397
- return { version: 1, slug: runSlug, manifest: file, phase: runIsComplete(m) ? 'finalization' : 'active', actions, inspections, mutations, blocked }
398
- }
399
-
400
- export function renderNextOperations(result = {}) {
401
- const out = [`NEXT OPERATIONS ${result.slug ?? '(no slug)'} phase ${result.phase ?? 'unknown'}`]
402
- const section = (title, rows) => {
403
- out.push('', ` ${title}`)
404
- if (!rows?.length) { out.push(' (none)'); return }
405
- for (const row of rows) {
406
- out.push(` ${row.id} ${row.authorizes?.transition ?? 'no transition'}`)
407
- out.push(` cwd: ${row.cwd}`)
408
- out.push(` argv: ${JSON.stringify(row.argv)}`)
409
- out.push(` expected exits: ${row.expectedExitCodes.join(', ')}`)
410
- if (row.authorizedBy?.length) out.push(` authorized by: ${JSON.stringify(row.authorizedBy)}`)
411
- }
412
- }
413
- section('INSPECTIONS (read-only)', result.inspections)
414
- section('MUTATIONS (state/auth present)', result.mutations)
415
- out.push('', ' BLOCKED MUTATIONS')
416
- if (!result.blocked?.length) out.push(' (none)')
417
- else for (const row of result.blocked) out.push(` ${row.id} ${row.authorizes?.transition ?? 'no transition'} — ${row.reason}`)
418
- return out.join('\n')
419
- }