@mannyc1/ts-release 0.0.7 → 0.2.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 (635) hide show
  1. package/ARCHITECTURE.md +124 -92
  2. package/CHANGELOG.md +115 -0
  3. package/LICENSE +21 -0
  4. package/README.md +278 -324
  5. package/SPEC.md +206 -310
  6. package/apps/release-ts/README.md +32 -0
  7. package/apps/release-ts/src/cli/command.ts +164 -0
  8. package/apps/release-ts/src/cli/commands.ts +351 -0
  9. package/apps/release-ts/src/cli/main.ts +43 -0
  10. package/apps/release-ts/src/cli/node-main.ts +49 -0
  11. package/apps/ts-release-agents/LICENSE +21 -0
  12. package/apps/ts-release-agents/README.md +21 -0
  13. package/apps/ts-release-agents/evals/cases.json +11 -0
  14. package/apps/ts-release-agents/manifests/claude.json +5 -0
  15. package/apps/ts-release-agents/manifests/codex.json +5 -0
  16. package/apps/ts-release-agents/package.json +11 -0
  17. package/apps/ts-release-agents/skills/release/SKILL.md +48 -0
  18. package/apps/ts-release-agents/skills/release/references/configuration.md +18 -0
  19. package/apps/ts-release-agents/skills/release/references/recovery.md +10 -0
  20. package/apps/ts-release-agents/skills/release/references/staged-workflow.md +12 -0
  21. package/apps/ts-release-agents/skills/release/references/target-selection.md +13 -0
  22. package/apps/ts-release-agents/skills/release/references/verification.md +14 -0
  23. package/apps/ts-release-agents/src/build.ts +105 -0
  24. package/apps/ts-release-agents/src/check.ts +117 -0
  25. package/apps/ts-release-agents/src/install.ts +121 -0
  26. package/apps/ts-release-agents/test/contract.test.ts +56 -0
  27. package/apps/ts-release-agents/tsconfig.json +5 -0
  28. package/dist/api/api.d.ts +15 -0
  29. package/dist/api/api.d.ts.map +1 -0
  30. package/dist/api/api.js +244 -0
  31. package/dist/api/api.js.map +1 -0
  32. package/dist/api/errors.d.ts +39 -0
  33. package/dist/api/errors.d.ts.map +1 -0
  34. package/dist/api/errors.js +32 -0
  35. package/dist/api/errors.js.map +1 -0
  36. package/dist/api/input.d.ts +10 -0
  37. package/dist/api/input.d.ts.map +1 -0
  38. package/dist/api/input.js +73 -0
  39. package/dist/api/input.js.map +1 -0
  40. package/dist/api/runtime.d.ts +18 -0
  41. package/dist/api/runtime.d.ts.map +1 -0
  42. package/dist/api/runtime.js +7 -0
  43. package/dist/api/runtime.js.map +1 -0
  44. package/dist/api/types.d.ts +76 -0
  45. package/dist/api/types.d.ts.map +1 -0
  46. package/dist/api/types.js +17 -0
  47. package/dist/api/types.js.map +1 -0
  48. package/dist/bin/ts-release.js +39239 -0
  49. package/dist/capabilities/bun-targets.d.ts +3 -0
  50. package/dist/capabilities/bun-targets.d.ts.map +1 -0
  51. package/dist/capabilities/bun-targets.js +3 -0
  52. package/dist/capabilities/bun-targets.js.map +1 -0
  53. package/dist/capabilities/field-ownership.d.ts +17 -0
  54. package/dist/capabilities/field-ownership.d.ts.map +1 -0
  55. package/dist/capabilities/field-ownership.js +40 -0
  56. package/dist/capabilities/field-ownership.js.map +1 -0
  57. package/dist/capabilities/module.d.ts +91 -0
  58. package/dist/capabilities/module.d.ts.map +1 -0
  59. package/dist/capabilities/module.js +2 -0
  60. package/dist/capabilities/module.js.map +1 -0
  61. package/dist/capabilities/registry.d.ts +829 -0
  62. package/dist/capabilities/registry.d.ts.map +1 -0
  63. package/dist/capabilities/registry.js +291 -0
  64. package/dist/capabilities/registry.js.map +1 -0
  65. package/dist/config/config.d.ts +6 -0
  66. package/dist/config/config.d.ts.map +1 -0
  67. package/dist/config/config.js +42 -0
  68. package/dist/config/config.js.map +1 -0
  69. package/dist/correction/coordinator.d.ts +248 -0
  70. package/dist/correction/coordinator.d.ts.map +1 -0
  71. package/dist/correction/coordinator.js +274 -0
  72. package/dist/correction/coordinator.js.map +1 -0
  73. package/dist/correction/index.d.ts +3 -0
  74. package/dist/correction/index.d.ts.map +1 -0
  75. package/dist/correction/index.js +3 -0
  76. package/dist/correction/index.js.map +1 -0
  77. package/dist/correction/intent.d.ts +111 -0
  78. package/dist/correction/intent.d.ts.map +1 -0
  79. package/dist/correction/intent.js +146 -0
  80. package/dist/correction/intent.js.map +1 -0
  81. package/dist/drivers/archive.d.ts +8 -0
  82. package/dist/drivers/archive.d.ts.map +1 -0
  83. package/dist/drivers/archive.js +101 -0
  84. package/dist/drivers/archive.js.map +1 -0
  85. package/dist/drivers/contain.d.ts +6 -0
  86. package/dist/drivers/contain.d.ts.map +1 -0
  87. package/dist/drivers/contain.js +17 -0
  88. package/dist/drivers/contain.js.map +1 -0
  89. package/dist/drivers/environment.d.ts +4 -0
  90. package/dist/drivers/environment.d.ts.map +1 -0
  91. package/dist/drivers/environment.js +18 -0
  92. package/dist/drivers/environment.js.map +1 -0
  93. package/dist/drivers/errors.d.ts +9 -0
  94. package/dist/drivers/errors.d.ts.map +1 -0
  95. package/dist/drivers/errors.js +6 -0
  96. package/dist/drivers/errors.js.map +1 -0
  97. package/dist/drivers/glob.d.ts +2 -0
  98. package/dist/drivers/glob.d.ts.map +1 -0
  99. package/dist/drivers/glob.js +162 -0
  100. package/dist/drivers/glob.js.map +1 -0
  101. package/dist/drivers/process.d.ts +48 -0
  102. package/dist/drivers/process.d.ts.map +1 -0
  103. package/dist/drivers/process.js +257 -0
  104. package/dist/drivers/process.js.map +1 -0
  105. package/dist/drivers/redact.d.ts +4 -0
  106. package/dist/drivers/redact.d.ts.map +1 -0
  107. package/dist/drivers/redact.js +24 -0
  108. package/dist/drivers/redact.js.map +1 -0
  109. package/dist/drivers/seccomp-helper-source.d.ts +7 -0
  110. package/dist/drivers/seccomp-helper-source.d.ts.map +1 -0
  111. package/dist/drivers/seccomp-helper-source.js +121 -0
  112. package/dist/drivers/seccomp-helper-source.js.map +1 -0
  113. package/dist/drivers/utils.d.ts +4 -0
  114. package/dist/drivers/utils.d.ts.map +1 -0
  115. package/dist/drivers/utils.js +5 -0
  116. package/dist/drivers/utils.js.map +1 -0
  117. package/dist/drivers/workspace.d.ts +6 -0
  118. package/dist/drivers/workspace.d.ts.map +1 -0
  119. package/dist/drivers/workspace.js +49 -0
  120. package/dist/drivers/workspace.js.map +1 -0
  121. package/dist/extensions/provider-adapter.d.ts +48 -0
  122. package/dist/extensions/provider-adapter.d.ts.map +1 -0
  123. package/dist/extensions/provider-adapter.js +64 -0
  124. package/dist/extensions/provider-adapter.js.map +1 -0
  125. package/dist/host.d.ts +33 -0
  126. package/dist/host.d.ts.map +1 -0
  127. package/dist/host.js +20 -0
  128. package/dist/host.js.map +1 -0
  129. package/dist/index.d.ts +19 -1
  130. package/dist/index.d.ts.map +1 -1
  131. package/dist/index.js +12 -1
  132. package/dist/index.js.map +1 -1
  133. package/dist/model/artifact-collection.d.ts +84 -0
  134. package/dist/model/artifact-collection.d.ts.map +1 -0
  135. package/dist/model/artifact-collection.js +185 -0
  136. package/dist/model/artifact-collection.js.map +1 -0
  137. package/dist/model/authority.d.ts +89 -0
  138. package/dist/model/authority.d.ts.map +1 -0
  139. package/dist/model/authority.js +80 -0
  140. package/dist/model/authority.js.map +1 -0
  141. package/dist/model/bun-targets.d.ts +34 -0
  142. package/dist/model/bun-targets.d.ts.map +1 -0
  143. package/dist/model/bun-targets.js +17 -0
  144. package/dist/model/bun-targets.js.map +1 -0
  145. package/dist/model/canonical.d.ts +8 -0
  146. package/dist/model/canonical.d.ts.map +1 -0
  147. package/dist/model/canonical.js +205 -0
  148. package/dist/model/canonical.js.map +1 -0
  149. package/dist/model/catalog.d.ts +76 -0
  150. package/dist/model/catalog.d.ts.map +1 -0
  151. package/dist/model/catalog.js +174 -0
  152. package/dist/model/catalog.js.map +1 -0
  153. package/dist/model/digest.d.ts +53 -0
  154. package/dist/model/digest.d.ts.map +1 -0
  155. package/dist/model/digest.js +127 -0
  156. package/dist/model/digest.js.map +1 -0
  157. package/dist/model/errors.d.ts +14 -0
  158. package/dist/model/errors.d.ts.map +1 -0
  159. package/dist/model/errors.js +8 -0
  160. package/dist/model/errors.js.map +1 -0
  161. package/dist/model/primitives.d.ts +19 -0
  162. package/dist/model/primitives.d.ts.map +1 -0
  163. package/dist/model/primitives.js +18 -0
  164. package/dist/model/primitives.js.map +1 -0
  165. package/dist/model/pypi.d.ts +12 -0
  166. package/dist/model/pypi.d.ts.map +1 -0
  167. package/dist/model/pypi.js +20 -0
  168. package/dist/model/pypi.js.map +1 -0
  169. package/dist/model/python-distribution.d.ts +33 -0
  170. package/dist/model/python-distribution.d.ts.map +1 -0
  171. package/dist/model/python-distribution.js +367 -0
  172. package/dist/model/python-distribution.js.map +1 -0
  173. package/dist/model/secret-patterns.d.ts +2 -0
  174. package/dist/model/secret-patterns.d.ts.map +1 -0
  175. package/dist/model/secret-patterns.js +12 -0
  176. package/dist/model/secret-patterns.js.map +1 -0
  177. package/dist/platform/bun.d.ts +7 -0
  178. package/dist/platform/bun.d.ts.map +1 -0
  179. package/dist/platform/bun.js +15 -0
  180. package/dist/platform/bun.js.map +1 -0
  181. package/dist/platform/credentials.d.ts +32 -0
  182. package/dist/platform/credentials.d.ts.map +1 -0
  183. package/dist/platform/credentials.js +772 -0
  184. package/dist/platform/credentials.js.map +1 -0
  185. package/dist/platform/host-support.d.ts +2 -0
  186. package/dist/platform/host-support.d.ts.map +1 -0
  187. package/dist/platform/host-support.js +4 -0
  188. package/dist/platform/host-support.js.map +1 -0
  189. package/dist/platform/node.d.ts +7 -0
  190. package/dist/platform/node.d.ts.map +1 -0
  191. package/dist/platform/node.js +15 -0
  192. package/dist/platform/node.js.map +1 -0
  193. package/dist/platform/release-runtime.d.ts +11 -0
  194. package/dist/platform/release-runtime.d.ts.map +1 -0
  195. package/dist/platform/release-runtime.js +34 -0
  196. package/dist/platform/release-runtime.js.map +1 -0
  197. package/dist/platform/services.d.ts +15 -0
  198. package/dist/platform/services.d.ts.map +1 -0
  199. package/dist/platform/services.js +30 -0
  200. package/dist/platform/services.js.map +1 -0
  201. package/dist/platform/source-observer.d.ts +5 -0
  202. package/dist/platform/source-observer.d.ts.map +1 -0
  203. package/dist/platform/source-observer.js +159 -0
  204. package/dist/platform/source-observer.js.map +1 -0
  205. package/dist/provider-sdk.d.ts +14 -0
  206. package/dist/provider-sdk.d.ts.map +1 -0
  207. package/dist/provider-sdk.js +10 -0
  208. package/dist/provider-sdk.js.map +1 -0
  209. package/dist/publication/adapter.d.ts +212 -0
  210. package/dist/publication/adapter.d.ts.map +1 -0
  211. package/dist/publication/adapter.js +83 -0
  212. package/dist/publication/adapter.js.map +1 -0
  213. package/dist/publication/authority.d.ts +137 -0
  214. package/dist/publication/authority.d.ts.map +1 -0
  215. package/dist/publication/authority.js +233 -0
  216. package/dist/publication/authority.js.map +1 -0
  217. package/dist/publication/catalog-git.d.ts +37 -0
  218. package/dist/publication/catalog-git.d.ts.map +1 -0
  219. package/dist/publication/catalog-git.js +451 -0
  220. package/dist/publication/catalog-git.js.map +1 -0
  221. package/dist/publication/claim.d.ts +39 -0
  222. package/dist/publication/claim.d.ts.map +1 -0
  223. package/dist/publication/claim.js +31 -0
  224. package/dist/publication/claim.js.map +1 -0
  225. package/dist/publication/coordinator.d.ts +246 -0
  226. package/dist/publication/coordinator.d.ts.map +1 -0
  227. package/dist/publication/coordinator.js +370 -0
  228. package/dist/publication/coordinator.js.map +1 -0
  229. package/dist/publication/github.d.ts +36 -0
  230. package/dist/publication/github.d.ts.map +1 -0
  231. package/dist/publication/github.js +644 -0
  232. package/dist/publication/github.js.map +1 -0
  233. package/dist/publication/http.d.ts +69 -0
  234. package/dist/publication/http.d.ts.map +1 -0
  235. package/dist/publication/http.js +29 -0
  236. package/dist/publication/http.js.map +1 -0
  237. package/dist/publication/index.d.ts +5 -0
  238. package/dist/publication/index.d.ts.map +1 -0
  239. package/dist/publication/index.js +5 -0
  240. package/dist/publication/index.js.map +1 -0
  241. package/dist/publication/npm.d.ts +40 -0
  242. package/dist/publication/npm.d.ts.map +1 -0
  243. package/dist/publication/npm.js +359 -0
  244. package/dist/publication/npm.js.map +1 -0
  245. package/dist/publication/profiles.d.ts +136 -0
  246. package/dist/publication/profiles.d.ts.map +1 -0
  247. package/dist/publication/profiles.js +90 -0
  248. package/dist/publication/profiles.js.map +1 -0
  249. package/dist/publication/publisher.d.ts +82 -0
  250. package/dist/publication/publisher.d.ts.map +1 -0
  251. package/dist/publication/publisher.js +53 -0
  252. package/dist/publication/publisher.js.map +1 -0
  253. package/dist/publication/pypi.d.ts +45 -0
  254. package/dist/publication/pypi.d.ts.map +1 -0
  255. package/dist/publication/pypi.js +369 -0
  256. package/dist/publication/pypi.js.map +1 -0
  257. package/dist/publication/recovery.d.ts +663 -0
  258. package/dist/publication/recovery.d.ts.map +1 -0
  259. package/dist/publication/recovery.js +252 -0
  260. package/dist/publication/recovery.js.map +1 -0
  261. package/dist/publication/report.d.ts +1061 -0
  262. package/dist/publication/report.d.ts.map +1 -0
  263. package/dist/publication/report.js +441 -0
  264. package/dist/publication/report.js.map +1 -0
  265. package/dist/recipes/config.d.ts +296 -0
  266. package/dist/recipes/config.d.ts.map +1 -0
  267. package/dist/recipes/config.js +325 -0
  268. package/dist/recipes/config.js.map +1 -0
  269. package/dist/release/capabilities.d.ts +12 -0
  270. package/dist/release/capabilities.d.ts.map +1 -0
  271. package/dist/release/capabilities.js +395 -0
  272. package/dist/release/capabilities.js.map +1 -0
  273. package/dist/release/compiler.d.ts +5 -0
  274. package/dist/release/compiler.d.ts.map +1 -0
  275. package/dist/release/compiler.js +19 -0
  276. package/dist/release/compiler.js.map +1 -0
  277. package/dist/release/config.d.ts +5 -0
  278. package/dist/release/config.d.ts.map +1 -0
  279. package/dist/release/config.js +10 -0
  280. package/dist/release/config.js.map +1 -0
  281. package/dist/release/context.d.ts +96 -0
  282. package/dist/release/context.d.ts.map +1 -0
  283. package/dist/release/context.js +168 -0
  284. package/dist/release/context.js.map +1 -0
  285. package/dist/release/graph.d.ts +271 -0
  286. package/dist/release/graph.d.ts.map +1 -0
  287. package/dist/release/graph.js +766 -0
  288. package/dist/release/graph.js.map +1 -0
  289. package/dist/release/inspect.d.ts +88 -0
  290. package/dist/release/inspect.d.ts.map +1 -0
  291. package/dist/release/inspect.js +75 -0
  292. package/dist/release/inspect.js.map +1 -0
  293. package/dist/release/prepare.d.ts +24 -0
  294. package/dist/release/prepare.d.ts.map +1 -0
  295. package/dist/release/prepare.js +1116 -0
  296. package/dist/release/prepare.js.map +1 -0
  297. package/dist/release/prepared-ref.d.ts +60 -0
  298. package/dist/release/prepared-ref.d.ts.map +1 -0
  299. package/dist/release/prepared-ref.js +131 -0
  300. package/dist/release/prepared-ref.js.map +1 -0
  301. package/dist/release/prepared-store.d.ts +90 -0
  302. package/dist/release/prepared-store.d.ts.map +1 -0
  303. package/dist/release/prepared-store.js +337 -0
  304. package/dist/release/prepared-store.js.map +1 -0
  305. package/dist/release/prepared.d.ts +172 -0
  306. package/dist/release/prepared.d.ts.map +1 -0
  307. package/dist/release/prepared.js +268 -0
  308. package/dist/release/prepared.js.map +1 -0
  309. package/dist/release/staging.d.ts +31 -0
  310. package/dist/release/staging.d.ts.map +1 -0
  311. package/dist/release/staging.js +218 -0
  312. package/dist/release/staging.js.map +1 -0
  313. package/dist/resolve/authored.d.ts +53 -0
  314. package/dist/resolve/authored.d.ts.map +1 -0
  315. package/dist/resolve/authored.js +55 -0
  316. package/dist/resolve/authored.js.map +1 -0
  317. package/dist/resolve/encode.d.ts +3 -0
  318. package/dist/resolve/encode.d.ts.map +1 -0
  319. package/dist/resolve/encode.js +21 -0
  320. package/dist/resolve/encode.js.map +1 -0
  321. package/dist/resolve/errors.d.ts +7 -0
  322. package/dist/resolve/errors.d.ts.map +1 -0
  323. package/dist/resolve/errors.js +17 -0
  324. package/dist/resolve/errors.js.map +1 -0
  325. package/dist/resolve/facts.d.ts +12 -0
  326. package/dist/resolve/facts.d.ts.map +1 -0
  327. package/dist/resolve/facts.js +18 -0
  328. package/dist/resolve/facts.js.map +1 -0
  329. package/dist/resolve/resolve.d.ts +10 -0
  330. package/dist/resolve/resolve.d.ts.map +1 -0
  331. package/dist/resolve/resolve.js +242 -0
  332. package/dist/resolve/resolve.js.map +1 -0
  333. package/dist/store.d.ts +11 -0
  334. package/dist/store.d.ts.map +1 -0
  335. package/dist/store.js +9 -0
  336. package/dist/store.js.map +1 -0
  337. package/examples/README.md +20 -30
  338. package/examples/github-release/release.config.json +11 -18
  339. package/examples/homebrew-tap/README.md +11 -0
  340. package/examples/homebrew-tap/release.config.json +40 -28
  341. package/examples/multi-target/release.config.json +28 -53
  342. package/examples/npm-first-publish/release.config.json +23 -25
  343. package/examples/npm-only/release.config.json +22 -30
  344. package/examples/portable-cli/README.md +17 -0
  345. package/examples/portable-cli/artifacts/portable-example-0.1.0-darwin-arm64 +1 -0
  346. package/examples/portable-cli/artifacts/portable-example-0.1.0-darwin-x64 +1 -0
  347. package/examples/portable-cli/artifacts/portable-example-0.1.0-linux-arm64 +1 -0
  348. package/examples/portable-cli/artifacts/portable-example-0.1.0-linux-x64 +1 -0
  349. package/examples/portable-cli/artifacts/portable-example-0.1.0-windows-x64.exe +1 -0
  350. package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-macosx_10_15_x86_64.whl +1 -0
  351. package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-macosx_11_0_arm64.whl +1 -0
  352. package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-manylinux2014_aarch64.whl +1 -0
  353. package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-manylinux2014_x86_64.whl +1 -0
  354. package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-win_amd64.whl +1 -0
  355. package/examples/portable-cli/package.json +9 -0
  356. package/examples/portable-cli/release.config.json +53 -0
  357. package/examples/portable-cli/src/cli.ts +3 -0
  358. package/examples/pypi-registry/README.md +8 -0
  359. package/examples/scoop-bucket/README.md +10 -0
  360. package/examples/scoop-bucket/release.config.json +40 -30
  361. package/package.json +60 -156
  362. package/src/api/api.ts +365 -0
  363. package/src/api/errors.ts +36 -0
  364. package/src/api/input.ts +92 -0
  365. package/src/api/runtime.ts +14 -0
  366. package/src/api/types.ts +103 -0
  367. package/src/capabilities/bun-targets.ts +2 -0
  368. package/src/capabilities/field-ownership.ts +60 -0
  369. package/src/capabilities/module.ts +158 -0
  370. package/src/capabilities/registry.ts +333 -0
  371. package/src/config/config.ts +48 -0
  372. package/src/correction/coordinator.ts +336 -0
  373. package/src/correction/index.ts +2 -0
  374. package/src/correction/intent.ts +172 -0
  375. package/src/drivers/archive.ts +102 -0
  376. package/src/drivers/contain.ts +20 -0
  377. package/src/drivers/environment.ts +28 -0
  378. package/src/drivers/errors.ts +6 -0
  379. package/src/drivers/glob.ts +137 -0
  380. package/src/drivers/process.ts +340 -0
  381. package/src/drivers/redact.ts +31 -0
  382. package/src/drivers/seccomp-helper-source.ts +121 -0
  383. package/src/drivers/utils.ts +10 -0
  384. package/src/drivers/workspace.ts +40 -0
  385. package/src/extensions/provider-adapter.ts +99 -0
  386. package/src/host.ts +140 -0
  387. package/src/index.ts +64 -0
  388. package/src/model/artifact-collection.ts +237 -0
  389. package/src/model/authority.ts +116 -0
  390. package/src/model/bun-targets.ts +34 -0
  391. package/src/model/canonical.ts +205 -0
  392. package/src/model/catalog.ts +220 -0
  393. package/src/model/digest.ts +172 -0
  394. package/src/model/errors.ts +6 -0
  395. package/src/model/primitives.ts +37 -0
  396. package/src/model/pypi.ts +30 -0
  397. package/src/model/python-distribution.ts +360 -0
  398. package/src/model/secret-patterns.ts +11 -0
  399. package/src/platform/bun.ts +38 -0
  400. package/src/platform/credentials.ts +1083 -0
  401. package/src/platform/host-support.ts +4 -0
  402. package/src/platform/node.ts +38 -0
  403. package/src/platform/release-runtime.ts +47 -0
  404. package/src/platform/services.ts +57 -0
  405. package/src/platform/source-observer.ts +193 -0
  406. package/src/provider-sdk.ts +48 -0
  407. package/src/publication/adapter.ts +132 -0
  408. package/src/publication/authority.ts +397 -0
  409. package/src/publication/catalog-git.ts +603 -0
  410. package/src/publication/claim.ts +51 -0
  411. package/src/publication/coordinator.ts +578 -0
  412. package/src/publication/github.ts +954 -0
  413. package/src/publication/http.ts +101 -0
  414. package/src/publication/index.ts +4 -0
  415. package/src/publication/npm.ts +525 -0
  416. package/src/publication/profiles.ts +94 -0
  417. package/src/publication/publisher.ts +130 -0
  418. package/src/publication/pypi.ts +463 -0
  419. package/src/publication/recovery.ts +349 -0
  420. package/src/publication/report.ts +628 -0
  421. package/src/recipes/config.ts +395 -0
  422. package/src/release/capabilities.ts +459 -0
  423. package/src/release/compiler.ts +28 -0
  424. package/src/release/config.ts +12 -0
  425. package/src/release/context.ts +223 -0
  426. package/src/release/graph.ts +866 -0
  427. package/src/release/inspect.ts +83 -0
  428. package/src/release/prepare.ts +1331 -0
  429. package/src/release/prepared-ref.ts +193 -0
  430. package/src/release/prepared-store.ts +377 -0
  431. package/src/release/prepared.ts +299 -0
  432. package/src/release/staging.ts +257 -0
  433. package/src/resolve/authored.ts +64 -0
  434. package/src/resolve/encode.ts +22 -0
  435. package/src/resolve/errors.ts +15 -0
  436. package/src/resolve/facts.ts +18 -0
  437. package/src/resolve/resolve.ts +328 -0
  438. package/src/store.ts +28 -0
  439. package/templates/README.md +55 -72
  440. package/templates/bun-cli-github/README.md +7 -9
  441. package/templates/bun-cli-github/release.config.json +35 -73
  442. package/templates/github-actions/release.yml +57 -0
  443. package/templates/github-actions/reviewed-release.yml +80 -0
  444. package/templates/multi-target-homebrew/README.md +10 -0
  445. package/templates/multi-target-homebrew/release.config.json +43 -60
  446. package/templates/multi-target-scoop/README.md +10 -0
  447. package/templates/multi-target-scoop/release.config.json +43 -62
  448. package/templates/npm-github/release.config.json +25 -38
  449. package/templates/npm-github/reviewed-release.config.json +37 -0
  450. package/templates/npm-only/README.md +10 -0
  451. package/templates/npm-only/release.config.json +15 -30
  452. package/templates/portable-cli/README.md +13 -0
  453. package/templates/portable-cli/release.config.json +53 -0
  454. package/dist/artifacts/adapter.d.ts +0 -39
  455. package/dist/artifacts/adapter.d.ts.map +0 -1
  456. package/dist/artifacts/adapter.js +0 -23
  457. package/dist/artifacts/adapter.js.map +0 -1
  458. package/dist/artifacts/registry.d.ts +0 -21
  459. package/dist/artifacts/registry.d.ts.map +0 -1
  460. package/dist/artifacts/registry.js +0 -21
  461. package/dist/artifacts/registry.js.map +0 -1
  462. package/dist/config/errors.d.ts +0 -24
  463. package/dist/config/errors.d.ts.map +0 -1
  464. package/dist/config/errors.js +0 -19
  465. package/dist/config/errors.js.map +0 -1
  466. package/dist/config/load.d.ts +0 -9
  467. package/dist/config/load.d.ts.map +0 -1
  468. package/dist/config/load.js +0 -29
  469. package/dist/config/load.js.map +0 -1
  470. package/dist/config/schema.d.ts +0 -12
  471. package/dist/config/schema.d.ts.map +0 -1
  472. package/dist/config/schema.js +0 -19
  473. package/dist/config/schema.js.map +0 -1
  474. package/dist/domain/artifact.d.ts +0 -118
  475. package/dist/domain/artifact.d.ts.map +0 -1
  476. package/dist/domain/artifact.js +0 -197
  477. package/dist/domain/artifact.js.map +0 -1
  478. package/dist/domain/evidence.d.ts +0 -59
  479. package/dist/domain/evidence.d.ts.map +0 -1
  480. package/dist/domain/evidence.js +0 -51
  481. package/dist/domain/evidence.js.map +0 -1
  482. package/dist/domain/operation.d.ts +0 -143
  483. package/dist/domain/operation.d.ts.map +0 -1
  484. package/dist/domain/operation.js +0 -198
  485. package/dist/domain/operation.js.map +0 -1
  486. package/dist/domain/release.d.ts +0 -93
  487. package/dist/domain/release.d.ts.map +0 -1
  488. package/dist/domain/release.js +0 -82
  489. package/dist/domain/release.js.map +0 -1
  490. package/dist/domain/remote-state.d.ts +0 -58
  491. package/dist/domain/remote-state.d.ts.map +0 -1
  492. package/dist/domain/remote-state.js +0 -55
  493. package/dist/domain/remote-state.js.map +0 -1
  494. package/dist/domain/target.d.ts +0 -148
  495. package/dist/domain/target.d.ts.map +0 -1
  496. package/dist/domain/target.js +0 -151
  497. package/dist/domain/target.js.map +0 -1
  498. package/dist/host/host.d.ts +0 -32
  499. package/dist/host/host.d.ts.map +0 -1
  500. package/dist/host/host.js +0 -24
  501. package/dist/host/host.js.map +0 -1
  502. package/dist/host/http-live.d.ts +0 -6
  503. package/dist/host/http-live.d.ts.map +0 -1
  504. package/dist/host/http-live.js +0 -86
  505. package/dist/host/http-live.js.map +0 -1
  506. package/dist/host/http.d.ts +0 -43
  507. package/dist/host/http.d.ts.map +0 -1
  508. package/dist/host/http.js +0 -59
  509. package/dist/host/http.js.map +0 -1
  510. package/dist/host/platform.d.ts +0 -10
  511. package/dist/host/platform.d.ts.map +0 -1
  512. package/dist/host/platform.js +0 -114
  513. package/dist/host/platform.js.map +0 -1
  514. package/dist/host/test.d.ts +0 -23
  515. package/dist/host/test.d.ts.map +0 -1
  516. package/dist/host/test.js +0 -222
  517. package/dist/host/test.js.map +0 -1
  518. package/dist/internal/workspace-path.d.ts +0 -15
  519. package/dist/internal/workspace-path.d.ts.map +0 -1
  520. package/dist/internal/workspace-path.js +0 -36
  521. package/dist/internal/workspace-path.js.map +0 -1
  522. package/dist/planner/artifact-inventory.d.ts +0 -9
  523. package/dist/planner/artifact-inventory.d.ts.map +0 -1
  524. package/dist/planner/artifact-inventory.js +0 -86
  525. package/dist/planner/artifact-inventory.js.map +0 -1
  526. package/dist/planner/create-release-plan.d.ts +0 -5
  527. package/dist/planner/create-release-plan.d.ts.map +0 -1
  528. package/dist/planner/create-release-plan.js +0 -25
  529. package/dist/planner/create-release-plan.js.map +0 -1
  530. package/dist/planner/errors.d.ts +0 -67
  531. package/dist/planner/errors.d.ts.map +0 -1
  532. package/dist/planner/errors.js +0 -58
  533. package/dist/planner/errors.js.map +0 -1
  534. package/dist/planner/evidence-recorder.d.ts +0 -28
  535. package/dist/planner/evidence-recorder.d.ts.map +0 -1
  536. package/dist/planner/evidence-recorder.js +0 -362
  537. package/dist/planner/evidence-recorder.js.map +0 -1
  538. package/dist/planner/executor.d.ts +0 -58
  539. package/dist/planner/executor.d.ts.map +0 -1
  540. package/dist/planner/executor.js +0 -147
  541. package/dist/planner/executor.js.map +0 -1
  542. package/dist/planner/normalize-release.d.ts +0 -19
  543. package/dist/planner/normalize-release.d.ts.map +0 -1
  544. package/dist/planner/normalize-release.js +0 -342
  545. package/dist/planner/normalize-release.js.map +0 -1
  546. package/dist/planner/reconcile.d.ts +0 -17
  547. package/dist/planner/reconcile.d.ts.map +0 -1
  548. package/dist/planner/reconcile.js +0 -259
  549. package/dist/planner/reconcile.js.map +0 -1
  550. package/dist/planner/render-plan.d.ts +0 -15
  551. package/dist/planner/render-plan.d.ts.map +0 -1
  552. package/dist/planner/render-plan.js +0 -231
  553. package/dist/planner/render-plan.js.map +0 -1
  554. package/dist/targets/adapter-helpers.d.ts +0 -79
  555. package/dist/targets/adapter-helpers.d.ts.map +0 -1
  556. package/dist/targets/adapter-helpers.js +0 -194
  557. package/dist/targets/adapter-helpers.js.map +0 -1
  558. package/dist/targets/adapter.d.ts +0 -17
  559. package/dist/targets/adapter.d.ts.map +0 -1
  560. package/dist/targets/adapter.js +0 -2
  561. package/dist/targets/adapter.js.map +0 -1
  562. package/dist/targets/github-release.d.ts +0 -25
  563. package/dist/targets/github-release.d.ts.map +0 -1
  564. package/dist/targets/github-release.js +0 -82
  565. package/dist/targets/github-release.js.map +0 -1
  566. package/dist/targets/github.d.ts +0 -11
  567. package/dist/targets/github.d.ts.map +0 -1
  568. package/dist/targets/github.js +0 -117
  569. package/dist/targets/github.js.map +0 -1
  570. package/dist/targets/homebrew.d.ts +0 -11
  571. package/dist/targets/homebrew.d.ts.map +0 -1
  572. package/dist/targets/homebrew.js +0 -210
  573. package/dist/targets/homebrew.js.map +0 -1
  574. package/dist/targets/live.d.ts +0 -5
  575. package/dist/targets/live.d.ts.map +0 -1
  576. package/dist/targets/live.js +0 -50
  577. package/dist/targets/live.js.map +0 -1
  578. package/dist/targets/npm.d.ts +0 -10
  579. package/dist/targets/npm.d.ts.map +0 -1
  580. package/dist/targets/npm.js +0 -112
  581. package/dist/targets/npm.js.map +0 -1
  582. package/dist/targets/pypi.d.ts +0 -11
  583. package/dist/targets/pypi.d.ts.map +0 -1
  584. package/dist/targets/pypi.js +0 -141
  585. package/dist/targets/pypi.js.map +0 -1
  586. package/dist/targets/registry.d.ts +0 -25
  587. package/dist/targets/registry.d.ts.map +0 -1
  588. package/dist/targets/registry.js +0 -34
  589. package/dist/targets/registry.js.map +0 -1
  590. package/dist/targets/scoop.d.ts +0 -11
  591. package/dist/targets/scoop.d.ts.map +0 -1
  592. package/dist/targets/scoop.js +0 -88
  593. package/dist/targets/scoop.js.map +0 -1
  594. package/dist/types/effect-internal.d.ts +0 -5
  595. package/dist/types/effect-internal.d.ts.map +0 -1
  596. package/dist/types/effect-internal.js +0 -2
  597. package/dist/types/effect-internal.js.map +0 -1
  598. package/dist/workflows/config.d.ts +0 -202
  599. package/dist/workflows/config.d.ts.map +0 -1
  600. package/dist/workflows/config.js +0 -420
  601. package/dist/workflows/config.js.map +0 -1
  602. package/dist/workflows/diagnostics.d.ts +0 -62
  603. package/dist/workflows/diagnostics.d.ts.map +0 -1
  604. package/dist/workflows/diagnostics.js +0 -530
  605. package/dist/workflows/diagnostics.js.map +0 -1
  606. package/dist/workflows/distribution.d.ts +0 -25
  607. package/dist/workflows/distribution.d.ts.map +0 -1
  608. package/dist/workflows/distribution.js +0 -24
  609. package/dist/workflows/distribution.js.map +0 -1
  610. package/dist/workflows/evidence.d.ts +0 -17
  611. package/dist/workflows/evidence.d.ts.map +0 -1
  612. package/dist/workflows/evidence.js +0 -32
  613. package/dist/workflows/evidence.js.map +0 -1
  614. package/dist/workflows/index.d.ts +0 -8
  615. package/dist/workflows/index.d.ts.map +0 -1
  616. package/dist/workflows/index.js +0 -7
  617. package/dist/workflows/index.js.map +0 -1
  618. package/dist/workflows/init.d.ts +0 -84
  619. package/dist/workflows/init.d.ts.map +0 -1
  620. package/dist/workflows/init.js +0 -487
  621. package/dist/workflows/init.js.map +0 -1
  622. package/dist/workflows/live.d.ts +0 -9
  623. package/dist/workflows/live.d.ts.map +0 -1
  624. package/dist/workflows/live.js +0 -7
  625. package/dist/workflows/live.js.map +0 -1
  626. package/dist/workflows/options.d.ts +0 -30
  627. package/dist/workflows/options.d.ts.map +0 -1
  628. package/dist/workflows/options.js +0 -16
  629. package/dist/workflows/options.js.map +0 -1
  630. package/examples/non-strict-skips/artifacts/release-example-skips-0.1.0.tgz +0 -1
  631. package/examples/non-strict-skips/release.config.json +0 -31
  632. package/examples/pypi-registry/release.config.json +0 -31
  633. package/templates/github-actions/plan-and-approved-execute.yml +0 -58
  634. package/templates/github-actions/plan-only.yml +0 -33
  635. package/templates/github-actions/trusted-publishing.yml +0 -61
@@ -0,0 +1,578 @@
1
+ import * as Clock from "effect/Clock"
2
+ import * as Duration from "effect/Duration"
3
+ import * as Effect from "effect/Effect"
4
+ import * as Exit from "effect/Exit"
5
+ import * as Result from "effect/Result"
6
+ import * as Schema from "effect/Schema"
7
+ import {
8
+ SubjectId,
9
+ type CredentialRequest
10
+ } from "../model/authority.js"
11
+ import {
12
+ CredentialProvider,
13
+ type CredentialAuthorityError,
14
+ type CredentialGrant,
15
+ type CredentialStrategyUnsupported,
16
+ type CredentialUnavailable,
17
+ type MutationCredentialGrant
18
+ } from "./authority.js"
19
+ import {
20
+ ConclusiveProviderRejection,
21
+ CredentialStrategyUnsupportedCause,
22
+ CredentialUnavailableCause,
23
+ DependencyBlocked,
24
+ InconclusiveObservation,
25
+ ObservedSubject,
26
+ OutcomeUnknown,
27
+ PresentEquivalent,
28
+ ProviderBlocked,
29
+ RejectedBeforeDispatch,
30
+ Started,
31
+ SafeReason,
32
+ absentObservation,
33
+ differentObservation,
34
+ equivalentObservation,
35
+ inconclusiveObservation,
36
+ makeAlreadyEquivalent,
37
+ makeAuthorityAcquired,
38
+ makeAuthorityAcquiredButMutationNotDispatched,
39
+ makeBlockedSubject,
40
+ makeConvergedAfterMutation,
41
+ makeNotReached,
42
+ makeObservationReport,
43
+ makeReleaseReport,
44
+ makeUncertainSubject,
45
+ type Applied,
46
+ type AuthorityAcquired,
47
+ type CredentialFailureCause,
48
+ type MutationAttempt,
49
+ type MutationDecision,
50
+ type Observation,
51
+ type ObservationClassification,
52
+ type ObservationReport,
53
+ type ProviderDecision,
54
+ type RejectedByProvider,
55
+ type ReleaseReport,
56
+ type ReportConstructionError,
57
+ type SubjectReport
58
+ } from "./report.js"
59
+ import {
60
+ RecoveryCapabilityProfile,
61
+ isReadConvergenceLagCapable,
62
+ type RecoveryCapabilityProfile as RecoveryCapabilityProfileType
63
+ } from "./recovery.js"
64
+
65
+ export class ReleaseSubjectError
66
+ extends Schema.TaggedErrorClass<ReleaseSubjectError>()("ReleaseSubjectError", {
67
+ subject: SubjectId,
68
+ phase: Schema.Literals(["observe", "mutate"]),
69
+ commitment: Schema.Literals(["before-dispatch", "unknown"]),
70
+ reason: SafeReason
71
+ }) {}
72
+
73
+ export class ReleaseCoordinatorConstructionError
74
+ extends Schema.TaggedErrorClass<ReleaseCoordinatorConstructionError>()(
75
+ "ReleaseCoordinatorConstructionError",
76
+ { reason: SafeReason }
77
+ ) {}
78
+
79
+ /** Provider-neutral subject derived from one verified prepared intent. */
80
+ export type ReleaseObservationContext =
81
+ | { readonly phase: "pre-mutation" }
82
+ | { readonly phase: "post-mutation", readonly attempt: MutationAttempt }
83
+
84
+ export interface ReleaseSubject {
85
+ readonly id: SubjectId
86
+ /** Durable provider policy. Coordinator retries are derived only from this value. */
87
+ readonly recovery: RecoveryCapabilityProfileType
88
+ /** Earlier prepared subjects that must converge before this subject may run. */
89
+ readonly prerequisites?: ReadonlyArray<SubjectId>
90
+ readonly observationRequests: readonly [CredentialRequest, ...Array<CredentialRequest>]
91
+ readonly mutationRequest: CredentialRequest
92
+ /** Required by every durable-cas-required recovery profile. Runs before mutation authority acquisition. */
93
+ readonly claimMutation?: (
94
+ decision: MutationDecision
95
+ ) => Effect.Effect<void, ReleaseSubjectError>
96
+ readonly observe: (
97
+ grant: CredentialGrant,
98
+ context: ReleaseObservationContext
99
+ ) => Effect.Effect<Observation, ReleaseSubjectError>
100
+ readonly decide: (observation: Observation) => ProviderDecision
101
+ readonly mutate: (
102
+ decision: MutationDecision,
103
+ grant: MutationCredentialGrant
104
+ ) => Effect.Effect<MutationAttempt, ReleaseSubjectError>
105
+ }
106
+
107
+ export interface ReleaseSubjectsInput {
108
+ /** Local, already-verified prepared identity; always makes the report nonempty. */
109
+ readonly prepared: SubjectId
110
+ /** Dependency-ordered remote subjects. */
111
+ readonly subjects: ReadonlyArray<ReleaseSubject>
112
+ }
113
+
114
+ const constructionFailure = (reason: string | SafeReason): ReleaseCoordinatorConstructionError =>
115
+ new ReleaseCoordinatorConstructionError({
116
+ reason: typeof reason === "string" ? SafeReason.make(reason) : reason
117
+ })
118
+
119
+ const validateRequestIdentity = (
120
+ subject: ReleaseSubject,
121
+ request: CredentialRequest,
122
+ purpose: "observe" | "mutation"
123
+ ): void => {
124
+ if (request.subject !== subject.id) {
125
+ throw constructionFailure("A credential request does not match its prepared subject identity.")
126
+ }
127
+ if (purpose === "observe" ? request.purpose !== "observe" : request.purpose === "observe") {
128
+ throw constructionFailure("A credential request is assigned to the wrong authority phase.")
129
+ }
130
+ if (purpose === "mutation" && request.strategy.kind === "anonymous") {
131
+ throw constructionFailure("A mutation credential request cannot use anonymous authority.")
132
+ }
133
+ }
134
+
135
+ const validateInput = (input: ReleaseSubjectsInput): Effect.Effect<void, ReleaseCoordinatorConstructionError> =>
136
+ Effect.try({
137
+ try: () => {
138
+ const identities = new Set<string>([input.prepared])
139
+ for (const subject of input.subjects) {
140
+ if (identities.has(subject.id)) {
141
+ throw constructionFailure("Prepared and remote subject identities must be unique.")
142
+ }
143
+ const prerequisites = new Set<SubjectId>()
144
+ for (const prerequisite of subject.prerequisites ?? []) {
145
+ if (!identities.has(prerequisite)) {
146
+ throw constructionFailure("Every prerequisite must identify the prepared subject or an earlier remote subject.")
147
+ }
148
+ if (prerequisites.has(prerequisite)) {
149
+ throw constructionFailure("A release subject cannot declare the same prerequisite twice.")
150
+ }
151
+ prerequisites.add(prerequisite)
152
+ }
153
+ if (subject.observationRequests.length === 0) {
154
+ throw constructionFailure("Every remote subject requires at least one observation strategy.")
155
+ }
156
+ Schema.decodeUnknownSync(RecoveryCapabilityProfile, { onExcessProperty: "error" })(subject.recovery)
157
+ if (subject.recovery.historyRequirement === "durable-cas-required" && subject.claimMutation === undefined) {
158
+ throw constructionFailure("A durable-cas-required subject has no terminal mutation-claim boundary.")
159
+ }
160
+ subject.observationRequests.forEach((request, index) => {
161
+ validateRequestIdentity(subject, request, "observe")
162
+ if (request.strategy.kind === "anonymous" && index !== 0) {
163
+ throw constructionFailure("Anonymous observation must be the first declared strategy.")
164
+ }
165
+ })
166
+ validateRequestIdentity(subject, subject.mutationRequest, "mutation")
167
+ identities.add(subject.id)
168
+ }
169
+ },
170
+ catch: (cause) => cause instanceof ReleaseCoordinatorConstructionError
171
+ ? cause
172
+ : constructionFailure("The release subject contract is invalid.")
173
+ })
174
+
175
+ const fromReportResult = <A>(
176
+ result: Result.Result<A, ReportConstructionError>
177
+ ): Effect.Effect<A, ReleaseCoordinatorConstructionError> => Result.isFailure(result)
178
+ ? Effect.fail(constructionFailure(result.failure.reason))
179
+ : Effect.succeed(result.success)
180
+
181
+ type ReportableCredentialFailure = CredentialUnavailable | CredentialStrategyUnsupported
182
+
183
+ // The error contributes only its closed tag. Host error fields, especially
184
+ // `reason`, are transient diagnostics and may contain credential material.
185
+ const credentialFailureCause = (
186
+ request: CredentialRequest,
187
+ error: ReportableCredentialFailure
188
+ ): CredentialFailureCause => error._tag === "CredentialUnavailable"
189
+ ? CredentialUnavailableCause.make({
190
+ provider: request.provider,
191
+ purpose: request.purpose,
192
+ strategy: request.strategy.kind
193
+ })
194
+ : CredentialStrategyUnsupportedCause.make({
195
+ provider: request.provider,
196
+ purpose: request.purpose,
197
+ strategy: request.strategy.kind
198
+ })
199
+
200
+ const unavailableObservation = (
201
+ subject: SubjectId,
202
+ request?: CredentialRequest,
203
+ error?: ReportableCredentialFailure
204
+ ): InconclusiveObservation =>
205
+ InconclusiveObservation.make({
206
+ subject,
207
+ reason: SafeReason.make("Observation authority or provider read was unavailable."),
208
+ ...(request === undefined || error === undefined ? {} : { cause: credentialFailureCause(request, error) })
209
+ })
210
+
211
+ const subjectObservationFailure = (
212
+ subject: ReleaseSubject,
213
+ error: ReleaseSubjectError
214
+ ): Effect.Effect<Observation, ReleaseCoordinatorConstructionError> => error.subject !== subject.id
215
+ ? Effect.fail(constructionFailure("A subject error does not match its prepared subject identity."))
216
+ : Effect.succeed(InconclusiveObservation.make({ subject: subject.id, reason: error.reason }))
217
+
218
+ const observeOnce = (
219
+ credentials: CredentialProvider["Service"],
220
+ subject: ReleaseSubject,
221
+ request: CredentialRequest,
222
+ context: ReleaseObservationContext
223
+ ): Effect.Effect<{
224
+ readonly observation: Observation
225
+ readonly authorities: ReadonlyArray<AuthorityAcquired>
226
+ }, ReleaseCoordinatorConstructionError> =>
227
+ credentials.acquireForObservation(request).pipe(
228
+ Effect.flatMap((grant) => makeAuthority(request, grant).pipe(
229
+ Effect.flatMap((authority) => subject.observe(grant, context).pipe(
230
+ Effect.catch((error) => subjectObservationFailure(subject, error)),
231
+ Effect.map((observation) => ({ observation, authorities: [authority] }))
232
+ ))
233
+ )),
234
+ Effect.catchTags({
235
+ CredentialUnavailable: (error) => Effect.succeed({
236
+ observation: unavailableObservation(subject.id, request, error), authorities: []
237
+ }),
238
+ CredentialAudienceMismatch: () => Effect.succeed({
239
+ observation: unavailableObservation(subject.id), authorities: []
240
+ }),
241
+ CredentialPurposeMismatch: () => Effect.succeed({
242
+ observation: unavailableObservation(subject.id), authorities: []
243
+ }),
244
+ CredentialStrategyUnsupported: (error) => Effect.succeed({
245
+ observation: unavailableObservation(subject.id, request, error), authorities: []
246
+ }),
247
+ CredentialSubjectMismatch: () => Effect.succeed({
248
+ observation: unavailableObservation(subject.id), authorities: []
249
+ })
250
+ }),
251
+ Effect.flatMap((result) => result.observation.subject === subject.id
252
+ ? Effect.succeed(result)
253
+ : Effect.fail(constructionFailure("An observation does not match its prepared subject identity.")))
254
+ )
255
+
256
+ const observeTrace = Effect.fn("observeReleaseSubject")(function*(
257
+ credentials: CredentialProvider["Service"],
258
+ subject: ReleaseSubject,
259
+ context: ReleaseObservationContext
260
+ ) {
261
+ const observations: Array<Observation> = []
262
+ const authorities: Array<AuthorityAcquired> = []
263
+ for (const request of subject.observationRequests) {
264
+ const observed = yield* observeOnce(credentials, subject, request, context)
265
+ let observation = observed.observation
266
+ authorities.push(...observed.authorities)
267
+ const visibilityPendingIsAdmitted = context.phase === "post-mutation" &&
268
+ context.attempt._tag !== "RejectedBeforeDispatch" &&
269
+ context.attempt._tag !== "RejectedByProvider" &&
270
+ isReadConvergenceLagCapable(subject.recovery)
271
+ if (observation._tag === "VisibilityPending" && !visibilityPendingIsAdmitted) {
272
+ observation = InconclusiveObservation.make({
273
+ subject: subject.id,
274
+ reason: SafeReason.make(
275
+ "VisibilityPending was not admitted by a lag-capable same-invocation mutation profile and was treated as inconclusive."
276
+ )
277
+ })
278
+ }
279
+ observations.push(observation)
280
+ if (observation._tag !== "Inconclusive") break
281
+ }
282
+ return {
283
+ observations: observations as [Observation, ...Array<Observation>],
284
+ authorities
285
+ }
286
+ })
287
+
288
+ const retryEligibleObservation = (observation: Observation): boolean =>
289
+ observation._tag === "VisibilityPending" || observation._tag === "Inconclusive"
290
+
291
+ const retryDelayMs = (
292
+ profile: RecoveryCapabilityProfileType,
293
+ completedAttempts: number
294
+ ): number => {
295
+ const { baseMs, factor, capMs } = profile.readConvergence.observationRetry.backoff
296
+ const scaled = baseMs * factor ** Math.max(0, completedAttempts - 1)
297
+ return Math.min(capMs, scaled)
298
+ }
299
+
300
+ const observeAfterMutation = Effect.fn("observeReleaseSubjectAfterMutation")(function*(
301
+ credentials: CredentialProvider["Service"],
302
+ subject: ReleaseSubject,
303
+ attempt: MutationAttempt
304
+ ) {
305
+ const observations: Array<Observation> = []
306
+ const authorities: Array<AuthorityAcquired> = []
307
+ const policy = subject.recovery.readConvergence.observationRetry
308
+ const maxAttempts = attempt._tag === "RejectedBeforeDispatch" ? 1 : policy.maxAttempts
309
+ const startedAt = yield* Clock.currentTimeMillis
310
+ let completedAttempts = 0
311
+
312
+ while (completedAttempts < maxAttempts) {
313
+ if (completedAttempts > 0) {
314
+ const delayMs = retryDelayMs(subject.recovery, completedAttempts)
315
+ const now = yield* Clock.currentTimeMillis
316
+ const elapsedMs = Math.max(0, now - startedAt)
317
+ if (elapsedMs + delayMs > policy.totalBudgetMs) break
318
+ yield* Effect.sleep(Duration.millis(delayMs))
319
+ }
320
+
321
+ const observed = yield* observeTrace(credentials, subject, { phase: "post-mutation", attempt })
322
+ observations.push(...observed.observations)
323
+ authorities.push(...observed.authorities)
324
+ completedAttempts += 1
325
+ if (!retryEligibleObservation(observed.observations.at(-1)!)) break
326
+ }
327
+
328
+ return {
329
+ observations: observations as [Observation, ...Array<Observation>],
330
+ authorities
331
+ }
332
+ })
333
+
334
+ const classifyObservation = (observation: Observation): ObservationClassification => {
335
+ switch (observation._tag) {
336
+ case "PresentEquivalent":
337
+ return equivalentObservation()
338
+ case "PresentDifferent":
339
+ return differentObservation(observation.differences)
340
+ case "AuthoritativelyAbsent":
341
+ return absentObservation(observation.basis)
342
+ case "VisibilityPending":
343
+ return inconclusiveObservation(SafeReason.make("Provider visibility remains pending."))
344
+ case "Inconclusive":
345
+ return inconclusiveObservation(observation.reason, observation.cause)
346
+ }
347
+ }
348
+
349
+ const providerBlockedFromAuthority = (
350
+ subject: SubjectId,
351
+ request: CredentialRequest,
352
+ error: CredentialAuthorityError
353
+ ): ProviderBlocked => ProviderBlocked.make({
354
+ subject,
355
+ reason: SafeReason.make("Mutation authority was unavailable for the exact prepared request."),
356
+ ...(error._tag === "CredentialUnavailable" || error._tag === "CredentialStrategyUnsupported"
357
+ ? { cause: credentialFailureCause(request, error) }
358
+ : {})
359
+ })
360
+
361
+ const makeAuthority = (
362
+ request: CredentialRequest,
363
+ grant: CredentialGrant
364
+ ): Effect.Effect<AuthorityAcquired, ReleaseCoordinatorConstructionError> => {
365
+ const purposes = [...grant.purposes]
366
+ if (purposes.length === 0) {
367
+ return Effect.fail(constructionFailure("A credential grant carries no authority purpose."))
368
+ }
369
+ if (grant.subject !== request.subject || grant.provider !== request.provider ||
370
+ grant.audience !== request.audience || !grant.purposes.has(request.purpose)) {
371
+ return Effect.fail(constructionFailure("A credential grant does not match the exact prepared request."))
372
+ }
373
+ return fromReportResult(makeAuthorityAcquired({
374
+ subject: request.subject,
375
+ provider: request.provider,
376
+ audience: request.audience,
377
+ requestedPurpose: request.purpose,
378
+ grantKind: grant._tag,
379
+ purposes: purposes as [typeof purposes[number], ...Array<typeof purposes[number]>]
380
+ }))
381
+ }
382
+
383
+ const decide = (
384
+ subject: ReleaseSubject,
385
+ observation: Observation
386
+ ): Effect.Effect<ProviderDecision, ReleaseCoordinatorConstructionError> => Effect.try({
387
+ try: () => subject.decide(observation),
388
+ catch: () => constructionFailure("The provider decision function did not return a total decision.")
389
+ }).pipe(Effect.flatMap((decision) => decision.subject === subject.id
390
+ ? Effect.succeed(decision)
391
+ : Effect.fail(constructionFailure("A provider decision does not match its prepared subject identity."))))
392
+
393
+ const performMutation = (
394
+ subject: ReleaseSubject,
395
+ decision: MutationDecision,
396
+ grant: MutationCredentialGrant
397
+ ): Effect.Effect<MutationAttempt, ReleaseCoordinatorConstructionError> => subject.mutate(decision, grant).pipe(
398
+ Effect.catch((error) => error.subject !== subject.id
399
+ ? Effect.fail(constructionFailure("A mutation error does not match its prepared subject identity."))
400
+ : Effect.succeed(error.commitment === "before-dispatch"
401
+ ? RejectedBeforeDispatch.make({ subject: subject.id, reason: error.reason })
402
+ : OutcomeUnknown.make({ subject: subject.id, reason: error.reason }))),
403
+ Effect.flatMap((attempt) => attempt.subject === subject.id
404
+ ? Effect.succeed(attempt)
405
+ : Effect.fail(constructionFailure("A mutation attempt does not match its prepared subject identity.")))
406
+ )
407
+
408
+ const uncertainAttempt = (
409
+ attempt: Exclude<MutationAttempt, RejectedBeforeDispatch>
410
+ ): Started | Applied | OutcomeUnknown | RejectedByProvider => attempt
411
+
412
+ const convergedAttempt = (
413
+ attempt: MutationAttempt
414
+ ): Applied | OutcomeUnknown | RejectedByProvider => attempt._tag === "Started"
415
+ ? OutcomeUnknown.make({
416
+ subject: attempt.subject,
417
+ reason: SafeReason.make("Mutation started without a terminal provider response before reobservation.")
418
+ })
419
+ : attempt as Applied | OutcomeUnknown | RejectedByProvider
420
+
421
+ const publishSubject = Effect.fn("publishReleaseSubject")(function*(
422
+ credentials: CredentialProvider["Service"],
423
+ subject: ReleaseSubject
424
+ ) {
425
+ const before = yield* observeTrace(credentials, subject, { phase: "pre-mutation" })
426
+ const decision = yield* decide(subject, before.observations.at(-1)!)
427
+ switch (decision._tag) {
428
+ case "AlreadyEquivalent":
429
+ return yield* fromReportResult(makeAlreadyEquivalent(
430
+ subject.id,
431
+ before.observations,
432
+ before.authorities
433
+ ))
434
+ case "Conflict":
435
+ case "Blocked":
436
+ return yield* fromReportResult(makeBlockedSubject({
437
+ subject: subject.id,
438
+ observationAuthorities: before.authorities,
439
+ observations: before.observations,
440
+ cause: decision
441
+ }))
442
+ case "NeedsMutation":
443
+ case "ProviderAuthorizedCreate": {
444
+ if (subject.claimMutation !== undefined) {
445
+ const claimed = yield* Effect.exit(subject.claimMutation(decision))
446
+ if (Exit.isFailure(claimed)) {
447
+ return yield* fromReportResult(makeBlockedSubject({
448
+ subject: subject.id,
449
+ observationAuthorities: before.authorities,
450
+ observations: before.observations,
451
+ cause: ProviderBlocked.make({
452
+ subject: subject.id,
453
+ reason: SafeReason.make("The terminal publication claim was unavailable or already occupied.")
454
+ })
455
+ }))
456
+ }
457
+ }
458
+ const acquisition = yield* credentials.acquireForMutation(subject.mutationRequest, decision).pipe(
459
+ Effect.map((grant) => ({ _tag: "Granted", grant } as const)),
460
+ Effect.catch((error) => Effect.succeed({ _tag: "Denied", error } as const))
461
+ )
462
+ if (acquisition._tag === "Denied") {
463
+ return yield* fromReportResult(makeBlockedSubject({
464
+ subject: subject.id,
465
+ observationAuthorities: before.authorities,
466
+ observations: before.observations,
467
+ cause: providerBlockedFromAuthority(subject.id, subject.mutationRequest, acquisition.error)
468
+ }))
469
+ }
470
+ const grant = acquisition.grant
471
+ const authority = yield* makeAuthority(subject.mutationRequest, grant)
472
+ const attempt = yield* performMutation(subject, decision, grant)
473
+ const after = yield* observeAfterMutation(credentials, subject, attempt)
474
+ const observationAuthorities = [...before.authorities, ...after.authorities]
475
+ if (attempt._tag === "RejectedBeforeDispatch") {
476
+ const cause = yield* fromReportResult(makeAuthorityAcquiredButMutationNotDispatched({
477
+ subject: subject.id,
478
+ authority,
479
+ attempt
480
+ }))
481
+ return yield* fromReportResult(makeBlockedSubject({
482
+ subject: subject.id,
483
+ observationAuthorities,
484
+ observations: [...before.observations, ...after.observations],
485
+ cause
486
+ }))
487
+ }
488
+ if (after.observations.at(-1)?._tag === "PresentEquivalent") {
489
+ return yield* fromReportResult(makeConvergedAfterMutation({
490
+ subject: subject.id,
491
+ observationAuthorities,
492
+ preObservations: before.observations,
493
+ decision,
494
+ authority,
495
+ attempt: convergedAttempt(attempt),
496
+ postObservations: after.observations
497
+ }))
498
+ }
499
+ if (attempt._tag === "RejectedByProvider" &&
500
+ after.observations.at(-1)?._tag !== "Inconclusive" &&
501
+ after.observations.at(-1)?._tag !== "VisibilityPending") {
502
+ return yield* fromReportResult(makeBlockedSubject({
503
+ subject: subject.id,
504
+ observationAuthorities,
505
+ observations: before.observations,
506
+ cause: ConclusiveProviderRejection.make({
507
+ subject: subject.id,
508
+ fact: attempt.fact,
509
+ postObservations: after.observations
510
+ })
511
+ }))
512
+ }
513
+ return yield* fromReportResult(makeUncertainSubject({
514
+ subject: subject.id,
515
+ observationAuthorities,
516
+ observations: before.observations,
517
+ decision,
518
+ authority,
519
+ attempt: uncertainAttempt(attempt),
520
+ trace: after.observations
521
+ }))
522
+ }
523
+ }
524
+ })
525
+
526
+ /** Read-only projection. It never asks for publish or correction authority. */
527
+ export const observeReleaseSubjects = Effect.fn("observeReleaseSubjects")(function*(
528
+ input: ReleaseSubjectsInput
529
+ ): Effect.fn.Return<ObservationReport, ReleaseCoordinatorConstructionError, CredentialProvider> {
530
+ yield* validateInput(input)
531
+ const credentials = yield* CredentialProvider
532
+ const observed: [ObservedSubject, ...Array<ObservedSubject>] = [
533
+ new ObservedSubject({
534
+ subject: input.prepared,
535
+ observationAuthorities: [],
536
+ observation: equivalentObservation()
537
+ })
538
+ ]
539
+ for (const subject of input.subjects) {
540
+ const trace = yield* observeTrace(credentials, subject, { phase: "pre-mutation" })
541
+ observed.push(new ObservedSubject({
542
+ subject: subject.id,
543
+ observationAuthorities: trace.authorities,
544
+ observation: classifyObservation(trace.observations.at(-1)!)
545
+ }))
546
+ }
547
+ return makeObservationReport(observed)
548
+ })
549
+
550
+ /** Dependency-ordered observation, lazy authority, mutation, and reobservation. */
551
+ export const publishReleaseSubjects = Effect.fn("publishReleaseSubjects")(function*(
552
+ input: ReleaseSubjectsInput
553
+ ): Effect.fn.Return<ReleaseReport, ReleaseCoordinatorConstructionError, CredentialProvider> {
554
+ yield* validateInput(input)
555
+ const credentials = yield* CredentialProvider
556
+ const localObservation = PresentEquivalent.make({ subject: input.prepared })
557
+ const local = yield* fromReportResult(makeAlreadyEquivalent(input.prepared, [localObservation], []))
558
+ const reports: [SubjectReport, ...Array<SubjectReport>] = [local]
559
+ const converged = new Map<SubjectId, boolean>([[input.prepared, true]])
560
+ for (const subject of input.subjects) {
561
+ const prerequisite = (subject.prerequisites ?? []).find((candidate) => converged.get(candidate) !== true)
562
+ if (prerequisite !== undefined) {
563
+ reports.push(yield* fromReportResult(makeNotReached(
564
+ subject.id,
565
+ new DependencyBlocked({ prerequisite })
566
+ )))
567
+ converged.set(subject.id, false)
568
+ continue
569
+ }
570
+ const report = yield* publishSubject(credentials, subject)
571
+ reports.push(report)
572
+ converged.set(
573
+ subject.id,
574
+ report._tag === "AlreadyEquivalent" || report._tag === "ConvergedAfterMutation"
575
+ )
576
+ }
577
+ return makeReleaseReport(reports)
578
+ })