@intentius/chant 0.79.0 → 0.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +30 -0
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts +5 -0
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +13 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +13 -0
- package/dist/audit/terraform-state.d.ts.map +1 -1
- package/dist/cli/command-group.d.ts +6 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +44 -3
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +8 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/import-agents.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts +6 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/vendor.d.ts +5 -0
- package/dist/cli/commands/vendor.d.ts.map +1 -1
- package/dist/cli/conflict-check.d.ts +6 -0
- package/dist/cli/conflict-check.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/init.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +7 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/promote.d.ts +20 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -0
- package/dist/cli/handlers/run-generate.d.ts +39 -0
- package/dist/cli/handlers/run-generate.d.ts.map +1 -0
- package/dist/cli/handlers/run.d.ts +4 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/vendor.d.ts +8 -3
- package/dist/cli/handlers/vendor.d.ts.map +1 -1
- package/dist/cli/main.d.ts +12 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +14 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +46 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts +10 -0
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +3 -1
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/components/driver.d.ts +16 -2
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/gate-plan.d.ts +40 -0
- package/dist/components/gate-plan.d.ts.map +1 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts +7 -1
- package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -1
- package/dist/components/presets/ecs-fargate.d.ts +10 -4
- package/dist/components/presets/ecs-fargate.d.ts.map +1 -1
- package/dist/components/promote.d.ts +264 -0
- package/dist/components/promote.d.ts.map +1 -0
- package/dist/components/sandbox/driver.d.ts.map +1 -1
- package/dist/components/sandbox/run.d.ts.map +1 -1
- package/dist/components/verbs/build-archive.d.ts +22 -11
- package/dist/components/verbs/build-archive.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/config-static.d.ts +59 -0
- package/dist/config-static.d.ts.map +1 -0
- package/dist/config.d.ts +104 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/content-digest.d.ts +17 -0
- package/dist/content-digest.d.ts.map +1 -0
- package/dist/discovery/files.d.ts +48 -4
- package/dist/discovery/files.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +32 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/sandbox/driver.d.ts +20 -0
- package/dist/discovery/sandbox/driver.d.ts.map +1 -1
- package/dist/discovery/sandbox/fork.d.ts +20 -0
- package/dist/discovery/sandbox/fork.d.ts.map +1 -1
- package/dist/discovery/walk.d.ts +119 -0
- package/dist/discovery/walk.d.ts.map +1 -0
- package/dist/graph-ir.d.ts +17 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/join-key.d.ts +31 -0
- package/dist/join-key.d.ts.map +1 -0
- package/dist/lexicon-module.d.ts +110 -0
- package/dist/lexicon-module.d.ts.map +1 -0
- package/dist/lexicon.d.ts +82 -1
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/build-ledger-store.d.ts.map +1 -1
- package/dist/lifecycle/digest.d.ts +5 -1
- package/dist/lifecycle/digest.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +20 -5
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +64 -0
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/lease.d.ts +8 -1
- package/dist/lifecycle/lease.d.ts.map +1 -1
- package/dist/lifecycle/legacy-digest.d.ts +59 -0
- package/dist/lifecycle/legacy-digest.d.ts.map +1 -0
- package/dist/lifecycle/member-ledger.d.ts +67 -0
- package/dist/lifecycle/member-ledger.d.ts.map +1 -0
- package/dist/lifecycle/plan-digest.d.ts +4 -3
- package/dist/lifecycle/plan-digest.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +42 -1
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts +90 -5
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +2 -3
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +32 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/propose-upgrade.d.ts +75 -0
- package/dist/op/activities/propose-upgrade.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -1
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-name.d.ts +7 -0
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-summary.d.ts +16 -0
- package/dist/op/gate-summary.d.ts.map +1 -1
- package/dist/op/gate.d.ts +16 -2
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/project-root.d.ts +26 -0
- package/dist/project-root.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts +37 -0
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts +11 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -0
- package/dist/workspace/checks/generated.d.ts +156 -0
- package/dist/workspace/checks/generated.d.ts.map +1 -0
- package/dist/workspace/checks/ledgers.d.ts +78 -0
- package/dist/workspace/checks/ledgers.d.ts.map +1 -0
- package/dist/workspace/checks/links.d.ts +25 -0
- package/dist/workspace/checks/links.d.ts.map +1 -0
- package/dist/workspace/checks/pipelines.d.ts +76 -0
- package/dist/workspace/checks/pipelines.d.ts.map +1 -0
- package/dist/workspace/checks.d.ts +161 -0
- package/dist/workspace/checks.d.ts.map +1 -0
- package/dist/workspace/compose-graph.d.ts +115 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -0
- package/dist/workspace/compose-reports.d.ts +68 -0
- package/dist/workspace/compose-reports.d.ts.map +1 -0
- package/dist/workspace/declaration.d.ts +220 -0
- package/dist/workspace/declaration.d.ts.map +1 -0
- package/dist/workspace/declaration.schema.json +434 -0
- package/dist/workspace/generated-files.d.ts +65 -0
- package/dist/workspace/generated-files.d.ts.map +1 -0
- package/dist/workspace/glob.d.ts +13 -0
- package/dist/workspace/glob.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts +75 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -0
- package/dist/workspace/init.d.ts +96 -0
- package/dist/workspace/init.d.ts.map +1 -0
- package/dist/workspace/jsonc.d.ts +38 -0
- package/dist/workspace/jsonc.d.ts.map +1 -0
- package/dist/workspace/kinds.d.ts +192 -0
- package/dist/workspace/kinds.d.ts.map +1 -0
- package/dist/workspace/lineage-check.d.ts +84 -0
- package/dist/workspace/lineage-check.d.ts.map +1 -0
- package/dist/workspace/lineage-cli.d.ts +31 -0
- package/dist/workspace/lineage-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-init.d.ts +101 -0
- package/dist/workspace/lineage-init.d.ts.map +1 -0
- package/dist/workspace/lineage-lock.d.ts +258 -0
- package/dist/workspace/lineage-lock.d.ts.map +1 -0
- package/dist/workspace/lineage-merge.d.ts +14 -0
- package/dist/workspace/lineage-merge.d.ts.map +1 -0
- package/dist/workspace/lineage-migrations.d.ts +178 -0
- package/dist/workspace/lineage-migrations.d.ts.map +1 -0
- package/dist/workspace/lineage-update.d.ts +59 -0
- package/dist/workspace/lineage-update.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts +40 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade.d.ts +146 -0
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -0
- package/dist/workspace/lineage-vendor.d.ts +63 -0
- package/dist/workspace/lineage-vendor.d.ts.map +1 -0
- package/dist/workspace/lineage-version.d.ts +32 -0
- package/dist/workspace/lineage-version.d.ts.map +1 -0
- package/dist/workspace/links.d.ts +150 -0
- package/dist/workspace/links.d.ts.map +1 -0
- package/dist/workspace/ls.d.ts +99 -0
- package/dist/workspace/ls.d.ts.map +1 -0
- package/dist/workspace/member-commands.d.ts +139 -0
- package/dist/workspace/member-commands.d.ts.map +1 -0
- package/dist/workspace/member-handles.d.ts +54 -0
- package/dist/workspace/member-handles.d.ts.map +1 -0
- package/dist/workspace/member-pipeline.d.ts +143 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -0
- package/dist/workspace/member-run.d.ts +67 -0
- package/dist/workspace/member-run.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +65 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -0
- package/dist/workspace/record-source.d.ts +22 -0
- package/dist/workspace/record-source.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +81 -0
- package/dist/workspace/records-cli.d.ts.map +1 -0
- package/dist/workspace/records.d.ts +118 -0
- package/dist/workspace/records.d.ts.map +1 -0
- package/dist/workspace/root-refusal.d.ts +53 -0
- package/dist/workspace/root-refusal.d.ts.map +1 -0
- package/dist/workspace/status.d.ts +150 -0
- package/dist/workspace/status.d.ts.map +1 -0
- package/dist/workspace/template-manifest.d.ts +90 -0
- package/dist/workspace/template-manifest.d.ts.map +1 -0
- package/dist/workspace/tree.d.ts +36 -0
- package/dist/workspace/tree.d.ts.map +1 -0
- package/dist/workspace/trust/attestor.d.ts +67 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -0
- package/dist/workspace/trust/policy.d.ts +111 -0
- package/dist/workspace/trust/policy.d.ts.map +1 -0
- package/dist/workspace/trust/provenance.d.ts +67 -0
- package/dist/workspace/trust/provenance.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +43 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -0
- package/dist/workspace/trust/test-repo.d.ts +42 -0
- package/dist/workspace/trust/test-repo.d.ts.map +1 -0
- package/dist/workspace/trust/verify-cli.d.ts +9 -0
- package/dist/workspace/trust/verify-cli.d.ts.map +1 -0
- package/dist/workspace/trust/verify.d.ts +66 -0
- package/dist/workspace/trust/verify.d.ts.map +1 -0
- package/dist/workspace/which-chant.d.ts +58 -0
- package/dist/workspace/which-chant.d.ts.map +1 -0
- package/dist/workspace/workspace-kinds.schema.json +133 -0
- package/package.json +4 -3
- package/src/audit/core.ts +13 -2
- package/src/audit/discover.ts +102 -36
- package/src/audit/report-html.ts +3 -1
- package/src/audit/report-model.ts +14 -1
- package/src/audit/terraform-state.test.ts +81 -1
- package/src/audit/terraform-state.ts +25 -0
- package/src/cli/command-group.ts +7 -0
- package/src/cli/commands/audit-truncation.test.ts +167 -0
- package/src/cli/commands/audit.ts +132 -29
- package/src/cli/commands/check-lexicon-examples.ts +2 -1
- package/src/cli/commands/check-lexicon.ts +24 -0
- package/src/cli/commands/doctor.test.ts +30 -0
- package/src/cli/commands/doctor.ts +21 -0
- package/src/cli/commands/import-agents.ts +15 -4
- package/src/cli/commands/init.ts +76 -25
- package/src/cli/commands/lint.test.ts +52 -1
- package/src/cli/commands/lint.ts +41 -71
- package/src/cli/commands/onboard.ts +15 -0
- package/src/cli/commands/update.ts +18 -5
- package/src/cli/commands/vendor.ts +11 -6
- package/src/cli/conflict-check.test.ts +18 -1
- package/src/cli/conflict-check.ts +13 -0
- package/src/cli/discovery-skip.test.ts +168 -0
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +38 -0
- package/src/cli/handlers/build-member.test.ts +127 -0
- package/src/cli/handlers/build.test.ts +11 -0
- package/src/cli/handlers/build.ts +35 -1
- package/src/cli/handlers/components.test.ts +127 -0
- package/src/cli/handlers/components.ts +70 -8
- package/src/cli/handlers/dev.ts +6 -1
- package/src/cli/handlers/fan-out.ts +9 -2
- package/src/cli/handlers/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +4 -2
- package/src/cli/handlers/init.ts +54 -1
- package/src/cli/handlers/lifecycle.ts +2 -1
- package/src/cli/handlers/misc.ts +16 -0
- package/src/cli/handlers/operator.test.ts +68 -0
- package/src/cli/handlers/operator.ts +39 -10
- package/src/cli/handlers/promote.test.ts +377 -0
- package/src/cli/handlers/promote.ts +391 -0
- package/src/cli/handlers/run-generate-member.test.ts +83 -0
- package/src/cli/handlers/run-generate.test.ts +158 -0
- package/src/cli/handlers/run-generate.ts +168 -0
- package/src/cli/handlers/run.test.ts +47 -0
- package/src/cli/handlers/run.ts +41 -6
- package/src/cli/handlers/vendor.ts +147 -43
- package/src/cli/main.test.ts +137 -1
- package/src/cli/main.ts +290 -24
- package/src/cli/mcp/op-tools.ts +2 -1
- package/src/cli/path-lexicon-messages.test.ts +152 -0
- package/src/cli/plugins.ts +43 -5
- package/src/cli/registry.ts +46 -1
- package/src/cli/static-config-read.test.ts +208 -0
- package/src/codegen/fetch.test.ts +63 -1
- package/src/codegen/fetch.ts +30 -1
- package/src/components/__fixtures__/alb-ecs-service.json +0 -8
- package/src/components/capability-plugin-loader.ts +3 -1
- package/src/components/cli-support.ts +9 -3
- package/src/components/discover.test.ts +27 -57
- package/src/components/discover.ts +20 -51
- package/src/components/driver.test.ts +90 -12
- package/src/components/driver.ts +43 -5
- package/src/components/fan-out-support.ts +2 -1
- package/src/components/gate-plan.ts +53 -0
- package/src/components/pilots/README.md +1 -1
- package/src/components/pilots/alb-ecs.pilot.ts +7 -7
- package/src/components/presets/ecs-fargate.ts +10 -5
- package/src/components/promote.test.ts +645 -0
- package/src/components/promote.ts +641 -0
- package/src/components/sandbox/driver.ts +8 -8
- package/src/components/sandbox/run.ts +4 -1
- package/src/components/verbs/build-archive.ts +27 -21
- package/src/components/verbs/run-agent.ts +3 -5
- package/src/config-static.test.ts +136 -0
- package/src/config-static.ts +232 -0
- package/src/config.test.ts +40 -0
- package/src/config.ts +161 -11
- package/src/content-digest.test.ts +120 -0
- package/src/content-digest.ts +21 -0
- package/src/discovery/files.test.ts +127 -1
- package/src/discovery/files.ts +91 -59
- package/src/discovery/fold-import.ts +127 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/sandbox/driver.ts +73 -35
- package/src/discovery/sandbox/fork-diagnostic.test.ts +136 -13
- package/src/discovery/sandbox/fork.ts +83 -43
- package/src/discovery/sandbox/path-lexicon-multifile.test.ts +285 -0
- package/src/discovery/sandbox/path-lexicon-parity.test.ts +162 -0
- package/src/discovery/walk.test.ts +263 -0
- package/src/discovery/walk.ts +334 -0
- package/src/effect-receipt.ts +2 -2
- package/src/graph-ir.ts +18 -1
- package/src/join-key.test.ts +25 -0
- package/src/join-key.ts +39 -0
- package/src/lexicon-module.test.ts +203 -0
- package/src/lexicon-module.ts +215 -0
- package/src/lexicon.ts +105 -1
- package/src/lifecycle/build-ledger-store.ts +13 -2
- package/src/lifecycle/digest.ts +9 -5
- package/src/lifecycle/gate-ledger.test.ts +20 -0
- package/src/lifecycle/gate-ledger.ts +28 -6
- package/src/lifecycle/git.test.ts +13 -1
- package/src/lifecycle/git.ts +221 -176
- package/src/lifecycle/lease.ts +25 -10
- package/src/lifecycle/legacy-digest.test.ts +206 -0
- package/src/lifecycle/legacy-digest.ts +103 -0
- package/src/lifecycle/member-ledger.test.ts +319 -0
- package/src/lifecycle/member-ledger.ts +145 -0
- package/src/lifecycle/plan-digest.ts +7 -7
- package/src/lifecycle/release-ledger.test.ts +20 -0
- package/src/lifecycle/release-ledger.ts +53 -3
- package/src/lifecycle/status.test.ts +151 -1
- package/src/lifecycle/status.ts +228 -7
- package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +2 -3
- package/src/op/activities/activity-contracts.ts +37 -0
- package/src/op/activities/index.ts +10 -0
- package/src/op/activities/propose-upgrade.ts +206 -0
- package/src/op/activity-contract-registry.ts +18 -4
- package/src/op/activity-registry.ts +26 -0
- package/src/op/discover.test.ts +14 -4
- package/src/op/discover.ts +19 -23
- package/src/op/gate-approval.ts +3 -1
- package/src/op/gate-name.ts +8 -0
- package/src/op/gate-summary.test.ts +14 -0
- package/src/op/gate-summary.ts +25 -2
- package/src/op/gate.ts +36 -7
- package/src/op/generate-pipeline.test.ts +14 -5
- package/src/op/generate-pipeline.ts +2 -1
- package/src/op/runtimes/local.ts +2 -1
- package/src/project-root.test.ts +66 -1
- package/src/project-root.ts +48 -0
- package/src/workspace/__fixtures__/contract-repo.ts +100 -0
- package/src/workspace/__fixtures__/two-member-workspace.ts +35 -0
- package/src/workspace/behold-kinds.test.ts +162 -0
- package/src/workspace/check-contract.test.ts +127 -0
- package/src/workspace/check.schema.json +347 -0
- package/src/workspace/checks/generated.test.ts +287 -0
- package/src/workspace/checks/generated.ts +475 -0
- package/src/workspace/checks/ledgers.test.ts +139 -0
- package/src/workspace/checks/ledgers.ts +246 -0
- package/src/workspace/checks/links.ts +183 -0
- package/src/workspace/checks/pipelines.test.ts +133 -0
- package/src/workspace/checks/pipelines.ts +225 -0
- package/src/workspace/checks-repo.test.ts +71 -0
- package/src/workspace/checks.test.ts +450 -0
- package/src/workspace/checks.ts +461 -0
- package/src/workspace/compose-graph.test.ts +146 -0
- package/src/workspace/compose-graph.ts +228 -0
- package/src/workspace/compose-reports.test.ts +109 -0
- package/src/workspace/compose-reports.ts +186 -0
- package/src/workspace/declaration.schema.json +434 -0
- package/src/workspace/declaration.test.ts +347 -0
- package/src/workspace/declaration.ts +623 -0
- package/src/workspace/generated-files.test.ts +114 -0
- package/src/workspace/generated-files.ts +94 -0
- package/src/workspace/glob.ts +49 -0
- package/src/workspace/graph-cli.ts +220 -0
- package/src/workspace/graph-contract.test.ts +204 -0
- package/src/workspace/graph.schema.json +662 -0
- package/src/workspace/init.test.ts +223 -0
- package/src/workspace/init.ts +585 -0
- package/src/workspace/jsonc.test.ts +65 -0
- package/src/workspace/jsonc.ts +231 -0
- package/src/workspace/kinds.test.ts +288 -0
- package/src/workspace/kinds.ts +506 -0
- package/src/workspace/lineage-check.ts +264 -0
- package/src/workspace/lineage-cli.ts +114 -0
- package/src/workspace/lineage-init.test.ts +231 -0
- package/src/workspace/lineage-init.ts +365 -0
- package/src/workspace/lineage-lock.test.ts +224 -0
- package/src/workspace/lineage-lock.ts +357 -0
- package/src/workspace/lineage-merge.ts +44 -0
- package/src/workspace/lineage-migrations.test.ts +178 -0
- package/src/workspace/lineage-migrations.ts +343 -0
- package/src/workspace/lineage-update.ts +160 -0
- package/src/workspace/lineage-upgrade-cli.ts +151 -0
- package/src/workspace/lineage-upgrade.test.ts +474 -0
- package/src/workspace/lineage-upgrade.ts +624 -0
- package/src/workspace/lineage-vendor.test.ts +126 -0
- package/src/workspace/lineage-vendor.ts +215 -0
- package/src/workspace/lineage-version.ts +127 -0
- package/src/workspace/links.test.ts +344 -0
- package/src/workspace/links.ts +331 -0
- package/src/workspace/ls-contract.test.ts +200 -0
- package/src/workspace/ls.schema.json +190 -0
- package/src/workspace/ls.ts +254 -0
- package/src/workspace/member-commands.e2e.test.ts +244 -0
- package/src/workspace/member-commands.test.ts +217 -0
- package/src/workspace/member-commands.ts +540 -0
- package/src/workspace/member-handles.test.ts +76 -0
- package/src/workspace/member-handles.ts +228 -0
- package/src/workspace/member-pipeline.test.ts +135 -0
- package/src/workspace/member-pipeline.ts +317 -0
- package/src/workspace/member-run.ts +146 -0
- package/src/workspace/read-contract.test.ts +129 -0
- package/src/workspace/reason-codes.test.ts +113 -0
- package/src/workspace/reason-codes.ts +81 -0
- package/src/workspace/record-source.ts +117 -0
- package/src/workspace/records-cli.ts +198 -0
- package/src/workspace/records-contract.test.ts +93 -0
- package/src/workspace/records.schema.json +150 -0
- package/src/workspace/records.test.ts +243 -0
- package/src/workspace/records.ts +384 -0
- package/src/workspace/root-refusal.ts +97 -0
- package/src/workspace/status-contract.test.ts +308 -0
- package/src/workspace/status.schema.json +210 -0
- package/src/workspace/status.ts +412 -0
- package/src/workspace/template-manifest.test.ts +79 -0
- package/src/workspace/template-manifest.ts +221 -0
- package/src/workspace/tree.ts +132 -0
- package/src/workspace/trust/attestor.ts +93 -0
- package/src/workspace/trust/policy.ts +306 -0
- package/src/workspace/trust/provenance.ts +181 -0
- package/src/workspace/trust/ssh-commit.ts +138 -0
- package/src/workspace/trust/test-repo.ts +138 -0
- package/src/workspace/trust/trust.test.ts +397 -0
- package/src/workspace/trust/verify-cli.ts +56 -0
- package/src/workspace/trust/verify.ts +194 -0
- package/src/workspace/which-chant.test.ts +117 -0
- package/src/workspace/which-chant.ts +151 -0
- package/src/workspace/workspace-kinds.schema.json +133 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lexicons declared by module path (chant #2520).
|
|
3
|
+
*
|
|
4
|
+
* A `lexicons` entry has always been a bare name, and chant resolves a name to
|
|
5
|
+
* the package `@intentius/chant-lexicon-<name>`. A project that writes its own
|
|
6
|
+
* lexicon in the same repo can instead point at the module:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* export default {
|
|
10
|
+
* lexicons: ["fly", { name: "site", module: "./lexicon/index.ts" }],
|
|
11
|
+
* } satisfies ChantConfig;
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The path is resolved against the directory holding `chant.config.ts`. That
|
|
15
|
+
* module is the whole lexicon: its `LexiconPlugin` export is the plugin, the
|
|
16
|
+
* plugin's `activities()` and `activityContracts()` members stand in for the
|
|
17
|
+
* `op/activities` and `op/activity-contracts` subpaths, a `CapabilityPlugin`
|
|
18
|
+
* export feeds the capability registry, and an `evaluateGatePolicy` export
|
|
19
|
+
* stands in for the `gate-policy` subpath.
|
|
20
|
+
*
|
|
21
|
+
* Loaders take lexicon names, so the config loader records each path-declared
|
|
22
|
+
* name here and every loader asks {@link lexiconModulePath} before falling back
|
|
23
|
+
* to the package name. A name declared as a plain string is never recorded, so
|
|
24
|
+
* a project that names lexicons by package takes exactly the path it always did.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* One `lexicons` entry: a package-backed name, or a name plus the module that
|
|
31
|
+
* implements it. `root` (chant#2590) names the directory the lexicon's files
|
|
32
|
+
* live in, for fold's trust; see {@link pathLexiconRoot}.
|
|
33
|
+
*/
|
|
34
|
+
export type LexiconDeclaration = string | { name: string; module: string; root?: string };
|
|
35
|
+
|
|
36
|
+
/** The lexicon name an entry declares, whichever form it takes. */
|
|
37
|
+
export function lexiconDeclarationName(entry: LexiconDeclaration): string {
|
|
38
|
+
return typeof entry === "string" ? entry : entry.name;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Every entry's name, in declaration order. `undefined` in, `undefined` out. */
|
|
42
|
+
export function lexiconNames(entries: readonly LexiconDeclaration[]): string[];
|
|
43
|
+
export function lexiconNames(entries: readonly LexiconDeclaration[] | undefined): string[] | undefined;
|
|
44
|
+
export function lexiconNames(entries: readonly LexiconDeclaration[] | undefined): string[] | undefined {
|
|
45
|
+
return entries?.map(lexiconDeclarationName);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Name to absolute module path, for the lexicons declared by path in the configs loaded so far. */
|
|
49
|
+
const modulePaths = new Map<string, string>();
|
|
50
|
+
|
|
51
|
+
/** Name to the absolute directory fold trusts for that lexicon (chant#2590). Absent: only the module is trusted. */
|
|
52
|
+
const moduleRoots = new Map<string, string>();
|
|
53
|
+
|
|
54
|
+
/** True when `path` is `dir` itself or lies under it. Both are absolute. */
|
|
55
|
+
function isWithin(path: string, dir: string): boolean {
|
|
56
|
+
const rel = relative(dir, path);
|
|
57
|
+
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* chant#2590 — the directory fold trusts for a lexicon declared by path.
|
|
62
|
+
*
|
|
63
|
+
* A package has a root that bounds its subpaths. A module path has none, so
|
|
64
|
+
* the directory is either the one `root` names, resolved like `module`, or the
|
|
65
|
+
* module's own directory. It must lie inside the project (`baseDir`, the
|
|
66
|
+
* directory holding `chant.config.ts`) and must not contain the project's
|
|
67
|
+
* source directory (`sourceDir`, relative to `baseDir`, default `.`). The
|
|
68
|
+
* module must lie inside it.
|
|
69
|
+
*
|
|
70
|
+
* Returns the directory, or `undefined` when only the module file is trusted.
|
|
71
|
+
* A declared `root` that breaks a rule returns a `problem` to report; the
|
|
72
|
+
* module's own directory breaking one (a module at the project root, say)
|
|
73
|
+
* just leaves the lexicon with its module file.
|
|
74
|
+
*/
|
|
75
|
+
export function pathLexiconRoot(
|
|
76
|
+
entry: { module: string; root?: string },
|
|
77
|
+
baseDir: string,
|
|
78
|
+
sourceDir = ".",
|
|
79
|
+
): { root?: string; problem?: string } {
|
|
80
|
+
const modulePath = isAbsolute(entry.module) ? entry.module : resolve(baseDir, entry.module);
|
|
81
|
+
const declared = entry.root !== undefined;
|
|
82
|
+
const candidate = declared
|
|
83
|
+
? isAbsolute(entry.root!)
|
|
84
|
+
? resolve(entry.root!)
|
|
85
|
+
: resolve(baseDir, entry.root!)
|
|
86
|
+
: dirname(modulePath);
|
|
87
|
+
const project = resolve(baseDir);
|
|
88
|
+
const source = resolve(baseDir, sourceDir);
|
|
89
|
+
let problem: string | undefined;
|
|
90
|
+
if (!isWithin(candidate, project)) problem = `${candidate} is outside the project (${project})`;
|
|
91
|
+
else if (isWithin(source, candidate)) problem = `${candidate} contains the project's source directory (${source})`;
|
|
92
|
+
else if (!isWithin(modulePath, candidate)) problem = `the module ${modulePath} is not inside ${candidate}`;
|
|
93
|
+
if (problem === undefined) return { root: candidate };
|
|
94
|
+
return declared ? { problem } : {};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Record the path-declared entries of one loaded config. `baseDir` is the
|
|
99
|
+
* directory the config file sits in. A plain-string entry for a name removes
|
|
100
|
+
* any path recorded for it earlier in this process, so loading a second
|
|
101
|
+
* project (the example harness does) cannot leave the first one's module in
|
|
102
|
+
* place of a package.
|
|
103
|
+
*
|
|
104
|
+
* Returns the recorded name-to-path map for this config, empty when every
|
|
105
|
+
* entry is a plain name.
|
|
106
|
+
*/
|
|
107
|
+
export function registerLexiconDeclarations(
|
|
108
|
+
entries: readonly LexiconDeclaration[] | undefined,
|
|
109
|
+
baseDir: string,
|
|
110
|
+
sourceDir?: string,
|
|
111
|
+
): Record<string, string> {
|
|
112
|
+
const recorded: Record<string, string> = {};
|
|
113
|
+
for (const entry of entries ?? []) {
|
|
114
|
+
if (typeof entry === "string") {
|
|
115
|
+
modulePaths.delete(entry);
|
|
116
|
+
moduleRoots.delete(entry);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const path = declaredModulePath(entry, baseDir);
|
|
120
|
+
modulePaths.set(entry.name, path);
|
|
121
|
+
recorded[entry.name] = path;
|
|
122
|
+
const { root } = pathLexiconRoot(entry, baseDir, sourceDir);
|
|
123
|
+
if (root === undefined) moduleRoots.delete(entry.name);
|
|
124
|
+
else moduleRoots.set(entry.name, root);
|
|
125
|
+
}
|
|
126
|
+
return recorded;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function declaredModulePath(entry: { module: string }, baseDir: string): string {
|
|
130
|
+
return isAbsolute(entry.module) ? entry.module : resolve(baseDir, entry.module);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* chant#2589 — the name-to-path map `entries` declare, without recording it.
|
|
135
|
+
* For a caller that must name path lexicons in its messages but must never
|
|
136
|
+
* let a loader import one (`chant audit` runs no project code).
|
|
137
|
+
*/
|
|
138
|
+
export function pathLexiconMap(entries: readonly LexiconDeclaration[], baseDir: string): Map<string, string> {
|
|
139
|
+
const map = new Map<string, string>();
|
|
140
|
+
for (const entry of entries) {
|
|
141
|
+
if (typeof entry !== "string") map.set(entry.name, declaredModulePath(entry, baseDir));
|
|
142
|
+
}
|
|
143
|
+
return map;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** A copy of the paths recorded so far in this process. */
|
|
147
|
+
export function recordedLexiconModules(): Map<string, string> {
|
|
148
|
+
return new Map(modulePaths);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The absolute module path `name` was declared with, or `undefined` for a package-backed lexicon. */
|
|
152
|
+
export function lexiconModulePath(name: string): string | undefined {
|
|
153
|
+
return modulePaths.get(name);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* chant#2590 — the directory fold trusts for a path-declared lexicon, or
|
|
158
|
+
* `undefined` when only its module file is trusted (or `name` is package-backed).
|
|
159
|
+
* See {@link pathLexiconRoot}.
|
|
160
|
+
*/
|
|
161
|
+
export function lexiconModuleRoot(name: string): string | undefined {
|
|
162
|
+
return moduleRoots.get(name);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Forget every recorded path. For tests. */
|
|
166
|
+
export function resetLexiconModules(): void {
|
|
167
|
+
modulePaths.clear();
|
|
168
|
+
moduleRoots.clear();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Import the module a path-declared lexicon names. Returns `undefined` when
|
|
173
|
+
* `name` is package-backed, so a caller falls through to its package import.
|
|
174
|
+
* An import failure is thrown with the declared path in the message.
|
|
175
|
+
*/
|
|
176
|
+
export async function importLexiconModule(name: string): Promise<Record<string, unknown> | undefined> {
|
|
177
|
+
const path = modulePaths.get(name);
|
|
178
|
+
if (path === undefined) return undefined;
|
|
179
|
+
try {
|
|
180
|
+
return (await import(path)) as Record<string, unknown>;
|
|
181
|
+
} catch (err) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`lexicon "${name}" is declared with module ${path}, which could not be loaded: ` +
|
|
184
|
+
(err instanceof Error ? err.message : String(err)),
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* chant#2578 — how a message should name a lexicon. A package-backed name is
|
|
191
|
+
* its npm package; a lexicon declared by path is that path, relative to
|
|
192
|
+
* `fromDir` when it sits under it. `paths` defaults to the paths recorded in
|
|
193
|
+
* this process; see {@link pathLexiconMap} for a map that is not recorded.
|
|
194
|
+
*/
|
|
195
|
+
export function lexiconSourceLabel(
|
|
196
|
+
name: string,
|
|
197
|
+
fromDir: string = process.cwd(),
|
|
198
|
+
paths: ReadonlyMap<string, string> = modulePaths,
|
|
199
|
+
): string {
|
|
200
|
+
const path = paths.get(name);
|
|
201
|
+
if (path === undefined) return `@intentius/chant-lexicon-${name}`;
|
|
202
|
+
const rel = relative(fromDir, path);
|
|
203
|
+
return rel === "" || rel.startsWith("..") || isAbsolute(rel) ? path : `./${rel.split(sep).join("/")}`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The npm packages that provide `names`, leaving out every lexicon declared by
|
|
208
|
+
* path: those have nothing to install. `paths` as in {@link lexiconSourceLabel}.
|
|
209
|
+
*/
|
|
210
|
+
export function lexiconPackagesToInstall(
|
|
211
|
+
names: readonly string[],
|
|
212
|
+
paths: ReadonlyMap<string, string> = modulePaths,
|
|
213
|
+
): string[] {
|
|
214
|
+
return names.filter((name) => !paths.has(name)).map((name) => `@intentius/chant-lexicon-${name}`);
|
|
215
|
+
}
|
package/src/lexicon.ts
CHANGED
|
@@ -486,6 +486,93 @@ export interface ComponentPipelineOptions {
|
|
|
486
486
|
* this too when the file should land under some other name than its stage.
|
|
487
487
|
*/
|
|
488
488
|
opsFileName?: string;
|
|
489
|
+
/**
|
|
490
|
+
* An environment to promote to after the deploy (#2575). The component
|
|
491
|
+
* generators add one job that needs every component job and runs
|
|
492
|
+
* `chant components promote --from <env> --to <promoteTo>`. Each component
|
|
493
|
+
* job also uploads the files its build steps wrote (see
|
|
494
|
+
* `promoteArchivePaths` in `components/promote.ts`), and the promote job
|
|
495
|
+
* downloads them, since a promote publishes from the archive on disk.
|
|
496
|
+
* Unset, the pipeline has no promote job and is unchanged.
|
|
497
|
+
*/
|
|
498
|
+
promoteTo?: string;
|
|
499
|
+
/**
|
|
500
|
+
* The promote job's command as argv, when `promoteTo` is set. Default
|
|
501
|
+
* `["chant", "components", "promote", "--from", <env>, "--to", <promoteTo>]`.
|
|
502
|
+
*/
|
|
503
|
+
promoteCommand?: string[];
|
|
504
|
+
/**
|
|
505
|
+
* The workspace member this pipeline belongs to (#2542, #2524 D19). Core
|
|
506
|
+
* sets it when the project sits in a member of a `chant.workspace.json`;
|
|
507
|
+
* a caller never needs to. Unset, every generator's output is unchanged.
|
|
508
|
+
* Set, the pipeline keeps the triggers it has without a member, and the
|
|
509
|
+
* ones that can take a path filter (an Op's `push` and `pull_request`, and
|
|
510
|
+
* GitLab's job rules) are limited to the member's files and the pipeline
|
|
511
|
+
* file. Its jobs run in the member's directory, and its names carry the
|
|
512
|
+
* member's name so two members' pipelines never collide.
|
|
513
|
+
*/
|
|
514
|
+
member?: PipelineMember;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** Where a workspace member sits, for a generated pipeline (#2542). */
|
|
518
|
+
export interface PipelineMember {
|
|
519
|
+
/** The member's name in the declaration. */
|
|
520
|
+
name: string;
|
|
521
|
+
/**
|
|
522
|
+
* The member's directory relative to the repository root, with `/`
|
|
523
|
+
* separators, or `"."` for a member at the root.
|
|
524
|
+
*/
|
|
525
|
+
dir: string;
|
|
526
|
+
/**
|
|
527
|
+
* Directories (repository-relative) that belong to other members or to
|
|
528
|
+
* example groups. Only a member at `"."` uses them: its path filter is the
|
|
529
|
+
* whole repository minus these.
|
|
530
|
+
*/
|
|
531
|
+
exclude?: string[];
|
|
532
|
+
/** The generated component pipeline's path, repository-relative. Added to the path filter. */
|
|
533
|
+
file?: string;
|
|
534
|
+
/**
|
|
535
|
+
* The directory (repository-relative) the generated Op pipeline files land
|
|
536
|
+
* in. Each file's own path is added to its path filter.
|
|
537
|
+
*/
|
|
538
|
+
fileDir?: string;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/** `path` (relative to the member's directory) made relative to the repository root. Absolute paths pass through. */
|
|
542
|
+
export function memberRepoPath(member: PipelineMember, path: string): string {
|
|
543
|
+
if (path.startsWith("/") || member.dir === "." || member.dir === "") return path;
|
|
544
|
+
const rest = path.replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
545
|
+
return rest === "" || rest === "." ? member.dir : `${member.dir}/${rest}`;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* The path filter for a member's `push` or `pull_request` trigger in GitHub
|
|
550
|
+
* Actions syntax (also Forgejo's): the member's directory and the pipeline
|
|
551
|
+
* file. A member at
|
|
552
|
+
* `"."` gets every path, minus the other members' directories.
|
|
553
|
+
*/
|
|
554
|
+
export function memberPathFilter(member: PipelineMember, file: string | undefined = member.file): string[] {
|
|
555
|
+
const paths =
|
|
556
|
+
member.dir === "." || member.dir === ""
|
|
557
|
+
? ["**", ...(member.exclude ?? []).map((d) => `!${d}/**`)]
|
|
558
|
+
: [`${member.dir}/**`];
|
|
559
|
+
if (file && !paths.includes(file)) paths.push(file);
|
|
560
|
+
return paths;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* The `rules: changes:` list for a member's GitLab jobs, or undefined for a
|
|
565
|
+
* member at `"."`: GitLab's `changes` has no exclusions, so the root member's
|
|
566
|
+
* jobs run on every change.
|
|
567
|
+
*/
|
|
568
|
+
export function memberGitlabChanges(member: PipelineMember, file: string | undefined = member.file): string[] | undefined {
|
|
569
|
+
if (member.dir === "." || member.dir === "") return undefined;
|
|
570
|
+
return [`${member.dir}/**/*`, ...(file ? [file] : [])];
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/** A shell word for `cd` into the member's directory. */
|
|
574
|
+
export function memberShellDir(member: PipelineMember): string {
|
|
575
|
+
return /^[A-Za-z0-9._\/-]+$/.test(member.dir) ? member.dir : `'${member.dir.replace(/'/g, `'\\''`)}'`;
|
|
489
576
|
}
|
|
490
577
|
|
|
491
578
|
/** The synthesized CI pipeline for a component graph (generate mode). */
|
|
@@ -939,6 +1026,17 @@ export interface LexiconPlugin {
|
|
|
939
1026
|
*/
|
|
940
1027
|
activityContracts?(): ActivityContract[];
|
|
941
1028
|
|
|
1029
|
+
/**
|
|
1030
|
+
* Op activity implementations, keyed by the name a step's `fn` calls them
|
|
1031
|
+
* by (chant #2520). Read only for a lexicon declared by module path
|
|
1032
|
+
* (`{ name, module }` in `chant.config.ts`), which has no
|
|
1033
|
+
* `@intentius/chant-lexicon-<name>/op/activities` subpath for
|
|
1034
|
+
* `loadActivities` to import. A package-backed lexicon keeps using the
|
|
1035
|
+
* subpath and leaves this out.
|
|
1036
|
+
*/
|
|
1037
|
+
activities?(): Record<string, (args: Record<string, unknown>, signal?: AbortSignal) => Promise<unknown>>
|
|
1038
|
+
| Promise<Record<string, (args: Record<string, unknown>, signal?: AbortSignal) => Promise<unknown>>>;
|
|
1039
|
+
|
|
942
1040
|
/**
|
|
943
1041
|
* Audit catalog metadata (title/tier/fix/authority/category) for this
|
|
944
1042
|
* lexicon's `postSynthChecks`, keyed by check id — the per-provider half of
|
|
@@ -1912,7 +2010,13 @@ export interface ResourceMetadata {
|
|
|
1912
2010
|
status: string;
|
|
1913
2011
|
/** ISO timestamp of last update */
|
|
1914
2012
|
lastUpdated?: string;
|
|
1915
|
-
/**
|
|
2013
|
+
/**
|
|
2014
|
+
* Cloud-assigned output properties. Two keys are read by `components status
|
|
2015
|
+
* --live` (#2513): `digest` (the artifact digest serving now) and `gitSha`
|
|
2016
|
+
* (the commit it was built from). Set them when the live read knows them; a
|
|
2017
|
+
* value that differs from the recorded release reports the component
|
|
2018
|
+
* `drifted`. See `LIVE_IDENTITY_ATTRIBUTES` in lifecycle/status.ts.
|
|
2019
|
+
*/
|
|
1916
2020
|
attributes?: Record<string, unknown>;
|
|
1917
2021
|
/**
|
|
1918
2022
|
* Live ownership verdict from the resource's marker (#119/#120). `owned` =
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
57
|
import { sortedJsonReplacer } from "../utils";
|
|
58
|
+
import { flagLegacyDigest, warnOnLegacyDigests, withoutReadFlags } from "./legacy-digest";
|
|
58
59
|
import { writeBlobToPath, readBlobFromPath, listFilesInDir } from "./git";
|
|
59
60
|
import type { BuildArchiveManifest } from "../components/verbs/build-archive";
|
|
60
61
|
|
|
@@ -86,7 +87,8 @@ export async function persistBuildManifest(
|
|
|
86
87
|
manifest: BuildArchiveManifest,
|
|
87
88
|
opts?: { cwd?: string },
|
|
88
89
|
): Promise<{ commit: string }> {
|
|
89
|
-
|
|
90
|
+
// `flags` are read-side only (#2514); a manifest read back and persisted again never keeps them.
|
|
91
|
+
const json = JSON.stringify(withoutReadFlags(manifest), sortedJsonReplacer);
|
|
90
92
|
const commit = await writeBlobToPath(BUILDS_DIR, manifestFilename(manifest.manifestDigest), json, "Build manifest", opts);
|
|
91
93
|
return { commit };
|
|
92
94
|
}
|
|
@@ -104,11 +106,20 @@ export async function readBuildManifest(
|
|
|
104
106
|
): Promise<BuildArchiveManifest | null> {
|
|
105
107
|
const content = await readBlobFromPath(BUILDS_DIR, manifestFilename(manifestDigest), opts);
|
|
106
108
|
if (!content) return null;
|
|
109
|
+
let manifest: BuildArchiveManifest;
|
|
107
110
|
try {
|
|
108
|
-
|
|
111
|
+
manifest = JSON.parse(content) as BuildArchiveManifest;
|
|
109
112
|
} catch {
|
|
110
113
|
return null;
|
|
111
114
|
}
|
|
115
|
+
// chant #2514 — a manifest whose digests predate real SHA-256 is flagged
|
|
116
|
+
// legacy-digest, never re-keyed, and the project is warned once.
|
|
117
|
+
const digests = [
|
|
118
|
+
manifest.manifestDigest,
|
|
119
|
+
...(Array.isArray(manifest.contents) ? manifest.contents.map((e) => e?.digest) : []),
|
|
120
|
+
];
|
|
121
|
+
warnOnLegacyDigests(digests, "build manifests", opts?.cwd);
|
|
122
|
+
return flagLegacyDigest(manifest, digests);
|
|
112
123
|
}
|
|
113
124
|
|
|
114
125
|
/** List the `manifestDigest` of every build manifest persisted on the orphan branch. */
|
package/src/lifecycle/digest.ts
CHANGED
|
@@ -7,15 +7,19 @@
|
|
|
7
7
|
import type { BuildResult } from "../build";
|
|
8
8
|
import type { Declarable } from "../declarable";
|
|
9
9
|
import type { BuildDigest, ResourceDigest, DigestDiff } from "./types";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { canonicalJson } from "../effect-receipt";
|
|
11
|
+
import { contentDigest } from "../content-digest";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Hash an entity's props deterministically
|
|
14
|
+
* Hash an entity's props deterministically: bare hex SHA-256 over their
|
|
15
|
+
* `canonicalJson` (chant #2514). Props are first passed through
|
|
16
|
+
* `JSON.stringify`, which applies `toJSON` and drops what JSON cannot hold
|
|
17
|
+
* (a function-valued prop, say), so a prop that serialises today still
|
|
18
|
+
* hashes rather than throwing.
|
|
15
19
|
*/
|
|
16
20
|
export function hashProps(props: unknown): string {
|
|
17
|
-
const
|
|
18
|
-
return
|
|
21
|
+
const plain: unknown = JSON.parse(JSON.stringify(props) ?? "null");
|
|
22
|
+
return contentDigest(canonicalJson(plain)).slice("sha256:".length);
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
/**
|
|
@@ -253,6 +253,26 @@ describe("lifecycle/gate-ledger — pending facts (#2119)", () => {
|
|
|
253
253
|
expect(resolutions).toEqual([]);
|
|
254
254
|
});
|
|
255
255
|
});
|
|
256
|
+
|
|
257
|
+
// #2574: an environment that is present but not a string would read as an
|
|
258
|
+
// approval bound to no environment, which a component gate treats differently.
|
|
259
|
+
test("a line whose environment is not a string is malformed", async () => {
|
|
260
|
+
await withTestDir(async (dir) => {
|
|
261
|
+
await initRepo(dir);
|
|
262
|
+
await writeBlobToPath(
|
|
263
|
+
"_gates", "search-service.jsonl",
|
|
264
|
+
[
|
|
265
|
+
JSON.stringify({ version: 1, op: "search-service", gate: "g", resolvedBy: "alex", timestamp: "2026-01-01T00:00:00.000Z", environment: 7 }),
|
|
266
|
+
JSON.stringify({ version: 1, op: "search-service", gate: "g", resolvedBy: "alex", timestamp: "2026-01-01T00:00:00.000Z", environment: "prod" }),
|
|
267
|
+
].join("\n"),
|
|
268
|
+
"hand-written",
|
|
269
|
+
{ cwd: dir },
|
|
270
|
+
);
|
|
271
|
+
const { resolutions, malformed } = await readGateLedger("search-service", { cwd: dir });
|
|
272
|
+
expect(malformed).toBe(1);
|
|
273
|
+
expect(resolutions.map((r) => r.environment)).toEqual(["prod"]);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
256
276
|
});
|
|
257
277
|
|
|
258
278
|
/**
|
|
@@ -140,6 +140,13 @@ export interface GateResolutionRecord {
|
|
|
140
140
|
* such a record proves is that somebody approved *something*.
|
|
141
141
|
*/
|
|
142
142
|
planDigest?: string;
|
|
143
|
+
/**
|
|
144
|
+
* The environment this approval is for (#2574), copied off the pending fact
|
|
145
|
+
* it answers. Set for a component gate, whose ledger is keyed by component
|
|
146
|
+
* rather than by environment. A component gate counts only a resolution
|
|
147
|
+
* recorded for its own environment and plan.
|
|
148
|
+
*/
|
|
149
|
+
environment?: string;
|
|
143
150
|
/**
|
|
144
151
|
* The channel this resolution was authored on (chant#2384) — set by the
|
|
145
152
|
* writer, never by the caller. `chant approve` records `"cli"`, the
|
|
@@ -217,10 +224,18 @@ export interface PendingGateRecord {
|
|
|
217
224
|
* resolution by default, so approving the standing fact approves the plan
|
|
218
225
|
* the approver was shown rather than the next run's.
|
|
219
226
|
*
|
|
220
|
-
* Absent when the gate binds no plan (
|
|
221
|
-
*
|
|
227
|
+
* Absent when the gate binds no plan (an authored Op `gate` step with no
|
|
228
|
+
* `plan`), which is the shape every gate had before #2300. A component gate
|
|
229
|
+
* binds one since #2574.
|
|
222
230
|
*/
|
|
223
231
|
planDigest?: string;
|
|
232
|
+
/**
|
|
233
|
+
* The environment the run reached this gate in (#2574). Set by the
|
|
234
|
+
* component driver, whose gate ledger is keyed by component, so pending
|
|
235
|
+
* facts for staging and prod stand side by side and `chant approve --env`
|
|
236
|
+
* can pick one.
|
|
237
|
+
*/
|
|
238
|
+
environment?: string;
|
|
224
239
|
/**
|
|
225
240
|
* The channel the run that reached this gate was driven from (chant#2384).
|
|
226
241
|
*
|
|
@@ -260,8 +275,8 @@ function filename(op: string): string {
|
|
|
260
275
|
* fact was appended to (#2243) rather than describing it in prose that drifts
|
|
261
276
|
* from `DIR`.
|
|
262
277
|
*/
|
|
263
|
-
export function gateLedgerPath(op: string): string {
|
|
264
|
-
return `${DIR}/${filename(op)}`;
|
|
278
|
+
export function gateLedgerPath(op: string, memberPrefix = ""): string {
|
|
279
|
+
return `${memberPrefix}${DIR}/${filename(op)}`;
|
|
265
280
|
}
|
|
266
281
|
|
|
267
282
|
/** Append one immutable gate-resolution record. Does not push to the remote — call `pushLifecycle` (./git.ts) afterward, same two-step shape every other ledger write here uses. Retries on `RefCASConflictError` the same way `appendConvergeRecord` does (./converge-ledger.ts) — a concurrent writer to a different op's/env's file on the same orphan branch is the ordinary case, not an edge case. The baseline read must be `readPathSha` + `readBlobBySha` rather than `readBlobFromPath`, so the exact sha `existing` came from can be passed as `expectPriorPathSha` — see `writeBlobToPath` (./git.ts) for the race that closes. */
|
|
@@ -361,6 +376,13 @@ export async function readGateLedger(
|
|
|
361
376
|
malformed++;
|
|
362
377
|
continue;
|
|
363
378
|
}
|
|
379
|
+
// #2574: the same reasoning for the environment a component gate
|
|
380
|
+
// approval is bound to.
|
|
381
|
+
const rawEnv: unknown = (parsed as { environment?: unknown }).environment;
|
|
382
|
+
if (rawEnv !== undefined && typeof rawEnv !== "string") {
|
|
383
|
+
malformed++;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
364
386
|
if (parsed.kind === "pending") {
|
|
365
387
|
if (typeof parsed.expiresAt !== "string") {
|
|
366
388
|
malformed++;
|
|
@@ -442,8 +464,8 @@ export interface PlanBoundResolution {
|
|
|
442
464
|
* which a run answers yes to no matter what has changed since, and this asks
|
|
443
465
|
* "is there an approval of *this*".
|
|
444
466
|
*
|
|
445
|
-
* - `planDigest` `undefined` — the gate binds no plan (
|
|
446
|
-
*
|
|
467
|
+
* - `planDigest` `undefined` — the gate binds no plan (an Op `gate` step
|
|
468
|
+
* authored with no `plan`). Falls straight through to
|
|
447
469
|
* {@link latestResolutionSince}: gates that never claimed to bind a plan
|
|
448
470
|
* behave exactly as they did before #2300.
|
|
449
471
|
* - A resolution whose `planDigest` equals `planDigest` answers the gate.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { withTestDir } from "@intentius/chant-test-utils";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
|
-
import { writeFileSync } from "node:fs";
|
|
4
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import {
|
|
@@ -130,6 +130,18 @@ describe("lifecycle/git", () => {
|
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
+
test("a write from a subdirectory of the repository keeps the rest of the branch (#2550)", async () => {
|
|
134
|
+
await withTestDir(async (dir) => {
|
|
135
|
+
await initRepo(dir);
|
|
136
|
+
await writeBlobToPath("_builds", "a.json", "{}", "m", { cwd: dir });
|
|
137
|
+
const sub = join(dir, "project");
|
|
138
|
+
mkdirSync(sub);
|
|
139
|
+
await writeBlobToPath("_gates", "op.jsonl", "{}", "m", { cwd: sub });
|
|
140
|
+
expect(await listFilesInDir("_builds", { cwd: sub })).toEqual(["a.json"]);
|
|
141
|
+
expect(await readBlobFromPath("_gates", "op.jsonl", { cwd: dir })).toBe("{}");
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
133
145
|
test("readBlobFromPath returns null for a missing directory/file", async () => {
|
|
134
146
|
await withTestDir(async (dir) => {
|
|
135
147
|
await initRepo(dir);
|