@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.
- package/DEVELOPER.md +26 -24
- package/README.md +18 -18
- package/assets/governance/single-maintainer-gitflow/activation-v2-graph.json +1583 -0
- package/dist/adapters/credentials/github-enrollment.d.ts +51 -0
- package/dist/adapters/credentials/github-enrollment.js +226 -0
- package/dist/adapters/credentials/github-enrollment.js.map +1 -0
- package/dist/adapters/credentials/protected-input.d.ts +7 -0
- package/dist/adapters/credentials/protected-input.js +68 -0
- package/dist/adapters/credentials/protected-input.js.map +1 -0
- package/dist/adapters/filesystem/executables.d.ts +2 -0
- package/dist/adapters/filesystem/executables.js +70 -0
- package/dist/adapters/filesystem/executables.js.map +1 -0
- package/dist/adapters/filesystem/update-previews.d.ts +9 -0
- package/dist/adapters/filesystem/update-previews.js +45 -2
- package/dist/adapters/filesystem/update-previews.js.map +1 -1
- package/dist/adapters/filesystem/workstation-attempts.d.ts +13 -0
- package/dist/adapters/filesystem/workstation-attempts.js +78 -0
- package/dist/adapters/filesystem/workstation-attempts.js.map +1 -0
- package/dist/adapters/github/activation-rest.d.ts +40 -0
- package/dist/adapters/github/activation-rest.js +272 -0
- package/dist/adapters/github/activation-rest.js.map +1 -0
- package/dist/adapters/process/shell-command.d.ts +1 -0
- package/dist/adapters/process/shell-command.js +5 -2
- package/dist/adapters/process/shell-command.js.map +1 -1
- package/dist/adapters/state/azure-blob.d.ts +43 -0
- package/dist/adapters/state/azure-blob.js +335 -0
- package/dist/adapters/state/azure-blob.js.map +1 -0
- package/dist/adapters/state/darwin-capabilities.d.ts +33 -0
- package/dist/adapters/state/darwin-capabilities.js +146 -0
- package/dist/adapters/state/darwin-capabilities.js.map +1 -0
- package/dist/adapters/state/darwin-native-host.d.ts +32 -0
- package/dist/adapters/state/darwin-native-host.js +294 -0
- package/dist/adapters/state/darwin-native-host.js.map +1 -0
- package/dist/adapters/state/darwin-profile.d.ts +6 -0
- package/dist/adapters/state/darwin-profile.js +56 -0
- package/dist/adapters/state/darwin-profile.js.map +1 -0
- package/dist/adapters/state/darwin-system-program.d.ts +1 -0
- package/dist/adapters/state/darwin-system-program.js +94 -0
- package/dist/adapters/state/darwin-system-program.js.map +1 -0
- package/dist/adapters/state/local-qualification.d.ts +16 -0
- package/dist/adapters/state/local-qualification.js +299 -0
- package/dist/adapters/state/local-qualification.js.map +1 -0
- package/dist/adapters/state/local.d.ts +25 -0
- package/dist/adapters/state/local.js +145 -0
- package/dist/adapters/state/local.js.map +1 -0
- package/dist/adapters/state/native-command.d.ts +21 -0
- package/dist/adapters/state/native-command.js +82 -0
- package/dist/adapters/state/native-command.js.map +1 -0
- package/dist/adapters/state/native-files.d.ts +2 -0
- package/dist/adapters/state/native-files.js +47 -0
- package/dist/adapters/state/native-files.js.map +1 -0
- package/dist/adapters/state/native-system.d.ts +38 -0
- package/dist/adapters/state/native-system.js +86 -0
- package/dist/adapters/state/native-system.js.map +1 -0
- package/dist/adapters/state/opentofu.d.ts +35 -0
- package/dist/adapters/state/opentofu.js +350 -0
- package/dist/adapters/state/opentofu.js.map +1 -0
- package/dist/adapters/state/owned-process.d.ts +43 -0
- package/dist/adapters/state/owned-process.js +252 -0
- package/dist/adapters/state/owned-process.js.map +1 -0
- package/dist/adapters/state/posix-lock-program.d.ts +1 -0
- package/dist/adapters/state/posix-lock-program.js +185 -0
- package/dist/adapters/state/posix-lock-program.js.map +1 -0
- package/dist/adapters/state/posix-native-lock.d.ts +15 -0
- package/dist/adapters/state/posix-native-lock.js +145 -0
- package/dist/adapters/state/posix-native-lock.js.map +1 -0
- package/dist/adapters/state/protected-workspace.d.ts +57 -0
- package/dist/adapters/state/protected-workspace.js +412 -0
- package/dist/adapters/state/protected-workspace.js.map +1 -0
- package/dist/application/context.d.ts +3 -0
- package/dist/application/diagnose/doctor.d.ts +5 -0
- package/dist/application/diagnose/doctor.js +21 -5
- package/dist/application/diagnose/doctor.js.map +1 -1
- package/dist/application/diagnose/generated-project.js +2 -7
- package/dist/application/diagnose/generated-project.js.map +1 -1
- package/dist/application/initialize/fixture.js +8 -1
- package/dist/application/initialize/fixture.js.map +1 -1
- package/dist/application/initialize/use-case.js +70 -36
- package/dist/application/initialize/use-case.js.map +1 -1
- package/dist/application/project/manifest.js +5 -4
- package/dist/application/project/manifest.js.map +1 -1
- package/dist/application/state-migration/execution.d.ts +32 -0
- package/dist/application/state-migration/execution.js +440 -0
- package/dist/application/state-migration/execution.js.map +1 -0
- package/dist/application/state-migration/index.d.ts +22 -0
- package/dist/application/state-migration/index.js +17 -0
- package/dist/application/state-migration/index.js.map +1 -0
- package/dist/application/state-migration/lifecycle.d.ts +3 -0
- package/dist/application/state-migration/lifecycle.js +149 -0
- package/dist/application/state-migration/lifecycle.js.map +1 -0
- package/dist/application/state-migration/planning.d.ts +7 -0
- package/dist/application/state-migration/planning.js +150 -0
- package/dist/application/state-migration/planning.js.map +1 -0
- package/dist/application/state-migration/recovery.d.ts +5 -0
- package/dist/application/state-migration/recovery.js +145 -0
- package/dist/application/state-migration/recovery.js.map +1 -0
- package/dist/application/state-migration/runtime.d.ts +32 -0
- package/dist/application/state-migration/runtime.js +188 -0
- package/dist/application/state-migration/runtime.js.map +1 -0
- package/dist/application/state-migration/service.d.ts +2 -0
- package/dist/application/state-migration/service.js +20 -0
- package/dist/application/state-migration/service.js.map +1 -0
- package/dist/application/update/command-guidance.d.ts +19 -2
- package/dist/application/update/command-guidance.js +20 -6
- package/dist/application/update/command-guidance.js.map +1 -1
- package/dist/application/update/guidance-context.d.ts +2 -0
- package/dist/application/update/guidance-context.js +35 -0
- package/dist/application/update/guidance-context.js.map +1 -0
- package/dist/application/update/inspection.d.ts +21 -4
- package/dist/application/update/inspection.js +88 -29
- package/dist/application/update/inspection.js.map +1 -1
- package/dist/application/update/migration-runtime.js +25 -13
- package/dist/application/update/migration-runtime.js.map +1 -1
- package/dist/application/update/output.d.ts +4 -1
- package/dist/application/update/output.js +14 -4
- package/dist/application/update/output.js.map +1 -1
- package/dist/application/update/preview.d.ts +10 -1
- package/dist/application/update/preview.js +26 -4
- package/dist/application/update/preview.js.map +1 -1
- package/dist/application/update/revalidation-plan.d.ts +1 -0
- package/dist/application/update/revalidation-plan.js +27 -15
- package/dist/application/update/revalidation-plan.js.map +1 -1
- package/dist/application/update/revalidation.d.ts +4 -1
- package/dist/application/update/revalidation.js +64 -23
- package/dist/application/update/revalidation.js.map +1 -1
- package/dist/application/update/use-case.js +50 -29
- package/dist/application/update/use-case.js.map +1 -1
- package/dist/cli/args/definitions.js +11 -3
- package/dist/cli/args/definitions.js.map +1 -1
- package/dist/cli/args/parser.js +29 -3
- package/dist/cli/args/parser.js.map +1 -1
- package/dist/domain/governance/activation/approvals.d.ts +264 -7
- package/dist/domain/governance/activation/approvals.js +166 -23
- package/dist/domain/governance/activation/approvals.js.map +1 -1
- package/dist/domain/governance/activation/capabilities.js +5 -2
- package/dist/domain/governance/activation/capabilities.js.map +1 -1
- package/dist/domain/governance/activation/evidence.d.ts +2 -1
- package/dist/domain/governance/activation/evidence.js +64 -11
- package/dist/domain/governance/activation/evidence.js.map +1 -1
- package/dist/domain/governance/activation/graph.d.ts +11 -384
- package/dist/domain/governance/activation/graph.js +424 -379
- package/dist/domain/governance/activation/graph.js.map +1 -1
- package/dist/domain/governance/activation/inputs.d.ts +3 -1
- package/dist/domain/governance/activation/inputs.js +47 -6
- package/dist/domain/governance/activation/inputs.js.map +1 -1
- package/dist/domain/governance/activation/operations.d.ts +4 -1
- package/dist/domain/governance/activation/operations.js +110 -22
- package/dist/domain/governance/activation/operations.js.map +1 -1
- package/dist/domain/governance/activation/readiness.d.ts +10 -0
- package/dist/domain/governance/activation/readiness.js +77 -38
- package/dist/domain/governance/activation/readiness.js.map +1 -1
- package/dist/domain/governance/activation/types.d.ts +151 -9
- package/dist/domain/governance/activation/types.js +23 -1
- package/dist/domain/governance/activation/types.js.map +1 -1
- package/dist/domain/governance/activation/validators.d.ts +4 -1
- package/dist/domain/governance/activation/validators.js +419 -26
- package/dist/domain/governance/activation/validators.js.map +1 -1
- package/dist/domain/governance/policy/identity.d.ts +55 -13
- package/dist/domain/governance/policy/identity.js +28 -8
- package/dist/domain/governance/policy/identity.js.map +1 -1
- package/dist/domain/project/artifact-lifecycle.d.ts +2 -1
- package/dist/domain/project/artifact-lifecycle.js +7 -1
- package/dist/domain/project/artifact-lifecycle.js.map +1 -1
- package/dist/domain/project/catalog.d.ts +50 -1
- package/dist/domain/project/catalog.js +104 -4
- package/dist/domain/project/catalog.js.map +1 -1
- package/dist/domain/project/contracts.d.ts +9 -1
- package/dist/domain/project/infrastructure-layout.d.ts +1 -1
- package/dist/domain/project/infrastructure-layout.js +3 -1
- package/dist/domain/project/infrastructure-layout.js.map +1 -1
- package/dist/domain/project/manifest/governance.d.ts +1 -1
- package/dist/domain/project/manifest/governance.js +9 -10
- package/dist/domain/project/manifest/governance.js.map +1 -1
- package/dist/domain/project/planning.js +4 -5
- package/dist/domain/project/planning.js.map +1 -1
- package/dist/domain/repair/stateful-bounded.d.ts +1 -0
- package/dist/domain/repair/stateful-bounded.js +37 -0
- package/dist/domain/repair/stateful-bounded.js.map +1 -0
- package/dist/domain/repair/stateful-invariants.d.ts +28 -0
- package/dist/domain/repair/stateful-invariants.js +278 -0
- package/dist/domain/repair/stateful-invariants.js.map +1 -0
- package/dist/domain/repair/stateful.d.ts +690 -0
- package/dist/domain/repair/stateful.js +26 -0
- package/dist/domain/repair/stateful.js.map +1 -0
- package/dist/domain/workstation/constraints.d.ts +1 -0
- package/dist/domain/workstation/constraints.js.map +1 -1
- package/dist/domain/workstation/contracts.d.ts +42 -0
- package/dist/domain/workstation/contracts.js +2 -0
- package/dist/domain/workstation/contracts.js.map +1 -0
- package/dist/domain/workstation/executables.d.ts +19 -0
- package/dist/domain/workstation/executables.js +68 -0
- package/dist/domain/workstation/executables.js.map +1 -0
- package/dist/domain/workstation/versions.d.ts +8 -0
- package/dist/domain/workstation/versions.js +145 -0
- package/dist/domain/workstation/versions.js.map +1 -0
- package/dist/framework-adapters.d.ts +2 -0
- package/dist/framework-adapters.js +148 -22
- package/dist/framework-adapters.js.map +1 -1
- package/dist/framework-validation.d.ts +6 -0
- package/dist/framework-validation.js +89 -24
- package/dist/framework-validation.js.map +1 -1
- package/dist/generators/common/base.js +57 -17
- package/dist/generators/common/base.js.map +1 -1
- package/dist/governance-activation/activation-state.js +39 -7
- package/dist/governance-activation/activation-state.js.map +1 -1
- package/dist/governance-activation/authority-records.d.ts +4 -0
- package/dist/governance-activation/authority-records.js +24 -0
- package/dist/governance-activation/authority-records.js.map +1 -0
- package/dist/governance-activation/commands.d.ts +9 -1
- package/dist/governance-activation/commands.js +392 -97
- package/dist/governance-activation/commands.js.map +1 -1
- package/dist/governance-activation/compatibility.d.ts +11 -8
- package/dist/governance-activation/compatibility.js +37 -23
- package/dist/governance-activation/compatibility.js.map +1 -1
- package/dist/governance-activation/doctor.js +40 -16
- package/dist/governance-activation/doctor.js.map +1 -1
- package/dist/governance-activation/github-config.d.ts +17 -0
- package/dist/governance-activation/github-config.js +88 -0
- package/dist/governance-activation/github-config.js.map +1 -0
- package/dist/governance-activation/github-discovery.d.ts +18 -0
- package/dist/governance-activation/github-discovery.js +229 -0
- package/dist/governance-activation/github-discovery.js.map +1 -0
- package/dist/governance-activation/github-ports.d.ts +12 -0
- package/dist/governance-activation/github-ports.js +4 -0
- package/dist/governance-activation/github-ports.js.map +1 -0
- package/dist/governance-activation/github-publication.d.ts +3 -0
- package/dist/governance-activation/github-publication.js +190 -0
- package/dist/governance-activation/github-publication.js.map +1 -0
- package/dist/governance-activation/historical-common.d.ts +98 -0
- package/dist/governance-activation/historical-common.js +269 -0
- package/dist/governance-activation/historical-common.js.map +1 -0
- package/dist/governance-activation/historical-credential-policy.d.ts +3 -0
- package/dist/governance-activation/historical-credential-policy.js +91 -0
- package/dist/governance-activation/historical-credential-policy.js.map +1 -0
- package/dist/governance-activation/historical-inputs.d.ts +6 -0
- package/dist/governance-activation/historical-inputs.js +57 -0
- package/dist/governance-activation/historical-inputs.js.map +1 -0
- package/dist/governance-activation/historical-safety.d.ts +9 -0
- package/dist/governance-activation/historical-safety.js +102 -0
- package/dist/governance-activation/historical-safety.js.map +1 -0
- package/dist/governance-activation/historical-source-metadata.d.ts +31 -0
- package/dist/governance-activation/historical-source-metadata.js +55 -0
- package/dist/governance-activation/historical-source-metadata.js.map +1 -0
- package/dist/governance-activation/historical-state.d.ts +12 -50
- package/dist/governance-activation/historical-state.js +195 -308
- package/dist/governance-activation/historical-state.js.map +1 -1
- package/dist/governance-activation/historical-v1-auxiliary.d.ts +1 -0
- package/dist/governance-activation/historical-v1-auxiliary.js +43 -0
- package/dist/governance-activation/historical-v1-auxiliary.js.map +1 -0
- package/dist/governance-activation/historical-v1-phase-contracts.d.ts +31 -0
- package/dist/governance-activation/historical-v1-phase-contracts.js +46 -0
- package/dist/governance-activation/historical-v1-phase-contracts.js.map +1 -0
- package/dist/governance-activation/historical-v2.d.ts +53 -0
- package/dist/governance-activation/historical-v2.js +313 -0
- package/dist/governance-activation/historical-v2.js.map +1 -0
- package/dist/governance-activation/history-contracts.d.ts +21 -12
- package/dist/governance-activation/history-contracts.js +99 -27
- package/dist/governance-activation/history-contracts.js.map +1 -1
- package/dist/governance-activation/inputs.d.ts +8 -2
- package/dist/governance-activation/inputs.js +41 -6
- package/dist/governance-activation/inputs.js.map +1 -1
- package/dist/governance-activation/migration-history.d.ts +43 -5
- package/dist/governance-activation/migration-history.js +231 -50
- package/dist/governance-activation/migration-history.js.map +1 -1
- package/dist/governance-activation/migration.d.ts +7 -4
- package/dist/governance-activation/migration.js +53 -252
- package/dist/governance-activation/migration.js.map +1 -1
- package/dist/governance-activation/phase-azure.d.ts +3 -0
- package/dist/governance-activation/phase-azure.js +205 -0
- package/dist/governance-activation/phase-azure.js.map +1 -0
- package/dist/governance-activation/phase-bootstrap-state.js +3 -0
- package/dist/governance-activation/phase-bootstrap-state.js.map +1 -1
- package/dist/governance-activation/phase-github.d.ts +3 -0
- package/dist/governance-activation/phase-github.js +192 -0
- package/dist/governance-activation/phase-github.js.map +1 -0
- package/dist/governance-activation/phase-governance.js +8 -2
- package/dist/governance-activation/phase-governance.js.map +1 -1
- package/dist/governance-activation/public-plans.d.ts +36 -0
- package/dist/governance-activation/public-plans.js +100 -0
- package/dist/governance-activation/public-plans.js.map +1 -0
- package/dist/governance-activation/read-only.d.ts +3 -1
- package/dist/governance-activation/read-only.js +7 -3
- package/dist/governance-activation/read-only.js.map +1 -1
- package/dist/governance-activation/reconciliation.js +10 -0
- package/dist/governance-activation/reconciliation.js.map +1 -1
- package/dist/governance-activation/seed-lifecycle.js +14 -25
- package/dist/governance-activation/seed-lifecycle.js.map +1 -1
- package/dist/governance-activation/source-of-truth.d.ts +21 -5
- package/dist/governance-activation/source-of-truth.js +102 -13
- package/dist/governance-activation/source-of-truth.js.map +1 -1
- package/dist/governance-activation/task-projection.d.ts +4 -1
- package/dist/governance-activation/task-projection.js +32 -3
- package/dist/governance-activation/task-projection.js.map +1 -1
- package/dist/governance-activation/task-writes.d.ts +42 -0
- package/dist/governance-activation/task-writes.js +250 -0
- package/dist/governance-activation/task-writes.js.map +1 -0
- package/dist/governance-activation/transition-files.d.ts +12 -0
- package/dist/governance-activation/transition-files.js +109 -0
- package/dist/governance-activation/transition-files.js.map +1 -0
- package/dist/governance-activation/transition-planning.d.ts +3 -1
- package/dist/governance-activation/transition-planning.js +158 -20
- package/dist/governance-activation/transition-planning.js.map +1 -1
- package/dist/governance-activation/transition-ports.d.ts +31 -3
- package/dist/governance-activation/transition-records.d.ts +11 -1
- package/dist/governance-activation/transition-records.js +52 -6
- package/dist/governance-activation/transition-records.js.map +1 -1
- package/dist/governance-activation/transitions.d.ts +5 -0
- package/dist/governance-activation/transitions.js +271 -37
- package/dist/governance-activation/transitions.js.map +1 -1
- package/dist/governance-assessment/engine.js +22 -15
- package/dist/governance-assessment/engine.js.map +1 -1
- package/dist/governance-assessment/project.d.ts +2 -0
- package/dist/governance-assessment/project.js +2 -1
- package/dist/governance-assessment/project.js.map +1 -1
- package/dist/interactive.d.ts +0 -1
- package/dist/interactive.js +3 -22
- package/dist/interactive.js.map +1 -1
- package/dist/openspec-profile.d.ts +2 -1
- package/dist/openspec-profile.js +11 -41
- package/dist/openspec-profile.js.map +1 -1
- package/dist/repository-governance.d.ts +10 -5
- package/dist/repository-governance.js +178 -92
- package/dist/repository-governance.js.map +1 -1
- package/dist/telemetry/contract.d.ts +1 -1
- package/dist/telemetry/contract.js +3 -0
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/workstation-catalog.d.ts +13 -1
- package/dist/workstation-catalog.js +122 -0
- package/dist/workstation-catalog.js.map +1 -1
- package/dist/workstation.d.ts +69 -14
- package/dist/workstation.js +600 -215
- package/dist/workstation.js.map +1 -1
- package/docs/cli-reference.md +94 -23
- package/docs/configuration-and-manifests.md +11 -10
- package/docs/prerequisites.md +93 -18
- package/docs/repository-governance.md +68 -40
- package/docs/spec-workflows-and-agents.md +132 -13
- package/docs/troubleshooting.md +28 -0
- 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.
|
|
6
|
-
readonly title: 'Liftoff governance approval envelope
|
|
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
|
|
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.
|
|
171
|
-
readonly title: 'Liftoff governance approval envelope
|
|
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
|
|
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
|
|
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.
|
|
31
|
-
title: 'Liftoff governance approval envelope
|
|
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
|
|
110
|
-
export const
|
|
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:
|
|
520
|
+
identity: subscriptionId,
|
|
481
521
|
repository: null,
|
|
482
|
-
subscriptionId
|
|
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
|
|
497
|
-
|
|
498
|
-
|
|
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:
|
|
505
|
-
|
|
506
|
-
|
|
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:
|
|
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
|
|
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
|