@msn-control/liftoff 0.10.3 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEVELOPER.md +364 -20
- package/README.md +29 -29
- package/assets/governance/single-maintainer-gitflow/assessment-controls.json +39 -0
- package/assets/supported-stack.json +7 -138
- package/dist/adapters/azure/governance-assessment.d.ts +2 -0
- package/dist/adapters/azure/governance-assessment.js +21 -0
- package/dist/adapters/azure/governance-assessment.js.map +1 -0
- package/dist/adapters/filesystem/atomic-write.d.ts +2 -0
- package/dist/adapters/filesystem/atomic-write.js +74 -0
- package/dist/adapters/filesystem/atomic-write.js.map +1 -0
- package/dist/adapters/filesystem/errors.d.ts +2 -0
- package/dist/adapters/filesystem/errors.js +8 -0
- package/dist/adapters/filesystem/errors.js.map +1 -0
- package/dist/adapters/filesystem/manifest-file.d.ts +1 -0
- package/dist/adapters/filesystem/manifest-file.js +21 -0
- package/dist/adapters/filesystem/manifest-file.js.map +1 -0
- package/dist/adapters/filesystem/project-config.d.ts +4 -0
- package/dist/adapters/filesystem/project-config.js +157 -0
- package/dist/adapters/filesystem/project-config.js.map +1 -0
- package/dist/adapters/filesystem/project-discovery.d.ts +2 -0
- package/dist/adapters/filesystem/project-discovery.js +30 -0
- package/dist/adapters/filesystem/project-discovery.js.map +1 -0
- package/dist/adapters/filesystem/project-files.d.ts +6 -0
- package/dist/adapters/filesystem/project-files.js +68 -0
- package/dist/adapters/filesystem/project-files.js.map +1 -0
- package/dist/adapters/filesystem/project-lock.d.ts +8 -0
- package/dist/adapters/filesystem/project-lock.js +164 -0
- package/dist/adapters/filesystem/project-lock.js.map +1 -0
- package/dist/adapters/filesystem/project-paths.d.ts +2 -0
- package/dist/adapters/filesystem/project-paths.js +60 -0
- package/dist/adapters/filesystem/project-paths.js.map +1 -0
- package/dist/adapters/filesystem/project-transaction.d.ts +23 -0
- package/dist/adapters/filesystem/project-transaction.js +158 -0
- package/dist/adapters/filesystem/project-transaction.js.map +1 -0
- package/dist/adapters/git/governance-assessment.d.ts +22 -0
- package/dist/adapters/git/governance-assessment.js +161 -0
- package/dist/adapters/git/governance-assessment.js.map +1 -0
- package/dist/adapters/github/governance-assessment.d.ts +2 -0
- package/dist/adapters/github/governance-assessment.js +18 -0
- package/dist/adapters/github/governance-assessment.js.map +1 -0
- package/dist/adapters/packaged-assets/package-root.d.ts +3 -0
- package/dist/adapters/packaged-assets/package-root.js +21 -0
- package/dist/adapters/packaged-assets/package-root.js.map +1 -0
- package/dist/adapters/packaged-assets/supported-stack.d.ts +4 -0
- package/dist/adapters/packaged-assets/supported-stack.js +18 -0
- package/dist/adapters/packaged-assets/supported-stack.js.map +1 -0
- package/dist/adapters/packaged-assets/template-assets.d.ts +23 -0
- package/dist/adapters/packaged-assets/template-assets.js +36 -0
- package/dist/adapters/packaged-assets/template-assets.js.map +1 -0
- package/dist/adapters/process/shell-command.d.ts +5 -0
- package/dist/adapters/process/shell-command.js +21 -0
- package/dist/adapters/process/shell-command.js.map +1 -0
- package/dist/application/context.d.ts +20 -0
- package/dist/application/context.js +2 -0
- package/dist/application/context.js.map +1 -0
- package/dist/application/diagnose/doctor.d.ts +21 -0
- package/dist/application/diagnose/doctor.js +586 -0
- package/dist/application/diagnose/doctor.js.map +1 -0
- package/dist/application/diagnose/generated-project.d.ts +1 -0
- package/dist/application/diagnose/generated-project.js +105 -0
- package/dist/application/diagnose/generated-project.js.map +1 -0
- package/dist/application/diagnose/validate.d.ts +6 -0
- package/dist/application/diagnose/validate.js +27 -0
- package/dist/application/diagnose/validate.js.map +1 -0
- package/dist/application/initialize/fixture.d.ts +2 -0
- package/dist/application/initialize/fixture.js +39 -0
- package/dist/application/initialize/fixture.js.map +1 -0
- package/dist/application/initialize/inputs.d.ts +3 -0
- package/dist/application/initialize/inputs.js +35 -0
- package/dist/application/initialize/inputs.js.map +1 -0
- package/dist/application/initialize/use-case.d.ts +23 -0
- package/dist/application/initialize/use-case.js +360 -0
- package/dist/application/initialize/use-case.js.map +1 -0
- package/dist/application/migrate/use-case.d.ts +7 -0
- package/dist/application/migrate/use-case.js +378 -0
- package/dist/application/migrate/use-case.js.map +1 -0
- package/dist/application/project/catalog.d.ts +46 -0
- package/dist/application/project/catalog.js +12 -0
- package/dist/application/project/catalog.js.map +1 -0
- package/dist/application/project/manifest.d.ts +2 -0
- package/dist/application/project/manifest.js +29 -0
- package/dist/application/project/manifest.js.map +1 -0
- package/dist/application/project/planning.d.ts +6 -0
- package/dist/application/project/planning.js +12 -0
- package/dist/application/project/planning.js.map +1 -0
- package/dist/application/update/planning.d.ts +40 -0
- package/dist/application/update/planning.js +293 -0
- package/dist/application/update/planning.js.map +1 -0
- package/dist/application/update/reporting.d.ts +42 -0
- package/dist/application/update/reporting.js +325 -0
- package/dist/application/update/reporting.js.map +1 -0
- package/dist/application/update/use-case.d.ts +8 -0
- package/dist/application/update/use-case.js +600 -0
- package/dist/application/update/use-case.js.map +1 -0
- package/dist/application/upgrade/use-case.d.ts +6 -0
- package/dist/application/upgrade/use-case.js +82 -0
- package/dist/application/upgrade/use-case.js.map +1 -0
- package/dist/args.d.ts +5 -65
- package/dist/args.js +4 -441
- package/dist/args.js.map +1 -1
- package/dist/artifact-lifecycle.d.ts +1 -21
- package/dist/artifact-lifecycle.js +1 -73
- package/dist/artifact-lifecycle.js.map +1 -1
- package/dist/catalogs.d.ts +1 -50
- package/dist/catalogs.js +1 -472
- package/dist/catalogs.js.map +1 -1
- package/dist/cli/args/contracts.d.ts +51 -0
- package/dist/cli/args/contracts.js +2 -0
- package/dist/cli/args/contracts.js.map +1 -0
- package/dist/cli/args/definitions.d.ts +6 -0
- package/dist/cli/args/definitions.js +187 -0
- package/dist/cli/args/definitions.js.map +1 -0
- package/dist/cli/args/help.d.ts +5 -0
- package/dist/cli/args/help.js +130 -0
- package/dist/cli/args/help.js.map +1 -0
- package/dist/cli/args/parser.d.ts +5 -0
- package/dist/cli/args/parser.js +145 -0
- package/dist/cli/args/parser.js.map +1 -0
- package/dist/cli/args/readers.d.ts +4 -0
- package/dist/cli/args/readers.js +19 -0
- package/dist/cli/args/readers.js.map +1 -0
- package/dist/cli/commands/diagnose.d.ts +4 -0
- package/dist/cli/commands/diagnose.js +12 -0
- package/dist/cli/commands/diagnose.js.map +1 -0
- package/dist/cli/commands/dispatch.d.ts +3 -0
- package/dist/cli/commands/dispatch.js +94 -0
- package/dist/cli/commands/dispatch.js.map +1 -0
- package/dist/cli/commands/help.d.ts +3 -0
- package/dist/cli/commands/help.js +32 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/helpers.d.ts +8 -0
- package/dist/cli/commands/helpers.js +97 -0
- package/dist/cli/commands/helpers.js.map +1 -0
- package/dist/cli/commands/initialize.d.ts +3 -0
- package/dist/cli/commands/initialize.js +7 -0
- package/dist/cli/commands/initialize.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +3 -0
- package/dist/cli/commands/migrate.js +8 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +3 -0
- package/dist/cli/commands/plan.js +34 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/reference.d.ts +5 -0
- package/dist/cli/commands/reference.js +43 -0
- package/dist/cli/commands/reference.js.map +1 -0
- package/dist/cli/commands/update.d.ts +3 -0
- package/dist/cli/commands/update.js +9 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/commands/upgrade.d.ts +3 -0
- package/dist/cli/commands/upgrade.js +7 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/context.d.ts +1 -0
- package/dist/cli/context.js +2 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/project-options.d.ts +3 -0
- package/dist/cli/project-options.js +41 -0
- package/dist/cli/project-options.js.map +1 -0
- package/dist/cli.d.ts +3 -3
- package/dist/cli.js +4 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +4 -35
- package/dist/commands.js +3 -2812
- package/dist/commands.js.map +1 -1
- package/dist/container-validation.d.ts +1 -1
- package/dist/domain/governance/activation/approvals.d.ts +351 -0
- package/dist/domain/governance/activation/approvals.js +528 -0
- package/dist/domain/governance/activation/approvals.js.map +1 -0
- package/dist/domain/governance/activation/canonical-json.d.ts +4 -0
- package/dist/domain/governance/activation/canonical-json.js +39 -0
- package/dist/domain/governance/activation/canonical-json.js.map +1 -0
- package/dist/domain/governance/activation/capabilities.d.ts +7 -0
- package/dist/domain/governance/activation/capabilities.js +33 -0
- package/dist/domain/governance/activation/capabilities.js.map +1 -0
- package/dist/domain/governance/activation/evidence.d.ts +63 -0
- package/dist/domain/governance/activation/evidence.js +444 -0
- package/dist/domain/governance/activation/evidence.js.map +1 -0
- package/dist/domain/governance/activation/graph.d.ts +397 -0
- package/dist/domain/governance/activation/graph.js +441 -0
- package/dist/domain/governance/activation/graph.js.map +1 -0
- package/dist/domain/governance/activation/inputs.d.ts +25 -0
- package/dist/domain/governance/activation/inputs.js +63 -0
- package/dist/domain/governance/activation/inputs.js.map +1 -0
- package/dist/domain/governance/activation/operations.d.ts +19 -0
- package/dist/domain/governance/activation/operations.js +181 -0
- package/dist/domain/governance/activation/operations.js.map +1 -0
- package/dist/domain/governance/activation/readiness.d.ts +27 -0
- package/dist/domain/governance/activation/readiness.js +269 -0
- package/dist/domain/governance/activation/readiness.js.map +1 -0
- package/dist/domain/governance/activation/types.d.ts +399 -0
- package/dist/domain/governance/activation/types.js +109 -0
- package/dist/domain/governance/activation/types.js.map +1 -0
- package/dist/domain/governance/activation/validators.d.ts +22 -0
- package/dist/domain/governance/activation/validators.js +1347 -0
- package/dist/domain/governance/activation/validators.js.map +1 -0
- package/dist/domain/governance/assessment/catalog.d.ts +14 -0
- package/dist/domain/governance/assessment/catalog.js +105 -0
- package/dist/domain/governance/assessment/catalog.js.map +1 -0
- package/dist/domain/governance/assessment/errors.d.ts +10 -0
- package/dist/domain/governance/assessment/errors.js +22 -0
- package/dist/domain/governance/assessment/errors.js.map +1 -0
- package/dist/domain/governance/assessment/index.d.ts +8 -0
- package/dist/domain/governance/assessment/index.js +9 -0
- package/dist/domain/governance/assessment/index.js.map +1 -0
- package/dist/domain/governance/assessment/live-normalize.d.ts +19 -0
- package/dist/domain/governance/assessment/live-normalize.js +403 -0
- package/dist/domain/governance/assessment/live-normalize.js.map +1 -0
- package/dist/domain/governance/assessment/predicates.d.ts +36 -0
- package/dist/domain/governance/assessment/predicates.js +545 -0
- package/dist/domain/governance/assessment/predicates.js.map +1 -0
- package/dist/domain/governance/assessment/report.d.ts +23 -0
- package/dist/domain/governance/assessment/report.js +266 -0
- package/dist/domain/governance/assessment/report.js.map +1 -0
- package/dist/domain/governance/assessment/sanitize.d.ts +8 -0
- package/dist/domain/governance/assessment/sanitize.js +60 -0
- package/dist/domain/governance/assessment/sanitize.js.map +1 -0
- package/dist/domain/governance/assessment/types.d.ts +174 -0
- package/dist/domain/governance/assessment/types.js +13 -0
- package/dist/domain/governance/assessment/types.js.map +1 -0
- package/dist/domain/governance/assessment/yaml.d.ts +6 -0
- package/dist/domain/governance/assessment/yaml.js +30 -0
- package/dist/domain/governance/assessment/yaml.js.map +1 -0
- package/dist/domain/governance/policy/identity.d.ts +64 -0
- package/dist/domain/governance/policy/identity.js +112 -0
- package/dist/domain/governance/policy/identity.js.map +1 -0
- package/dist/domain/migration/dependencies.d.ts +7 -0
- package/dist/domain/migration/dependencies.js +221 -0
- package/dist/domain/migration/dependencies.js.map +1 -0
- package/dist/domain/migration/inventory.d.ts +3 -0
- package/dist/domain/migration/inventory.js +15 -0
- package/dist/domain/migration/inventory.js.map +1 -0
- package/dist/domain/project/artifact-lifecycle.d.ts +69 -0
- package/dist/domain/project/artifact-lifecycle.js +137 -0
- package/dist/domain/project/artifact-lifecycle.js.map +1 -0
- package/dist/domain/project/catalog.d.ts +48 -0
- package/dist/domain/project/catalog.js +507 -0
- package/dist/domain/project/catalog.js.map +1 -0
- package/dist/domain/project/contracts.d.ts +247 -0
- package/dist/domain/project/contracts.js +2 -0
- package/dist/domain/project/contracts.js.map +1 -0
- package/dist/domain/project/errors.d.ts +3 -0
- package/dist/domain/project/errors.js +7 -0
- package/dist/domain/project/errors.js.map +1 -0
- package/dist/domain/project/infrastructure-layout.d.ts +26 -0
- package/dist/domain/project/infrastructure-layout.js +124 -0
- package/dist/domain/project/infrastructure-layout.js.map +1 -0
- package/dist/domain/project/inputs.d.ts +21 -0
- package/dist/domain/project/inputs.js +41 -0
- package/dist/domain/project/inputs.js.map +1 -0
- package/dist/domain/project/manifest/artifacts.d.ts +8 -0
- package/dist/domain/project/manifest/artifacts.js +150 -0
- package/dist/domain/project/manifest/artifacts.js.map +1 -0
- package/dist/domain/project/manifest/context.d.ts +13 -0
- package/dist/domain/project/manifest/context.js +2 -0
- package/dist/domain/project/manifest/context.js.map +1 -0
- package/dist/domain/project/manifest/fields.d.ts +6 -0
- package/dist/domain/project/manifest/fields.js +36 -0
- package/dist/domain/project/manifest/fields.js.map +1 -0
- package/dist/domain/project/manifest/governance.d.ts +8 -0
- package/dist/domain/project/manifest/governance.js +161 -0
- package/dist/domain/project/manifest/governance.js.map +1 -0
- package/dist/domain/project/manifest/project-identity.d.ts +6 -0
- package/dist/domain/project/manifest/project-identity.js +273 -0
- package/dist/domain/project/manifest/project-identity.js.map +1 -0
- package/dist/domain/project/manifest/reader.d.ts +9 -0
- package/dist/domain/project/manifest/reader.js +141 -0
- package/dist/domain/project/manifest/reader.js.map +1 -0
- package/dist/domain/project/paths.d.ts +2 -0
- package/dist/domain/project/paths.js +29 -0
- package/dist/domain/project/paths.js.map +1 -0
- package/dist/domain/project/planning.d.ts +19 -0
- package/dist/domain/project/planning.js +329 -0
- package/dist/domain/project/planning.js.map +1 -0
- package/dist/domain/project/retired-workload.d.ts +4 -0
- package/dist/domain/project/retired-workload.js +18 -0
- package/dist/domain/project/retired-workload.js.map +1 -0
- package/dist/domain/project/supported-stack.d.ts +113 -0
- package/dist/domain/project/supported-stack.js +426 -0
- package/dist/domain/project/supported-stack.js.map +1 -0
- package/dist/domain/workstation/constraints.d.ts +6 -0
- package/dist/domain/workstation/constraints.js +8 -0
- package/dist/domain/workstation/constraints.js.map +1 -0
- package/dist/file-system.d.ts +10 -41
- package/dist/file-system.js +9 -1204
- package/dist/file-system.js.map +1 -1
- package/dist/framework-adapters.d.ts +1 -1
- package/dist/framework-validation.d.ts +1 -1
- package/dist/framework-validation.js +1 -1
- package/dist/framework-validation.js.map +1 -1
- package/dist/genai-templates.d.ts +2 -9
- package/dist/genai-templates.js +4 -5
- package/dist/genai-templates.js.map +1 -1
- package/dist/generators/common/artifacts.d.ts +6 -0
- package/dist/generators/common/artifacts.js +30 -0
- package/dist/generators/common/artifacts.js.map +1 -0
- package/dist/generators/common/base.d.ts +17 -0
- package/dist/generators/common/base.js +421 -0
- package/dist/generators/common/base.js.map +1 -0
- package/dist/generators/common/dotenv-validation.d.ts +2 -0
- package/dist/generators/common/dotenv-validation.js +80 -0
- package/dist/generators/common/dotenv-validation.js.map +1 -0
- package/dist/generators/common/environments.d.ts +3 -0
- package/dist/generators/common/environments.js +15 -0
- package/dist/generators/common/environments.js.map +1 -0
- package/dist/generators/common/frontend.d.ts +15 -0
- package/dist/generators/common/frontend.js +166 -0
- package/dist/generators/common/frontend.js.map +1 -0
- package/dist/generators/common/python-settings.d.ts +2 -0
- package/dist/generators/common/python-settings.js +70 -0
- package/dist/generators/common/python-settings.js.map +1 -0
- package/dist/generators/common/spec-workflow.d.ts +28 -0
- package/dist/generators/common/spec-workflow.js +567 -0
- package/dist/generators/common/spec-workflow.js.map +1 -0
- package/dist/generators/common/values.d.ts +13 -0
- package/dist/generators/common/values.js +16 -0
- package/dist/generators/common/values.js.map +1 -0
- package/dist/generators/containers/compose.d.ts +7 -0
- package/dist/generators/containers/compose.js +196 -0
- package/dist/generators/containers/compose.js.map +1 -0
- package/dist/generators/containers/context.d.ts +1 -0
- package/dist/generators/containers/context.js +43 -0
- package/dist/generators/containers/context.js.map +1 -0
- package/dist/generators/containers/images.d.ts +7 -0
- package/dist/generators/containers/images.js +119 -0
- package/dist/generators/containers/images.js.map +1 -0
- package/dist/generators/context.d.ts +42 -0
- package/dist/generators/context.js +38 -0
- package/dist/generators/context.js.map +1 -0
- package/dist/generators/genai/backend.d.ts +20 -0
- package/dist/generators/genai/backend.js +667 -0
- package/dist/generators/genai/backend.js.map +1 -0
- package/dist/generators/genai/configuration.d.ts +3 -0
- package/dist/generators/genai/configuration.js +43 -0
- package/dist/generators/genai/configuration.js.map +1 -0
- package/dist/generators/genai/database.d.ts +7 -0
- package/dist/generators/genai/database.js +71 -0
- package/dist/generators/genai/database.js.map +1 -0
- package/dist/generators/genai/functions.d.ts +15 -0
- package/dist/generators/genai/functions.js +156 -0
- package/dist/generators/genai/functions.js.map +1 -0
- package/dist/generators/genai/index.d.ts +8 -0
- package/dist/generators/genai/index.js +11 -0
- package/dist/generators/genai/index.js.map +1 -0
- package/dist/generators/genai/patterns.d.ts +9 -0
- package/dist/generators/genai/patterns.js +309 -0
- package/dist/generators/genai/patterns.js.map +1 -0
- package/dist/generators/infrastructure/azure.d.ts +18 -0
- package/dist/generators/infrastructure/azure.js +655 -0
- package/dist/generators/infrastructure/azure.js.map +1 -0
- package/dist/generators/infrastructure/names.d.ts +23 -0
- package/dist/generators/infrastructure/names.js +51 -0
- package/dist/generators/infrastructure/names.js.map +1 -0
- package/dist/generators/standard/configuration.d.ts +3 -0
- package/dist/generators/standard/configuration.js +26 -0
- package/dist/generators/standard/configuration.js.map +1 -0
- package/dist/generators/standard/go.d.ts +17 -0
- package/dist/generators/standard/go.js +338 -0
- package/dist/generators/standard/go.js.map +1 -0
- package/dist/generators/standard/index.d.ts +12 -0
- package/dist/generators/standard/index.js +12 -0
- package/dist/generators/standard/index.js.map +1 -0
- package/dist/generators/standard/node.d.ts +20 -0
- package/dist/generators/standard/node.js +314 -0
- package/dist/generators/standard/node.js.map +1 -0
- package/dist/generators/standard/python.d.ts +18 -0
- package/dist/generators/standard/python.js +191 -0
- package/dist/generators/standard/python.js.map +1 -0
- package/dist/go-template-assets.js +3 -3
- package/dist/go-template-assets.js.map +1 -1
- package/dist/governance-activation/activation-state.d.ts +1 -1
- package/dist/governance-activation/activation-state.js +46 -9
- package/dist/governance-activation/activation-state.js.map +1 -1
- package/dist/governance-activation/approvals.d.ts +1 -188
- package/dist/governance-activation/approvals.js +1 -507
- package/dist/governance-activation/approvals.js.map +1 -1
- package/dist/governance-activation/canonical-json.d.ts +1 -3
- package/dist/governance-activation/canonical-json.js +1 -35
- package/dist/governance-activation/canonical-json.js.map +1 -1
- package/dist/governance-activation/commands.d.ts +1 -1
- package/dist/governance-activation/commands.js +107 -103
- package/dist/governance-activation/commands.js.map +1 -1
- package/dist/governance-activation/compatibility.d.ts +11 -3
- package/dist/governance-activation/compatibility.js +31 -4
- package/dist/governance-activation/compatibility.js.map +1 -1
- package/dist/governance-activation/credentials.d.ts +3 -3
- package/dist/governance-activation/credentials.js +5 -5
- package/dist/governance-activation/credentials.js.map +1 -1
- package/dist/governance-activation/doctor.d.ts +1 -1
- package/dist/governance-activation/doctor.js +28 -96
- package/dist/governance-activation/doctor.js.map +1 -1
- package/dist/governance-activation/evidence.d.ts +1 -49
- package/dist/governance-activation/evidence.js +1 -245
- package/dist/governance-activation/evidence.js.map +1 -1
- package/dist/governance-activation/git-ignore.d.ts +9 -0
- package/dist/governance-activation/git-ignore.js +76 -0
- package/dist/governance-activation/git-ignore.js.map +1 -0
- package/dist/governance-activation/graph.d.ts +1 -397
- package/dist/governance-activation/graph.js +1 -440
- package/dist/governance-activation/graph.js.map +1 -1
- package/dist/governance-activation/identity.d.ts +1 -35
- package/dist/governance-activation/identity.js +1 -88
- package/dist/governance-activation/identity.js.map +1 -1
- package/dist/governance-activation/index.d.ts +3 -0
- package/dist/governance-activation/index.js +3 -0
- package/dist/governance-activation/index.js.map +1 -1
- package/dist/governance-activation/inputs.d.ts +8 -0
- package/dist/governance-activation/inputs.js +146 -0
- package/dist/governance-activation/inputs.js.map +1 -0
- package/dist/governance-activation/migration.d.ts +4 -3
- package/dist/governance-activation/migration.js +14 -6
- package/dist/governance-activation/migration.js.map +1 -1
- package/dist/governance-activation/phase-bootstrap-state.d.ts +3 -0
- package/dist/governance-activation/phase-bootstrap-state.js +128 -0
- package/dist/governance-activation/phase-bootstrap-state.js.map +1 -0
- package/dist/governance-activation/phase-discovery.d.ts +2 -0
- package/dist/governance-activation/phase-discovery.js +88 -0
- package/dist/governance-activation/phase-discovery.js.map +1 -0
- package/dist/governance-activation/phase-governance.d.ts +4 -0
- package/dist/governance-activation/phase-governance.js +141 -0
- package/dist/governance-activation/phase-governance.js.map +1 -0
- package/dist/governance-activation/phase-publication.d.ts +8 -0
- package/dist/governance-activation/phase-publication.js +318 -0
- package/dist/governance-activation/phase-publication.js.map +1 -0
- package/dist/governance-activation/read-only.d.ts +30 -0
- package/dist/governance-activation/read-only.js +93 -0
- package/dist/governance-activation/read-only.js.map +1 -0
- package/dist/governance-activation/readiness.d.ts +1 -24
- package/dist/governance-activation/readiness.js +1 -220
- package/dist/governance-activation/readiness.js.map +1 -1
- package/dist/governance-activation/reconciliation.d.ts +1 -1
- package/dist/governance-activation/reconciliation.js +3 -3
- package/dist/governance-activation/reconciliation.js.map +1 -1
- package/dist/governance-activation/release-integrity.d.ts +1 -1
- package/dist/governance-activation/release-integrity.js +2 -2
- package/dist/governance-activation/release-integrity.js.map +1 -1
- package/dist/governance-activation/seed-lifecycle.d.ts +10 -2
- package/dist/governance-activation/seed-lifecycle.js +244 -66
- package/dist/governance-activation/seed-lifecycle.js.map +1 -1
- package/dist/governance-activation/source-of-truth.d.ts +5 -3
- package/dist/governance-activation/source-of-truth.js +32 -52
- package/dist/governance-activation/source-of-truth.js.map +1 -1
- package/dist/governance-activation/spec-kit-seed.d.ts +9 -0
- package/dist/governance-activation/spec-kit-seed.js +43 -0
- package/dist/governance-activation/spec-kit-seed.js.map +1 -0
- package/dist/governance-activation/task-projection.d.ts +1 -1
- package/dist/governance-activation/transition-planning.d.ts +15 -0
- package/dist/governance-activation/transition-planning.js +250 -0
- package/dist/governance-activation/transition-planning.js.map +1 -0
- package/dist/governance-activation/transition-ports.d.ts +150 -0
- package/dist/governance-activation/transition-ports.js +2 -0
- package/dist/governance-activation/transition-ports.js.map +1 -0
- package/dist/governance-activation/transition-process.d.ts +7 -0
- package/dist/governance-activation/transition-process.js +21 -0
- package/dist/governance-activation/transition-process.js.map +1 -0
- package/dist/governance-activation/transition-records.d.ts +56 -0
- package/dist/governance-activation/transition-records.js +208 -0
- package/dist/governance-activation/transition-records.js.map +1 -0
- package/dist/governance-activation/transitions.d.ts +6 -157
- package/dist/governance-activation/transitions.js +143 -2045
- package/dist/governance-activation/transitions.js.map +1 -1
- package/dist/governance-activation/types.d.ts +1 -390
- package/dist/governance-activation/types.js +1 -107
- package/dist/governance-activation/types.js.map +1 -1
- package/dist/governance-activation/validators.d.ts +1 -17
- package/dist/governance-activation/validators.js +1 -1304
- package/dist/governance-activation/validators.js.map +1 -1
- package/dist/governance-assessment/catalog.d.ts +7 -0
- package/dist/governance-assessment/catalog.js +31 -0
- package/dist/governance-assessment/catalog.js.map +1 -0
- package/dist/governance-assessment/command.d.ts +10 -0
- package/dist/governance-assessment/command.js +77 -0
- package/dist/governance-assessment/command.js.map +1 -0
- package/dist/governance-assessment/engine.d.ts +14 -0
- package/dist/governance-assessment/engine.js +935 -0
- package/dist/governance-assessment/engine.js.map +1 -0
- package/dist/governance-assessment/index.d.ts +5 -0
- package/dist/governance-assessment/index.js +6 -0
- package/dist/governance-assessment/index.js.map +1 -0
- package/dist/governance-assessment/live-normalize.d.ts +1 -0
- package/dist/governance-assessment/live-normalize.js +2 -0
- package/dist/governance-assessment/live-normalize.js.map +1 -0
- package/dist/governance-assessment/live-transport.d.ts +34 -0
- package/dist/governance-assessment/live-transport.js +455 -0
- package/dist/governance-assessment/live-transport.js.map +1 -0
- package/dist/governance-assessment/live.d.ts +2 -0
- package/dist/governance-assessment/live.js +689 -0
- package/dist/governance-assessment/live.js.map +1 -0
- package/dist/governance-assessment/predicates.d.ts +1 -0
- package/dist/governance-assessment/predicates.js +2 -0
- package/dist/governance-assessment/predicates.js.map +1 -0
- package/dist/governance-assessment/project.d.ts +33 -0
- package/dist/governance-assessment/project.js +178 -0
- package/dist/governance-assessment/project.js.map +1 -0
- package/dist/governance-assessment/readers.d.ts +14 -0
- package/dist/governance-assessment/readers.js +122 -0
- package/dist/governance-assessment/readers.js.map +1 -0
- package/dist/governance-assessment/report.d.ts +1 -0
- package/dist/governance-assessment/report.js +2 -0
- package/dist/governance-assessment/report.js.map +1 -0
- package/dist/governance-assessment/sanitize.d.ts +1 -0
- package/dist/governance-assessment/sanitize.js +2 -0
- package/dist/governance-assessment/sanitize.js.map +1 -0
- package/dist/governance-assessment/types.d.ts +6 -0
- package/dist/governance-assessment/types.js +2 -0
- package/dist/governance-assessment/types.js.map +1 -0
- package/dist/governance-assessment/yaml.d.ts +1 -0
- package/dist/governance-assessment/yaml.js +2 -0
- package/dist/governance-assessment/yaml.js.map +1 -0
- package/dist/init-filesystem.d.ts +1 -1
- package/dist/init-filesystem.js +126 -174
- package/dist/init-filesystem.js.map +1 -1
- package/dist/interactive.d.ts +2 -2
- package/dist/interactive.js +42 -41
- package/dist/interactive.js.map +1 -1
- package/dist/migrate-plan.d.ts +5 -1
- package/dist/migrate-plan.js +87 -10
- package/dist/migrate-plan.js.map +1 -1
- package/dist/npm-template-assets.js +5 -4
- package/dist/npm-template-assets.js.map +1 -1
- package/dist/openspec-profile.d.ts +1 -1
- package/dist/opentofu-template-assets.js +3 -3
- package/dist/opentofu-template-assets.js.map +1 -1
- package/dist/package-identity.d.ts +3 -0
- package/dist/package-identity.js +13 -2
- package/dist/package-identity.js.map +1 -1
- package/dist/planner.d.ts +1 -19
- package/dist/planner.js +1 -565
- package/dist/planner.js.map +1 -1
- package/dist/process-runner.d.ts +5 -1
- package/dist/process-runner.js +164 -8
- package/dist/process-runner.js.map +1 -1
- package/dist/project-dependencies.d.ts +5 -3
- package/dist/project-dependencies.js +74 -63
- package/dist/project-dependencies.js.map +1 -1
- package/dist/published-verifier.js +67 -30
- package/dist/published-verifier.js.map +1 -1
- package/dist/python-template-assets.js +6 -9
- package/dist/python-template-assets.js.map +1 -1
- package/dist/reconcile.d.ts +1 -1
- package/dist/reconcile.js +2 -2
- package/dist/reconcile.js.map +1 -1
- package/dist/release-identity.js +19 -0
- package/dist/release-identity.js.map +1 -1
- package/dist/repository-governance.d.ts +10 -2
- package/dist/repository-governance.js +240 -99
- package/dist/repository-governance.js.map +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/scan.d.ts +6 -2
- package/dist/scan.js +78 -30
- package/dist/scan.js.map +1 -1
- package/dist/self-upgrade.d.ts +1 -1
- package/dist/self-upgrade.js +20 -7
- package/dist/self-upgrade.js.map +1 -1
- package/dist/stable-release.js +7 -1
- package/dist/stable-release.js.map +1 -1
- package/dist/standard-templates.d.ts +2 -2
- package/dist/standard-templates.js +6 -841
- package/dist/standard-templates.js.map +1 -1
- package/dist/supported-stack.d.ts +3 -115
- package/dist/supported-stack.js +4 -438
- package/dist/supported-stack.js.map +1 -1
- package/dist/telemetry/contract.d.ts +2 -0
- package/dist/telemetry/contract.js +7 -0
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/templates.d.ts +10 -20
- package/dist/templates.js +52 -3238
- package/dist/templates.js.map +1 -1
- package/dist/types.d.ts +1 -263
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.js +2 -6
- package/dist/version.js.map +1 -1
- package/dist/workstation-catalog.d.ts +5 -2
- package/dist/workstation-catalog.js +33 -2
- package/dist/workstation-catalog.js.map +1 -1
- package/dist/workstation.d.ts +4 -3
- package/dist/workstation.js +86 -32
- package/dist/workstation.js.map +1 -1
- package/docs/assets/liftoff-terminal.svg +3 -3
- package/docs/azure-deployment.md +107 -10
- package/docs/cli-reference.md +127 -11
- package/docs/configuration-and-manifests.md +150 -35
- package/docs/existing-repositories.md +33 -6
- package/docs/getting-started.md +44 -31
- package/docs/prerequisites.md +34 -35
- package/docs/project-structure.md +45 -47
- package/docs/repository-governance.md +207 -24
- package/docs/safety-and-consent.md +65 -12
- package/docs/spec-workflows-and-agents.md +31 -28
- package/docs/supported-stack.md +11 -9
- package/docs/telemetry.md +9 -3
- package/docs/troubleshooting.md +107 -49
- package/docs/workloads.md +53 -62
- package/package.json +6 -8
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/UPSTREAM_LICENSE.txt +0 -21
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/catalog.json +0 -520
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/packaged/gitignore +0 -27
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/README.md +0 -25
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/components.json +0 -22
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/eslint.config.js +0 -23
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/index.html +0 -13
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/package-lock.json +0 -6156
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/package.json +0 -60
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/public/power-apps.svg +0 -55
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/App.tsx +0 -17
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/assets/react.svg +0 -1
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/mode-toggle.tsx +0 -37
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/badge.tsx +0 -46
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/button.tsx +0 -60
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/calendar.tsx +0 -211
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/card.tsx +0 -92
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/chart.tsx +0 -355
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/checkbox.tsx +0 -32
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/command.tsx +0 -184
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/dialog.tsx +0 -141
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/dropdown-menu.tsx +0 -257
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/input.tsx +0 -21
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/label.tsx +0 -22
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/popover.tsx +0 -46
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/progress.tsx +0 -29
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/select.tsx +0 -185
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/separator.tsx +0 -26
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/skeleton.tsx +0 -13
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/table.tsx +0 -114
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/tabs.tsx +0 -66
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/textarea.tsx +0 -18
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/tooltip.tsx +0 -61
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/hooks/use-theme.ts +0 -11
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/index.css +0 -131
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/lib/utils.ts +0 -6
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/main.tsx +0 -10
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/pages/_layout.tsx +0 -31
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/pages/home.tsx +0 -59
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/pages/not-found.tsx +0 -14
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/providers/query-provider.tsx +0 -26
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/providers/sonner-provider.tsx +0 -23
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/providers/theme-provider.tsx +0 -64
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/router.tsx +0 -24
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/tsconfig.app.json +0 -34
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/tsconfig.json +0 -13
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/tsconfig.node.json +0 -26
- package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/vite.config.ts +0 -19
- package/assets/power-apps-code-app/README.md +0 -27
- package/dist/code-apps-plugin.d.ts +0 -13
- package/dist/code-apps-plugin.js +0 -69
- package/dist/code-apps-plugin.js.map +0 -1
- package/dist/power-apps-assets.d.ts +0 -30
- package/dist/power-apps-assets.js +0 -131
- package/dist/power-apps-assets.js.map +0 -1
- package/dist/power-apps-templates.d.ts +0 -3
- package/dist/power-apps-templates.js +0 -173
- package/dist/power-apps-templates.js.map +0 -1
- package/dist/power-apps-validation.d.ts +0 -1
- package/dist/power-apps-validation.js +0 -79
- package/dist/power-apps-validation.js.map +0 -1
|
@@ -0,0 +1,935 @@
|
|
|
1
|
+
import { canonicalJson, canonicalSha256, sha256Hex } from '../domain/governance/activation/canonical-json.js';
|
|
2
|
+
import { currentActivationIdentity, canonicalPhaseGraph } from '../domain/governance/activation/graph.js';
|
|
3
|
+
import { canonicalApprovalEnvelopeHash, evaluateApprovalForTransitionPlan, transitionPlanForPhase } from '../domain/governance/activation/approvals.js';
|
|
4
|
+
import { inspectCurrentActivationEvidence } from '../governance-activation/read-only.js';
|
|
5
|
+
import { governanceChangeMetadataFileName, validateGovernanceChangeMetadata } from '../governance-activation/source-of-truth.js';
|
|
6
|
+
import { phaseIds } from '../domain/governance/activation/types.js';
|
|
7
|
+
import { loadAssessmentCatalog } from './catalog.js';
|
|
8
|
+
import { inspectAssessmentProject, ordinaryGitAssessmentProject } from './project.js';
|
|
9
|
+
import { inspectAssessmentGit, repositoryName } from '../adapters/git/governance-assessment.js';
|
|
10
|
+
import { AssessmentFiles, AssessmentInputError, parseAssessmentJson } from './readers.js';
|
|
11
|
+
import { parseAssessmentWorkflow } from './yaml.js';
|
|
12
|
+
import { collectLiveAssessment } from './live.js';
|
|
13
|
+
import { classifyFinding, assembleAssessmentReport } from '../domain/governance/assessment/report.js';
|
|
14
|
+
import { actionReferences, effectiveProtectedRefs, effectiveRequiredContextBindings, failOpenFlags, pinnedActions, protectedRefs, requiredCheckContexts, requiredContextBindings, runnerAlignment, securityPipeline, singleMaintainer, tagControls, workflowPermissions, observedRequiredContexts } from '../domain/governance/assessment/predicates.js';
|
|
15
|
+
import { isRecord, jsonValue, notObserved, observed, sanitizeAssessmentText, source } from '../domain/governance/assessment/sanitize.js';
|
|
16
|
+
const documentationPaths = [
|
|
17
|
+
['CONTRIBUTING.md'], ['docs', 'operations', 'github-governance.md'],
|
|
18
|
+
['docs', 'security', 'scanning.md'], ['docs', 'operations', 'alerting.md'],
|
|
19
|
+
['docs', 'operations', 'service-health.md'], ['docs', 'security', 'audit-evidence.md'],
|
|
20
|
+
['docs', 'ai-acceptable-use.md']
|
|
21
|
+
];
|
|
22
|
+
function activationAuthorityFingerprint(inspection) {
|
|
23
|
+
if (inspection.status === 'not-started') {
|
|
24
|
+
return canonicalSha256({ status: inspection.status });
|
|
25
|
+
}
|
|
26
|
+
const firstContext = inspection.contexts[phaseIds[0]];
|
|
27
|
+
const stable = JSON.parse(JSON.stringify({
|
|
28
|
+
status: inspection.status,
|
|
29
|
+
state: inspection.state,
|
|
30
|
+
snapshot: inspection.snapshot,
|
|
31
|
+
records: inspection.records,
|
|
32
|
+
reviewedPlans: firstContext.reviewedPlans ?? [],
|
|
33
|
+
selections: inspection.selections
|
|
34
|
+
}));
|
|
35
|
+
return canonicalSha256(stable);
|
|
36
|
+
}
|
|
37
|
+
async function localFacts(files, project, capturedAt) {
|
|
38
|
+
const workflowParts = await files.list(['.github', 'workflows'], ['.yml', '.yaml']);
|
|
39
|
+
const workflows = [];
|
|
40
|
+
const workflowDigests = {};
|
|
41
|
+
for (const parts of workflowParts) {
|
|
42
|
+
const text = await files.read(parts);
|
|
43
|
+
if (text === null)
|
|
44
|
+
throw new AssessmentInputError('inputs-changed', 'Workflow disappeared during inspection.', parts.join('/'));
|
|
45
|
+
workflows.push(parseAssessmentWorkflow(text, parts.join('/')));
|
|
46
|
+
workflowDigests[parts.join('/')] = sha256Hex(text);
|
|
47
|
+
}
|
|
48
|
+
const rulesets = [];
|
|
49
|
+
const ruleDigests = {};
|
|
50
|
+
for (const parts of await files.list(['governance', 'rulesets'], ['.json'])) {
|
|
51
|
+
const text = await files.read(parts);
|
|
52
|
+
if (text === null)
|
|
53
|
+
throw new AssessmentInputError('inputs-changed', 'Ruleset disappeared during inspection.', parts.join('/'));
|
|
54
|
+
const value = parseAssessmentJson(text, parts.join('/'));
|
|
55
|
+
if (!isRecord(value) || !['branch', 'tag', 'push'].includes(String(value.target)) ||
|
|
56
|
+
!['active', 'disabled', 'evaluate'].includes(String(value.enforcement)) || !Array.isArray(value.rules) ||
|
|
57
|
+
!Array.isArray(value.bypass_actors)) {
|
|
58
|
+
throw new AssessmentInputError('malformed-ruleset', 'Ruleset payload has invalid target, enforcement, rule, or bypass fields.', parts.join('/'));
|
|
59
|
+
}
|
|
60
|
+
rulesets.push(value);
|
|
61
|
+
ruleDigests[parts.join('/')] = sha256Hex(text);
|
|
62
|
+
}
|
|
63
|
+
const codeownerFiles = [];
|
|
64
|
+
for (const parts of [['CODEOWNERS'], ['.github', 'CODEOWNERS'], ['docs', 'CODEOWNERS']]) {
|
|
65
|
+
if (await files.read(parts) !== null)
|
|
66
|
+
codeownerFiles.push(parts.join('/'));
|
|
67
|
+
}
|
|
68
|
+
const missingDocs = [];
|
|
69
|
+
const docFacts = {};
|
|
70
|
+
for (const parts of documentationPaths) {
|
|
71
|
+
const text = await files.read(parts);
|
|
72
|
+
docFacts[parts.join('/')] = text === null ? null : sha256Hex(text);
|
|
73
|
+
if (text === null)
|
|
74
|
+
missingDocs.push(parts.join('/'));
|
|
75
|
+
}
|
|
76
|
+
const runbooks = await files.list(['docs', 'runbooks'], ['.md']);
|
|
77
|
+
if (runbooks.length === 0)
|
|
78
|
+
missingDocs.push('docs/runbooks/*.md');
|
|
79
|
+
let baseline = null;
|
|
80
|
+
if (project.state?.activeChange) {
|
|
81
|
+
const active = project.state.activeChange;
|
|
82
|
+
const parts = active.kind === 'openspec'
|
|
83
|
+
? ['openspec', 'changes', active.id, governanceChangeMetadataFileName]
|
|
84
|
+
: ['specs', active.id, governanceChangeMetadataFileName];
|
|
85
|
+
const text = await files.read(parts);
|
|
86
|
+
if (text !== null) {
|
|
87
|
+
try {
|
|
88
|
+
const metadata = validateGovernanceChangeMetadata(parseAssessmentJson(text, parts.join('/')));
|
|
89
|
+
if (metadata.changeId !== active.id ||
|
|
90
|
+
metadata.workflowKind !== active.kind ||
|
|
91
|
+
canonicalJson(metadata.activationIdentity) !== canonicalJson(project.state.identity)) {
|
|
92
|
+
throw new Error('Active governance metadata does not match the recorded change and identity.');
|
|
93
|
+
}
|
|
94
|
+
baseline = metadata.baselineSha === '0'.repeat(64) ? null : metadata.baselineSha;
|
|
95
|
+
if (baseline && project.inputSnapshot && baseline !== project.inputSnapshot.baselineSha) {
|
|
96
|
+
project.diagnostics.push({
|
|
97
|
+
code: 'current-input-mismatch',
|
|
98
|
+
severity: 'warning',
|
|
99
|
+
source: parts.join('/'),
|
|
100
|
+
message: 'The recorded governance baseline does not match the independently recomputed current input snapshot; evidence-backed scope is withheld.'
|
|
101
|
+
});
|
|
102
|
+
baseline = null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
project.diagnostics.push({
|
|
107
|
+
code: 'unverified-active-baseline', severity: 'warning', source: parts.join('/'),
|
|
108
|
+
message: sanitizeAssessmentText(error instanceof Error ? error.message : 'Active governance metadata cannot be interpreted.')
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
project.bindingBaseline = baseline;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const docObservation = observed(missingDocs.length === 0, source('file', 'docs and CONTRIBUTING.md', capturedAt, docFacts));
|
|
115
|
+
docObservation.reason = missingDocs.length ? `Missing documentation inventory: ${missingDocs.join(', ')}` : 'Required documentation inventory exists; content and rehearsals need separate proof.';
|
|
116
|
+
return {
|
|
117
|
+
workflows, rulesets,
|
|
118
|
+
workflowSource: source('file', '.github/workflows', capturedAt, workflowDigests),
|
|
119
|
+
ruleSource: source('file', 'governance/rulesets', capturedAt, ruleDigests),
|
|
120
|
+
codeowners: observed(codeownerFiles.length > 0, source('file', 'CODEOWNERS inventory', capturedAt, codeownerFiles)),
|
|
121
|
+
documentation: docObservation, baseline
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function boundPlan(project, phaseId, now) {
|
|
125
|
+
const currentContext = project.evidenceContexts?.[phaseId];
|
|
126
|
+
const selection = project.activationSelections?.[phaseId];
|
|
127
|
+
const selected = selection?.issues.length === 0 ? selection.selected : null;
|
|
128
|
+
const payload = selected?.payload;
|
|
129
|
+
if (!project.state || !currentContext || !selected ||
|
|
130
|
+
!isRecord(payload) || typeof payload.planDigest !== 'string' ||
|
|
131
|
+
typeof payload.savedPlanDigest !== 'string' ||
|
|
132
|
+
project.identity.availability !== 'known')
|
|
133
|
+
return null;
|
|
134
|
+
const candidates = (currentContext.reviewedPlans ?? []).filter((plan) => plan.phaseId === phaseId &&
|
|
135
|
+
plan.planDigest === payload.planDigest &&
|
|
136
|
+
canonicalSha256(plan) === payload.savedPlanDigest &&
|
|
137
|
+
plan.baselineDigest === currentContext.baselineSha &&
|
|
138
|
+
canonicalJson(plan.identity) === canonicalJson(currentActivationIdentity) &&
|
|
139
|
+
Date.parse(plan.createdAt) <= now.getTime() &&
|
|
140
|
+
Date.parse(plan.createdAt) <= Date.parse(project.state.phases[phaseId].updatedAt)).sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
|
|
141
|
+
const plan = candidates[0];
|
|
142
|
+
if (!plan || plan.baselineDigest === '0'.repeat(64) || plan.inputDigest === '1'.repeat(64) ||
|
|
143
|
+
plan.baselineDigest !== currentContext.baselineSha ||
|
|
144
|
+
plan.inputDigest !== currentContext.inputDigest ||
|
|
145
|
+
plan.transitionDigest !== currentContext.transition.transitionDigest ||
|
|
146
|
+
candidates.some((candidate) => candidate.createdAt === plan.createdAt && candidate.planDigest !== plan.planDigest))
|
|
147
|
+
return null;
|
|
148
|
+
return plan;
|
|
149
|
+
}
|
|
150
|
+
export function selectBoundAssessmentEvidence(project, phaseId, now) {
|
|
151
|
+
const plan = boundPlan(project, phaseId, now);
|
|
152
|
+
const selection = project.activationSelections?.[phaseId];
|
|
153
|
+
const selected = selection?.issues.length === 0 ? selection.selected : null;
|
|
154
|
+
if (!plan || !project.state || !selected ||
|
|
155
|
+
selected.header.result !== project.state.phases[phaseId].state)
|
|
156
|
+
return null;
|
|
157
|
+
return selected;
|
|
158
|
+
}
|
|
159
|
+
function applicability(control, project, now) {
|
|
160
|
+
if (control.applicability === 'always')
|
|
161
|
+
return 'applicable';
|
|
162
|
+
if (control.applicability === 'api')
|
|
163
|
+
return project.kind === 'liftoff' ? 'applicable' : 'unknown';
|
|
164
|
+
const discovery = selectBoundAssessmentEvidence(project, 'phase-0-complete', now);
|
|
165
|
+
if (!project.state || discovery?.header.result !== 'verified')
|
|
166
|
+
return 'unknown';
|
|
167
|
+
return (control.applicability === 'private-dast'
|
|
168
|
+
? project.state.applicability.privateStagingDast
|
|
169
|
+
: project.state.applicability.statePath === 'bootstrap-local') ? 'applicable' : 'inapplicable';
|
|
170
|
+
}
|
|
171
|
+
function approvalBindsRepository(project, approval, repository, now) {
|
|
172
|
+
const currentBaseline = project.inputSnapshot?.baselineSha ??
|
|
173
|
+
project.bindingBaseline;
|
|
174
|
+
if (!project.state || !currentBaseline)
|
|
175
|
+
return false;
|
|
176
|
+
const plan = boundPlan(project, approval.phaseId, now);
|
|
177
|
+
const phase = canonicalPhaseGraph.phases.find((entry) => entry.id === approval.phaseId);
|
|
178
|
+
const authority = plan && phase ? transitionPlanForPhase(phase, project.state, {
|
|
179
|
+
phaseId: phase.id,
|
|
180
|
+
baselineSha: plan.baselineDigest,
|
|
181
|
+
inputDigest: plan.inputDigest,
|
|
182
|
+
transitionDigest: plan.transitionDigest
|
|
183
|
+
}) : null;
|
|
184
|
+
return approval.baselineSha === currentBaseline &&
|
|
185
|
+
project.identity.availability === 'known' &&
|
|
186
|
+
Date.parse(approval.approvedAt) <= now.getTime() &&
|
|
187
|
+
Date.parse(approval.expiresAt) > now.getTime() &&
|
|
188
|
+
project.state.phases[approval.phaseId].approvals.includes(approval.id) &&
|
|
189
|
+
Boolean(plan && authority && phase?.approvalGate.required) &&
|
|
190
|
+
authority?.planDigest === approval.planDigest &&
|
|
191
|
+
!evaluateApprovalForTransitionPlan(authority, [approval], { now }).approvalRequired &&
|
|
192
|
+
plan.approval.envelopeId === approval.id &&
|
|
193
|
+
plan.approval.envelopeHash === canonicalApprovalEnvelopeHash(approval) &&
|
|
194
|
+
approval.destinations.some((destination) => destination.type === 'repository' &&
|
|
195
|
+
(destination.repository ?? destination.identity).toLowerCase() === repository.toLowerCase());
|
|
196
|
+
}
|
|
197
|
+
export function resolveAssessmentLiveScope(project, git, now) {
|
|
198
|
+
let repository = git.originState === 'verified' ? git.repository : null;
|
|
199
|
+
let remoteBindingVerified = false;
|
|
200
|
+
if (git.repository && project.state?.remoteBinding) {
|
|
201
|
+
const remote = project.state.remoteBinding;
|
|
202
|
+
const bound = repositoryName(remote.name, remote.id);
|
|
203
|
+
const matchingPush = git.pushUrls.length === 1 &&
|
|
204
|
+
git.pushUrls[0] === remote.pushUrl &&
|
|
205
|
+
project.inputSnapshot?.git.pushUrls.length === 1 &&
|
|
206
|
+
project.inputSnapshot.git.pushUrls[0] === remote.pushUrl;
|
|
207
|
+
if (!bound ||
|
|
208
|
+
`${git.repository.owner}/${git.repository.name}`.toLowerCase() !==
|
|
209
|
+
`${bound.owner}/${bound.name}`.toLowerCase() ||
|
|
210
|
+
!matchingPush) {
|
|
211
|
+
project.diagnostics.push({
|
|
212
|
+
code: 'repository-binding-conflict',
|
|
213
|
+
severity: 'warning',
|
|
214
|
+
source: 'Git/activation state',
|
|
215
|
+
message: 'Git fetch/push origin and the verified remote repository binding disagree; evidence-backed provider scope is withheld.'
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
repository = { ...git.repository, id: bound.id };
|
|
220
|
+
remoteBindingVerified = true;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const scope = {
|
|
224
|
+
repository,
|
|
225
|
+
refs: repository ? ['develop', 'main'] : [],
|
|
226
|
+
...(repository ? { refPrefixes: ['release/', 'hotfix/'] } : {}),
|
|
227
|
+
environments: project.project ? [...project.project.workload.environments] : [],
|
|
228
|
+
runner: null, azure: []
|
|
229
|
+
};
|
|
230
|
+
if (!repository || !project.state)
|
|
231
|
+
return scope;
|
|
232
|
+
if (!remoteBindingVerified) {
|
|
233
|
+
project.diagnostics.push({
|
|
234
|
+
code: 'remote-binding-unavailable',
|
|
235
|
+
severity: 'warning',
|
|
236
|
+
source: 'activation state',
|
|
237
|
+
message: 'No matching verified remote repository binding is available; evidence-backed runner and Azure reads are withheld.'
|
|
238
|
+
});
|
|
239
|
+
return scope;
|
|
240
|
+
}
|
|
241
|
+
const fullName = `${repository.owner}/${repository.name}`;
|
|
242
|
+
const azureBindings = new Map();
|
|
243
|
+
const conflictingAzureBindings = new Set();
|
|
244
|
+
for (const approval of project.approvals) {
|
|
245
|
+
if (!approvalBindsRepository(project, approval, fullName, now))
|
|
246
|
+
continue;
|
|
247
|
+
const environments = approval.destinations.filter((destination) => destination.type === 'environment' &&
|
|
248
|
+
['dev', 'staging', 'prod'].includes(destination.identity)).map((destination) => destination.identity);
|
|
249
|
+
if (new Set(environments).size !== 1)
|
|
250
|
+
continue;
|
|
251
|
+
const environment = environments[0];
|
|
252
|
+
if (environment !== 'dev' && environment !== 'staging' && environment !== 'prod')
|
|
253
|
+
continue;
|
|
254
|
+
const selected = selectBoundAssessmentEvidence(project, approval.phaseId, now);
|
|
255
|
+
const payload = selected?.payload;
|
|
256
|
+
const assessmentScope = isRecord(payload) && isRecord(payload.assessmentScope)
|
|
257
|
+
? payload.assessmentScope
|
|
258
|
+
: null;
|
|
259
|
+
if (!assessmentScope || !Array.isArray(assessmentScope.azure)) {
|
|
260
|
+
if (approval.resources.some((resource) => resource.identity.startsWith('/subscriptions/')) &&
|
|
261
|
+
!project.diagnostics.some((entry) => entry.code === 'unbound-resource-role' && entry.source === approval.phaseId)) {
|
|
262
|
+
project.diagnostics.push({
|
|
263
|
+
code: 'unbound-resource-role',
|
|
264
|
+
severity: 'warning',
|
|
265
|
+
source: approval.phaseId,
|
|
266
|
+
message: 'Approved ARM resources lacked a current body-committed evidence payload with explicit assessment environment and role; Azure reads were withheld.'
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
for (const value of assessmentScope.azure) {
|
|
272
|
+
if (!isRecord(value) ||
|
|
273
|
+
Object.keys(value).some((key) => ![
|
|
274
|
+
'subscriptionId', 'environment', 'resourceId', 'resourceType', 'role'
|
|
275
|
+
].includes(key)) ||
|
|
276
|
+
typeof value.subscriptionId !== 'string' ||
|
|
277
|
+
value.environment !== environment ||
|
|
278
|
+
typeof value.resourceId !== 'string' ||
|
|
279
|
+
typeof value.resourceType !== 'string' ||
|
|
280
|
+
!['state', 'application', 'runner-network'].includes(String(value.role))) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const subscriptionId = value.subscriptionId;
|
|
284
|
+
const resourceId = value.resourceId;
|
|
285
|
+
const resourceType = value.resourceType;
|
|
286
|
+
const match = resourceId.match(/^\/subscriptions\/([a-f0-9-]{36})\/resourceGroups\/[^/]+\/providers\/([A-Za-z0-9.]+)\/([A-Za-z0-9]+)\/[^/]+(?:\/[A-Za-z0-9]+\/[^/]+)*$/iu);
|
|
287
|
+
if (!match ||
|
|
288
|
+
match[1].toLowerCase() !== subscriptionId.toLowerCase() ||
|
|
289
|
+
!approval.resources.some((resource) => resource.identity.toLowerCase() === resourceId.toLowerCase()) ||
|
|
290
|
+
!approval.destinations.some((destination) => destination.type === 'subscription' &&
|
|
291
|
+
(destination.subscriptionId ?? destination.identity).toLowerCase() === subscriptionId.toLowerCase())) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const binding = {
|
|
295
|
+
subscriptionId,
|
|
296
|
+
environment: environment,
|
|
297
|
+
resourceId,
|
|
298
|
+
resourceType,
|
|
299
|
+
role: value.role
|
|
300
|
+
};
|
|
301
|
+
const key = binding.resourceId.toLowerCase();
|
|
302
|
+
const previous = azureBindings.get(key);
|
|
303
|
+
if (conflictingAzureBindings.has(key) ||
|
|
304
|
+
previous && (previous.environment !== binding.environment ||
|
|
305
|
+
previous.role !== binding.role ||
|
|
306
|
+
previous.resourceType.toLowerCase() !== binding.resourceType.toLowerCase())) {
|
|
307
|
+
azureBindings.delete(key);
|
|
308
|
+
conflictingAzureBindings.add(key);
|
|
309
|
+
project.diagnostics.push({
|
|
310
|
+
code: 'conflicting-resource-binding',
|
|
311
|
+
severity: 'warning',
|
|
312
|
+
source: binding.resourceId,
|
|
313
|
+
message: 'Conflicting body-committed ARM environment, role, or resource-type bindings were excluded before live scope deduplication.'
|
|
314
|
+
});
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (!previous)
|
|
318
|
+
azureBindings.set(key, binding);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
const runnerRecord = selectBoundAssessmentEvidence(project, 'runner-ready', now);
|
|
322
|
+
const rawRunner = runnerRecord?.payload;
|
|
323
|
+
const runnerApproval = project.approvals.some((approval) => approval.phaseId === 'runner-ready' &&
|
|
324
|
+
approvalBindsRepository(project, approval, fullName, now));
|
|
325
|
+
if (runnerApproval && runnerRecord?.header.result === 'verified' &&
|
|
326
|
+
isRecord(rawRunner) && rawRunner.kind === 'runner-ready.v1' &&
|
|
327
|
+
rawRunner.organization === repository.owner && Number.isInteger(rawRunner.runnerId) && Number(rawRunner.runnerId) > 0 &&
|
|
328
|
+
(rawRunner.groupId === null || Number.isInteger(rawRunner.groupId)) &&
|
|
329
|
+
(rawRunner.networkConfigurationId === null || typeof rawRunner.networkConfigurationId === 'string')) {
|
|
330
|
+
scope.runner = {
|
|
331
|
+
organization: repository.owner, runnerId: Number(rawRunner.runnerId),
|
|
332
|
+
groupId: rawRunner.groupId === null ? null : Number(rawRunner.groupId),
|
|
333
|
+
networkConfigurationId: typeof rawRunner.networkConfigurationId === 'string' ? rawRunner.networkConfigurationId : null
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
scope.azure = [...azureBindings.values()]
|
|
337
|
+
.sort((a, b) => a.resourceId.localeCompare(b.resourceId, 'en'));
|
|
338
|
+
return scope;
|
|
339
|
+
}
|
|
340
|
+
function predicateObservation(check, from) {
|
|
341
|
+
if (check.value === null || from === null)
|
|
342
|
+
return notObserved(check.reason, from);
|
|
343
|
+
if (check.absent)
|
|
344
|
+
return absent(sanitizeAssessmentText(check.reason), from);
|
|
345
|
+
const observation = observed(check.value, from);
|
|
346
|
+
if (observation.availability === 'observed')
|
|
347
|
+
observation.reason = sanitizeAssessmentText(check.reason);
|
|
348
|
+
return observation;
|
|
349
|
+
}
|
|
350
|
+
function absent(reason, from) {
|
|
351
|
+
return from ? { availability: 'missing', value: null, source: from, reason } : notObserved(reason);
|
|
352
|
+
}
|
|
353
|
+
function livePredicate(live, key, evaluate) {
|
|
354
|
+
const observation = live.observations[key];
|
|
355
|
+
if (!observation || observation.availability !== 'observed')
|
|
356
|
+
return observation ?? notObserved(`Live ${key} was not collected.`);
|
|
357
|
+
const converted = predicateObservation(evaluate(observation.value), observation.source);
|
|
358
|
+
if (converted.availability === 'not-observed')
|
|
359
|
+
converted.value = observation.value;
|
|
360
|
+
else
|
|
361
|
+
converted.facts = observation.value;
|
|
362
|
+
return converted;
|
|
363
|
+
}
|
|
364
|
+
function partialLivePredicate(live, key, evaluate) {
|
|
365
|
+
const observation = live.observations[key];
|
|
366
|
+
if (!observation) {
|
|
367
|
+
return {
|
|
368
|
+
observation: notObserved(`Live ${key} was not collected.`),
|
|
369
|
+
provenDifference: false
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
if (observation.availability === 'observed') {
|
|
373
|
+
return {
|
|
374
|
+
observation: livePredicate(live, key, evaluate),
|
|
375
|
+
provenDifference: false
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
if (observation.facts !== undefined) {
|
|
379
|
+
const evaluated = evaluate(observation.facts);
|
|
380
|
+
if (evaluated.value === false) {
|
|
381
|
+
return {
|
|
382
|
+
observation: {
|
|
383
|
+
...observation,
|
|
384
|
+
reason: `${observation.reason ?? 'Live collection was incomplete.'} Independently observed facts prove a difference: ${evaluated.reason}`
|
|
385
|
+
},
|
|
386
|
+
provenDifference: true
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return { observation, provenDifference: false };
|
|
391
|
+
}
|
|
392
|
+
function bool(value, reason) { return { value, reason }; }
|
|
393
|
+
function rows(value) {
|
|
394
|
+
return Array.isArray(value) && value.every(isRecord) ? value : null;
|
|
395
|
+
}
|
|
396
|
+
export function findAssessmentException(control, approvals, scope, baseline, resource, now) {
|
|
397
|
+
if (!control.exceptionAllowed || !scope.repository || !baseline || !resource)
|
|
398
|
+
return null;
|
|
399
|
+
const found = approvals.find((approval) => control.phaseIds.includes(approval.phaseId) && canonicalJson(approval.identity) === canonicalJson(currentActivationIdentity) &&
|
|
400
|
+
approval.baselineSha === baseline && approval.policyExceptions.includes(control.id) &&
|
|
401
|
+
Date.parse(approval.approvedAt) <= now.getTime() && Date.parse(approval.expiresAt) > now.getTime() &&
|
|
402
|
+
approval.resources.some((entry) => entry.type === 'action-reference' && entry.identity === resource) &&
|
|
403
|
+
approval.destinations.some((entry) => entry.type === 'repository' &&
|
|
404
|
+
(entry.repository ?? entry.identity).toLowerCase() === scope.repository.toLowerCase()) &&
|
|
405
|
+
(scope.environment === null || approval.destinations.some((entry) => entry.type === 'environment' && entry.identity === scope.environment)));
|
|
406
|
+
return found ? { id: found.id, expiresAt: found.expiresAt, envelopeDigest: canonicalApprovalEnvelopeHash(found) } : null;
|
|
407
|
+
}
|
|
408
|
+
export function rejectedAssessmentExceptionDiagnostic(control, approvals, exception) {
|
|
409
|
+
if (exception !== null ||
|
|
410
|
+
!approvals.some((approval) => approval.policyExceptions.includes(control.id))) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
code: 'rejected-exception',
|
|
415
|
+
severity: 'warning',
|
|
416
|
+
source: control.id,
|
|
417
|
+
message: 'A recorded exception was rejected because its current identity, baseline, scope, resource, approval time, or expiry did not match this finding.'
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function evidenceObservation(project, git, capturedAt) {
|
|
421
|
+
if (!project.state)
|
|
422
|
+
return notObserved('No compatible activation state is available.');
|
|
423
|
+
if (project.invalidEvidence)
|
|
424
|
+
return notObserved('Some evidence is malformed, sensitive, or uses unsupported schemas.');
|
|
425
|
+
if (git.head)
|
|
426
|
+
return notObserved('Current-worktree evidence binding cannot be established from stored state alone; historical records are not fresh live proof.');
|
|
427
|
+
const complete = canonicalPhaseGraph.phases.every((phase) => {
|
|
428
|
+
const stored = project.state.phases[phase.id];
|
|
429
|
+
if (stored.state === 'inapplicable')
|
|
430
|
+
return false;
|
|
431
|
+
if (stored.state === 'approved')
|
|
432
|
+
return false;
|
|
433
|
+
const record = selectBoundAssessmentEvidence(project, phase.id, new Date(capturedAt));
|
|
434
|
+
return record?.header.result === stored.state && ['verified', 'retained', 'disposed'].includes(stored.state);
|
|
435
|
+
});
|
|
436
|
+
if (!complete)
|
|
437
|
+
return notObserved('Baseline/setup progress, approvals, applicability and current readback are not all established.');
|
|
438
|
+
return observed(true, source('evidence', 'governance/evidence', capturedAt, project.evidence.map((record) => record.header.phaseId)));
|
|
439
|
+
}
|
|
440
|
+
function invalidateUnstableObservations(findings, kinds, reason) {
|
|
441
|
+
for (const finding of findings) {
|
|
442
|
+
if (finding.applicability === 'inapplicable')
|
|
443
|
+
continue;
|
|
444
|
+
let invalidated = false;
|
|
445
|
+
for (const layer of finding.requiredProof) {
|
|
446
|
+
const observation = finding.observations[layer];
|
|
447
|
+
if (observation?.source && kinds.has(observation.source.kind)) {
|
|
448
|
+
finding.observations[layer] = notObserved(reason);
|
|
449
|
+
invalidated = true;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (!invalidated)
|
|
453
|
+
continue;
|
|
454
|
+
finding.exception = null;
|
|
455
|
+
finding.missingProof = finding.requiredProof.filter((layer) => finding.observations[layer]?.availability === 'not-observed');
|
|
456
|
+
const remaining = finding.requiredProof
|
|
457
|
+
.map((layer) => finding.observations[layer])
|
|
458
|
+
.filter((observation) => observation !== undefined);
|
|
459
|
+
const absent = remaining.some((observation) => observation.availability === 'missing');
|
|
460
|
+
const differs = remaining.some((observation) => observation.availability === 'observed' &&
|
|
461
|
+
canonicalJson(observation.value) !== canonicalJson(finding.expected));
|
|
462
|
+
finding.classification = differs
|
|
463
|
+
? 'conflicting'
|
|
464
|
+
: absent
|
|
465
|
+
? 'missing'
|
|
466
|
+
: 'not-observed';
|
|
467
|
+
finding.reasons.push(reason);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function invalidateChangedActivationAuthority(findings, controls) {
|
|
471
|
+
const controlsById = new Map(controls.map((control) => [
|
|
472
|
+
control.id,
|
|
473
|
+
control
|
|
474
|
+
]));
|
|
475
|
+
for (const finding of findings) {
|
|
476
|
+
const control = controlsById.get(finding.controlId);
|
|
477
|
+
const evidenceDerivedApplicability = control?.applicability === 'private-dast' ||
|
|
478
|
+
control?.applicability === 'state-path';
|
|
479
|
+
if (finding.applicability === 'inapplicable' &&
|
|
480
|
+
!evidenceDerivedApplicability ||
|
|
481
|
+
!(evidenceDerivedApplicability ||
|
|
482
|
+
finding.requiredProof.includes('evidence') ||
|
|
483
|
+
finding.controlId.startsWith('azure.') ||
|
|
484
|
+
finding.controlId.startsWith('runner.') ||
|
|
485
|
+
finding.controlId.startsWith('evidence.'))) {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (evidenceDerivedApplicability) {
|
|
489
|
+
finding.applicability = 'unknown';
|
|
490
|
+
}
|
|
491
|
+
finding.classification = 'not-observed';
|
|
492
|
+
finding.exception = null;
|
|
493
|
+
finding.missingProof = [...finding.requiredProof];
|
|
494
|
+
finding.reasons.push('Current activation inputs, evidence references, evidence bodies, or reviewed plans changed during collection; evidence-backed scope and results were withheld.');
|
|
495
|
+
for (const layer of finding.requiredProof) {
|
|
496
|
+
finding.observations[layer] = notObserved('Activation authority changed during collection.');
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
async function managedFindings(control, project, files, scope, capturedAt) {
|
|
501
|
+
const findings = [];
|
|
502
|
+
if (project.renderedCore.length === 0) {
|
|
503
|
+
return [classifyFinding({
|
|
504
|
+
control, scope, applicability: 'applicable',
|
|
505
|
+
observations: {
|
|
506
|
+
recorded: notObserved(project.kind === 'git'
|
|
507
|
+
? 'Ordinary Git repositories have no recorded Liftoff ownership inventory.'
|
|
508
|
+
: 'Historical agent selection is not recorded.'),
|
|
509
|
+
declared: notObserved(project.kind === 'git'
|
|
510
|
+
? 'No Liftoff managed handoff is inferred for an ordinary Git repository.'
|
|
511
|
+
: 'The current handoff cannot be rendered without inferring agent configuration.')
|
|
512
|
+
}
|
|
513
|
+
})];
|
|
514
|
+
}
|
|
515
|
+
for (const artifact of project.renderedCore) {
|
|
516
|
+
const text = await files.read(artifact.pathParts);
|
|
517
|
+
const expected = `sha256:${sha256Hex(artifact.content)}`;
|
|
518
|
+
const actual = text === null ? null : `sha256:${sha256Hex(text)}`;
|
|
519
|
+
const entry = project.managedEntries.find((record) => record.logicalName === artifact.logicalName);
|
|
520
|
+
const recorded = entry ? observed(entry.contentHash, source('file', 'liftoff.manifest.json', capturedAt, entry.contentHash))
|
|
521
|
+
: notObserved(project.manifest ? 'Artifact is not in the recorded ownership inventory.' : 'Unsupported manifest identity; managed ownership is not being inferred.');
|
|
522
|
+
const declared = actual === null
|
|
523
|
+
? absent('The exact target managed path is absent.', source('file', artifact.pathParts.join('/'), capturedAt))
|
|
524
|
+
: observed(actual, source('file', artifact.pathParts.join('/'), capturedAt, actual));
|
|
525
|
+
const difference = actual !== null && actual !== expected && entry?.contentHash === actual ? 'outdated'
|
|
526
|
+
: actual !== null && actual !== expected ? 'conflicting'
|
|
527
|
+
: actual === expected && entry && entry.contentHash !== expected ? 'outdated' : undefined;
|
|
528
|
+
findings.push(classifyFinding({
|
|
529
|
+
control, scope: { ...scope, resource: artifact.pathParts.join('/') }, expected,
|
|
530
|
+
applicability: 'applicable', observations: { recorded, declared }, difference
|
|
531
|
+
}));
|
|
532
|
+
}
|
|
533
|
+
return findings;
|
|
534
|
+
}
|
|
535
|
+
function evaluateControl(control, project, facts, live, git, liveScope, now) {
|
|
536
|
+
const capturedAt = now.toISOString();
|
|
537
|
+
const scope = { repository: liveScope.repository ? `${liveScope.repository.owner}/${liveScope.repository.name}` : null, environment: null, resource: null };
|
|
538
|
+
const observations = {};
|
|
539
|
+
let expected = control.expected;
|
|
540
|
+
let exceptionResource;
|
|
541
|
+
let difference;
|
|
542
|
+
const declaredRules = (predicate) => facts.rulesets.length
|
|
543
|
+
? predicateObservation(predicate(facts.rulesets), facts.ruleSource) : absent('No declared ruleset payloads were found.', facts.ruleSource);
|
|
544
|
+
switch (control.evaluator) {
|
|
545
|
+
case 'identity': {
|
|
546
|
+
expected = jsonValue({ manifest: currentActivationIdentity, state: project.identity.stateSource === 'not-started' ? null : currentActivationIdentity });
|
|
547
|
+
observations.recorded = project.identity.recordedActivationIdentity === null
|
|
548
|
+
? notObserved('This historical manifest does not record an activation tuple.')
|
|
549
|
+
: observed({ manifest: project.identity.recordedActivationIdentity, state: project.stateIdentity }, source('file', 'manifest and activation identity', capturedAt, { manifest: project.identity.recordedActivationIdentity, state: project.stateIdentity }));
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
case 'default-branch': {
|
|
553
|
+
const remote = live.observations['github.repository'];
|
|
554
|
+
observations.live = remote?.availability === 'observed' && isRecord(remote.value) && typeof remote.value.defaultBranch === 'string' && remote.source
|
|
555
|
+
? observed(remote.value.defaultBranch, remote.source) : remote ?? notObserved('Live repository default branch was not observed.');
|
|
556
|
+
if (project.state)
|
|
557
|
+
observations.recorded = observed(project.state.repository.defaultBranch, source('file', 'governance/activation-state.json', capturedAt, project.state.repository.defaultBranch));
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
case 'protected-refs': {
|
|
561
|
+
const repository = live.observations['github.repository'];
|
|
562
|
+
const branch = repository?.availability === 'observed' && isRecord(repository.value) && typeof repository.value.defaultBranch === 'string'
|
|
563
|
+
? repository.value.defaultBranch : null;
|
|
564
|
+
observations.declared = declaredRules((value) => protectedRefs(value, project.state?.repository.defaultBranch ?? null));
|
|
565
|
+
const rules = live.observations['github.rulesets'];
|
|
566
|
+
const branches = live.observations['github.branches'];
|
|
567
|
+
const families = live.observations['github.ref-families'];
|
|
568
|
+
if (rules?.availability === 'observed' &&
|
|
569
|
+
branches?.availability === 'observed' &&
|
|
570
|
+
families?.availability === 'observed') {
|
|
571
|
+
observations.live = predicateObservation(effectiveProtectedRefs(rules.value, branches.value, families.value, branch), rules.source);
|
|
572
|
+
observations.live.facts = jsonValue({
|
|
573
|
+
rulesets: rules.value,
|
|
574
|
+
branches: branches.value,
|
|
575
|
+
refFamilies: families.value
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
observations.live = notObserved('Complete repository/inherited rulesets, classic/effective branch protection, and bounded release/hotfix enumeration are required.');
|
|
580
|
+
}
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
case 'single-maintainer':
|
|
584
|
+
observations.declared = declaredRules(singleMaintainer);
|
|
585
|
+
observations.live = livePredicate(live, 'github.rulesets', singleMaintainer);
|
|
586
|
+
break;
|
|
587
|
+
case 'tag-controls': {
|
|
588
|
+
const app = live.observations['github.actions-app'];
|
|
589
|
+
if (app?.availability === 'observed' && isRecord(app.value) && typeof app.value.id === 'number' &&
|
|
590
|
+
app.value.id > 0 && app.value.slug === 'github-actions') {
|
|
591
|
+
const appId = app.value.id;
|
|
592
|
+
observations.declared = declaredRules((value) => tagControls(value, appId));
|
|
593
|
+
observations.live = livePredicate(live, 'github.rulesets', (value) => tagControls(value, appId));
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
observations.declared = facts.rulesets.length
|
|
597
|
+
? notObserved('The declared tag actor cannot be verified without the GitHub Actions application identity.', facts.ruleSource)
|
|
598
|
+
: absent('No declared tag rulesets were found.', facts.ruleSource);
|
|
599
|
+
observations.live = notObserved('The GitHub Actions bypass application identity was not observed.');
|
|
600
|
+
}
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
case 'no-codeowners':
|
|
604
|
+
observations.declared = facts.codeowners;
|
|
605
|
+
break;
|
|
606
|
+
case 'required-contexts': {
|
|
607
|
+
const contexts = requiredCheckContexts(facts.rulesets);
|
|
608
|
+
observations.declared = contexts === null ? notObserved('Required context declarations cannot be interpreted.')
|
|
609
|
+
: contexts.length ? predicateObservation(bool(true, `Declared contexts: ${contexts.map((entry) => entry.name).join(', ')}`), facts.ruleSource)
|
|
610
|
+
: absent('No required status check contexts were declared.', facts.ruleSource);
|
|
611
|
+
const rules = live.observations['github.rulesets'];
|
|
612
|
+
const branches = live.observations['github.branches'];
|
|
613
|
+
const families = live.observations['github.ref-families'];
|
|
614
|
+
const repository = live.observations['github.repository'];
|
|
615
|
+
const branch = repository?.availability === 'observed' && isRecord(repository.value) && typeof repository.value.defaultBranch === 'string'
|
|
616
|
+
? repository.value.defaultBranch : null;
|
|
617
|
+
const familyRefs = families?.availability === 'observed' && isRecord(families.value) &&
|
|
618
|
+
Array.isArray(families.value.refs)
|
|
619
|
+
? families.value.refs.filter((value) => typeof value === 'string')
|
|
620
|
+
: [];
|
|
621
|
+
const assessedRefs = [...new Set([...liveScope.refs, ...familyRefs])].sort();
|
|
622
|
+
const comparisonRefs = assessedRefs.length ? assessedRefs : ['develop', 'main'];
|
|
623
|
+
const declaredBindings = requiredContextBindings(facts.rulesets, comparisonRefs, branch);
|
|
624
|
+
const rulesetBindings = rules?.availability === 'observed'
|
|
625
|
+
? requiredContextBindings(rules.value, comparisonRefs, branch)
|
|
626
|
+
: null;
|
|
627
|
+
const enforcedBindings = rules?.availability === 'observed' && branches?.availability === 'observed'
|
|
628
|
+
? effectiveRequiredContextBindings(rules.value, branches.value, comparisonRefs, branch)
|
|
629
|
+
: rulesetBindings;
|
|
630
|
+
observations.declared.facts = jsonValue(declaredBindings);
|
|
631
|
+
const bindingsDiffer = Boolean(contexts?.length && declaredBindings && rulesetBindings &&
|
|
632
|
+
canonicalJson(declaredBindings) !== canonicalJson(rulesetBindings));
|
|
633
|
+
if (bindingsDiffer) {
|
|
634
|
+
difference = 'conflicting';
|
|
635
|
+
const checks = live.observations['github.checks'];
|
|
636
|
+
const reason = 'Declared and enforced per-ref required context/application bindings differ.';
|
|
637
|
+
observations.live = checks?.availability === 'observed'
|
|
638
|
+
? predicateObservation(bool(false, reason), rules?.source ?? null)
|
|
639
|
+
: notObserved(`${reason} Check-run proof remains unobserved: ${checks?.reason ?? 'not collected'}`, rules?.source ?? null);
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
observations.live = rules?.availability === 'observed' &&
|
|
643
|
+
families?.availability === 'observed' &&
|
|
644
|
+
declaredBindings &&
|
|
645
|
+
enforcedBindings
|
|
646
|
+
? livePredicate(live, 'github.checks', (value) => observedRequiredContexts(rules.value, value, branch, branches?.value))
|
|
647
|
+
: notObserved('Live rule and exact commit-bound check observations are required.');
|
|
648
|
+
}
|
|
649
|
+
observations.live.facts = jsonValue({
|
|
650
|
+
enforcedBindings, checkRuns: live.observations['github.checks']?.value ?? null
|
|
651
|
+
});
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
case 'push-protection':
|
|
655
|
+
observations.live = livePredicate(live, 'github.security', (value) => {
|
|
656
|
+
if (!isRecord(value))
|
|
657
|
+
return bool(null, 'Security feature metadata is unavailable.');
|
|
658
|
+
const scanning = isRecord(value.secret_scanning) ? value.secret_scanning.status : value.secretScanning;
|
|
659
|
+
const protection = isRecord(value.secret_scanning_push_protection) ? value.secret_scanning_push_protection.status : value.pushProtection;
|
|
660
|
+
return bool(typeof scanning === 'string' && typeof protection === 'string' ? scanning === 'enabled' && protection === 'enabled' : null, 'Secret scanning and push protection must both be enabled; omitted fields are not evidence of absence.');
|
|
661
|
+
});
|
|
662
|
+
break;
|
|
663
|
+
case 'workflow-permissions':
|
|
664
|
+
observations.declared = facts.workflows.length ? predicateObservation(workflowPermissions(facts.workflows), facts.workflowSource)
|
|
665
|
+
: absent('No workflow files were found.', facts.workflowSource);
|
|
666
|
+
break;
|
|
667
|
+
case 'action-pinning': {
|
|
668
|
+
const check = pinnedActions(facts.workflows);
|
|
669
|
+
observations.declared = predicateObservation(check, facts.workflowSource);
|
|
670
|
+
exceptionResource = check.exceptionResource;
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
case 'fail-open-flags':
|
|
674
|
+
observations.declared = predicateObservation(failOpenFlags(facts.workflows, facts.rulesets), facts.workflowSource);
|
|
675
|
+
break;
|
|
676
|
+
case 'pipeline': {
|
|
677
|
+
observations.declared = predicateObservation(securityPipeline(facts.workflows), facts.workflowSource);
|
|
678
|
+
if (observations.declared.availability === 'not-observed' && facts.workflowSource) {
|
|
679
|
+
const inventory = observed(jsonValue({ actionReferences: actionReferences(facts.workflows) }), facts.workflowSource);
|
|
680
|
+
if (inventory.availability === 'observed')
|
|
681
|
+
observations.declared.value = inventory.value;
|
|
682
|
+
else
|
|
683
|
+
observations.declared = inventory;
|
|
684
|
+
}
|
|
685
|
+
observations.live = notObserved('Complete stage-role enforcement requires execution evidence, not a generic green workflow.');
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
688
|
+
case 'environments':
|
|
689
|
+
expected = true;
|
|
690
|
+
observations.live = livePredicate(live, 'github.environments', (value) => {
|
|
691
|
+
const environments = rows(value);
|
|
692
|
+
if (!environments)
|
|
693
|
+
return bool(null, 'Environment inventory is unavailable.');
|
|
694
|
+
const selected = liveScope.environments.map((name) => environments.find((entry) => entry.name === name));
|
|
695
|
+
if (selected.some((entry) => !entry))
|
|
696
|
+
return {
|
|
697
|
+
...bool(false, 'A declared deployment environment is absent from the complete live inventory.'), absent: true
|
|
698
|
+
};
|
|
699
|
+
return bool(selected.every((entry) => typeof entry?.reviewers === 'number') ? selected.every((entry) => entry?.reviewers === 0) : null, 'Declared deployment environments must not require human reviewers.');
|
|
700
|
+
});
|
|
701
|
+
break;
|
|
702
|
+
case 'runner': {
|
|
703
|
+
observations.recorded = liveScope.runner ? observed(true, source('evidence', 'runner-ready repository binding', capturedAt, jsonValue(liveScope.runner))) : notObserved('No current, exact runner/group/network binding is available.');
|
|
704
|
+
observations.live = livePredicate(live, 'github.runner', (value) => runnerAlignment(value, scope.repository));
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
case 'providers': {
|
|
708
|
+
const evaluation = partialLivePredicate(live, 'azure.providers', (value) => {
|
|
709
|
+
const providers = rows(value);
|
|
710
|
+
return bool(!providers?.length ? null : providers.every((entry) => entry.registrationState === 'Registered'), 'Every explicitly required namespace must have terminal Registered readback.');
|
|
711
|
+
});
|
|
712
|
+
observations.live = evaluation.observation;
|
|
713
|
+
if (evaluation.provenDifference)
|
|
714
|
+
difference = 'conflicting';
|
|
715
|
+
expected = true;
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
case 'storage': {
|
|
719
|
+
const evaluation = partialLivePredicate(live, 'azure.resources', (value) => {
|
|
720
|
+
const storage = rows(value)?.filter((entry) => String(entry.resourceType).toLowerCase() === 'microsoft.storage/storageaccounts');
|
|
721
|
+
if (!storage?.length)
|
|
722
|
+
return bool(null, 'No explicit environment/role-bound storage observation is available.');
|
|
723
|
+
const values = storage.map((entry) => {
|
|
724
|
+
const sku = isRecord(entry.sku) ? entry.sku.name : entry.sku;
|
|
725
|
+
if (typeof sku !== 'string' || !['state', 'application'].includes(String(entry.role)) || !['dev', 'staging', 'prod'].includes(String(entry.environment)))
|
|
726
|
+
return null;
|
|
727
|
+
return sku.endsWith(entry.role === 'state' || entry.environment !== 'dev' ? '_ZRS' : '_LRS');
|
|
728
|
+
});
|
|
729
|
+
return bool(values.includes(false) ? false : values.includes(null) ? null : true, 'State storage requires ZRS; application storage uses Dev LRS and Staging/Production ZRS.');
|
|
730
|
+
});
|
|
731
|
+
observations.live = evaluation.observation;
|
|
732
|
+
if (evaluation.provenDifference)
|
|
733
|
+
difference = 'conflicting';
|
|
734
|
+
expected = true;
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
case 'network': {
|
|
738
|
+
observations.recorded = liveScope.azure.some((entry) => entry.role === 'runner-network') ? observed(true, source('evidence', 'approved Staging network bindings', capturedAt, jsonValue(liveScope.azure))) : notObserved('No approved Staging network bindings were available.');
|
|
739
|
+
const evaluation = partialLivePredicate(live, 'azure.resources', (value) => {
|
|
740
|
+
const network = rows(value)?.filter((entry) => entry.role === 'runner-network');
|
|
741
|
+
if (!network?.length)
|
|
742
|
+
return bool(null, 'No bound runner-network readback is available.');
|
|
743
|
+
const subnets = network.filter((entry) => String(entry.resourceType).toLowerCase() === 'microsoft.network/virtualnetworks/subnets');
|
|
744
|
+
if (subnets.some((entry) => isRecord(entry.properties) && entry.properties.defaultOutboundAccess === true))
|
|
745
|
+
return bool(false, 'A runner subnet still enables implicit default outbound access.');
|
|
746
|
+
return bool(null, 'Full subnet, route, DNS and exclusive NAT/Firewall proof is required; observed metadata alone is insufficient.');
|
|
747
|
+
});
|
|
748
|
+
observations.live = evaluation.observation;
|
|
749
|
+
if (evaluation.provenDifference)
|
|
750
|
+
difference = 'conflicting';
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
case 'evidence':
|
|
754
|
+
observations.evidence = evidenceObservation(project, git, capturedAt);
|
|
755
|
+
break;
|
|
756
|
+
case 'documentation':
|
|
757
|
+
observations.declared = facts.documentation;
|
|
758
|
+
break;
|
|
759
|
+
default:
|
|
760
|
+
for (const layer of control.proofLayers)
|
|
761
|
+
observations[layer] = notObserved(`No supported evaluator exists for ${control.id}; the required proof is not inferred.`);
|
|
762
|
+
}
|
|
763
|
+
const exception = findAssessmentException(control, project.approvals, scope, facts.baseline, exceptionResource, now);
|
|
764
|
+
const exceptionDiagnostic = rejectedAssessmentExceptionDiagnostic(control, project.approvals, exception);
|
|
765
|
+
if (exceptionDiagnostic &&
|
|
766
|
+
!project.diagnostics.some((entry) => entry.code === 'rejected-exception' && entry.source === control.id)) {
|
|
767
|
+
project.diagnostics.push(exceptionDiagnostic);
|
|
768
|
+
}
|
|
769
|
+
return classifyFinding({
|
|
770
|
+
control, scope, expected, applicability: applicability(control, project, now), observations, difference,
|
|
771
|
+
exception
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
export async function assessGovernance(projectRoot, options = {}) {
|
|
775
|
+
const now = options.now ?? (() => new Date());
|
|
776
|
+
const captured = now();
|
|
777
|
+
const files = new AssessmentFiles(projectRoot);
|
|
778
|
+
let target = null;
|
|
779
|
+
let projectIdentity = {
|
|
780
|
+
availability: 'unavailable', manifestVersion: null, cliVersion: null, profile: null,
|
|
781
|
+
policyVersion: null, recordedActivationIdentity: null, stateSource: 'unavailable'
|
|
782
|
+
};
|
|
783
|
+
try {
|
|
784
|
+
const loaded = loadAssessmentCatalog();
|
|
785
|
+
target = loaded.target;
|
|
786
|
+
let project;
|
|
787
|
+
let git;
|
|
788
|
+
let initialActivationInspection = null;
|
|
789
|
+
let initialActivationFingerprint = null;
|
|
790
|
+
try {
|
|
791
|
+
project = await inspectAssessmentProject(files);
|
|
792
|
+
git = await inspectAssessmentGit(projectRoot, options.runner);
|
|
793
|
+
}
|
|
794
|
+
catch (error) {
|
|
795
|
+
if (!(error instanceof AssessmentInputError) || error.code !== 'project-not-found') {
|
|
796
|
+
throw error;
|
|
797
|
+
}
|
|
798
|
+
git = await inspectAssessmentGit(projectRoot, options.runner);
|
|
799
|
+
if (!git.isRepository) {
|
|
800
|
+
throw new AssessmentInputError('project-not-found', 'No Liftoff manifest or valid Git worktree was found at the selected boundary.');
|
|
801
|
+
}
|
|
802
|
+
project = ordinaryGitAssessmentProject();
|
|
803
|
+
}
|
|
804
|
+
if (project.manifest) {
|
|
805
|
+
try {
|
|
806
|
+
const activation = await inspectCurrentActivationEvidence(projectRoot, project.manifest, {
|
|
807
|
+
...(options.runner ? { runner: options.runner } : {}),
|
|
808
|
+
now: captured
|
|
809
|
+
});
|
|
810
|
+
initialActivationInspection = activation;
|
|
811
|
+
initialActivationFingerprint = activationAuthorityFingerprint(activation);
|
|
812
|
+
if (activation.status === 'inspected') {
|
|
813
|
+
project.state = activation.state;
|
|
814
|
+
project.stateIdentity = jsonValue(activation.state.identity);
|
|
815
|
+
project.identity.stateSource = 'user';
|
|
816
|
+
project.inputSnapshot = activation.snapshot;
|
|
817
|
+
project.evidenceContexts = activation.contexts;
|
|
818
|
+
project.evidence = activation.records;
|
|
819
|
+
project.plans = activation.contexts[phaseIds[0]].reviewedPlans
|
|
820
|
+
? [...activation.contexts[phaseIds[0]].reviewedPlans]
|
|
821
|
+
: [];
|
|
822
|
+
project.activationSelections = activation.selections;
|
|
823
|
+
for (const [phaseId, selection] of Object.entries(activation.selections)) {
|
|
824
|
+
if (selection.selected && selection.historicalIssues?.length) {
|
|
825
|
+
project.diagnostics.push({
|
|
826
|
+
code: 'historical-evidence-ignored',
|
|
827
|
+
severity: 'info',
|
|
828
|
+
source: phaseId,
|
|
829
|
+
message: sanitizeAssessmentText(selection.historicalIssues.map((issue) => issue.message).join('; '))
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
if (selection.issues.length === 0)
|
|
833
|
+
continue;
|
|
834
|
+
project.invalidEvidence = true;
|
|
835
|
+
project.diagnostics.push({
|
|
836
|
+
code: 'invalid-current-evidence',
|
|
837
|
+
severity: 'warning',
|
|
838
|
+
source: phaseId,
|
|
839
|
+
message: sanitizeAssessmentText(selection.issues.map((issue) => issue.message).join('; '))
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
catch (error) {
|
|
845
|
+
project.identity.availability = 'unsupported';
|
|
846
|
+
project.identity.stateSource = 'unsupported';
|
|
847
|
+
project.diagnostics.push({
|
|
848
|
+
code: 'activation-history-diagnostic-only',
|
|
849
|
+
severity: 'warning',
|
|
850
|
+
source: 'activation read-only inspection',
|
|
851
|
+
message: sanitizeAssessmentText(error instanceof Error
|
|
852
|
+
? error.message
|
|
853
|
+
: 'Activation history could not be interpreted as current executable proof.')
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
projectIdentity = project.identity;
|
|
858
|
+
const disabled = project.identity.profile === 'none';
|
|
859
|
+
if (disabled)
|
|
860
|
+
return assembleAssessmentReport({
|
|
861
|
+
projectRoot, mode: options.live ? 'live' : 'local', target, projectIdentity,
|
|
862
|
+
snapshot: { capturedAt: captured.toISOString(), repository: null, localHead: null, worktreeDigest: files.digest(), inputsStable: await files.stable() },
|
|
863
|
+
findings: [], diagnostics: project.diagnostics, disabled: true
|
|
864
|
+
});
|
|
865
|
+
project.diagnostics.push(...git.issues.map((message) => ({
|
|
866
|
+
code: 'git-not-observed', severity: 'warning', message, source: 'Git metadata'
|
|
867
|
+
})));
|
|
868
|
+
const facts = await localFacts(files, project, captured.toISOString());
|
|
869
|
+
const scope = resolveAssessmentLiveScope(project, git, captured);
|
|
870
|
+
const repository = scope.repository ? `${scope.repository.owner}/${scope.repository.name}` : null;
|
|
871
|
+
const live = options.live ? await collectLiveAssessment(scope, { runner: options.runner, now })
|
|
872
|
+
: { observations: {}, diagnostics: [], refsStable: true };
|
|
873
|
+
const findings = [];
|
|
874
|
+
for (const control of loaded.catalog.controls) {
|
|
875
|
+
if (control.evaluator === 'managed-core') {
|
|
876
|
+
findings.push(...await managedFindings(control, project, files, { repository, environment: null, resource: null }, captured.toISOString()));
|
|
877
|
+
}
|
|
878
|
+
else
|
|
879
|
+
findings.push(evaluateControl(control, project, facts, live, git, scope, captured));
|
|
880
|
+
}
|
|
881
|
+
const worktreeDigest = files.digest();
|
|
882
|
+
let activationStable = true;
|
|
883
|
+
if (project.manifest && initialActivationInspection && initialActivationFingerprint) {
|
|
884
|
+
try {
|
|
885
|
+
const finalActivation = await inspectCurrentActivationEvidence(projectRoot, project.manifest, {
|
|
886
|
+
...(options.runner ? { runner: options.runner } : {}),
|
|
887
|
+
now: captured
|
|
888
|
+
});
|
|
889
|
+
activationStable =
|
|
890
|
+
activationAuthorityFingerprint(finalActivation) ===
|
|
891
|
+
initialActivationFingerprint;
|
|
892
|
+
}
|
|
893
|
+
catch {
|
|
894
|
+
activationStable = false;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
const finalGit = await inspectAssessmentGit(projectRoot, options.runner);
|
|
898
|
+
const filesStable = await files.stable();
|
|
899
|
+
const gitStable = canonicalJson(git) === canonicalJson(finalGit);
|
|
900
|
+
const inputsStable = filesStable && gitStable && activationStable;
|
|
901
|
+
if (!filesStable) {
|
|
902
|
+
invalidateUnstableObservations(findings, new Set(['file', 'evidence']), 'Relevant local files changed during collection; rerun assessment for current local proof.');
|
|
903
|
+
}
|
|
904
|
+
if (!gitStable) {
|
|
905
|
+
invalidateUnstableObservations(findings, new Set(['git']), 'Relevant Git metadata changed during collection; rerun assessment for current Git proof.');
|
|
906
|
+
}
|
|
907
|
+
if (!activationStable) {
|
|
908
|
+
invalidateChangedActivationAuthority(findings, loaded.catalog.controls);
|
|
909
|
+
project.diagnostics.push({
|
|
910
|
+
code: 'activation-authority-changed',
|
|
911
|
+
severity: 'warning',
|
|
912
|
+
source: 'activation read-only inspection',
|
|
913
|
+
message: 'Current activation inputs, evidence, state references, or reviewed plans changed during collection; dependent proof was withheld.'
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
return assembleAssessmentReport({
|
|
917
|
+
projectRoot, mode: options.live ? 'live' : 'local', target, projectIdentity,
|
|
918
|
+
snapshot: { capturedAt: captured.toISOString(), repository, localHead: git.head, worktreeDigest, inputsStable },
|
|
919
|
+
findings, diagnostics: [...project.diagnostics, ...live.diagnostics]
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
catch (error) {
|
|
923
|
+
const diagnostic = {
|
|
924
|
+
code: error instanceof AssessmentInputError ? error.code : 'assessment-error', severity: 'error',
|
|
925
|
+
message: sanitizeAssessmentText(error instanceof Error ? error.message : 'Assessment could not inspect its inputs.'),
|
|
926
|
+
source: error instanceof AssessmentInputError && error.source ? sanitizeAssessmentText(error.source) : null
|
|
927
|
+
};
|
|
928
|
+
return assembleAssessmentReport({
|
|
929
|
+
projectRoot, mode: options.live ? 'live' : 'local', target, projectIdentity,
|
|
930
|
+
snapshot: { capturedAt: captured.toISOString(), repository: null, localHead: null, worktreeDigest: files.digest(), inputsStable: false },
|
|
931
|
+
findings: [], diagnostics: [diagnostic], failed: true
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
//# sourceMappingURL=engine.js.map
|