@msn-control/liftoff 0.11.2 → 0.12.0

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 (339) hide show
  1. package/DEVELOPER.md +26 -24
  2. package/README.md +18 -18
  3. package/assets/governance/single-maintainer-gitflow/activation-v2-graph.json +1583 -0
  4. package/dist/adapters/credentials/github-enrollment.d.ts +51 -0
  5. package/dist/adapters/credentials/github-enrollment.js +226 -0
  6. package/dist/adapters/credentials/github-enrollment.js.map +1 -0
  7. package/dist/adapters/credentials/protected-input.d.ts +7 -0
  8. package/dist/adapters/credentials/protected-input.js +68 -0
  9. package/dist/adapters/credentials/protected-input.js.map +1 -0
  10. package/dist/adapters/filesystem/executables.d.ts +2 -0
  11. package/dist/adapters/filesystem/executables.js +70 -0
  12. package/dist/adapters/filesystem/executables.js.map +1 -0
  13. package/dist/adapters/filesystem/update-previews.d.ts +9 -0
  14. package/dist/adapters/filesystem/update-previews.js +45 -2
  15. package/dist/adapters/filesystem/update-previews.js.map +1 -1
  16. package/dist/adapters/filesystem/workstation-attempts.d.ts +13 -0
  17. package/dist/adapters/filesystem/workstation-attempts.js +78 -0
  18. package/dist/adapters/filesystem/workstation-attempts.js.map +1 -0
  19. package/dist/adapters/github/activation-rest.d.ts +40 -0
  20. package/dist/adapters/github/activation-rest.js +272 -0
  21. package/dist/adapters/github/activation-rest.js.map +1 -0
  22. package/dist/adapters/process/shell-command.d.ts +1 -0
  23. package/dist/adapters/process/shell-command.js +5 -2
  24. package/dist/adapters/process/shell-command.js.map +1 -1
  25. package/dist/adapters/state/azure-blob.d.ts +43 -0
  26. package/dist/adapters/state/azure-blob.js +335 -0
  27. package/dist/adapters/state/azure-blob.js.map +1 -0
  28. package/dist/adapters/state/darwin-capabilities.d.ts +33 -0
  29. package/dist/adapters/state/darwin-capabilities.js +146 -0
  30. package/dist/adapters/state/darwin-capabilities.js.map +1 -0
  31. package/dist/adapters/state/darwin-native-host.d.ts +32 -0
  32. package/dist/adapters/state/darwin-native-host.js +294 -0
  33. package/dist/adapters/state/darwin-native-host.js.map +1 -0
  34. package/dist/adapters/state/darwin-profile.d.ts +6 -0
  35. package/dist/adapters/state/darwin-profile.js +56 -0
  36. package/dist/adapters/state/darwin-profile.js.map +1 -0
  37. package/dist/adapters/state/darwin-system-program.d.ts +1 -0
  38. package/dist/adapters/state/darwin-system-program.js +94 -0
  39. package/dist/adapters/state/darwin-system-program.js.map +1 -0
  40. package/dist/adapters/state/local-qualification.d.ts +16 -0
  41. package/dist/adapters/state/local-qualification.js +299 -0
  42. package/dist/adapters/state/local-qualification.js.map +1 -0
  43. package/dist/adapters/state/local.d.ts +25 -0
  44. package/dist/adapters/state/local.js +145 -0
  45. package/dist/adapters/state/local.js.map +1 -0
  46. package/dist/adapters/state/native-command.d.ts +21 -0
  47. package/dist/adapters/state/native-command.js +82 -0
  48. package/dist/adapters/state/native-command.js.map +1 -0
  49. package/dist/adapters/state/native-files.d.ts +2 -0
  50. package/dist/adapters/state/native-files.js +47 -0
  51. package/dist/adapters/state/native-files.js.map +1 -0
  52. package/dist/adapters/state/native-system.d.ts +38 -0
  53. package/dist/adapters/state/native-system.js +86 -0
  54. package/dist/adapters/state/native-system.js.map +1 -0
  55. package/dist/adapters/state/opentofu.d.ts +35 -0
  56. package/dist/adapters/state/opentofu.js +350 -0
  57. package/dist/adapters/state/opentofu.js.map +1 -0
  58. package/dist/adapters/state/owned-process.d.ts +43 -0
  59. package/dist/adapters/state/owned-process.js +252 -0
  60. package/dist/adapters/state/owned-process.js.map +1 -0
  61. package/dist/adapters/state/posix-lock-program.d.ts +1 -0
  62. package/dist/adapters/state/posix-lock-program.js +185 -0
  63. package/dist/adapters/state/posix-lock-program.js.map +1 -0
  64. package/dist/adapters/state/posix-native-lock.d.ts +15 -0
  65. package/dist/adapters/state/posix-native-lock.js +145 -0
  66. package/dist/adapters/state/posix-native-lock.js.map +1 -0
  67. package/dist/adapters/state/protected-workspace.d.ts +57 -0
  68. package/dist/adapters/state/protected-workspace.js +412 -0
  69. package/dist/adapters/state/protected-workspace.js.map +1 -0
  70. package/dist/application/context.d.ts +3 -0
  71. package/dist/application/diagnose/doctor.d.ts +5 -0
  72. package/dist/application/diagnose/doctor.js +21 -5
  73. package/dist/application/diagnose/doctor.js.map +1 -1
  74. package/dist/application/diagnose/generated-project.js +2 -7
  75. package/dist/application/diagnose/generated-project.js.map +1 -1
  76. package/dist/application/initialize/fixture.js +8 -1
  77. package/dist/application/initialize/fixture.js.map +1 -1
  78. package/dist/application/initialize/use-case.js +70 -36
  79. package/dist/application/initialize/use-case.js.map +1 -1
  80. package/dist/application/project/manifest.js +5 -4
  81. package/dist/application/project/manifest.js.map +1 -1
  82. package/dist/application/state-migration/execution.d.ts +32 -0
  83. package/dist/application/state-migration/execution.js +440 -0
  84. package/dist/application/state-migration/execution.js.map +1 -0
  85. package/dist/application/state-migration/index.d.ts +22 -0
  86. package/dist/application/state-migration/index.js +17 -0
  87. package/dist/application/state-migration/index.js.map +1 -0
  88. package/dist/application/state-migration/lifecycle.d.ts +3 -0
  89. package/dist/application/state-migration/lifecycle.js +149 -0
  90. package/dist/application/state-migration/lifecycle.js.map +1 -0
  91. package/dist/application/state-migration/planning.d.ts +7 -0
  92. package/dist/application/state-migration/planning.js +150 -0
  93. package/dist/application/state-migration/planning.js.map +1 -0
  94. package/dist/application/state-migration/recovery.d.ts +5 -0
  95. package/dist/application/state-migration/recovery.js +145 -0
  96. package/dist/application/state-migration/recovery.js.map +1 -0
  97. package/dist/application/state-migration/runtime.d.ts +32 -0
  98. package/dist/application/state-migration/runtime.js +188 -0
  99. package/dist/application/state-migration/runtime.js.map +1 -0
  100. package/dist/application/state-migration/service.d.ts +2 -0
  101. package/dist/application/state-migration/service.js +20 -0
  102. package/dist/application/state-migration/service.js.map +1 -0
  103. package/dist/application/update/command-guidance.d.ts +19 -2
  104. package/dist/application/update/command-guidance.js +20 -6
  105. package/dist/application/update/command-guidance.js.map +1 -1
  106. package/dist/application/update/guidance-context.d.ts +2 -0
  107. package/dist/application/update/guidance-context.js +35 -0
  108. package/dist/application/update/guidance-context.js.map +1 -0
  109. package/dist/application/update/inspection.d.ts +21 -4
  110. package/dist/application/update/inspection.js +88 -29
  111. package/dist/application/update/inspection.js.map +1 -1
  112. package/dist/application/update/migration-runtime.js +25 -13
  113. package/dist/application/update/migration-runtime.js.map +1 -1
  114. package/dist/application/update/output.d.ts +4 -1
  115. package/dist/application/update/output.js +14 -4
  116. package/dist/application/update/output.js.map +1 -1
  117. package/dist/application/update/preview.d.ts +10 -1
  118. package/dist/application/update/preview.js +26 -4
  119. package/dist/application/update/preview.js.map +1 -1
  120. package/dist/application/update/revalidation-plan.d.ts +1 -0
  121. package/dist/application/update/revalidation-plan.js +27 -15
  122. package/dist/application/update/revalidation-plan.js.map +1 -1
  123. package/dist/application/update/revalidation.d.ts +4 -1
  124. package/dist/application/update/revalidation.js +64 -23
  125. package/dist/application/update/revalidation.js.map +1 -1
  126. package/dist/application/update/use-case.js +50 -29
  127. package/dist/application/update/use-case.js.map +1 -1
  128. package/dist/cli/args/definitions.js +11 -3
  129. package/dist/cli/args/definitions.js.map +1 -1
  130. package/dist/cli/args/parser.js +29 -3
  131. package/dist/cli/args/parser.js.map +1 -1
  132. package/dist/domain/governance/activation/approvals.d.ts +264 -7
  133. package/dist/domain/governance/activation/approvals.js +166 -23
  134. package/dist/domain/governance/activation/approvals.js.map +1 -1
  135. package/dist/domain/governance/activation/capabilities.js +5 -2
  136. package/dist/domain/governance/activation/capabilities.js.map +1 -1
  137. package/dist/domain/governance/activation/evidence.d.ts +2 -1
  138. package/dist/domain/governance/activation/evidence.js +64 -11
  139. package/dist/domain/governance/activation/evidence.js.map +1 -1
  140. package/dist/domain/governance/activation/graph.d.ts +11 -384
  141. package/dist/domain/governance/activation/graph.js +424 -379
  142. package/dist/domain/governance/activation/graph.js.map +1 -1
  143. package/dist/domain/governance/activation/inputs.d.ts +3 -1
  144. package/dist/domain/governance/activation/inputs.js +47 -6
  145. package/dist/domain/governance/activation/inputs.js.map +1 -1
  146. package/dist/domain/governance/activation/operations.d.ts +4 -1
  147. package/dist/domain/governance/activation/operations.js +110 -22
  148. package/dist/domain/governance/activation/operations.js.map +1 -1
  149. package/dist/domain/governance/activation/readiness.d.ts +10 -0
  150. package/dist/domain/governance/activation/readiness.js +77 -38
  151. package/dist/domain/governance/activation/readiness.js.map +1 -1
  152. package/dist/domain/governance/activation/types.d.ts +151 -9
  153. package/dist/domain/governance/activation/types.js +23 -1
  154. package/dist/domain/governance/activation/types.js.map +1 -1
  155. package/dist/domain/governance/activation/validators.d.ts +4 -1
  156. package/dist/domain/governance/activation/validators.js +419 -26
  157. package/dist/domain/governance/activation/validators.js.map +1 -1
  158. package/dist/domain/governance/policy/identity.d.ts +55 -13
  159. package/dist/domain/governance/policy/identity.js +28 -8
  160. package/dist/domain/governance/policy/identity.js.map +1 -1
  161. package/dist/domain/project/artifact-lifecycle.d.ts +2 -1
  162. package/dist/domain/project/artifact-lifecycle.js +7 -1
  163. package/dist/domain/project/artifact-lifecycle.js.map +1 -1
  164. package/dist/domain/project/catalog.d.ts +50 -1
  165. package/dist/domain/project/catalog.js +104 -4
  166. package/dist/domain/project/catalog.js.map +1 -1
  167. package/dist/domain/project/contracts.d.ts +9 -1
  168. package/dist/domain/project/infrastructure-layout.d.ts +1 -1
  169. package/dist/domain/project/infrastructure-layout.js +3 -1
  170. package/dist/domain/project/infrastructure-layout.js.map +1 -1
  171. package/dist/domain/project/manifest/governance.d.ts +1 -1
  172. package/dist/domain/project/manifest/governance.js +9 -10
  173. package/dist/domain/project/manifest/governance.js.map +1 -1
  174. package/dist/domain/project/planning.js +4 -5
  175. package/dist/domain/project/planning.js.map +1 -1
  176. package/dist/domain/repair/stateful-bounded.d.ts +1 -0
  177. package/dist/domain/repair/stateful-bounded.js +37 -0
  178. package/dist/domain/repair/stateful-bounded.js.map +1 -0
  179. package/dist/domain/repair/stateful-invariants.d.ts +28 -0
  180. package/dist/domain/repair/stateful-invariants.js +278 -0
  181. package/dist/domain/repair/stateful-invariants.js.map +1 -0
  182. package/dist/domain/repair/stateful.d.ts +690 -0
  183. package/dist/domain/repair/stateful.js +26 -0
  184. package/dist/domain/repair/stateful.js.map +1 -0
  185. package/dist/domain/workstation/constraints.d.ts +1 -0
  186. package/dist/domain/workstation/constraints.js.map +1 -1
  187. package/dist/domain/workstation/contracts.d.ts +42 -0
  188. package/dist/domain/workstation/contracts.js +2 -0
  189. package/dist/domain/workstation/contracts.js.map +1 -0
  190. package/dist/domain/workstation/executables.d.ts +19 -0
  191. package/dist/domain/workstation/executables.js +68 -0
  192. package/dist/domain/workstation/executables.js.map +1 -0
  193. package/dist/domain/workstation/versions.d.ts +8 -0
  194. package/dist/domain/workstation/versions.js +145 -0
  195. package/dist/domain/workstation/versions.js.map +1 -0
  196. package/dist/framework-adapters.d.ts +2 -0
  197. package/dist/framework-adapters.js +148 -22
  198. package/dist/framework-adapters.js.map +1 -1
  199. package/dist/framework-validation.d.ts +6 -0
  200. package/dist/framework-validation.js +89 -24
  201. package/dist/framework-validation.js.map +1 -1
  202. package/dist/generators/common/base.js +57 -17
  203. package/dist/generators/common/base.js.map +1 -1
  204. package/dist/governance-activation/activation-state.js +39 -7
  205. package/dist/governance-activation/activation-state.js.map +1 -1
  206. package/dist/governance-activation/authority-records.d.ts +4 -0
  207. package/dist/governance-activation/authority-records.js +24 -0
  208. package/dist/governance-activation/authority-records.js.map +1 -0
  209. package/dist/governance-activation/commands.d.ts +9 -1
  210. package/dist/governance-activation/commands.js +392 -97
  211. package/dist/governance-activation/commands.js.map +1 -1
  212. package/dist/governance-activation/compatibility.d.ts +11 -8
  213. package/dist/governance-activation/compatibility.js +37 -23
  214. package/dist/governance-activation/compatibility.js.map +1 -1
  215. package/dist/governance-activation/doctor.js +40 -16
  216. package/dist/governance-activation/doctor.js.map +1 -1
  217. package/dist/governance-activation/github-config.d.ts +17 -0
  218. package/dist/governance-activation/github-config.js +88 -0
  219. package/dist/governance-activation/github-config.js.map +1 -0
  220. package/dist/governance-activation/github-discovery.d.ts +18 -0
  221. package/dist/governance-activation/github-discovery.js +229 -0
  222. package/dist/governance-activation/github-discovery.js.map +1 -0
  223. package/dist/governance-activation/github-ports.d.ts +12 -0
  224. package/dist/governance-activation/github-ports.js +4 -0
  225. package/dist/governance-activation/github-ports.js.map +1 -0
  226. package/dist/governance-activation/github-publication.d.ts +3 -0
  227. package/dist/governance-activation/github-publication.js +190 -0
  228. package/dist/governance-activation/github-publication.js.map +1 -0
  229. package/dist/governance-activation/historical-common.d.ts +98 -0
  230. package/dist/governance-activation/historical-common.js +269 -0
  231. package/dist/governance-activation/historical-common.js.map +1 -0
  232. package/dist/governance-activation/historical-credential-policy.d.ts +3 -0
  233. package/dist/governance-activation/historical-credential-policy.js +91 -0
  234. package/dist/governance-activation/historical-credential-policy.js.map +1 -0
  235. package/dist/governance-activation/historical-inputs.d.ts +6 -0
  236. package/dist/governance-activation/historical-inputs.js +57 -0
  237. package/dist/governance-activation/historical-inputs.js.map +1 -0
  238. package/dist/governance-activation/historical-safety.d.ts +9 -0
  239. package/dist/governance-activation/historical-safety.js +102 -0
  240. package/dist/governance-activation/historical-safety.js.map +1 -0
  241. package/dist/governance-activation/historical-source-metadata.d.ts +31 -0
  242. package/dist/governance-activation/historical-source-metadata.js +55 -0
  243. package/dist/governance-activation/historical-source-metadata.js.map +1 -0
  244. package/dist/governance-activation/historical-state.d.ts +12 -50
  245. package/dist/governance-activation/historical-state.js +195 -308
  246. package/dist/governance-activation/historical-state.js.map +1 -1
  247. package/dist/governance-activation/historical-v1-auxiliary.d.ts +1 -0
  248. package/dist/governance-activation/historical-v1-auxiliary.js +43 -0
  249. package/dist/governance-activation/historical-v1-auxiliary.js.map +1 -0
  250. package/dist/governance-activation/historical-v1-phase-contracts.d.ts +31 -0
  251. package/dist/governance-activation/historical-v1-phase-contracts.js +46 -0
  252. package/dist/governance-activation/historical-v1-phase-contracts.js.map +1 -0
  253. package/dist/governance-activation/historical-v2.d.ts +53 -0
  254. package/dist/governance-activation/historical-v2.js +313 -0
  255. package/dist/governance-activation/historical-v2.js.map +1 -0
  256. package/dist/governance-activation/history-contracts.d.ts +21 -12
  257. package/dist/governance-activation/history-contracts.js +99 -27
  258. package/dist/governance-activation/history-contracts.js.map +1 -1
  259. package/dist/governance-activation/inputs.d.ts +8 -2
  260. package/dist/governance-activation/inputs.js +41 -6
  261. package/dist/governance-activation/inputs.js.map +1 -1
  262. package/dist/governance-activation/migration-history.d.ts +43 -5
  263. package/dist/governance-activation/migration-history.js +231 -50
  264. package/dist/governance-activation/migration-history.js.map +1 -1
  265. package/dist/governance-activation/migration.d.ts +7 -4
  266. package/dist/governance-activation/migration.js +53 -252
  267. package/dist/governance-activation/migration.js.map +1 -1
  268. package/dist/governance-activation/phase-azure.d.ts +3 -0
  269. package/dist/governance-activation/phase-azure.js +205 -0
  270. package/dist/governance-activation/phase-azure.js.map +1 -0
  271. package/dist/governance-activation/phase-bootstrap-state.js +3 -0
  272. package/dist/governance-activation/phase-bootstrap-state.js.map +1 -1
  273. package/dist/governance-activation/phase-github.d.ts +3 -0
  274. package/dist/governance-activation/phase-github.js +192 -0
  275. package/dist/governance-activation/phase-github.js.map +1 -0
  276. package/dist/governance-activation/phase-governance.js +8 -2
  277. package/dist/governance-activation/phase-governance.js.map +1 -1
  278. package/dist/governance-activation/public-plans.d.ts +36 -0
  279. package/dist/governance-activation/public-plans.js +100 -0
  280. package/dist/governance-activation/public-plans.js.map +1 -0
  281. package/dist/governance-activation/read-only.d.ts +3 -1
  282. package/dist/governance-activation/read-only.js +7 -3
  283. package/dist/governance-activation/read-only.js.map +1 -1
  284. package/dist/governance-activation/reconciliation.js +10 -0
  285. package/dist/governance-activation/reconciliation.js.map +1 -1
  286. package/dist/governance-activation/seed-lifecycle.js +14 -25
  287. package/dist/governance-activation/seed-lifecycle.js.map +1 -1
  288. package/dist/governance-activation/source-of-truth.d.ts +21 -5
  289. package/dist/governance-activation/source-of-truth.js +102 -13
  290. package/dist/governance-activation/source-of-truth.js.map +1 -1
  291. package/dist/governance-activation/task-projection.d.ts +4 -1
  292. package/dist/governance-activation/task-projection.js +32 -3
  293. package/dist/governance-activation/task-projection.js.map +1 -1
  294. package/dist/governance-activation/task-writes.d.ts +42 -0
  295. package/dist/governance-activation/task-writes.js +250 -0
  296. package/dist/governance-activation/task-writes.js.map +1 -0
  297. package/dist/governance-activation/transition-files.d.ts +12 -0
  298. package/dist/governance-activation/transition-files.js +109 -0
  299. package/dist/governance-activation/transition-files.js.map +1 -0
  300. package/dist/governance-activation/transition-planning.d.ts +3 -1
  301. package/dist/governance-activation/transition-planning.js +158 -20
  302. package/dist/governance-activation/transition-planning.js.map +1 -1
  303. package/dist/governance-activation/transition-ports.d.ts +31 -3
  304. package/dist/governance-activation/transition-records.d.ts +11 -1
  305. package/dist/governance-activation/transition-records.js +52 -6
  306. package/dist/governance-activation/transition-records.js.map +1 -1
  307. package/dist/governance-activation/transitions.d.ts +5 -0
  308. package/dist/governance-activation/transitions.js +271 -37
  309. package/dist/governance-activation/transitions.js.map +1 -1
  310. package/dist/governance-assessment/engine.js +22 -15
  311. package/dist/governance-assessment/engine.js.map +1 -1
  312. package/dist/governance-assessment/project.d.ts +2 -0
  313. package/dist/governance-assessment/project.js +2 -1
  314. package/dist/governance-assessment/project.js.map +1 -1
  315. package/dist/interactive.d.ts +0 -1
  316. package/dist/interactive.js +3 -22
  317. package/dist/interactive.js.map +1 -1
  318. package/dist/openspec-profile.d.ts +2 -1
  319. package/dist/openspec-profile.js +11 -41
  320. package/dist/openspec-profile.js.map +1 -1
  321. package/dist/repository-governance.d.ts +10 -5
  322. package/dist/repository-governance.js +178 -92
  323. package/dist/repository-governance.js.map +1 -1
  324. package/dist/telemetry/contract.d.ts +1 -1
  325. package/dist/telemetry/contract.js +3 -0
  326. package/dist/telemetry/contract.js.map +1 -1
  327. package/dist/workstation-catalog.d.ts +13 -1
  328. package/dist/workstation-catalog.js +122 -0
  329. package/dist/workstation-catalog.js.map +1 -1
  330. package/dist/workstation.d.ts +69 -14
  331. package/dist/workstation.js +600 -215
  332. package/dist/workstation.js.map +1 -1
  333. package/docs/cli-reference.md +94 -23
  334. package/docs/configuration-and-manifests.md +11 -10
  335. package/docs/prerequisites.md +93 -18
  336. package/docs/repository-governance.md +68 -40
  337. package/docs/spec-workflows-and-agents.md +132 -13
  338. package/docs/troubleshooting.md +28 -0
  339. package/package.json +1 -1
@@ -1,9 +1,199 @@
1
- import type { ApprovalCostCeiling, ApprovalDestinationScope, ApprovalEnvelope, ApprovalEvaluation, ApprovalGateKind, ApprovalResourceScope, EvidenceTransitionIdentity, HumanAuthorityQuestionKind, PhaseGraphNode, RequestedTransitionPlan, UserActivationState } from './types.js';
1
+ import type { ActivationConfiguration, ApprovalCostCeiling, ApprovalDestinationScope, ApprovalEnvelope, ApprovalEvaluation, ApprovalGateKind, ApprovalResourceScope, EvidenceTransitionIdentity, HumanAuthorityQuestionKind, PhaseGraphNode, RequestedTransitionPlan, SavedTransitionPlan, PlannedFileChange, TransitionOperation, UserActivationState } from './types.js';
2
2
  type ApprovalScope = ApprovalEnvelope | RequestedTransitionPlan;
3
+ export declare const approvalEnvelopeV3Schema: {
4
+ readonly $schema: 'https://json-schema.org/draft/2020-12/schema';
5
+ readonly $id: 'https://mission-control.local/liftoff/governance/approval-envelope.schema.v3.json';
6
+ readonly title: 'Liftoff governance approval envelope v3';
7
+ readonly type: 'object';
8
+ readonly additionalProperties: false;
9
+ readonly required: readonly ['schemaVersion', 'id', 'phaseId', 'gateKind', 'identity', 'baselineSha', 'planDigest', 'resources', 'destinations', 'permissions', 'costCeiling', 'policyExceptions', 'destructiveScope', 'expiresAt', 'approvedAt', 'approver'];
10
+ readonly properties: {
11
+ readonly schemaVersion: {
12
+ readonly const: number;
13
+ };
14
+ readonly id: {
15
+ readonly type: 'string';
16
+ readonly minLength: 1;
17
+ };
18
+ readonly phaseId: {
19
+ readonly type: 'string';
20
+ };
21
+ readonly scope: {
22
+ readonly enum: readonly ['local', 'activation', 'lifecycle'];
23
+ };
24
+ readonly coveredPhases: {
25
+ readonly type: 'array';
26
+ readonly uniqueItems: true;
27
+ readonly items: {
28
+ readonly enum: readonly ["seed-valid", "seed-verified", "seed-archived", "committed", "pushed", "phase-0-complete", "activation-approved", "bootstrap-workflow-source-ready", "credential-ready", "provider-ready", "state-path-selected", "existing-private-path", "bootstrap-local", "runner-ready", "private-backend-proof", "remote-import-verified", "remote-ready", "application-prerequisites-ready", "workflow-source-ready", "application-artifact-ready", "application-foundation", "dev-proof", "staging-qualified", "production-rehearsed", "green-red-proof", "enforcement-approved", "rulesets-applied", "live-readback", "bootstrap-state-disposed"];
29
+ };
30
+ };
31
+ readonly operationDigests: {
32
+ readonly type: 'array';
33
+ readonly uniqueItems: true;
34
+ readonly items: {
35
+ readonly type: 'string';
36
+ readonly pattern: '^[a-f0-9]{64}$';
37
+ };
38
+ };
39
+ readonly phasePlanDigests: {
40
+ readonly type: 'object';
41
+ readonly additionalProperties: {
42
+ readonly type: 'string';
43
+ readonly pattern: '^[a-f0-9]{64}$';
44
+ };
45
+ };
46
+ readonly gateKind: {
47
+ readonly type: 'string';
48
+ };
49
+ readonly identity: {
50
+ readonly type: 'object';
51
+ readonly additionalProperties: false;
52
+ readonly required: string[];
53
+ readonly properties: {
54
+ liftoffVersion: {
55
+ const: string;
56
+ };
57
+ manifestArtifactVersion: {
58
+ const: number;
59
+ };
60
+ policyVersion: {
61
+ const: string;
62
+ };
63
+ activationContractVersion: {
64
+ const: number;
65
+ };
66
+ phaseGraphSchemaVersion: {
67
+ const: number;
68
+ };
69
+ phaseGraphHash: {
70
+ const: string;
71
+ };
72
+ activationStateSchemaVersion: {
73
+ const: number;
74
+ };
75
+ evidenceHeaderSchemaVersion: {
76
+ const: number;
77
+ };
78
+ approvalEnvelopeSchemaVersion: {
79
+ const: number;
80
+ };
81
+ supersessionSchemaVersion: {
82
+ const: number;
83
+ };
84
+ credentialPolicySchemaVersion: {
85
+ const: number;
86
+ };
87
+ };
88
+ };
89
+ readonly baselineSha: {
90
+ readonly type: 'string';
91
+ readonly pattern: '^[a-f0-9]{64}$';
92
+ };
93
+ readonly planDigest: {
94
+ readonly type: 'string';
95
+ readonly pattern: '^[a-f0-9]{64}$';
96
+ };
97
+ readonly resources: {
98
+ readonly type: 'array';
99
+ readonly items: {
100
+ readonly type: 'object';
101
+ readonly additionalProperties: false;
102
+ readonly required: readonly ['type', 'identity'];
103
+ readonly properties: {
104
+ readonly type: {
105
+ readonly type: 'string';
106
+ readonly minLength: 1;
107
+ };
108
+ readonly identity: {
109
+ readonly type: 'string';
110
+ readonly minLength: 1;
111
+ };
112
+ };
113
+ };
114
+ };
115
+ readonly destinations: {
116
+ readonly type: 'array';
117
+ readonly items: {
118
+ readonly type: 'object';
119
+ readonly additionalProperties: false;
120
+ readonly required: readonly ['type', 'identity', 'repository', 'subscriptionId'];
121
+ readonly properties: {
122
+ readonly type: {
123
+ readonly enum: readonly ['repository', 'subscription', 'environment', 'tenant', 'local', 'external'];
124
+ };
125
+ readonly identity: {
126
+ readonly type: 'string';
127
+ readonly minLength: 1;
128
+ };
129
+ readonly repository: {
130
+ readonly type: readonly ['string', 'null'];
131
+ };
132
+ readonly subscriptionId: {
133
+ readonly type: readonly ['string', 'null'];
134
+ };
135
+ };
136
+ };
137
+ };
138
+ readonly permissions: {
139
+ readonly type: 'array';
140
+ readonly items: {
141
+ readonly type: 'string';
142
+ readonly minLength: 1;
143
+ };
144
+ };
145
+ readonly costCeiling: {
146
+ readonly type: 'object';
147
+ readonly additionalProperties: false;
148
+ readonly required: readonly ['currency', 'fixedMonthlyCents', 'usageMonthlyCents'];
149
+ readonly properties: {
150
+ readonly currency: {
151
+ readonly type: 'string';
152
+ readonly pattern: '^[A-Z]{3}$';
153
+ };
154
+ readonly fixedMonthlyCents: {
155
+ readonly type: 'integer';
156
+ readonly minimum: 0;
157
+ };
158
+ readonly usageMonthlyCents: {
159
+ readonly type: 'integer';
160
+ readonly minimum: 0;
161
+ };
162
+ };
163
+ };
164
+ readonly policyExceptions: {
165
+ readonly type: 'array';
166
+ readonly items: {
167
+ readonly type: 'string';
168
+ readonly minLength: 1;
169
+ };
170
+ };
171
+ readonly destructiveScope: {
172
+ readonly type: 'array';
173
+ readonly items: {
174
+ readonly type: 'string';
175
+ readonly minLength: 1;
176
+ };
177
+ };
178
+ readonly expiresAt: {
179
+ readonly type: 'string';
180
+ readonly format: 'date-time';
181
+ };
182
+ readonly approvedAt: {
183
+ readonly type: 'string';
184
+ readonly format: 'date-time';
185
+ };
186
+ readonly approver: {
187
+ readonly type: 'string';
188
+ readonly minLength: 1;
189
+ };
190
+ };
191
+ };
192
+ /** @deprecated Use approvalEnvelopeV3Schema for current execution. */
3
193
  export declare const approvalEnvelopeV2Schema: {
4
194
  readonly $schema: 'https://json-schema.org/draft/2020-12/schema';
5
- readonly $id: 'https://mission-control.local/liftoff/governance/approval-envelope.schema.v2.json';
6
- readonly title: 'Liftoff governance approval envelope v2';
195
+ readonly $id: 'https://mission-control.local/liftoff/governance/approval-envelope.schema.v3.json';
196
+ readonly title: 'Liftoff governance approval envelope v3';
7
197
  readonly type: 'object';
8
198
  readonly additionalProperties: false;
9
199
  readonly required: readonly ['schemaVersion', 'id', 'phaseId', 'gateKind', 'identity', 'baselineSha', 'planDigest', 'resources', 'destinations', 'permissions', 'costCeiling', 'policyExceptions', 'destructiveScope', 'expiresAt', 'approvedAt', 'approver'];
@@ -18,6 +208,31 @@ export declare const approvalEnvelopeV2Schema: {
18
208
  readonly phaseId: {
19
209
  readonly type: 'string';
20
210
  };
211
+ readonly scope: {
212
+ readonly enum: readonly ['local', 'activation', 'lifecycle'];
213
+ };
214
+ readonly coveredPhases: {
215
+ readonly type: 'array';
216
+ readonly uniqueItems: true;
217
+ readonly items: {
218
+ readonly enum: readonly ["seed-valid", "seed-verified", "seed-archived", "committed", "pushed", "phase-0-complete", "activation-approved", "bootstrap-workflow-source-ready", "credential-ready", "provider-ready", "state-path-selected", "existing-private-path", "bootstrap-local", "runner-ready", "private-backend-proof", "remote-import-verified", "remote-ready", "application-prerequisites-ready", "workflow-source-ready", "application-artifact-ready", "application-foundation", "dev-proof", "staging-qualified", "production-rehearsed", "green-red-proof", "enforcement-approved", "rulesets-applied", "live-readback", "bootstrap-state-disposed"];
219
+ };
220
+ };
221
+ readonly operationDigests: {
222
+ readonly type: 'array';
223
+ readonly uniqueItems: true;
224
+ readonly items: {
225
+ readonly type: 'string';
226
+ readonly pattern: '^[a-f0-9]{64}$';
227
+ };
228
+ };
229
+ readonly phasePlanDigests: {
230
+ readonly type: 'object';
231
+ readonly additionalProperties: {
232
+ readonly type: 'string';
233
+ readonly pattern: '^[a-f0-9]{64}$';
234
+ };
235
+ };
21
236
  readonly gateKind: {
22
237
  readonly type: 'string';
23
238
  };
@@ -164,11 +379,11 @@ export declare const approvalEnvelopeV2Schema: {
164
379
  };
165
380
  };
166
381
  };
167
- /** @deprecated The current schema is v2. */
382
+ /** @deprecated Use the immutable historical contracts to read v1 artifacts. */
168
383
  export declare const approvalEnvelopeV1Schema: {
169
384
  readonly $schema: 'https://json-schema.org/draft/2020-12/schema';
170
- readonly $id: 'https://mission-control.local/liftoff/governance/approval-envelope.schema.v2.json';
171
- readonly title: 'Liftoff governance approval envelope v2';
385
+ readonly $id: 'https://mission-control.local/liftoff/governance/approval-envelope.schema.v3.json';
386
+ readonly title: 'Liftoff governance approval envelope v3';
172
387
  readonly type: 'object';
173
388
  readonly additionalProperties: false;
174
389
  readonly required: readonly ['schemaVersion', 'id', 'phaseId', 'gateKind', 'identity', 'baselineSha', 'planDigest', 'resources', 'destinations', 'permissions', 'costCeiling', 'policyExceptions', 'destructiveScope', 'expiresAt', 'approvedAt', 'approver'];
@@ -183,6 +398,31 @@ export declare const approvalEnvelopeV1Schema: {
183
398
  readonly phaseId: {
184
399
  readonly type: 'string';
185
400
  };
401
+ readonly scope: {
402
+ readonly enum: readonly ['local', 'activation', 'lifecycle'];
403
+ };
404
+ readonly coveredPhases: {
405
+ readonly type: 'array';
406
+ readonly uniqueItems: true;
407
+ readonly items: {
408
+ readonly enum: readonly ["seed-valid", "seed-verified", "seed-archived", "committed", "pushed", "phase-0-complete", "activation-approved", "bootstrap-workflow-source-ready", "credential-ready", "provider-ready", "state-path-selected", "existing-private-path", "bootstrap-local", "runner-ready", "private-backend-proof", "remote-import-verified", "remote-ready", "application-prerequisites-ready", "workflow-source-ready", "application-artifact-ready", "application-foundation", "dev-proof", "staging-qualified", "production-rehearsed", "green-red-proof", "enforcement-approved", "rulesets-applied", "live-readback", "bootstrap-state-disposed"];
409
+ };
410
+ };
411
+ readonly operationDigests: {
412
+ readonly type: 'array';
413
+ readonly uniqueItems: true;
414
+ readonly items: {
415
+ readonly type: 'string';
416
+ readonly pattern: '^[a-f0-9]{64}$';
417
+ };
418
+ };
419
+ readonly phasePlanDigests: {
420
+ readonly type: 'object';
421
+ readonly additionalProperties: {
422
+ readonly type: 'string';
423
+ readonly pattern: '^[a-f0-9]{64}$';
424
+ };
425
+ };
186
426
  readonly gateKind: {
187
427
  readonly type: 'string';
188
428
  };
@@ -347,5 +587,22 @@ export declare function determineHumanAuthorityQuestion(plan: RequestedTransitio
347
587
  export declare function evaluateApprovalForTransitionPlan(plan: RequestedTransitionPlan, envelopes: readonly ApprovalEnvelope[], options?: {
348
588
  now?: Date;
349
589
  }): ApprovalEvaluation;
350
- export declare function transitionPlanForPhase(phase: PhaseGraphNode, state: UserActivationState, transition: EvidenceTransitionIdentity, _projectRoot?: string, publicationDestination?: string): RequestedTransitionPlan;
590
+ export declare function authorityOperations(operations: readonly TransitionOperation[]): readonly TransitionOperation[];
591
+ export declare function transitionAuthorityDigest(input: {
592
+ phase: PhaseGraphNode;
593
+ transitionDigest: string;
594
+ operations?: readonly TransitionOperation[];
595
+ configuration?: ActivationConfiguration;
596
+ fileChanges?: readonly PlannedFileChange[];
597
+ recovery?: boolean;
598
+ }): string;
599
+ export declare function transitionPlanForPhase(phase: PhaseGraphNode, state: UserActivationState, transition: EvidenceTransitionIdentity, _projectRoot?: string, publicationDestination?: string, planned?: {
600
+ operations: readonly TransitionOperation[];
601
+ configuration?: ActivationConfiguration;
602
+ fileChanges?: readonly PlannedFileChange[];
603
+ recovery?: boolean;
604
+ }): RequestedTransitionPlan;
605
+ export declare function approvalRequestForSavedPlan(plan: SavedTransitionPlan, phase: PhaseGraphNode, state: UserActivationState): RequestedTransitionPlan;
606
+ export declare function savedPlanAuthorityDigest(plan: SavedTransitionPlan, phase: PhaseGraphNode): string;
607
+ export declare function combineApprovalRequests(plans: readonly RequestedTransitionPlan[]): RequestedTransitionPlan;
351
608
  export {};
@@ -1,6 +1,7 @@
1
1
  import { canonicalJson, canonicalSha256 } from './canonical-json.js';
2
- import { currentActivationIdentity } from './graph.js';
2
+ import { canonicalPhaseGraph, currentActivationIdentity } from './graph.js';
3
3
  import { githubRepositoryFromPushUrl, remoteRepository } from './inputs.js';
4
+ import { phaseIds, phaseScope } from './types.js';
4
5
  const destinationTypes = new Set([
5
6
  'repository',
6
7
  'subscription',
@@ -25,10 +26,10 @@ const activationIdentityProperties = {
25
26
  supersessionSchemaVersion: { const: currentActivationIdentity.supersessionSchemaVersion },
26
27
  credentialPolicySchemaVersion: { const: currentActivationIdentity.credentialPolicySchemaVersion }
27
28
  };
28
- export const approvalEnvelopeV2Schema = {
29
+ export const approvalEnvelopeV3Schema = {
29
30
  $schema: 'https://json-schema.org/draft/2020-12/schema',
30
- $id: 'https://mission-control.local/liftoff/governance/approval-envelope.schema.v2.json',
31
- title: 'Liftoff governance approval envelope v2',
31
+ $id: 'https://mission-control.local/liftoff/governance/approval-envelope.schema.v3.json',
32
+ title: 'Liftoff governance approval envelope v3',
32
33
  type: 'object',
33
34
  additionalProperties: false,
34
35
  required: [
@@ -53,6 +54,10 @@ export const approvalEnvelopeV2Schema = {
53
54
  schemaVersion: { const: currentActivationIdentity.approvalEnvelopeSchemaVersion },
54
55
  id: { type: 'string', minLength: 1 },
55
56
  phaseId: { type: 'string' },
57
+ scope: { enum: ['local', 'activation', 'lifecycle'] },
58
+ coveredPhases: { type: 'array', uniqueItems: true, items: { enum: phaseIds } },
59
+ operationDigests: { type: 'array', uniqueItems: true, items: { type: 'string', pattern: '^[a-f0-9]{64}$' } },
60
+ phasePlanDigests: { type: 'object', additionalProperties: { type: 'string', pattern: '^[a-f0-9]{64}$' } },
56
61
  gateKind: { type: 'string' },
57
62
  identity: {
58
63
  type: 'object',
@@ -106,8 +111,10 @@ export const approvalEnvelopeV2Schema = {
106
111
  approver: { type: 'string', minLength: 1 }
107
112
  }
108
113
  };
109
- /** @deprecated The current schema is v2. */
110
- export const approvalEnvelopeV1Schema = approvalEnvelopeV2Schema;
114
+ /** @deprecated Use approvalEnvelopeV3Schema for current execution. */
115
+ export const approvalEnvelopeV2Schema = approvalEnvelopeV3Schema;
116
+ /** @deprecated Use the immutable historical contracts to read v1 artifacts. */
117
+ export const approvalEnvelopeV1Schema = approvalEnvelopeV3Schema;
111
118
  function cleanString(value, path) {
112
119
  if (typeof value !== 'string') {
113
120
  throw new Error(`${path} must be a string.`);
@@ -213,6 +220,20 @@ export function normalizeApprovalDestructiveScope(scope) {
213
220
  return sortedUnique(scope, 'approvalEnvelope.destructiveScope', (value, index) => normalizePlainScope(value, index, 'destructiveScope'), (value) => value);
214
221
  }
215
222
  export function normalizeApprovalScope(scope) {
223
+ const coveredPhases = scope.coveredPhases === undefined ? undefined : sortedUnique(scope.coveredPhases, 'approvalEnvelope.coveredPhases', (value) => {
224
+ if (!phaseIds.includes(value))
225
+ throw new Error(`Unsupported covered phase ${value}.`);
226
+ return value;
227
+ }, (value) => value);
228
+ const operationDigests = scope.operationDigests === undefined ? undefined : sortedUnique(scope.operationDigests, 'approvalEnvelope.operationDigests', (value) => digest(value, 'approvalEnvelope.operationDigests'), (value) => value);
229
+ const phasePlanDigests = {};
230
+ if (scope.phasePlanDigests) {
231
+ for (const id of Object.keys(scope.phasePlanDigests)) {
232
+ if (!phaseIds.includes(id))
233
+ throw new Error('An approval bundle contains an unknown phase.');
234
+ phasePlanDigests[id] = digest(scope.phasePlanDigests[id], `approvalEnvelope.phasePlanDigests.${id}`);
235
+ }
236
+ }
216
237
  return {
217
238
  phaseId: scope.phaseId,
218
239
  gateKind: scope.gateKind,
@@ -224,7 +245,11 @@ export function normalizeApprovalScope(scope) {
224
245
  permissions: normalizeApprovalPermissions(scope.permissions),
225
246
  costCeiling: normalizeApprovalCostCeiling(scope.costCeiling),
226
247
  policyExceptions: normalizeApprovalPolicyExceptions(scope.policyExceptions),
227
- destructiveScope: normalizeApprovalDestructiveScope(scope.destructiveScope)
248
+ destructiveScope: normalizeApprovalDestructiveScope(scope.destructiveScope),
249
+ ...(scope.scope === undefined ? {} : { scope: scope.scope }),
250
+ ...(coveredPhases ? { coveredPhases } : {}),
251
+ ...(operationDigests ? { operationDigests } : {}),
252
+ ...(scope.phasePlanDigests ? { phasePlanDigests } : {})
228
253
  };
229
254
  }
230
255
  /**
@@ -245,6 +270,10 @@ export function canonicalApprovalEnvelopeScope(envelope) {
245
270
  costCeiling: normalized.costCeiling,
246
271
  policyExceptions: normalized.policyExceptions,
247
272
  destructiveScope: normalized.destructiveScope,
273
+ ...(normalized.scope === undefined ? {} : { scope: normalized.scope }),
274
+ ...(normalized.coveredPhases === undefined ? {} : { coveredPhases: normalized.coveredPhases }),
275
+ ...(normalized.operationDigests === undefined ? {} : { operationDigests: normalized.operationDigests }),
276
+ ...(normalized.phasePlanDigests === undefined ? {} : { phasePlanDigests: normalized.phasePlanDigests }),
248
277
  expiresAt: timestamp(envelope.expiresAt, 'approvalEnvelope.expiresAt'),
249
278
  approvedAt: timestamp(envelope.approvedAt, 'approvalEnvelope.approvedAt'),
250
279
  approver: cleanString(envelope.approver, 'approvalEnvelope.approver')
@@ -326,6 +355,10 @@ function expansionReasons(approved, requested) {
326
355
  reasons.push(...exceptionMisses.map((exception) => `policy exception added: ${exception}`));
327
356
  const destructiveMisses = containsAll(approved.destructiveScope, requested.destructiveScope, (value) => value);
328
357
  reasons.push(...destructiveMisses.map((scope) => `destructive scope expanded: ${scope}`));
358
+ const phaseMisses = containsAll(approved.coveredPhases ?? [approved.phaseId], requested.coveredPhases ?? [requested.phaseId], (value) => value);
359
+ reasons.push(...phaseMisses.map((phase) => `phase scope expanded: ${phase}`));
360
+ const operationMisses = containsAll(approved.operationDigests ?? [], requested.operationDigests ?? [], (value) => value);
361
+ reasons.push(...operationMisses.map((operation) => `operation scope expanded: ${operation}`));
329
362
  return reasons;
330
363
  }
331
364
  function invalidationReasons(approved, requested) {
@@ -333,16 +366,20 @@ function invalidationReasons(approved, requested) {
333
366
  if (!sameIdentity(approved.identity, requested.identity)) {
334
367
  reasons.push('activation identity changed');
335
368
  }
336
- if (approved.phaseId !== requested.phaseId) {
369
+ if (approved.phaseId !== requested.phaseId && !approved.coveredPhases?.includes(requested.phaseId)) {
337
370
  reasons.push(`phase changed from ${approved.phaseId} to ${requested.phaseId}`);
338
371
  }
372
+ if ((approved.scope ?? phaseScope(approved.phaseId)) !== (requested.scope ?? phaseScope(requested.phaseId))) {
373
+ reasons.push('governance execution scope changed');
374
+ }
339
375
  if (approved.gateKind !== requested.gateKind) {
340
376
  reasons.push(`approval gate changed from ${approved.gateKind} to ${requested.gateKind}`);
341
377
  }
342
378
  if (approved.baselineSha !== requested.baselineSha) {
343
379
  reasons.push('baseline SHA changed');
344
380
  }
345
- if (approved.planDigest !== requested.planDigest) {
381
+ if (approved.planDigest !== requested.planDigest &&
382
+ (requested.phasePlanDigests !== undefined || approved.phasePlanDigests?.[requested.phaseId] !== requested.planDigest)) {
346
383
  reasons.push('plan authority digest changed');
347
384
  }
348
385
  return reasons;
@@ -474,12 +511,15 @@ function destinationsForPhase(phase, state, publicationDestination) {
474
511
  subscriptionId: null
475
512
  });
476
513
  }
477
- else if (mutation.startsWith('azure-')) {
514
+ else if (mutation.startsWith('azure-') || mutation.startsWith('backend-state-') || mutation === 'registry-publish') {
515
+ const subscriptionId = state.activationInputs?.azure?.subscriptionId;
516
+ if (!subscriptionId)
517
+ continue;
478
518
  destinations.set('subscription', {
479
519
  type: 'subscription',
480
- identity: 'phase-0-discovered-subscription',
520
+ identity: subscriptionId,
481
521
  repository: null,
482
- subscriptionId: 'phase-0-discovered-subscription'
522
+ subscriptionId
483
523
  });
484
524
  }
485
525
  else {
@@ -493,27 +533,59 @@ function destinationsForPhase(phase, state, publicationDestination) {
493
533
  }
494
534
  return [...destinations.values()];
495
535
  }
496
- export function transitionPlanForPhase(phase, state, transition, _projectRoot, publicationDestination) {
497
- const mutations = [...phase.allowedMutations.local, ...phase.allowedMutations.remote]
498
- .filter((mutation) => mutation !== 'none');
536
+ export function authorityOperations(operations) {
537
+ return operations.filter((operation) => operation.actionId !== 'governance.evidence.write' && operation.actionId !== 'governance.activation-state.write');
538
+ }
539
+ export function transitionAuthorityDigest(input) {
540
+ return canonicalSha256({
541
+ scope: phaseScope(input.phase.id),
542
+ phaseId: input.phase.id,
543
+ gateKind: input.phase.approvalGate.kind,
544
+ transitionDigest: input.transitionDigest,
545
+ allowedMutations: input.phase.allowedMutations,
546
+ operations: input.operations === undefined ? null : authorityOperations(input.operations),
547
+ configuration: input.configuration ?? null,
548
+ fileChanges: input.fileChanges ?? [],
549
+ recovery: input.recovery ?? false
550
+ });
551
+ }
552
+ export function transitionPlanForPhase(phase, state, transition, _projectRoot, publicationDestination, planned) {
553
+ const operations = planned === undefined ? undefined : authorityOperations(planned.operations);
554
+ const effects = operations?.flatMap((operation) => [operation, ...(operation.effects ?? [])]);
555
+ const mutations = effects ? [...new Set(effects.map((effect) => effect.mutationClass))] :
556
+ [...phase.allowedMutations.local, ...phase.allowedMutations.remote].filter((mutation) => mutation !== 'none');
557
+ const destinations = effects === undefined ? destinationsForPhase(phase, state, publicationDestination) :
558
+ [...new Map(effects.map(({ destination }) => {
559
+ const scope = {
560
+ type: destination.type, identity: destination.identity,
561
+ repository: destination.repository ?? null, subscriptionId: destination.subscriptionId ?? null
562
+ };
563
+ return [canonicalJson(scope), scope];
564
+ })).values()];
565
+ const budget = (phase.approvalGate.kind === 'activation-plan' || phase.approvalGate.kind === 'infrastructure-cost')
566
+ ? planned?.configuration?.budget ?? state.activationInputs?.budget : undefined;
499
567
  return normalizeApprovalScope({
568
+ scope: phaseScope(phase.id),
500
569
  phaseId: phase.id,
570
+ coveredPhases: [phase.id],
571
+ ...(operations ? { operationDigests: [...new Set(operations.map((operation) => canonicalSha256(operation)))] } : {}),
501
572
  gateKind: phase.approvalGate.kind,
502
573
  identity: state.identity,
503
574
  baselineSha: transition.baselineSha,
504
- planDigest: canonicalSha256({
505
- phaseId: phase.id,
506
- gateKind: phase.approvalGate.kind,
507
- transitionDigest: transition.transitionDigest,
508
- allowedMutations: phase.allowedMutations
575
+ planDigest: transitionAuthorityDigest({
576
+ phase, transitionDigest: transition.transitionDigest,
577
+ operations, configuration: planned?.configuration, fileChanges: planned?.fileChanges, recovery: planned?.recovery
509
578
  }),
510
- resources: mutations.map((mutation) => ({
579
+ resources: effects ? [...new Map(effects.map((effect) => {
580
+ const resource = { type: effect.mutationClass, identity: effect.destination.identity };
581
+ return [canonicalJson(resource), resource];
582
+ })).values()] : mutations.map((mutation) => ({
511
583
  type: mutation,
512
584
  identity: `${phase.id}:${mutation}`
513
585
  })),
514
- destinations: destinationsForPhase(phase, state, publicationDestination),
586
+ destinations,
515
587
  permissions: mutations,
516
- costCeiling: {
588
+ costCeiling: budget ?? {
517
589
  currency: 'USD',
518
590
  fixedMonthlyCents: 0,
519
591
  usageMonthlyCents: 0
@@ -525,4 +597,75 @@ export function transitionPlanForPhase(phase, state, transition, _projectRoot, p
525
597
  : []
526
598
  });
527
599
  }
600
+ export function approvalRequestForSavedPlan(plan, phase, state) {
601
+ const primary = transitionPlanForPhase(phase, state, {
602
+ phaseId: plan.phaseId, baselineSha: plan.baselineDigest,
603
+ inputDigest: plan.inputDigest, transitionDigest: plan.transitionDigest
604
+ }, undefined, undefined, { operations: plan.operations, configuration: plan.configuration, fileChanges: plan.fileChanges, recovery: plan.recovery });
605
+ if (!plan.approvalBundle?.length)
606
+ return primary;
607
+ const secondary = plan.approvalBundle.map((entry) => {
608
+ const node = canonicalPhaseForApproval(entry.phaseId);
609
+ return transitionPlanForPhase(node, state, {
610
+ phaseId: entry.phaseId, baselineSha: plan.baselineDigest, inputDigest: entry.inputDigest,
611
+ transitionDigest: entry.transitionDigest
612
+ }, undefined, undefined, { operations: entry.operations, configuration: plan.configuration, fileChanges: entry.fileChanges });
613
+ });
614
+ return combineApprovalRequests([primary, ...secondary]);
615
+ }
616
+ export function savedPlanAuthorityDigest(plan, phase) {
617
+ const primary = transitionAuthorityDigest({
618
+ phase, transitionDigest: plan.transitionDigest, operations: plan.operations,
619
+ configuration: plan.configuration, fileChanges: plan.fileChanges, recovery: plan.recovery
620
+ });
621
+ if (!plan.approvalBundle?.length)
622
+ return primary;
623
+ const phasePlanDigests = Object.fromEntries([
624
+ [phase.id, primary],
625
+ ...plan.approvalBundle.map((entry) => [entry.phaseId, transitionAuthorityDigest({
626
+ phase: canonicalPhaseForApproval(entry.phaseId), transitionDigest: entry.transitionDigest,
627
+ operations: entry.operations, fileChanges: entry.fileChanges, configuration: plan.configuration
628
+ })])
629
+ ]);
630
+ return canonicalSha256({ scope: phaseScope(phase.id), phasePlanDigests });
631
+ }
632
+ function canonicalPhaseForApproval(id) {
633
+ const phase = canonicalPhaseGraph.phases.find((entry) => entry.id === id);
634
+ if (!phase)
635
+ throw new Error(`Unknown approval phase ${id}.`);
636
+ return phase;
637
+ }
638
+ export function combineApprovalRequests(plans) {
639
+ if (plans.length === 0)
640
+ throw new Error('An approval bundle must name at least one resolved phase plan.');
641
+ const requests = plans.map(normalizeApprovalScope);
642
+ const first = requests[0];
643
+ if (requests.some((request) => !sameIdentity(first.identity, request.identity) ||
644
+ request.baselineSha !== first.baselineSha || request.gateKind !== first.gateKind ||
645
+ (request.scope ?? phaseScope(request.phaseId)) !== (first.scope ?? phaseScope(first.phaseId)) ||
646
+ request.costCeiling.currency !== first.costCeiling.currency || request.phasePlanDigests !== undefined)) {
647
+ throw new Error('An approval bundle requires one identity, baseline, scope, authority gate, and currency; nested bundles are not supported.');
648
+ }
649
+ if (new Set(requests.map((request) => request.phaseId)).size !== requests.length)
650
+ throw new Error('An approval bundle cannot repeat a phase.');
651
+ const distinct = (values) => [...new Map(values.map((value) => [canonicalJson(value), value])).values()];
652
+ const phasePlanDigests = Object.fromEntries(requests.map((request) => [request.phaseId, request.planDigest]));
653
+ return normalizeApprovalScope({
654
+ ...first,
655
+ planDigest: canonicalSha256({ scope: first.scope ?? phaseScope(first.phaseId), phasePlanDigests }),
656
+ coveredPhases: requests.map((request) => request.phaseId),
657
+ operationDigests: distinct(requests.flatMap((request) => request.operationDigests ?? [])),
658
+ phasePlanDigests,
659
+ resources: distinct(requests.flatMap((request) => request.resources)),
660
+ destinations: distinct(requests.flatMap((request) => request.destinations)),
661
+ permissions: distinct(requests.flatMap((request) => request.permissions)),
662
+ policyExceptions: distinct(requests.flatMap((request) => request.policyExceptions)),
663
+ destructiveScope: distinct(requests.flatMap((request) => request.destructiveScope)),
664
+ costCeiling: {
665
+ currency: first.costCeiling.currency,
666
+ fixedMonthlyCents: requests.reduce((sum, request) => sum + request.costCeiling.fixedMonthlyCents, 0),
667
+ usageMonthlyCents: requests.reduce((sum, request) => sum + request.costCeiling.usageMonthlyCents, 0)
668
+ }
669
+ });
670
+ }
528
671
  //# sourceMappingURL=approvals.js.map