@1aboveio/skills 0.19.2 → 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 (203) 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 +13 -7
  13. package/skills/payment/payment-analysis/references/chargebacks.md +43 -18
  14. package/skills/payment/payment-analysis/references/event-layers.md +4 -0
  15. package/skills/payment/payment-analysis/references/metrics.md +9 -7
  16. package/skills/payment/payment-analysis/references/report-template.md +3 -3
  17. package/skills/payment/payment-analysis/references/terminology.md +6 -1
  18. package/skills/payment/payment-analysis/references/visualization.md +1 -1
  19. package/skills/payment/tld-detection/SKILL.md +163 -0
  20. package/skills/payment/tld-detection/agents/openai.yaml +9 -0
  21. package/skills/payment/tld-detection/evals/evals.json +107 -0
  22. package/skills/payment/tld-detection/references/clean.md +51 -0
  23. package/skills/payment/tld-detection/references/clustering.md +219 -0
  24. package/skills/payment/tld-detection/references/decision.md +85 -0
  25. package/skills/payment/tld-detection/references/report-template.md +169 -0
  26. package/skills/payment/tld-detection/references/terminology.md +69 -0
  27. package/skills/payment/tld-detection/references/workflow.md +78 -0
  28. package/skills/payment/tld-detection/scripts/detect_ladders.py +390 -0
  29. package/skills/payment/tld-detection/tests/test_detect_ladders.py +132 -0
  30. package/skills/engineering/resolve-issues/SKILL.md +0 -168
  31. package/skills/engineering/resolve-issues/agents/openai.yaml +0 -9
  32. package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +0 -499
  33. package/skills/engineering/resolve-issues/references/breaker.md +0 -82
  34. package/skills/engineering/resolve-issues/references/deliverables.md +0 -27
  35. package/skills/engineering/resolve-issues/references/delivery.md +0 -108
  36. package/skills/engineering/resolve-issues/references/evidence-lane.md +0 -21
  37. package/skills/engineering/resolve-issues/references/exact-head-ci.md +0 -287
  38. package/skills/engineering/resolve-issues/references/fan-out.md +0 -33
  39. package/skills/engineering/resolve-issues/references/finalization.md +0 -68
  40. package/skills/engineering/resolve-issues/references/guarantees.md +0 -10
  41. package/skills/engineering/resolve-issues/references/high-risk.md +0 -29
  42. package/skills/engineering/resolve-issues/references/incidents/848/README.md +0 -156
  43. package/skills/engineering/resolve-issues/references/intake.md +0 -86
  44. package/skills/engineering/resolve-issues/references/integration-gate.md +0 -53
  45. package/skills/engineering/resolve-issues/references/interference.md +0 -87
  46. package/skills/engineering/resolve-issues/references/loop.md +0 -134
  47. package/skills/engineering/resolve-issues/references/model-catalog.md +0 -9
  48. package/skills/engineering/resolve-issues/references/postmortem.md +0 -27
  49. package/skills/engineering/resolve-issues/references/pre-flight-model-slots.md +0 -41
  50. package/skills/engineering/resolve-issues/references/pre-flight-recording-and-checkout.md +0 -48
  51. package/skills/engineering/resolve-issues/references/pre-flight.md +0 -41
  52. package/skills/engineering/resolve-issues/references/regression-checklist.md +0 -26
  53. package/skills/engineering/resolve-issues/references/run-state.md +0 -288
  54. package/skills/engineering/resolve-issues/references/sandboxed-testing.md +0 -48
  55. package/skills/engineering/resolve-issues/references/spawn-contract.md +0 -96
  56. package/skills/engineering/resolve-issues/references/terminal-evidence-journal.md +0 -40
  57. package/skills/engineering/resolve-issues/references/why.md +0 -653
  58. package/skills/engineering/resolve-issues/schemas/fix-round.schema.json +0 -49
  59. package/skills/engineering/resolve-issues/scripts/combine-and-verify.mjs +0 -721
  60. package/skills/engineering/resolve-issues/scripts/component-candidate.mjs +0 -962
  61. package/skills/engineering/resolve-issues/scripts/contract-revision.mjs +0 -220
  62. package/skills/engineering/resolve-issues/scripts/detect-delivery-mode.mjs +0 -420
  63. package/skills/engineering/resolve-issues/scripts/detect-target-branch.mjs +0 -256
  64. package/skills/engineering/resolve-issues/scripts/detect-workspace-mode.mjs +0 -168
  65. package/skills/engineering/resolve-issues/scripts/discover-models.mjs +0 -9
  66. package/skills/engineering/resolve-issues/scripts/doctrine.mjs +0 -62
  67. package/skills/engineering/resolve-issues/scripts/evidence-lifecycle-contract.mjs +0 -191
  68. package/skills/engineering/resolve-issues/scripts/exact-head-ci.mjs +0 -413
  69. package/skills/engineering/resolve-issues/scripts/exact-head-github-provider.mjs +0 -332
  70. package/skills/engineering/resolve-issues/scripts/finalize.mjs +0 -488
  71. package/skills/engineering/resolve-issues/scripts/fix-rounds.mjs +0 -3307
  72. package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-circular-1001.json +0 -16
  73. package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-valid-sequencing.json +0 -51
  74. package/skills/engineering/resolve-issues/scripts/fixtures/fmm-express-830-component-candidate.json +0 -17
  75. package/skills/engineering/resolve-issues/scripts/fixtures/head-check-set-1081.json +0 -166
  76. package/skills/engineering/resolve-issues/scripts/gate-value-series.mjs +0 -92
  77. package/skills/engineering/resolve-issues/scripts/guide-index.mjs +0 -73
  78. package/skills/engineering/resolve-issues/scripts/head-check-set.mjs +0 -159
  79. package/skills/engineering/resolve-issues/scripts/interference.mjs +0 -427
  80. package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +0 -9
  81. package/skills/engineering/resolve-issues/scripts/next-operations.mjs +0 -419
  82. package/skills/engineering/resolve-issues/scripts/postmortem.mjs +0 -909
  83. package/skills/engineering/resolve-issues/scripts/preflight-questions.mjs +0 -345
  84. package/skills/engineering/resolve-issues/scripts/reconcile-contained-unit-prs.mjs +0 -415
  85. package/skills/engineering/resolve-issues/scripts/release-state-contract.mjs +0 -697
  86. package/skills/engineering/resolve-issues/scripts/report.mjs +0 -494
  87. package/skills/engineering/resolve-issues/scripts/required-check-plan.mjs +0 -172
  88. package/skills/engineering/resolve-issues/scripts/round-metadata.mjs +0 -79
  89. package/skills/engineering/resolve-issues/scripts/run-state-review6-cases.mjs +0 -334
  90. package/skills/engineering/resolve-issues/scripts/run-state.mjs +0 -4792
  91. package/skills/engineering/resolve-issues/scripts/sandbox-selftest.mjs +0 -395
  92. package/skills/engineering/resolve-issues/scripts/spawn-contract.mjs +0 -290
  93. package/skills/engineering/resolve-issues/scripts/terminal-dispositions.mjs +0 -170
  94. package/skills/engineering/resolve-issues/scripts/terminal-evidence-journal.mjs +0 -293
  95. package/skills/engineering/resolve-issues/scripts/unit-kind.mjs +0 -197
  96. package/skills/engineering/resolve-issues/scripts/unit-lifecycle.mjs +0 -127
  97. package/skills/engineering/resolve-issues/scripts/watch-delivery.mjs +0 -893
  98. package/skills/engineering/resolve-issues/scripts/workspaces.mjs +0 -829
  99. package/skills/engineering/resolve-issues/workflows/independent-review.workflow.js +0 -290
  100. package/skills/engineering/resolve-issues/workflows/prior-art-scan.workflow.js +0 -80
  101. package/skills/engineering/resolve-issues/workflows/workflow-smoke.mjs +0 -102
  102. package/skills/engineering/resolve-release/LICENSE +0 -3
  103. package/skills/engineering/resolve-release/SKILL.md +0 -113
  104. package/skills/engineering/resolve-release/agents/openai.yaml +0 -9
  105. package/skills/engineering/resolve-release/references/assembly.md +0 -137
  106. package/skills/engineering/resolve-release/references/auto-when-green.md +0 -56
  107. package/skills/engineering/resolve-release/references/candidate.md +0 -167
  108. package/skills/engineering/resolve-release/references/exposure.md +0 -178
  109. package/skills/engineering/resolve-release/references/handoff.md +0 -24
  110. package/skills/engineering/resolve-release/references/postmortem.md +0 -230
  111. package/skills/engineering/resolve-release/references/preflight.md +0 -208
  112. package/skills/engineering/resolve-release/references/principles.md +0 -94
  113. package/skills/engineering/resolve-release/references/regression-checklist.md +0 -36
  114. package/skills/engineering/resolve-release/references/related-skills.md +0 -13
  115. package/skills/engineering/resolve-release/references/routing.md +0 -149
  116. package/skills/engineering/resolve-release/references/verified-sha-github-flow.md +0 -285
  117. package/skills/engineering/resolve-release/references/versioning.md +0 -202
  118. package/skills/engineering/resolve-release/references/why.md +0 -53
  119. package/skills/engineering/resolve-release/scripts/adapter-completion-artifact.mjs +0 -389
  120. package/skills/engineering/resolve-release/scripts/build-changes.mjs +0 -209
  121. package/skills/engineering/resolve-release/scripts/candidate-hygiene.mjs +0 -407
  122. package/skills/engineering/resolve-release/scripts/candidate-identity.mjs +0 -904
  123. package/skills/engineering/resolve-release/scripts/candidate-traffic.mjs +0 -81
  124. package/skills/engineering/resolve-release/scripts/checked-adapter-loader.mjs +0 -612
  125. package/skills/engineering/resolve-release/scripts/close-attempt.mjs +0 -135
  126. package/skills/engineering/resolve-release/scripts/closeout-release.mjs +0 -161
  127. package/skills/engineering/resolve-release/scripts/doctrine.mjs +0 -106
  128. package/skills/engineering/resolve-release/scripts/durable-processing.mjs +0 -522
  129. package/skills/engineering/resolve-release/scripts/ensure-target-green.mjs +0 -659
  130. package/skills/engineering/resolve-release/scripts/evidence-bundle.mjs +0 -1014
  131. package/skills/engineering/resolve-release/scripts/finalize-release.mjs +0 -526
  132. package/skills/engineering/resolve-release/scripts/fixtures/durable-processing-adapter.mjs +0 -169
  133. package/skills/engineering/resolve-release/scripts/green-gate.mjs +0 -599
  134. package/skills/engineering/resolve-release/scripts/isolated-adapter-evaluator.mjs +0 -752
  135. package/skills/engineering/resolve-release/scripts/metadata-pr-status.mjs +0 -56
  136. package/skills/engineering/resolve-release/scripts/metadata-sync.mjs +0 -1538
  137. package/skills/engineering/resolve-release/scripts/postmortem.mjs +0 -381
  138. package/skills/engineering/resolve-release/scripts/preflight-probes.mjs +0 -510
  139. package/skills/engineering/resolve-release/scripts/production-endpoints.mjs +0 -326
  140. package/skills/engineering/resolve-release/scripts/rc-circuit-breaker.mjs +0 -272
  141. package/skills/engineering/resolve-release/scripts/report.mjs +0 -417
  142. package/skills/engineering/resolve-release/scripts/reprobe-credentials.mjs +0 -114
  143. package/skills/engineering/resolve-release/scripts/revalidate-candidate.mjs +0 -238
  144. package/skills/engineering/resolve-release/scripts/review-packet.mjs +0 -503
  145. package/skills/engineering/resolve-release/scripts/rollback-floor.mjs +0 -263
  146. package/skills/engineering/resolve-release/scripts/version-assert.mjs +0 -339
  147. package/skills/engineering/resolve-release/scripts/version-postmortem.mjs +0 -485
  148. package/skills/engineering/resolve-release/scripts/version.mjs +0 -1199
  149. package/skills/engineering/resolve-release/scripts/watch-candidate-delivery.mjs +0 -449
  150. package/skills/engineering/resolve-release/vendor/ACORN-LICENSE +0 -21
  151. package/skills/engineering/resolve-release/vendor/README.md +0 -60
  152. package/skills/engineering/resolve-release/vendor/acorn.mjs +0 -6233
  153. package/skills/engineering/resolve-release/vendor/quickjs/core/LICENSE +0 -21
  154. package/skills/engineering/resolve-release/vendor/quickjs/core/README.md +0 -341
  155. package/skills/engineering/resolve-release/vendor/quickjs/core/README.template.md +0 -70
  156. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs +0 -2
  157. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs.map +0 -1
  158. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs +0 -7
  159. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs.map +0 -1
  160. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.mts +0 -2033
  161. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.ts +0 -2033
  162. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js +0 -7
  163. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js.map +0 -1
  164. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs +0 -2
  165. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs.map +0 -1
  166. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs +0 -2
  167. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs.map +0 -1
  168. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs +0 -2
  169. package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs.map +0 -1
  170. package/skills/engineering/resolve-release/vendor/quickjs/core/package.json +0 -49
  171. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/LICENSE +0 -21
  172. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/README.md +0 -5
  173. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.mts +0 -549
  174. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.ts +0 -549
  175. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js +0 -2
  176. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js.map +0 -1
  177. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs +0 -2
  178. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs.map +0 -1
  179. package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/package.json +0 -36
  180. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/LICENSE +0 -47
  181. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/README.md +0 -82
  182. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.d.ts +0 -11
  183. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.mjs +0 -22
  184. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cjs +0 -21
  185. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.cjs +0 -21
  186. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.d.ts +0 -11
  187. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.d.ts +0 -11
  188. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.mjs +0 -25
  189. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.wasm +0 -0
  190. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.mts +0 -85
  191. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.ts +0 -85
  192. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js +0 -2
  193. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js.map +0 -1
  194. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs +0 -2
  195. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs.map +0 -1
  196. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.mts +0 -20
  197. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.ts +0 -20
  198. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js +0 -2
  199. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js.map +0 -1
  200. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs +0 -2
  201. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs.map +0 -1
  202. package/skills/engineering/resolve-release/vendor/quickjs/release-sync/package.json +0 -61
  203. /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
- }