@intentius/chant 0.80.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/discover.d.ts +8 -16
- 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 +4 -5
- package/dist/audit/terraform-state.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +25 -9
- package/dist/cli/commands/audit.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.map +1 -1
- package/dist/cli/commands/lint.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/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/operator.d.ts +7 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/promote.d.ts +7 -4
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/run-generate.d.ts.map +1 -1
- 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/plugins.d.ts +12 -7
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +32 -0
- 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/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/gate-plan.d.ts +40 -0
- package/dist/components/gate-plan.d.ts.map +1 -0
- package/dist/components/promote.d.ts +60 -7
- package/dist/components/promote.d.ts.map +1 -1
- 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 +32 -10
- 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 +10 -5
- package/dist/discovery/files.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +20 -4
- 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 +4 -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 +51 -6
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon.d.ts +58 -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 +41 -8
- package/dist/lifecycle/legacy-digest.d.ts.map +1 -1
- 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 +18 -2
- 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/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/discover.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/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/records-cli.d.ts +28 -4
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records.d.ts.map +1 -1
- 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 +2 -1
- package/src/audit/discover.ts +66 -78
- package/src/audit/report-html.ts +3 -1
- package/src/audit/report-model.ts +14 -1
- package/src/audit/terraform-state.test.ts +4 -9
- package/src/audit/terraform-state.ts +4 -5
- package/src/cli/commands/audit-truncation.test.ts +167 -0
- package/src/cli/commands/audit.ts +89 -60
- package/src/cli/commands/check-lexicon.ts +24 -0
- package/src/cli/commands/doctor.test.ts +30 -0
- package/src/cli/commands/doctor.ts +18 -0
- package/src/cli/commands/import-agents.ts +6 -2
- package/src/cli/commands/init.ts +22 -3
- package/src/cli/commands/lint.test.ts +17 -23
- package/src/cli/commands/lint.ts +24 -84
- package/src/cli/commands/update.ts +13 -4
- package/src/cli/commands/vendor.ts +11 -6
- 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.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 +5 -3
- package/src/cli/handlers/fan-out.ts +9 -2
- package/src/cli/handlers/init.ts +54 -1
- package/src/cli/handlers/operator.test.ts +68 -0
- package/src/cli/handlers/operator.ts +37 -9
- package/src/cli/handlers/promote.test.ts +118 -2
- package/src/cli/handlers/promote.ts +82 -16
- package/src/cli/handlers/run-generate-member.test.ts +83 -0
- package/src/cli/handlers/run-generate.test.ts +6 -8
- package/src/cli/handlers/run-generate.ts +36 -1
- package/src/cli/handlers/run.test.ts +47 -0
- package/src/cli/handlers/run.ts +28 -5
- package/src/cli/handlers/vendor.ts +147 -43
- package/src/cli/main.test.ts +105 -1
- package/src/cli/main.ts +230 -17
- package/src/cli/plugins.ts +17 -17
- package/src/cli/registry.ts +32 -0
- 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/cli-support.ts +6 -1
- package/src/components/discover.test.ts +27 -57
- package/src/components/discover.ts +13 -68
- package/src/components/driver.test.ts +78 -1
- package/src/components/driver.ts +43 -7
- package/src/components/gate-plan.ts +53 -0
- package/src/components/promote.test.ts +276 -2
- package/src/components/promote.ts +119 -15
- 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 +2 -2
- package/src/config.ts +48 -15
- package/src/content-digest.test.ts +120 -0
- package/src/content-digest.ts +21 -0
- package/src/discovery/files.test.ts +10 -10
- package/src/discovery/files.ts +25 -81
- package/src/discovery/fold-import.ts +49 -5
- 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/walk.test.ts +263 -0
- package/src/discovery/walk.ts +334 -0
- package/src/effect-receipt.ts +2 -2
- package/src/graph-ir.ts +4 -1
- package/src/join-key.test.ts +25 -0
- package/src/join-key.ts +39 -0
- package/src/lexicon-module.ts +108 -10
- package/src/lexicon.ts +79 -1
- package/src/lifecycle/build-ledger-store.ts +11 -10
- 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 +115 -23
- package/src/lifecycle/legacy-digest.ts +60 -19
- 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 +23 -7
- package/src/lifecycle/status.test.ts +151 -1
- package/src/lifecycle/status.ts +228 -7
- 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/discover.test.ts +14 -4
- package/src/op/discover.ts +19 -27
- 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/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/records-cli.ts +86 -14
- package/src/workspace/records-contract.test.ts +5 -0
- package/src/workspace/records.schema.json +32 -2
- package/src/workspace/records.ts +3 -2
- 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
- package/dist/components/unbound-gate-approval.d.ts +0 -24
- package/dist/components/unbound-gate-approval.d.ts.map +0 -1
- package/dist/discovery/convergence.d.ts +0 -97
- package/dist/discovery/convergence.d.ts.map +0 -1
- package/src/cli/commands/audit-walk-warnings.test.ts +0 -156
- package/src/components/unbound-gate-approval.test.ts +0 -82
- package/src/components/unbound-gate-approval.ts +0 -51
- package/src/discovery/convergence.test.ts +0 -231
- package/src/discovery/convergence.ts +0 -399
- package/src/op/discover-convergence.test.ts +0 -70
package/src/join-key.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one join key for names matched across projects (#2524 D6, ws-008).
|
|
3
|
+
*
|
|
4
|
+
* A consumer's parameter and a producer's output are joined by name when
|
|
5
|
+
* nobody wrote the join down. Deploy matches names exactly, and real projects
|
|
6
|
+
* rarely spell both sides the same (`clusterArn` against `ClusterArn`), so an
|
|
7
|
+
* inferred join compares {@link joinKey}s and says how it matched: `exact`
|
|
8
|
+
* when the names are equal, `folded` when only case or punctuation differ.
|
|
9
|
+
* A declared member link is always compared exactly and never uses this.
|
|
10
|
+
*
|
|
11
|
+
* Before this module, core only published the handles (a graph IR's
|
|
12
|
+
* `exports` and `imports`) and each viewer matched them with its own rule.
|
|
13
|
+
* Every reader that infers a join by name uses this function instead, so the
|
|
14
|
+
* same workspace shows the same inferred edges everywhere. It imports nothing,
|
|
15
|
+
* so any reader can load it through `@intentius/chant/join-key`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** How an inferred join matched its two names. */
|
|
19
|
+
export type JoinLabel = "exact" | "folded";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The key two names are joined on: Unicode-normalised, lowercased, with every
|
|
23
|
+
* character that is not a letter or a digit removed. `ClusterArn`,
|
|
24
|
+
* `clusterArn`, `cluster_arn` and `cluster-arn` share the key `clusterarn`.
|
|
25
|
+
*/
|
|
26
|
+
export function joinKey(name: string): string {
|
|
27
|
+
return name.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]/gu, "");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* How `a` and `b` join: `exact` when they are the same string, `folded` when
|
|
32
|
+
* only their {@link joinKey}s are equal, and undefined when they don't join.
|
|
33
|
+
* Names whose key is empty never join.
|
|
34
|
+
*/
|
|
35
|
+
export function joinLabel(a: string, b: string): JoinLabel | undefined {
|
|
36
|
+
if (a === b) return a === "" ? undefined : "exact";
|
|
37
|
+
const key = joinKey(a);
|
|
38
|
+
return key !== "" && key === joinKey(b) ? "folded" : undefined;
|
|
39
|
+
}
|
package/src/lexicon-module.ts
CHANGED
|
@@ -24,10 +24,14 @@
|
|
|
24
24
|
* a project that names lexicons by package takes exactly the path it always did.
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
27
|
+
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
28
28
|
|
|
29
|
-
/**
|
|
30
|
-
|
|
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 };
|
|
31
35
|
|
|
32
36
|
/** The lexicon name an entry declares, whichever form it takes. */
|
|
33
37
|
export function lexiconDeclarationName(entry: LexiconDeclaration): string {
|
|
@@ -44,6 +48,52 @@ export function lexiconNames(entries: readonly LexiconDeclaration[] | undefined)
|
|
|
44
48
|
/** Name to absolute module path, for the lexicons declared by path in the configs loaded so far. */
|
|
45
49
|
const modulePaths = new Map<string, string>();
|
|
46
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
|
+
|
|
47
97
|
/**
|
|
48
98
|
* Record the path-declared entries of one loaded config. `baseDir` is the
|
|
49
99
|
* directory the config file sits in. A plain-string entry for a name removes
|
|
@@ -57,28 +107,65 @@ const modulePaths = new Map<string, string>();
|
|
|
57
107
|
export function registerLexiconDeclarations(
|
|
58
108
|
entries: readonly LexiconDeclaration[] | undefined,
|
|
59
109
|
baseDir: string,
|
|
110
|
+
sourceDir?: string,
|
|
60
111
|
): Record<string, string> {
|
|
61
112
|
const recorded: Record<string, string> = {};
|
|
62
113
|
for (const entry of entries ?? []) {
|
|
63
114
|
if (typeof entry === "string") {
|
|
64
115
|
modulePaths.delete(entry);
|
|
116
|
+
moduleRoots.delete(entry);
|
|
65
117
|
continue;
|
|
66
118
|
}
|
|
67
|
-
const path =
|
|
119
|
+
const path = declaredModulePath(entry, baseDir);
|
|
68
120
|
modulePaths.set(entry.name, path);
|
|
69
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);
|
|
70
125
|
}
|
|
71
126
|
return recorded;
|
|
72
127
|
}
|
|
73
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
|
+
|
|
74
151
|
/** The absolute module path `name` was declared with, or `undefined` for a package-backed lexicon. */
|
|
75
152
|
export function lexiconModulePath(name: string): string | undefined {
|
|
76
153
|
return modulePaths.get(name);
|
|
77
154
|
}
|
|
78
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
|
+
|
|
79
165
|
/** Forget every recorded path. For tests. */
|
|
80
166
|
export function resetLexiconModules(): void {
|
|
81
167
|
modulePaths.clear();
|
|
168
|
+
moduleRoots.clear();
|
|
82
169
|
}
|
|
83
170
|
|
|
84
171
|
/**
|
|
@@ -102,16 +189,27 @@ export async function importLexiconModule(name: string): Promise<Record<string,
|
|
|
102
189
|
/**
|
|
103
190
|
* chant#2578 — how a message should name a lexicon. A package-backed name is
|
|
104
191
|
* its npm package; a lexicon declared by path is that path, relative to
|
|
105
|
-
* `fromDir` when it sits under it.
|
|
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.
|
|
106
194
|
*/
|
|
107
|
-
export function lexiconSourceLabel(
|
|
108
|
-
|
|
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);
|
|
109
201
|
if (path === undefined) return `@intentius/chant-lexicon-${name}`;
|
|
110
202
|
const rel = relative(fromDir, path);
|
|
111
203
|
return rel === "" || rel.startsWith("..") || isAbsolute(rel) ? path : `./${rel.split(sep).join("/")}`;
|
|
112
204
|
}
|
|
113
205
|
|
|
114
|
-
/**
|
|
115
|
-
|
|
116
|
-
|
|
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}`);
|
|
117
215
|
}
|
package/src/lexicon.ts
CHANGED
|
@@ -501,6 +501,78 @@ export interface ComponentPipelineOptions {
|
|
|
501
501
|
* `["chant", "components", "promote", "--from", <env>, "--to", <promoteTo>]`.
|
|
502
502
|
*/
|
|
503
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, `'\\''`)}'`;
|
|
504
576
|
}
|
|
505
577
|
|
|
506
578
|
/** The synthesized CI pipeline for a component graph (generate mode). */
|
|
@@ -1938,7 +2010,13 @@ export interface ResourceMetadata {
|
|
|
1938
2010
|
status: string;
|
|
1939
2011
|
/** ISO timestamp of last update */
|
|
1940
2012
|
lastUpdated?: string;
|
|
1941
|
-
/**
|
|
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
|
+
*/
|
|
1942
2020
|
attributes?: Record<string, unknown>;
|
|
1943
2021
|
/**
|
|
1944
2022
|
* Live ownership verdict from the resource's marker (#119/#120). `owned` =
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
57
|
import { sortedJsonReplacer } from "../utils";
|
|
58
|
-
import { warnOnLegacyDigests } from "./legacy-digest";
|
|
58
|
+
import { flagLegacyDigest, warnOnLegacyDigests, withoutReadFlags } from "./legacy-digest";
|
|
59
59
|
import { writeBlobToPath, readBlobFromPath, listFilesInDir } from "./git";
|
|
60
60
|
import type { BuildArchiveManifest } from "../components/verbs/build-archive";
|
|
61
61
|
|
|
@@ -87,7 +87,8 @@ export async function persistBuildManifest(
|
|
|
87
87
|
manifest: BuildArchiveManifest,
|
|
88
88
|
opts?: { cwd?: string },
|
|
89
89
|
): Promise<{ commit: string }> {
|
|
90
|
-
|
|
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);
|
|
91
92
|
const commit = await writeBlobToPath(BUILDS_DIR, manifestFilename(manifest.manifestDigest), json, "Build manifest", opts);
|
|
92
93
|
return { commit };
|
|
93
94
|
}
|
|
@@ -111,14 +112,14 @@ export async function readBuildManifest(
|
|
|
111
112
|
} catch {
|
|
112
113
|
return null;
|
|
113
114
|
}
|
|
114
|
-
// chant #2514 — a
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
121
|
-
return manifest;
|
|
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);
|
|
122
123
|
}
|
|
123
124
|
|
|
124
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);
|