@1aboveio/skills 0.19.3 → 0.20.2

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