@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,522 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import crypto from 'node:crypto'
4
- import fs from 'node:fs'
5
- import path from 'node:path'
6
- import { CheckedAdapterError, loadCheckedAdapter } from './checked-adapter-loader.mjs'
7
- import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
8
-
9
- export const ADAPTER_EXPORT = 'DURABLE_PROCESSING_ADAPTER'
10
- export const ADAPTER_SCHEMA_VERSION = 1
11
- export const RECEIPT_SCHEMA = 'resolve-release/durable-processing-receipt@1'
12
- export const PAUSE_ORDER = Object.freeze(['database-admission', 'worker-scheduling'])
13
- export const RESUME_ORDER = Object.freeze([...PAUSE_ORDER].reverse())
14
-
15
- const CLI_WRITER = Object.freeze({ id: 'resolve-release/durable-processing' })
16
-
17
- const USAGE = `durable-processing.mjs - drive a repository-declared durable-processing adapter
18
-
19
- Usage:
20
- durable-processing.mjs preflight --declaration <release.json>
21
- durable-processing.mjs status --declaration <release.json> [options]
22
- durable-processing.mjs pause --declaration <release.json> [options]
23
- durable-processing.mjs quiescence --declaration <release.json> [options]
24
- durable-processing.mjs resume --declaration <release.json> [options]
25
- durable-processing.mjs down --declaration <release.json> [options]
26
- durable-processing.mjs up --declaration <release.json> [options]
27
- durable-processing.mjs unwind-pre-shift --declaration <release.json> [options]
28
-
29
- The declaration must decide durableProcessing.present as true or false. A true
30
- declaration names an ES module exporting DURABLE_PROCESSING_ADAPTER. The adapter
31
- contains typed data and an execute(request) function; declarations contain no shell
32
- commands, credentials, timestamps, or platform-specific snippets. The adapter is a
33
- dependency-free strict-UTF-8 .mjs file no larger than 256 KiB. Execution receives
34
- only the declared config and isolated snapshot-backed readTextFile/writeTextFile operations;
35
- validated final write intents commit in the host only after receipt validation. The host stages
36
- same-directory final and original files, validates every target identity immediately before the
37
- first deterministic replacement, and restores replaced targets in reverse if a later operation
38
- fails. Rollback failure is reported as ambiguous with affected targets. This recoverable
39
- transaction cannot provide cross-file kernel atomicity or guaranteed recovery from process crash
40
- or power loss during the replacement window.`
41
-
42
- const isObject = (value) => value != null && typeof value === 'object' && !Array.isArray(value)
43
- const nonEmptyString = (value) => typeof value === 'string' && value.trim() !== ''
44
- const safeGeneration = (value) => Number.isSafeInteger(value) && value >= 0
45
- const isoTimestamp = (value) => nonEmptyString(value) && Number.isFinite(Date.parse(value))
46
- const exactArray = (actual, expected) => Array.isArray(actual)
47
- && actual.length === expected.length
48
- && actual.every((value, index) => value === expected[index])
49
-
50
- function written(state, fields = {}) {
51
- return {
52
- schema: RECEIPT_SCHEMA,
53
- state,
54
- ...fields,
55
- writer: CLI_WRITER,
56
- writtenAt: new Date().toISOString(),
57
- }
58
- }
59
-
60
- function refused(...failed) {
61
- return written('refused', { failed: [...new Set(failed)] })
62
- }
63
-
64
- function readJson(file, failedId) {
65
- try {
66
- const value = JSON.parse(fs.readFileSync(file, 'utf8'))
67
- return { value }
68
- } catch (error) {
69
- return { error: refused(failedId), cause: error }
70
- }
71
- }
72
-
73
- function validateAdapter(adapter) {
74
- const failed = []
75
- if (!isObject(adapter) || adapter.schemaVersion !== ADAPTER_SCHEMA_VERSION) {
76
- failed.push('e:invalid-adapter-schema-version')
77
- }
78
- if (!nonEmptyString(adapter?.writerId)) failed.push('e:invalid-writer-id')
79
- if (!exactArray(adapter?.pauseOrder, PAUSE_ORDER)) failed.push('e:invalid-pause-order')
80
- if (!exactArray(adapter?.resumeOrder, RESUME_ORDER)) failed.push('e:resume-not-exact-reverse')
81
- const quiescence = adapter?.quiescence
82
- if (
83
- !isObject(quiescence)
84
- || !nonEmptyString(quiescence.component)
85
- || !nonEmptyString(quiescence.blockerField)
86
- || typeof quiescence.readyValue !== 'number'
87
- || !Number.isSafeInteger(quiescence.timeoutMs)
88
- || quiescence.timeoutMs <= 0
89
- ) failed.push('e:invalid-quiescence')
90
- if (adapter?.evidenceSchema !== RECEIPT_SCHEMA) failed.push('e:invalid-evidence-schema')
91
- if (typeof adapter?.execute !== 'function') failed.push('e:adapter-not-executable')
92
- return failed
93
- }
94
-
95
- export async function loadDeclaration(file, { onFilesystemBoundary } = {}) {
96
- const read = readJson(file, 'e:declaration-unreadable')
97
- if (read.error) return read
98
- const declaration = read.value
99
- if (!isObject(declaration) || declaration.schemaVersion !== 1) {
100
- return { error: refused('e:invalid-declaration-schema') }
101
- }
102
- if (!nonEmptyString(declaration.repositoryRoot)) {
103
- return { error: refused('e:repository-root-undeclared') }
104
- }
105
- const durable = declaration.durableProcessing
106
- if (!isObject(durable) || typeof durable.present !== 'boolean') {
107
- return { error: refused('e:durable-processing-undecided') }
108
- }
109
- if (durable.config != null && !isObject(durable.config)) {
110
- return { error: refused('e:adapter-config-invalid') }
111
- }
112
- if (durable.present === false) return { declaration, durable }
113
- if (!nonEmptyString(durable.adapter)) return { error: refused('e:adapter-undeclared') }
114
-
115
- const declarationDir = path.dirname(path.resolve(file))
116
- const repositoryRoot = path.isAbsolute(declaration.repositoryRoot)
117
- ? path.resolve(declaration.repositoryRoot)
118
- : path.resolve(declarationDir, declaration.repositoryRoot)
119
- let loaded
120
- try {
121
- loaded = await loadCheckedAdapter({
122
- repositoryRoot,
123
- adapterPath: durable.adapter,
124
- onFilesystemBoundary,
125
- })
126
- } catch (error) {
127
- const code = error instanceof CheckedAdapterError ? error.code : 'e:adapter-unloadable'
128
- return { error: refused(code) }
129
- }
130
- const adapter = loaded.module[ADAPTER_EXPORT]
131
- const failed = validateAdapter(adapter)
132
- if (failed.length) return { error: refused(...failed) }
133
- return { declaration, durable, adapter, adapterPath: loaded.canonicalPath }
134
- }
135
-
136
- export async function preflight(file) {
137
- const loaded = await loadDeclaration(file)
138
- if (loaded.error) return loaded.error
139
- if (loaded.durable.present === false) return written('ready', { present: false })
140
- const { adapter, adapterPath } = loaded
141
- return written('ready', {
142
- present: true,
143
- adapter: adapterPath,
144
- adapterSchemaVersion: adapter.schemaVersion,
145
- pauseOrder: [...adapter.pauseOrder],
146
- resumeOrder: [...adapter.resumeOrder],
147
- quiescence: { ...adapter.quiescence },
148
- evidenceSchema: adapter.evidenceSchema,
149
- adapterWriter: { id: adapter.writerId },
150
- })
151
- }
152
-
153
- export function normalizePlatformOutput(output) {
154
- if (!isObject(output)) return { error: 'e:platform-output-malformed' }
155
- const hasPayload = Object.hasOwn(output, 'payload')
156
- const hasText = Object.hasOwn(output, 'text')
157
- if (hasPayload === hasText) return { error: 'e:platform-output-ambiguous' }
158
- if (hasPayload) {
159
- if (!isObject(output.payload)) return { error: 'e:platform-output-malformed' }
160
- return { receipt: output.payload }
161
- }
162
- if (!nonEmptyString(output.text)) return { error: 'e:platform-output-malformed' }
163
- try {
164
- const receipt = JSON.parse(output.text)
165
- return isObject(receipt) ? { receipt } : { error: 'e:platform-output-malformed' }
166
- } catch {
167
- return { error: 'e:platform-output-malformed' }
168
- }
169
- }
170
-
171
- export function validateReceipt(receipt, request, writerId) {
172
- const required = [
173
- 'schema', 'state', 'operation', 'component', 'requestId', 'writer', 'writtenAt',
174
- 'generation', 'fence', 'observed',
175
- ]
176
- if (!isObject(receipt) || required.some((field) => receipt[field] == null)) {
177
- return ['e:receipt-partial']
178
- }
179
- const failed = []
180
- if (receipt.schema !== RECEIPT_SCHEMA) failed.push('e:receipt-schema-mismatch')
181
- if (!['observed', 'applied', 'refused'].includes(receipt.state)) failed.push('e:receipt-state-invalid')
182
- if (receipt.operation !== request.operation) failed.push('e:receipt-operation-mismatch')
183
- if (receipt.component !== request.component) failed.push('e:receipt-component-mismatch')
184
- if (receipt.requestId !== request.requestId) failed.push('e:receipt-request-mismatch')
185
- if (!isObject(receipt.writer) || receipt.writer.id !== writerId) failed.push('e:receipt-writer-mismatch')
186
- if (!isoTimestamp(receipt.writtenAt)) failed.push('e:receipt-timestamp-invalid')
187
- if (!safeGeneration(receipt.generation)) failed.push('e:receipt-generation-invalid')
188
- if (!nonEmptyString(receipt.fence)) failed.push('e:receipt-fence-invalid')
189
- if (!isObject(receipt.observed)) failed.push('e:receipt-observation-invalid')
190
- if (receipt.state === 'refused' && (
191
- !Array.isArray(receipt.failed) || receipt.failed.length === 0 || receipt.failed.some((id) => !nonEmptyString(id))
192
- )) failed.push('e:receipt-partial')
193
- if (receipt.state !== 'refused') {
194
- if (['pause', 'resume'].includes(request.operation) && receipt.state !== 'applied') {
195
- failed.push('e:receipt-effect-unproven')
196
- }
197
- if (request.operation === 'pause' && receipt.observed?.paused !== true) failed.push('e:receipt-effect-unproven')
198
- if (request.operation === 'resume' && receipt.observed?.resumed !== true) failed.push('e:receipt-effect-unproven')
199
- if (['status', 'quiescence'].includes(request.operation) && receipt.state !== 'observed') {
200
- failed.push('e:receipt-observation-unproven')
201
- }
202
- if (request.operation === 'quiescence'
203
- && (!Number.isSafeInteger(receipt.observed?.blockers) || receipt.observed.blockers < 0)) {
204
- failed.push('e:receipt-observation-incomplete')
205
- }
206
- if (request.operation === 'status' && (
207
- typeof receipt.observed?.admissionPaused !== 'boolean'
208
- || typeof receipt.observed?.schedulingPaused !== 'boolean'
209
- || !Number.isSafeInteger(receipt.observed?.blockers)
210
- || receipt.observed.blockers < 0
211
- || !isObject(receipt.observed?.traffic)
212
- )) failed.push('e:receipt-observation-incomplete')
213
- }
214
- return failed
215
- }
216
-
217
- export async function executeOperation(file, operation, options) {
218
- const loaded = await loadDeclaration(file)
219
- if (loaded.error) return loaded.error
220
- if (loaded.durable.present === false) return refused('e:durable-processing-not-present')
221
- const request = Object.freeze({
222
- schemaVersion: 1,
223
- operation,
224
- component: options.component,
225
- requestId: options.requestId ?? crypto.randomUUID(),
226
- expectedGeneration: options.generation,
227
- expectedFence: options.fence,
228
- servingRevision: options.servingRevision,
229
- })
230
- if (!nonEmptyString(request.component) || !nonEmptyString(request.requestId)) {
231
- return refused('e:request-invalid')
232
- }
233
- let isolated
234
- try {
235
- isolated = await loaded.adapter.execute(
236
- request,
237
- Object.freeze({ ...(loaded.durable.config ?? {}) }),
238
- )
239
- } catch (error) {
240
- const code = typeof error?.code === 'string' && error.code.startsWith('e:adapter-')
241
- ? error.code
242
- : 'e:adapter-execution-failed'
243
- return refused(code)
244
- }
245
- if (!isObject(isolated) || !Object.hasOwn(isolated, 'output')
246
- || typeof isolated.commit !== 'function') {
247
- return refused('e:adapter-execution-failed')
248
- }
249
- const normalized = normalizePlatformOutput(isolated.output)
250
- if (normalized.error) return refused(normalized.error)
251
- const failed = validateReceipt(normalized.receipt, request, loaded.adapter.writerId)
252
- if (failed.length) return refused(...failed)
253
- try {
254
- isolated.commit()
255
- } catch (error) {
256
- const code = typeof error?.code === 'string' && error.code.startsWith('e:adapter-')
257
- ? error.code
258
- : 'e:adapter-file-changed'
259
- if (error?.ambiguousEffects === true || error?.committedEffects === true) {
260
- return written('refused', {
261
- failed: [code],
262
- effects: error.ambiguousEffects === true ? 'ambiguous' : 'committed',
263
- affectedTargets: Array.isArray(error.affectedTargets) ? [...error.affectedTargets] : [],
264
- })
265
- }
266
- return refused(code)
267
- }
268
- return normalized.receipt
269
- }
270
-
271
- function sequenceReceipt(state, fields) {
272
- return written(state, fields)
273
- }
274
-
275
- function isSuccess(receipt) {
276
- return receipt.state === 'observed' || receipt.state === 'applied'
277
- }
278
-
279
- function identityFrom(receipt) {
280
- return { generation: receipt.generation, fence: receipt.fence }
281
- }
282
-
283
- async function status(file) {
284
- return executeOperation(file, 'status', { component: 'durable-state' })
285
- }
286
-
287
- export async function down(file, options) {
288
- const release = readJson(options.releaseState, 'e:release-state-unreadable')
289
- if (release.error) return release.error
290
- if (release.value?.skill !== 'resolve-release' || release.value?.step !== 'expose') {
291
- return refused('e:expose-step-not-recorded')
292
- }
293
- const initial = await status(file)
294
- if (!isSuccess(initial)) return initial
295
- const receipts = []
296
- let identity = identityFrom(initial)
297
- for (const component of PAUSE_ORDER) {
298
- const receipt = await executeOperation(file, 'pause', { component, ...identity })
299
- receipts.push(receipt)
300
- if (!isSuccess(receipt)) return sequenceReceipt('blocked', {
301
- failed: receipt.failed,
302
- pausedAt: receipts.find((item) => isSuccess(item))?.writtenAt,
303
- pauseOrder: [...PAUSE_ORDER],
304
- receipts,
305
- proof: receipt,
306
- })
307
- identity = identityFrom(receipt)
308
- }
309
- const loaded = await loadDeclaration(file)
310
- if (loaded.error) return loaded.error
311
- const deadline = Date.now() + loaded.adapter.quiescence.timeoutMs
312
- let proof
313
- do {
314
- proof = await executeOperation(file, 'quiescence', {
315
- component: 'durable-state',
316
- ...identity,
317
- })
318
- receipts.push(proof)
319
- if (!isSuccess(proof)) return sequenceReceipt('blocked', {
320
- failed: proof.failed,
321
- pausedAt: receipts[0]?.writtenAt,
322
- pauseOrder: [...PAUSE_ORDER],
323
- receipts,
324
- })
325
- if (proof.observed.blockers === 0) break
326
- if (Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 25))
327
- } while (Date.now() < deadline)
328
- if (proof.observed.blockers !== 0) {
329
- return sequenceReceipt('blocked', {
330
- failed: ['e:quiescence-timeout'],
331
- pausedAt: receipts[0]?.writtenAt,
332
- pauseOrder: [...PAUSE_ORDER],
333
- receipts,
334
- proof,
335
- })
336
- }
337
- return sequenceReceipt('ready-to-shift', {
338
- pausedAt: receipts[0]?.writtenAt,
339
- pauseOrder: [...PAUSE_ORDER],
340
- receipts,
341
- proof,
342
- shiftFence: identityFrom(proof),
343
- })
344
- }
345
-
346
- function trafficMatches(traffic, expected) {
347
- return isObject(traffic)
348
- && traffic.incumbentRevision === expected.incumbentRevision
349
- && traffic.incumbentPercent === expected.incumbentPercent
350
- && traffic.candidateRevision === expected.candidateRevision
351
- && traffic.candidatePercent === expected.candidatePercent
352
- }
353
-
354
- async function resumeSequence(file, options, initialStatus) {
355
- const receipts = [initialStatus]
356
- let identity = identityFrom(initialStatus)
357
- if (
358
- !safeGeneration(options.generation)
359
- || !nonEmptyString(options.fence)
360
- || options.generation !== identity.generation
361
- || options.fence !== identity.fence
362
- ) {
363
- return sequenceReceipt('ambiguous', {
364
- failed: ['e:serving-generation-mismatch'],
365
- receipts,
366
- })
367
- }
368
- for (const component of RESUME_ORDER) {
369
- const receipt = await executeOperation(file, 'resume', {
370
- component,
371
- ...identity,
372
- servingRevision: options.servingRevision,
373
- })
374
- receipts.push(receipt)
375
- if (!isSuccess(receipt)) {
376
- return sequenceReceipt('ambiguous', { failed: receipt.failed, receipts })
377
- }
378
- identity = identityFrom(receipt)
379
- }
380
- const confirmation = await status(file)
381
- receipts.push(confirmation)
382
- if (
383
- !isSuccess(confirmation)
384
- || confirmation.observed.schedulingPaused !== false
385
- || confirmation.observed.admissionPaused !== false
386
- ) {
387
- return sequenceReceipt('ambiguous', {
388
- failed: ['e:processing-restoration-ambiguous'],
389
- receipts,
390
- })
391
- }
392
- return sequenceReceipt('restored', {
393
- resumedAt: receipts.find((receipt) => receipt.operation === 'resume')?.writtenAt,
394
- resumeOrder: [...RESUME_ORDER],
395
- receipts,
396
- servingRevisionAtResume: options.servingRevision,
397
- restorationFence: identityFrom(confirmation),
398
- })
399
- }
400
-
401
- export async function up(file, options) {
402
- const observed = await status(file)
403
- if (!isSuccess(observed)) return observed
404
- const traffic = observed.observed.traffic
405
- const identitiesDeclared = nonEmptyString(options.incumbentRevision)
406
- && nonEmptyString(options.candidateRevision)
407
- && options.incumbentRevision !== options.candidateRevision
408
- const namedCandidateServes = options.servingRevision === options.candidateRevision
409
- && trafficMatches(traffic, {
410
- incumbentRevision: options.incumbentRevision,
411
- incumbentPercent: 0,
412
- candidateRevision: options.candidateRevision,
413
- candidatePercent: 100,
414
- })
415
- const namedIncumbentServes = options.servingRevision === options.incumbentRevision
416
- && trafficMatches(traffic, {
417
- incumbentRevision: options.incumbentRevision,
418
- incumbentPercent: 100,
419
- candidateRevision: options.candidateRevision,
420
- candidatePercent: 0,
421
- })
422
- if (!identitiesDeclared || (!namedCandidateServes && !namedIncumbentServes)) {
423
- return sequenceReceipt('ambiguous', {
424
- failed: ['e:serving-revision-unproven'],
425
- trafficProof: traffic,
426
- receipts: [observed],
427
- })
428
- }
429
- const resumed = await resumeSequence(file, options, observed)
430
- return resumed.state === 'restored'
431
- ? {
432
- ...resumed,
433
- trafficProof: traffic,
434
- incumbentRevision: options.incumbentRevision,
435
- candidateRevision: options.candidateRevision,
436
- }
437
- : resumed
438
- }
439
-
440
- export async function unwindPreShift(file, options) {
441
- const observed = await status(file)
442
- if (!isSuccess(observed)) return observed
443
- const trafficProof = observed.observed.traffic
444
- const expected = {
445
- incumbentRevision: options.incumbentRevision,
446
- incumbentPercent: 100,
447
- candidateRevision: options.candidateRevision,
448
- candidatePercent: 0,
449
- }
450
- if (!trafficMatches(trafficProof, expected)) {
451
- return sequenceReceipt('ambiguous', {
452
- failed: ['e:traffic-split-ambiguous'],
453
- trafficProof,
454
- receipts: [observed],
455
- })
456
- }
457
- const resumed = await resumeSequence(file, {
458
- ...options,
459
- servingRevision: options.incumbentRevision,
460
- }, observed)
461
- if (resumed.state !== 'restored') return resumed
462
- return { ...resumed, state: 'unwound', trafficProof }
463
- }
464
-
465
- function parseArgs(argv) {
466
- const positional = []
467
- const options = {}
468
- const unknown = []
469
- for (let index = 0; index < argv.length; index++) {
470
- const value = argv[index]
471
- if (value === '--help' || value === '-h') options.help = true
472
- else if (value === '--declaration') options.declaration = argv[++index]
473
- else if (value === '--component') options.component = argv[++index]
474
- else if (value === '--generation') options.generation = Number(argv[++index])
475
- else if (value === '--fence') options.fence = argv[++index]
476
- else if (value === '--serving-revision') options.servingRevision = argv[++index]
477
- else if (value === '--request-id') options.requestId = argv[++index]
478
- else if (value === '--release-state') options.releaseState = argv[++index]
479
- else if (value === '--incumbent-revision') options.incumbentRevision = argv[++index]
480
- else if (value === '--candidate-revision') options.candidateRevision = argv[++index]
481
- else if (value.startsWith('--')) unknown.push(value)
482
- else positional.push(value)
483
- }
484
- return { positional, options, unknown }
485
- }
486
-
487
- export async function main(argv) {
488
- const { positional, options, unknown } = parseArgs(argv)
489
- if (options.help || positional[0] === 'help') {
490
- console.log(USAGE)
491
- return 0
492
- }
493
- if (unknown.length) {
494
- console.log(JSON.stringify(refused('e:unknown-option')))
495
- return 2
496
- }
497
- if (!nonEmptyString(options.declaration)) {
498
- console.log(JSON.stringify(refused('e:declaration-required')))
499
- return 2
500
- }
501
- const command = positional[0]
502
- const operations = ['status', 'pause', 'quiescence', 'resume']
503
- const sequences = ['down', 'up', 'unwind-pre-shift']
504
- if (command !== 'preflight' && !operations.includes(command) && !sequences.includes(command)) {
505
- console.log(JSON.stringify(refused('e:unknown-command')))
506
- return 2
507
- }
508
- let receipt
509
- if (command === 'preflight') receipt = await preflight(options.declaration)
510
- else if (operations.includes(command)) receipt = await executeOperation(options.declaration, command, options)
511
- else if (command === 'down') receipt = await down(options.declaration, options)
512
- else if (command === 'up') receipt = await up(options.declaration, options)
513
- else receipt = await unwindPreShift(options.declaration, options)
514
- console.log(JSON.stringify(receipt))
515
- return ['ready', 'observed', 'applied', 'ready-to-shift', 'restored', 'unwound'].includes(receipt.state)
516
- ? 0
517
- : 1
518
- }
519
-
520
- if (isMainModule(import.meta.url)) {
521
- main(process.argv.slice(2)).then((code) => { process.exitCode = code })
522
- }