@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,415 +0,0 @@
1
- import { execFileSync, spawnSync } from 'node:child_process'
2
-
3
- const SHA_RE = /^[0-9a-f]{7,64}$/i
4
- const isObject = (value) => value != null && typeof value === 'object' && !Array.isArray(value)
5
- const nonEmpty = (value) => typeof value === 'string' && value.trim().length > 0
6
- const clone = (value) => structuredClone(value)
7
- const same = (left, right) => JSON.stringify(left) === JSON.stringify(right)
8
- const refNumber = (value) => {
9
- if (Number.isInteger(value) && value > 0) return value
10
- const match = String(value ?? '').match(/(?:^|#)(\d+)$/)
11
- return match ? Number(match[1]) : null
12
- }
13
-
14
- const COMPONENT_OUTCOMES = new Set([
15
- 'component-merged',
16
- 'component-unreadable',
17
- 'component-not-merged',
18
- 'component-merge-sha-mismatch',
19
- ])
20
- const UNIT_OUTCOMES = new Set([
21
- 'closed-superseded',
22
- 'already-closed',
23
- 'already-merged',
24
- 'head-drift',
25
- 'not-contained',
26
- 'unit-unreadable',
27
- 'ancestry-unreadable',
28
- 'preclose-unreadable',
29
- 'preclose-head-drift',
30
- 'preclose-not-open',
31
- 'close-failed',
32
- 'blocked-component',
33
- ])
34
- const TERMINAL_UNIT_OUTCOMES = new Set(['closed-superseded', 'already-closed', 'already-merged'])
35
- const FAILED_UNIT_OUTCOMES = new Set([
36
- 'head-drift',
37
- 'not-contained',
38
- 'unit-unreadable',
39
- 'ancestry-unreadable',
40
- 'preclose-unreadable',
41
- 'preclose-head-drift',
42
- 'preclose-not-open',
43
- 'close-failed',
44
- 'blocked-component',
45
- ])
46
-
47
- function normalizedPr(kind, requestedNumber, live) {
48
- if (!isObject(live)) throw new Error(`${kind} #${requestedNumber} returned no structured observation`)
49
- if (!Number.isInteger(live.number) || live.number <= 0) throw new Error(`${kind} #${requestedNumber} returned missing or malformed numeric identity`)
50
- if (live.number !== requestedNumber) throw new Error(`${kind} #${requestedNumber} returned identity ${live.number}`)
51
- if (!nonEmpty(live.state)) throw new Error(`${kind} #${requestedNumber} returned no state`)
52
- return { ...clone(live), number: requestedNumber }
53
- }
54
-
55
- function candidateUnitEvidence(component, generation) {
56
- const candidate = component?.candidate
57
- if (!isObject(candidate) || candidate.generation !== generation || candidate.frozen?.generation !== generation) {
58
- throw new Error(`component ${component?.id ?? '(unknown)'} has no frozen candidate evidence for delivered generation ${generation}`)
59
- }
60
- if (!Array.isArray(candidate.frozen.unitEvidence)) {
61
- throw new Error(`component ${component.id} frozen unitEvidence is missing`)
62
- }
63
- return candidate.frozen.unitEvidence
64
- }
65
-
66
- function reconciliationTargets(manifest) {
67
- if (manifest?.finalization !== undefined) throw new Error('contained unit PR reconciliation is pre-finalization only')
68
- if (!nonEmpty(manifest?.repository)) throw new Error('repository is required for contained unit PR reconciliation')
69
- if (!nonEmpty(manifest?.targetBranch)) throw new Error('targetBranch is required for contained unit PR reconciliation')
70
- const units = Array.isArray(manifest?.units) ? manifest.units : []
71
- const byId = new Map(units.map((unit) => [unit?.id, unit]))
72
- const targets = []
73
-
74
- for (const component of Array.isArray(manifest?.components) ? manifest.components : []) {
75
- const delivery = component?.delivery
76
- if (!isObject(delivery) || delivery.outcome !== 'merged') continue
77
- const componentPr = refNumber(delivery.pr)
78
- const publishedPr = refNumber(component.pr)
79
- if (componentPr == null || publishedPr !== componentPr) {
80
- throw new Error(`component ${component?.id ?? '(unknown)'} has no unambiguous published combined PR`)
81
- }
82
- if (!Number.isSafeInteger(delivery.generation) || delivery.generation < 1 || !SHA_RE.test(delivery.mergedSha ?? '')) {
83
- throw new Error(`component ${component.id} has malformed delivered generation or merge SHA`)
84
- }
85
- const evidence = candidateUnitEvidence(component, delivery.generation)
86
- const evidenceById = new Map(evidence.map((entry) => [entry?.unitId, entry]))
87
- const linkedUnits = []
88
- for (const unitId of Array.isArray(component.units) ? component.units : []) {
89
- const unit = byId.get(unitId)
90
- if (!unit || !same(unit.deliveryRef, { componentId: component.id, generation: delivery.generation })) continue
91
- const unitPr = refNumber(unit.pr)
92
- if (unitPr == null || unitPr === componentPr) continue
93
- const reviewed = evidenceById.get(unitId)
94
- if (!isObject(reviewed) || refNumber(reviewed.pr) !== unitPr) continue
95
- if (!SHA_RE.test(reviewed.headSha ?? '') || reviewed.preliminaryReview?.verdict !== 'PASS'
96
- || reviewed.preliminaryReview?.headSha !== reviewed.headSha) {
97
- throw new Error(`component ${component.id} unit ${unitId} has no exact reviewed unit head in publication evidence`)
98
- }
99
- linkedUnits.push({ unitId, pr: unitPr, reviewedHeadSha: reviewed.headSha })
100
- }
101
- if (linkedUnits.length) targets.push({ component, delivery, componentPr, linkedUnits })
102
- }
103
- return targets
104
- }
105
-
106
- function supersessionComment({ componentPr, reviewedHeadSha, targetRef, mergedSha }) {
107
- return `Closing this unit PR as superseded by combined PR #${componentPr}. The exact reviewed unit head ${reviewedHeadSha} is contained in freshly fetched ${targetRef}; the combined component merged as ${mergedSha}.`
108
- }
109
-
110
- function componentRecord(target, fetchedTarget, at) {
111
- return {
112
- at,
113
- target: {
114
- branch: fetchedTarget.branch,
115
- ref: fetchedTarget.ref,
116
- headSha: fetchedTarget.headSha,
117
- ...(nonEmpty(fetchedTarget.fetchedAt) ? { fetchedAt: fetchedTarget.fetchedAt } : {}),
118
- },
119
- componentPr: target.componentPr,
120
- generation: target.delivery.generation,
121
- mergedSha: target.delivery.mergedSha,
122
- component: null,
123
- outcome: null,
124
- units: target.linkedUnits.map((unit) => ({
125
- ...unit,
126
- live: null,
127
- headMatches: null,
128
- ancestry: null,
129
- outcome: 'blocked-component',
130
- })),
131
- }
132
- }
133
-
134
- export async function reconcileContainedUnitPrs(manifest, boundary) {
135
- const out = clone(manifest)
136
- const targets = reconciliationTargets(out)
137
- let fetchedTarget
138
- try {
139
- fetchedTarget = await boundary.fetchTarget(out.targetBranch)
140
- } catch (error) {
141
- return { ok: false, errors: [`fresh target fetch failed: ${error.message ?? error}`], manifest: out, target: null, reconciliations: [] }
142
- }
143
- if (!isObject(fetchedTarget) || fetchedTarget.branch !== out.targetBranch || !nonEmpty(fetchedTarget.ref) || !nonEmpty(fetchedTarget.headSha)) {
144
- return { ok: false, errors: ['fresh target fetch returned no matching branch/ref/head SHA'], manifest: out, target: fetchedTarget ?? null, reconciliations: [] }
145
- }
146
-
147
- const at = boundary.now()
148
- const records = targets.map((target) => componentRecord(target, fetchedTarget, at))
149
- const errors = []
150
-
151
- // Phase one observes every component and unit head before any outward mutation.
152
- for (let index = 0; index < targets.length; index++) {
153
- const target = targets[index]
154
- const record = records[index]
155
- try {
156
- record.component = normalizedPr('component PR', target.componentPr, await boundary.readPr(target.componentPr, out.repository))
157
- } catch (error) {
158
- record.outcome = 'component-unreadable'
159
- errors.push(`component ${target.component.id} PR #${target.componentPr} could not be verified live: ${error.message ?? error}`)
160
- continue
161
- }
162
- if (record.component.state !== 'MERGED' || record.component.merged !== true) {
163
- record.outcome = 'component-not-merged'
164
- errors.push(`component ${target.component.id} PR #${target.componentPr} is not live-verified merged`)
165
- continue
166
- }
167
- if (record.component.mergeCommitSha !== target.delivery.mergedSha) {
168
- record.outcome = 'component-merge-sha-mismatch'
169
- errors.push(`component ${target.component.id} PR #${target.componentPr} merge SHA is ${record.component.mergeCommitSha ?? 'missing'}, expected ${target.delivery.mergedSha}`)
170
- continue
171
- }
172
- record.outcome = 'component-merged'
173
-
174
- for (const unit of record.units) {
175
- try {
176
- unit.live = normalizedPr('unit PR', unit.pr, await boundary.readPr(unit.pr, out.repository))
177
- } catch (error) {
178
- unit.outcome = 'unit-unreadable'
179
- unit.error = error.message ?? String(error)
180
- errors.push(`unit ${unit.unitId} PR #${unit.pr} could not be verified live: ${unit.error}`)
181
- continue
182
- }
183
- unit.headMatches = unit.live.headSha === unit.reviewedHeadSha
184
- if (unit.live.state === 'MERGED' || unit.live.merged === true) {
185
- unit.outcome = 'already-merged'
186
- continue
187
- }
188
- if (unit.live.state === 'CLOSED') {
189
- unit.outcome = 'already-closed'
190
- continue
191
- }
192
- if (unit.live.state !== 'OPEN') {
193
- unit.outcome = 'unit-unreadable'
194
- unit.error = `unsupported live state ${unit.live.state}`
195
- errors.push(`unit ${unit.unitId} PR #${unit.pr} returned ${unit.error}`)
196
- continue
197
- }
198
- if (!unit.headMatches) {
199
- unit.outcome = 'head-drift'
200
- errors.push(`unit ${unit.unitId} PR #${unit.pr} head ${unit.live.headSha ?? 'missing'} does not equal reviewed head ${unit.reviewedHeadSha}`)
201
- continue
202
- }
203
- let contained
204
- try {
205
- contained = await boundary.isAncestor(unit.reviewedHeadSha, fetchedTarget.ref)
206
- } catch (error) {
207
- unit.outcome = 'ancestry-unreadable'
208
- unit.error = error.message ?? String(error)
209
- errors.push(`unit ${unit.unitId} ancestry could not be verified: ${unit.error}`)
210
- continue
211
- }
212
- unit.ancestry = { targetRef: fetchedTarget.ref, contained: contained === true }
213
- if (contained !== true) {
214
- unit.outcome = 'not-contained'
215
- errors.push(`unit ${unit.unitId} reviewed head ${unit.reviewedHeadSha} is not an ancestor of freshly fetched ${fetchedTarget.ref}`)
216
- continue
217
- }
218
- unit.outcome = 'pending-close'
219
- }
220
- }
221
-
222
- // Phase two re-reads each eligible head immediately before the close mutation.
223
- for (let index = 0; index < targets.length; index++) {
224
- const target = targets[index]
225
- const record = records[index]
226
- if (record.outcome !== 'component-merged') continue
227
- for (const unit of record.units.filter((entry) => entry.outcome === 'pending-close')) {
228
- let preclose
229
- try {
230
- preclose = normalizedPr('unit PR', unit.pr, await boundary.readPr(unit.pr, out.repository))
231
- } catch (error) {
232
- unit.outcome = 'preclose-unreadable'
233
- unit.error = error.message ?? String(error)
234
- errors.push(`unit ${unit.unitId} PR #${unit.pr} pre-close read failed: ${unit.error}`)
235
- continue
236
- }
237
- if (preclose.headSha !== unit.reviewedHeadSha) {
238
- unit.outcome = 'preclose-head-drift'
239
- unit.live = preclose
240
- unit.headMatches = false
241
- errors.push(`unit ${unit.unitId} PR #${unit.pr} moved to ${preclose.headSha ?? 'missing'} before close`)
242
- continue
243
- }
244
- if (preclose.state !== 'OPEN' || preclose.merged === true) {
245
- unit.outcome = preclose.state === 'MERGED' || preclose.merged === true ? 'already-merged' : preclose.state === 'CLOSED' ? 'already-closed' : 'preclose-not-open'
246
- unit.live = preclose
247
- if (unit.outcome === 'preclose-not-open') errors.push(`unit ${unit.unitId} PR #${unit.pr} is ${preclose.state}, not OPEN, before close`)
248
- continue
249
- }
250
- unit.comment = supersessionComment({
251
- componentPr: target.componentPr,
252
- reviewedHeadSha: unit.reviewedHeadSha,
253
- targetRef: fetchedTarget.ref,
254
- mergedSha: target.delivery.mergedSha,
255
- })
256
- try {
257
- const closed = normalizedPr('closed unit PR', unit.pr, await boundary.closePr(unit.pr, out.repository, unit.comment))
258
- if (!['CLOSED', 'MERGED'].includes(closed.state)) throw new Error(`close returned state ${closed.state}`)
259
- unit.live = closed
260
- unit.outcome = closed.state === 'MERGED' || closed.merged === true ? 'already-merged' : 'closed-superseded'
261
- } catch (error) {
262
- unit.outcome = 'close-failed'
263
- unit.error = error.message ?? String(error)
264
- errors.push(`unit ${unit.unitId} PR #${unit.pr} close failed: ${unit.error}`)
265
- }
266
- }
267
- }
268
-
269
- for (let index = 0; index < targets.length; index++) {
270
- const delivery = targets[index].component.delivery
271
- delivery.reconciliations = [...(Array.isArray(delivery.reconciliations) ? delivery.reconciliations : []), records[index]]
272
- }
273
- const recordErrors = reconciliationErrors(out)
274
- if (recordErrors.length) throw new Error(`invalid reconciliation result: ${recordErrors[0]}`)
275
- return {
276
- ok: errors.length === 0 && records.every((record) => record.outcome === 'component-merged'
277
- && record.units.every((unit) => !FAILED_UNIT_OUTCOMES.has(unit.outcome))),
278
- errors,
279
- manifest: out,
280
- target: fetchedTarget,
281
- reconciliations: records,
282
- }
283
- }
284
-
285
- export function unitPrReconciliationStatus(manifest) {
286
- let targets
287
- try {
288
- targets = reconciliationTargets(manifest)
289
- } catch (error) {
290
- return { state: 'invalid', required: true, complete: false, reason: error.message ?? String(error), components: [] }
291
- }
292
- if (!targets.length) return { state: 'not-required', required: false, complete: true, reason: 'no delivered combined component has explicitly published, distinct unit PRs', components: [] }
293
- const components = targets.map((target) => {
294
- const latest = Array.isArray(target.delivery.reconciliations) ? target.delivery.reconciliations.at(-1) : null
295
- const observed = new Map((Array.isArray(latest?.units) ? latest.units : []).map((unit) => [unit?.unitId, unit]))
296
- const pendingUnits = target.linkedUnits
297
- .filter((unit) => !TERMINAL_UNIT_OUTCOMES.has(observed.get(unit.unitId)?.outcome))
298
- .map((unit) => unit.unitId)
299
- return { componentId: target.component.id, componentPr: target.componentPr, pendingUnits, complete: pendingUnits.length === 0 }
300
- })
301
- const complete = components.every((component) => component.complete)
302
- return {
303
- state: complete ? 'complete' : 'required',
304
- required: true,
305
- complete,
306
- reason: complete ? 'all linked unit PRs have terminal live observations' : 'delivered combined components still have unreconciled unit PRs',
307
- components,
308
- }
309
- }
310
-
311
- export function reconciliationErrors(manifest) {
312
- const errors = []
313
- const units = new Map((Array.isArray(manifest?.units) ? manifest.units : []).map((unit) => [unit?.id, unit]))
314
- for (const [componentIndex, component] of (Array.isArray(manifest?.components) ? manifest.components : []).entries()) {
315
- const reconciliations = component?.delivery?.reconciliations
316
- if (reconciliations === undefined) continue
317
- const label = `components[${componentIndex}] (${component?.id ?? 'unknown'}).delivery.reconciliations`
318
- if (!Array.isArray(reconciliations)) { errors.push(`${label} must be an array`); continue }
319
- for (const [recordIndex, record] of reconciliations.entries()) {
320
- const at = `${label}[${recordIndex}]`
321
- if (!isObject(record)) { errors.push(`${at} must be an object`); continue }
322
- const unknown = Object.keys(record).filter((key) => !['at', 'target', 'componentPr', 'generation', 'mergedSha', 'component', 'outcome', 'units'].includes(key))
323
- if (unknown.length) errors.push(`${at} has unknown property ${unknown[0]}`)
324
- if (!nonEmpty(record.at) || !nonEmpty(record.target?.ref) || !SHA_RE.test(record.target?.headSha ?? '')) errors.push(`${at} requires timestamp and fresh target ref/head`)
325
- if (record.target?.branch !== manifest.targetBranch || record.target?.ref !== `origin/${manifest.targetBranch}`) errors.push(`${at}.target must equal the freshly fetched origin targetBranch`)
326
- if (record.componentPr !== refNumber(component.delivery?.pr) || record.generation !== component.delivery?.generation || record.mergedSha !== component.delivery?.mergedSha) {
327
- errors.push(`${at} must bind the canonical component delivery PR, generation, and merge SHA`)
328
- }
329
- if (!COMPONENT_OUTCOMES.has(record.outcome)) errors.push(`${at}.outcome is invalid`)
330
- if (record.component != null && record.component.number !== record.componentPr) errors.push(`${at}.component identity is stale`)
331
- if (record.outcome === 'component-merged'
332
- && (record.component?.state !== 'MERGED' || record.component?.merged !== true || record.component?.mergeCommitSha !== record.mergedSha)) {
333
- errors.push(`${at}.component does not prove the recorded merged outcome at the canonical merge SHA`)
334
- }
335
- if (!Array.isArray(record.units)) { errors.push(`${at}.units must be an array`); continue }
336
- const publishedEvidence = new Map((Array.isArray(component.candidate?.frozen?.unitEvidence) ? component.candidate.frozen.unitEvidence : [])
337
- .map((entry) => [entry?.unitId, entry]))
338
- const seen = new Set()
339
- for (const [unitIndex, row] of record.units.entries()) {
340
- const unitAt = `${at}.units[${unitIndex}]`
341
- if (!isObject(row)) { errors.push(`${unitAt} must be an object`); continue }
342
- const unknownUnit = Object.keys(row).filter((key) => !['unitId', 'pr', 'reviewedHeadSha', 'live', 'headMatches', 'ancestry', 'outcome', 'comment', 'error'].includes(key))
343
- if (unknownUnit.length) errors.push(`${unitAt} has unknown property ${unknownUnit[0]}`)
344
- if (!nonEmpty(row.unitId) || seen.has(row.unitId)) errors.push(`${unitAt}.unitId must be non-empty and unique`)
345
- seen.add(row.unitId)
346
- if (!UNIT_OUTCOMES.has(row.outcome)) errors.push(`${unitAt}.outcome is invalid`)
347
- if (!SHA_RE.test(row.reviewedHeadSha ?? '') || refNumber(row.pr) == null) errors.push(`${unitAt} requires unit PR and reviewed head`)
348
- const unit = units.get(row.unitId)
349
- if (!unit || !same(unit.deliveryRef, { componentId: component.id, generation: record.generation })) errors.push(`${unitAt} is not linked to this delivered component generation`)
350
- const published = publishedEvidence.get(row.unitId)
351
- if (!published || refNumber(published.pr) !== row.pr || published.headSha !== row.reviewedHeadSha
352
- || published.preliminaryReview?.verdict !== 'PASS' || published.preliminaryReview?.headSha !== row.reviewedHeadSha) {
353
- errors.push(`${unitAt} does not match the component's exact published reviewed-unit evidence`)
354
- }
355
- if (row.live != null && row.live.number !== row.pr) errors.push(`${unitAt}.live identity is stale`)
356
- if (row.ancestry != null && (row.ancestry.targetRef !== record.target?.ref || typeof row.ancestry.contained !== 'boolean')) errors.push(`${unitAt}.ancestry is stale`)
357
- if (row.outcome === 'closed-superseded'
358
- && (row.live?.state !== 'CLOSED' || row.headMatches !== true || row.ancestry?.contained !== true)) {
359
- errors.push(`${unitAt} closed-superseded outcome lacks exact-head, ancestry, or terminal-state proof`)
360
- }
361
- if (row.outcome === 'already-closed' && row.live?.state !== 'CLOSED') errors.push(`${unitAt} already-closed outcome lacks CLOSED state`)
362
- if (row.outcome === 'already-merged' && row.live?.state !== 'MERGED' && row.live?.merged !== true) errors.push(`${unitAt} already-merged outcome lacks MERGED state`)
363
- if (row.outcome === 'head-drift' && row.headMatches !== false) errors.push(`${unitAt} head-drift outcome lacks a mismatched live head`)
364
- if (row.outcome === 'not-contained' && row.ancestry?.contained !== false) errors.push(`${unitAt} not-contained outcome lacks negative fresh-target ancestry`)
365
- if (row.outcome === 'closed-superseded') {
366
- for (const expected of [`#${record.componentPr}`, row.reviewedHeadSha, record.target.ref, record.mergedSha]) {
367
- if (!nonEmpty(row.comment) || !row.comment.includes(expected)) errors.push(`${unitAt}.comment must name combined PR, reviewed head, target ref, and merge SHA`)
368
- }
369
- }
370
- }
371
- }
372
- }
373
- return errors
374
- }
375
-
376
- function runJson(command, args, cwd) {
377
- return JSON.parse(execFileSync(command, args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }))
378
- }
379
-
380
- export function createLiveReconciliationBoundary({ cwd = process.cwd(), now = () => new Date().toISOString() } = {}) {
381
- const readPr = async (id, repository) => {
382
- if (!Number.isInteger(id) || !nonEmpty(repository)) throw new Error('PR number and repository are required')
383
- const view = runJson('gh', ['pr', 'view', String(id), '--repo', repository, '--json', 'number,state,mergedAt,mergeCommit,url,headRefOid'], cwd)
384
- return {
385
- number: view.number,
386
- state: view.state,
387
- merged: view.state === 'MERGED' || nonEmpty(view.mergedAt),
388
- mergedAt: view.mergedAt ?? null,
389
- mergeCommitSha: view.mergeCommit?.oid ?? null,
390
- headSha: view.headRefOid ?? null,
391
- url: view.url,
392
- }
393
- }
394
- return {
395
- now,
396
- fetchTarget: async (branch) => {
397
- if (!nonEmpty(branch)) throw new Error('targetBranch is not recorded')
398
- execFileSync('git', ['fetch', 'origin', branch], { cwd, stdio: ['ignore', 'ignore', 'pipe'] })
399
- const ref = `origin/${branch}`
400
- const headSha = execFileSync('git', ['rev-parse', ref], { cwd, encoding: 'utf8' }).trim()
401
- return { remote: 'origin', branch, ref, headSha, fetchedAt: now() }
402
- },
403
- isAncestor: async (headSha, ref) => {
404
- const result = spawnSync('git', ['merge-base', '--is-ancestor', headSha, ref], { cwd, encoding: 'utf8' })
405
- if (result.status === 0) return true
406
- if (result.status === 1) return false
407
- throw new Error(result.stderr?.trim() || `git merge-base --is-ancestor exited ${result.status ?? 'without status'}`)
408
- },
409
- readPr,
410
- closePr: async (id, repository, comment) => {
411
- execFileSync('gh', ['pr', 'close', String(id), '--repo', repository, '--comment', comment], { cwd, stdio: ['ignore', 'ignore', 'pipe'] })
412
- return readPr(id, repository)
413
- },
414
- }
415
- }