@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.
- package/README.md +9 -4
- package/package.json +1 -1
- package/runtime/skills/distribution/generated/recipes.json +54 -48
- package/runtime/skills/distribution/scripts/bundles.mjs +113 -7
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +4 -6
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +3 -36
- package/skills/cicd-pipeline/mergify/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +14 -83
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +4 -6
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +3 -36
- package/skills/engineering/smoke/SKILL.md +1 -1
- package/skills/payment/payment-analysis/SKILL.md +13 -7
- package/skills/payment/payment-analysis/references/chargebacks.md +43 -18
- package/skills/payment/payment-analysis/references/event-layers.md +4 -0
- package/skills/payment/payment-analysis/references/metrics.md +9 -7
- package/skills/payment/payment-analysis/references/report-template.md +3 -3
- package/skills/payment/payment-analysis/references/terminology.md +6 -1
- package/skills/payment/payment-analysis/references/visualization.md +1 -1
- package/skills/payment/tld-detection/SKILL.md +163 -0
- package/skills/payment/tld-detection/agents/openai.yaml +9 -0
- package/skills/payment/tld-detection/evals/evals.json +107 -0
- package/skills/payment/tld-detection/references/clean.md +51 -0
- package/skills/payment/tld-detection/references/clustering.md +219 -0
- package/skills/payment/tld-detection/references/decision.md +85 -0
- package/skills/payment/tld-detection/references/report-template.md +169 -0
- package/skills/payment/tld-detection/references/terminology.md +69 -0
- package/skills/payment/tld-detection/references/workflow.md +78 -0
- package/skills/payment/tld-detection/scripts/detect_ladders.py +390 -0
- package/skills/payment/tld-detection/tests/test_detect_ladders.py +132 -0
- package/skills/engineering/resolve-issues/SKILL.md +0 -168
- package/skills/engineering/resolve-issues/agents/openai.yaml +0 -9
- package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +0 -499
- package/skills/engineering/resolve-issues/references/breaker.md +0 -82
- package/skills/engineering/resolve-issues/references/deliverables.md +0 -27
- package/skills/engineering/resolve-issues/references/delivery.md +0 -108
- package/skills/engineering/resolve-issues/references/evidence-lane.md +0 -21
- package/skills/engineering/resolve-issues/references/exact-head-ci.md +0 -287
- package/skills/engineering/resolve-issues/references/fan-out.md +0 -33
- package/skills/engineering/resolve-issues/references/finalization.md +0 -68
- package/skills/engineering/resolve-issues/references/guarantees.md +0 -10
- package/skills/engineering/resolve-issues/references/high-risk.md +0 -29
- package/skills/engineering/resolve-issues/references/incidents/848/README.md +0 -156
- package/skills/engineering/resolve-issues/references/intake.md +0 -86
- package/skills/engineering/resolve-issues/references/integration-gate.md +0 -53
- package/skills/engineering/resolve-issues/references/interference.md +0 -87
- package/skills/engineering/resolve-issues/references/loop.md +0 -134
- package/skills/engineering/resolve-issues/references/model-catalog.md +0 -9
- package/skills/engineering/resolve-issues/references/postmortem.md +0 -27
- package/skills/engineering/resolve-issues/references/pre-flight-model-slots.md +0 -41
- package/skills/engineering/resolve-issues/references/pre-flight-recording-and-checkout.md +0 -48
- package/skills/engineering/resolve-issues/references/pre-flight.md +0 -41
- package/skills/engineering/resolve-issues/references/regression-checklist.md +0 -26
- package/skills/engineering/resolve-issues/references/run-state.md +0 -288
- package/skills/engineering/resolve-issues/references/sandboxed-testing.md +0 -48
- package/skills/engineering/resolve-issues/references/spawn-contract.md +0 -96
- package/skills/engineering/resolve-issues/references/terminal-evidence-journal.md +0 -40
- package/skills/engineering/resolve-issues/references/why.md +0 -653
- package/skills/engineering/resolve-issues/schemas/fix-round.schema.json +0 -49
- package/skills/engineering/resolve-issues/scripts/combine-and-verify.mjs +0 -721
- package/skills/engineering/resolve-issues/scripts/component-candidate.mjs +0 -962
- package/skills/engineering/resolve-issues/scripts/contract-revision.mjs +0 -220
- package/skills/engineering/resolve-issues/scripts/detect-delivery-mode.mjs +0 -420
- package/skills/engineering/resolve-issues/scripts/detect-target-branch.mjs +0 -256
- package/skills/engineering/resolve-issues/scripts/detect-workspace-mode.mjs +0 -168
- package/skills/engineering/resolve-issues/scripts/discover-models.mjs +0 -9
- package/skills/engineering/resolve-issues/scripts/doctrine.mjs +0 -62
- package/skills/engineering/resolve-issues/scripts/evidence-lifecycle-contract.mjs +0 -191
- package/skills/engineering/resolve-issues/scripts/exact-head-ci.mjs +0 -413
- package/skills/engineering/resolve-issues/scripts/exact-head-github-provider.mjs +0 -332
- package/skills/engineering/resolve-issues/scripts/finalize.mjs +0 -488
- package/skills/engineering/resolve-issues/scripts/fix-rounds.mjs +0 -3307
- package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-circular-1001.json +0 -16
- package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-valid-sequencing.json +0 -51
- package/skills/engineering/resolve-issues/scripts/fixtures/fmm-express-830-component-candidate.json +0 -17
- package/skills/engineering/resolve-issues/scripts/fixtures/head-check-set-1081.json +0 -166
- package/skills/engineering/resolve-issues/scripts/gate-value-series.mjs +0 -92
- package/skills/engineering/resolve-issues/scripts/guide-index.mjs +0 -73
- package/skills/engineering/resolve-issues/scripts/head-check-set.mjs +0 -159
- package/skills/engineering/resolve-issues/scripts/interference.mjs +0 -427
- package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +0 -9
- package/skills/engineering/resolve-issues/scripts/next-operations.mjs +0 -419
- package/skills/engineering/resolve-issues/scripts/postmortem.mjs +0 -909
- package/skills/engineering/resolve-issues/scripts/preflight-questions.mjs +0 -345
- package/skills/engineering/resolve-issues/scripts/reconcile-contained-unit-prs.mjs +0 -415
- package/skills/engineering/resolve-issues/scripts/release-state-contract.mjs +0 -697
- package/skills/engineering/resolve-issues/scripts/report.mjs +0 -494
- package/skills/engineering/resolve-issues/scripts/required-check-plan.mjs +0 -172
- package/skills/engineering/resolve-issues/scripts/round-metadata.mjs +0 -79
- package/skills/engineering/resolve-issues/scripts/run-state-review6-cases.mjs +0 -334
- package/skills/engineering/resolve-issues/scripts/run-state.mjs +0 -4792
- package/skills/engineering/resolve-issues/scripts/sandbox-selftest.mjs +0 -395
- package/skills/engineering/resolve-issues/scripts/spawn-contract.mjs +0 -290
- package/skills/engineering/resolve-issues/scripts/terminal-dispositions.mjs +0 -170
- package/skills/engineering/resolve-issues/scripts/terminal-evidence-journal.mjs +0 -293
- package/skills/engineering/resolve-issues/scripts/unit-kind.mjs +0 -197
- package/skills/engineering/resolve-issues/scripts/unit-lifecycle.mjs +0 -127
- package/skills/engineering/resolve-issues/scripts/watch-delivery.mjs +0 -893
- package/skills/engineering/resolve-issues/scripts/workspaces.mjs +0 -829
- package/skills/engineering/resolve-issues/workflows/independent-review.workflow.js +0 -290
- package/skills/engineering/resolve-issues/workflows/prior-art-scan.workflow.js +0 -80
- package/skills/engineering/resolve-issues/workflows/workflow-smoke.mjs +0 -102
- package/skills/engineering/resolve-release/LICENSE +0 -3
- package/skills/engineering/resolve-release/SKILL.md +0 -113
- package/skills/engineering/resolve-release/agents/openai.yaml +0 -9
- package/skills/engineering/resolve-release/references/assembly.md +0 -137
- package/skills/engineering/resolve-release/references/auto-when-green.md +0 -56
- package/skills/engineering/resolve-release/references/candidate.md +0 -167
- package/skills/engineering/resolve-release/references/exposure.md +0 -178
- package/skills/engineering/resolve-release/references/handoff.md +0 -24
- package/skills/engineering/resolve-release/references/postmortem.md +0 -230
- package/skills/engineering/resolve-release/references/preflight.md +0 -208
- package/skills/engineering/resolve-release/references/principles.md +0 -94
- package/skills/engineering/resolve-release/references/regression-checklist.md +0 -36
- package/skills/engineering/resolve-release/references/related-skills.md +0 -13
- package/skills/engineering/resolve-release/references/routing.md +0 -149
- package/skills/engineering/resolve-release/references/verified-sha-github-flow.md +0 -285
- package/skills/engineering/resolve-release/references/versioning.md +0 -202
- package/skills/engineering/resolve-release/references/why.md +0 -53
- package/skills/engineering/resolve-release/scripts/adapter-completion-artifact.mjs +0 -389
- package/skills/engineering/resolve-release/scripts/build-changes.mjs +0 -209
- package/skills/engineering/resolve-release/scripts/candidate-hygiene.mjs +0 -407
- package/skills/engineering/resolve-release/scripts/candidate-identity.mjs +0 -904
- package/skills/engineering/resolve-release/scripts/candidate-traffic.mjs +0 -81
- package/skills/engineering/resolve-release/scripts/checked-adapter-loader.mjs +0 -612
- package/skills/engineering/resolve-release/scripts/close-attempt.mjs +0 -135
- package/skills/engineering/resolve-release/scripts/closeout-release.mjs +0 -161
- package/skills/engineering/resolve-release/scripts/doctrine.mjs +0 -106
- package/skills/engineering/resolve-release/scripts/durable-processing.mjs +0 -522
- package/skills/engineering/resolve-release/scripts/ensure-target-green.mjs +0 -659
- package/skills/engineering/resolve-release/scripts/evidence-bundle.mjs +0 -1014
- package/skills/engineering/resolve-release/scripts/finalize-release.mjs +0 -526
- package/skills/engineering/resolve-release/scripts/fixtures/durable-processing-adapter.mjs +0 -169
- package/skills/engineering/resolve-release/scripts/green-gate.mjs +0 -599
- package/skills/engineering/resolve-release/scripts/isolated-adapter-evaluator.mjs +0 -752
- package/skills/engineering/resolve-release/scripts/metadata-pr-status.mjs +0 -56
- package/skills/engineering/resolve-release/scripts/metadata-sync.mjs +0 -1538
- package/skills/engineering/resolve-release/scripts/postmortem.mjs +0 -381
- package/skills/engineering/resolve-release/scripts/preflight-probes.mjs +0 -510
- package/skills/engineering/resolve-release/scripts/production-endpoints.mjs +0 -326
- package/skills/engineering/resolve-release/scripts/rc-circuit-breaker.mjs +0 -272
- package/skills/engineering/resolve-release/scripts/report.mjs +0 -417
- package/skills/engineering/resolve-release/scripts/reprobe-credentials.mjs +0 -114
- package/skills/engineering/resolve-release/scripts/revalidate-candidate.mjs +0 -238
- package/skills/engineering/resolve-release/scripts/review-packet.mjs +0 -503
- package/skills/engineering/resolve-release/scripts/rollback-floor.mjs +0 -263
- package/skills/engineering/resolve-release/scripts/version-assert.mjs +0 -339
- package/skills/engineering/resolve-release/scripts/version-postmortem.mjs +0 -485
- package/skills/engineering/resolve-release/scripts/version.mjs +0 -1199
- package/skills/engineering/resolve-release/scripts/watch-candidate-delivery.mjs +0 -449
- package/skills/engineering/resolve-release/vendor/ACORN-LICENSE +0 -21
- package/skills/engineering/resolve-release/vendor/README.md +0 -60
- package/skills/engineering/resolve-release/vendor/acorn.mjs +0 -6233
- package/skills/engineering/resolve-release/vendor/quickjs/core/LICENSE +0 -21
- package/skills/engineering/resolve-release/vendor/quickjs/core/README.md +0 -341
- package/skills/engineering/resolve-release/vendor/quickjs/core/README.template.md +0 -70
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs +0 -7
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.mts +0 -2033
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.ts +0 -2033
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js +0 -7
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/core/package.json +0 -49
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/LICENSE +0 -21
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/README.md +0 -5
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.mts +0 -549
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.ts +0 -549
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/package.json +0 -36
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/LICENSE +0 -47
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/README.md +0 -82
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.d.ts +0 -11
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.mjs +0 -22
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cjs +0 -21
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.cjs +0 -21
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.d.ts +0 -11
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.d.ts +0 -11
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.mjs +0 -25
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.wasm +0 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.mts +0 -85
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.ts +0 -85
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.mts +0 -20
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.ts +0 -20
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs +0 -2
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs.map +0 -1
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/package.json +0 -61
- /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
|
-
}
|