@intentius/chant 0.124.46 → 0.124.49
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/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
- package/dist/__golden__/wave-loop/op-by-apply-hold.json +10 -0
- package/dist/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
- package/dist/__golden__/wave-loop/op-by-apply-pass.json +10 -0
- package/dist/__golden__/wave-loop/op-by-apply-skip.json +10 -0
- package/dist/__golden__/wave-loop/op-cost-member-clash.json +6 -0
- package/dist/__golden__/wave-loop/op-cost-member.json +13 -0
- package/dist/__golden__/wave-loop/op-denied-source.json +16 -0
- package/dist/__golden__/wave-loop/op-denied.json +16 -0
- package/dist/__golden__/wave-loop/op-discover-rest-short.json +6 -0
- package/dist/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
- package/dist/__golden__/wave-loop/op-discover-rest.json +26 -0
- package/dist/__golden__/wave-loop/op-discover-wide.json +26 -0
- package/dist/__golden__/wave-loop/op-held-on-again.json +16 -0
- package/dist/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
- package/dist/__golden__/wave-loop/op-held-on.json +16 -0
- package/dist/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-held.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-pass.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
- package/dist/__golden__/wave-loop/op-hooks-share.json +33 -0
- package/dist/__golden__/wave-loop/op-outcome-held.json +10 -0
- package/dist/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
- package/dist/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
- package/dist/__golden__/wave-loop/op-outcome-replan.json +10 -0
- package/dist/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
- package/dist/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
- package/dist/__golden__/wave-loop/op-overridden.json +16 -0
- package/dist/__golden__/wave-loop/op-per-run.json +20 -0
- package/dist/__golden__/wave-loop/op-point.json +13 -0
- package/dist/__golden__/wave-loop/op-pr-review.json +10 -0
- package/dist/__golden__/wave-loop/op-sealed.json +10 -0
- package/dist/__golden__/wave-loop/op-spend-change-only.json +10 -0
- package/dist/__golden__/wave-loop/op-spend.json +10 -0
- package/dist/__golden__/wave-loop/op-wave-commands.json +55 -0
- package/dist/__golden__/wave-loop/op-wide-refused.json +26 -0
- package/dist/__golden__/wave-loop/op-wide-waiting.json +29 -0
- package/dist/__golden__/wave-loop/op-wide.json +39 -0
- package/dist/action-pins.d.ts +61 -0
- package/dist/action-pins.d.ts.map +1 -0
- package/dist/action-pins.js +77 -0
- package/dist/cli/commands/carve.d.ts +3 -0
- package/dist/cli/commands/carve.d.ts.map +1 -1
- package/dist/cli/commands/carve.js +36 -7
- package/dist/cli/commands/import-report.d.ts +26 -0
- package/dist/cli/commands/import-report.d.ts.map +1 -0
- package/dist/cli/commands/import-report.js +65 -0
- package/dist/cli/commands/import.d.ts +11 -1
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/import.js +22 -3
- package/dist/cli/commands/migrate.d.ts +13 -1
- package/dist/cli/commands/migrate.d.ts.map +1 -1
- package/dist/cli/commands/migrate.js +134 -10
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +41 -14
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +6 -1
- package/dist/cli/handlers/graph-reader.d.ts +26 -0
- package/dist/cli/handlers/graph-reader.d.ts.map +1 -0
- package/dist/cli/handlers/graph-reader.js +41 -0
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +14 -0
- package/dist/cli/handlers/ledger.d.ts +3 -0
- package/dist/cli/handlers/ledger.d.ts.map +1 -0
- package/dist/cli/handlers/ledger.js +55 -0
- package/dist/cli/handlers/migrate.d.ts.map +1 -1
- package/dist/cli/handlers/migrate.js +18 -11
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/misc.js +12 -1
- package/dist/cli/handlers/pr-env.js +2 -2
- package/dist/cli/handlers/pr.d.ts +15 -1
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +183 -40
- package/dist/cli/handlers/query.d.ts +7 -2
- package/dist/cli/handlers/query.d.ts.map +1 -1
- package/dist/cli/handlers/query.js +16 -4
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +6 -3
- package/dist/cli/handlers/search.d.ts +2 -4
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/handlers/search.js +13 -39
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +87 -14
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +47 -2
- package/dist/cli/registry.d.ts +22 -4
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/builders.d.ts +5 -1
- package/dist/components/builders.d.ts.map +1 -1
- package/dist/components/builders.js +5 -0
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.js +16 -1
- package/dist/components/capability-plugin.d.ts +14 -0
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/pr-hooks.d.ts +21 -1
- package/dist/components/pr-hooks.d.ts.map +1 -1
- package/dist/components/pr-hooks.js +72 -4
- package/dist/components/pr-pipeline.d.ts +45 -10
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +47 -14
- package/dist/components/pr-policy.d.ts +36 -0
- package/dist/components/pr-policy.d.ts.map +1 -1
- package/dist/components/pr-policy.js +36 -0
- package/dist/components/pr-run.d.ts +3 -0
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +1 -0
- package/dist/components/starter-plugin.d.ts +1 -0
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/starter-plugin.js +5 -1
- package/dist/components/verbs/index.d.ts +2 -0
- package/dist/components/verbs/index.d.ts.map +1 -1
- package/dist/components/verbs/index.js +2 -0
- package/dist/components/verbs/publish-artifact.d.ts +65 -0
- package/dist/components/verbs/publish-artifact.d.ts.map +1 -0
- package/dist/components/verbs/publish-artifact.js +74 -0
- package/dist/components/verbs/publish-image.d.ts +129 -0
- package/dist/components/verbs/publish-image.d.ts.map +1 -0
- package/dist/components/verbs/publish-image.js +145 -0
- package/dist/config-environments.d.ts +20 -0
- package/dist/config-environments.d.ts.map +1 -1
- package/dist/config-environments.js +12 -0
- package/dist/config.d.ts +25 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -2
- package/dist/gate-decision.d.ts +20 -2
- package/dist/gate-decision.d.ts.map +1 -1
- package/dist/gate-decision.js +30 -3
- package/dist/graph-ir.d.ts +25 -0
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-walk.d.ts +66 -0
- package/dist/graph-walk.d.ts.map +1 -0
- package/dist/graph-walk.js +70 -0
- package/dist/import/parser.d.ts +19 -0
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/lexicon.d.ts +94 -6
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +11 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +2 -5
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/ledger-read.d.ts +65 -0
- package/dist/lifecycle/ledger-read.d.ts.map +1 -0
- package/dist/lifecycle/ledger-read.js +160 -0
- package/dist/lifecycle/release-workstation.d.ts +7 -0
- package/dist/lifecycle/release-workstation.d.ts.map +1 -1
- package/dist/lifecycle/release-workstation.js +25 -0
- package/dist/lifecycle/run-ledger.d.ts +4 -3
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +1 -0
- package/dist/lint/discover.js +3 -1
- package/dist/lint/post-synth.d.ts +127 -1
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lint/post-synth.js +84 -2
- package/dist/lint/rules/op/index.d.ts +1 -0
- package/dist/lint/rules/op/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.js +2 -0
- package/dist/lint/rules/op/ops019-workspace-op-target.d.ts +32 -0
- package/dist/lint/rules/op/ops019-workspace-op-target.d.ts.map +1 -0
- package/dist/lint/rules/op/ops019-workspace-op-target.js +63 -0
- package/dist/op/activities/activity-contracts.d.ts +9 -6
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +5 -2
- package/dist/op/activities/index.d.ts +2 -2
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/index.js +1 -1
- package/dist/op/activities/review-agent.d.ts +90 -4
- package/dist/op/activities/review-agent.d.ts.map +1 -1
- package/dist/op/activities/review-agent.js +217 -19
- package/dist/op/activities/work-evidence.d.ts +8 -1
- package/dist/op/activities/work-evidence.d.ts.map +1 -1
- package/dist/op/activities/work-evidence.js +1 -0
- package/dist/op/builders.d.ts +9 -5
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +9 -5
- package/dist/op/composites/apply-op.d.ts +29 -2
- package/dist/op/composites/apply-op.d.ts.map +1 -1
- package/dist/op/composites/apply-op.js +20 -1
- package/dist/op/gate-approval-base.d.ts +25 -0
- package/dist/op/gate-approval-base.d.ts.map +1 -0
- package/dist/op/gate-approval-base.js +71 -0
- package/dist/op/gate-approval.d.ts +18 -9
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-approval.js +13 -31
- package/dist/op/gate-name.d.ts +13 -0
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-name.js +14 -1
- package/dist/op/gate-quorum.d.ts +100 -3
- package/dist/op/gate-quorum.d.ts.map +1 -1
- package/dist/op/gate-quorum.js +108 -6
- package/dist/op/gate-review.d.ts +9 -0
- package/dist/op/gate-review.d.ts.map +1 -1
- package/dist/op/gate-review.js +13 -0
- package/dist/op/gate.d.ts +47 -5
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +25 -4
- package/dist/op/index.d.ts +3 -3
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +1 -1
- package/dist/op/local-executor.d.ts +45 -3
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +68 -7
- package/dist/op/op-waves-run.d.ts +26 -1
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +259 -13
- package/dist/op/op-waves.d.ts +72 -5
- package/dist/op/op-waves.d.ts.map +1 -1
- package/dist/op/op-waves.js +110 -15
- package/dist/op/runtime.d.ts +11 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/schedule-overdue.d.ts +16 -0
- package/dist/op/schedule-overdue.d.ts.map +1 -1
- package/dist/op/schedule-overdue.js +36 -0
- package/dist/op/stage-hooks.d.ts +9 -2
- package/dist/op/stage-hooks.d.ts.map +1 -1
- package/dist/op/stage-hooks.js +7 -5
- package/dist/op/workspace-run-env.d.ts +22 -0
- package/dist/op/workspace-run-env.d.ts.map +1 -0
- package/dist/op/workspace-run-env.js +21 -0
- package/dist/pr-forge.d.ts +24 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +47 -0
- package/dist/pr-loop.d.ts +10 -0
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +4 -0
- package/dist/pr-migrations.d.ts +62 -0
- package/dist/pr-migrations.d.ts.map +1 -0
- package/dist/pr-migrations.js +50 -0
- package/dist/pr-post.d.ts +42 -0
- package/dist/pr-post.d.ts.map +1 -1
- package/dist/pr-post.js +68 -2
- package/dist/read-filter.d.ts +65 -0
- package/dist/read-filter.d.ts.map +1 -0
- package/dist/read-filter.js +179 -0
- package/dist/run-event.d.ts +1 -1
- package/dist/run-event.d.ts.map +1 -1
- package/dist/terraform/carve.d.ts +24 -3
- package/dist/terraform/carve.d.ts.map +1 -1
- package/dist/terraform/carve.js +32 -14
- package/dist/terraform/cuts.d.ts +97 -0
- package/dist/terraform/cuts.d.ts.map +1 -0
- package/dist/terraform/cuts.js +138 -0
- package/dist/terraform/folds.d.ts +26 -0
- package/dist/terraform/folds.d.ts.map +1 -0
- package/dist/terraform/folds.js +38 -0
- package/dist/terraform/graph.d.ts.map +1 -1
- package/dist/terraform/graph.js +10 -0
- package/dist/terraform/reach.d.ts +140 -0
- package/dist/terraform/reach.d.ts.map +1 -0
- package/dist/terraform/reach.js +170 -0
- package/dist/terraform/score.d.ts +14 -0
- package/dist/terraform/score.d.ts.map +1 -1
- package/dist/terraform/score.js +14 -31
- package/dist/terraform/state.d.ts.map +1 -1
- package/dist/terraform/state.js +4 -2
- package/dist/terraform/types.d.ts +8 -0
- package/dist/terraform/types.d.ts.map +1 -1
- package/dist/wave-blocked.d.ts +10 -1
- package/dist/wave-blocked.d.ts.map +1 -1
- package/dist/wave-blocked.js +31 -3
- package/dist/wave-runtime.mjs +264 -254
- package/dist/workspace/check.schema.json +13 -2
- package/dist/workspace/checks/ci-branch.d.ts +44 -0
- package/dist/workspace/checks/ci-branch.d.ts.map +1 -0
- package/dist/workspace/checks/ci-branch.js +133 -0
- package/dist/workspace/checks/gate-approval.d.ts +58 -0
- package/dist/workspace/checks/gate-approval.d.ts.map +1 -0
- package/dist/workspace/checks/gate-approval.js +103 -0
- package/dist/workspace/checks/links.d.ts +3 -0
- package/dist/workspace/checks/links.d.ts.map +1 -1
- package/dist/workspace/checks/links.js +12 -1
- package/dist/workspace/checks/org.d.ts +48 -0
- package/dist/workspace/checks/org.d.ts.map +1 -0
- package/dist/workspace/checks/org.js +156 -0
- package/dist/workspace/checks.d.ts +12 -1
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +63 -7
- package/dist/workspace/ci-branches.d.ts +16 -0
- package/dist/workspace/ci-branches.d.ts.map +1 -1
- package/dist/workspace/ci-branches.js +27 -0
- package/dist/workspace/ci-green-cli.js +7 -7
- package/dist/workspace/ci-green-workflow.d.ts +1 -1
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +13 -8
- package/dist/workspace/ci-green.d.ts +14 -0
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +44 -11
- package/dist/workspace/ci-run.d.ts +9 -1
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +67 -6
- package/dist/workspace/ci-serve.d.ts +9 -0
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +114 -76
- package/dist/workspace/compose-graph.d.ts +14 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/composites.d.ts +4 -1
- package/dist/workspace/composites.d.ts.map +1 -1
- package/dist/workspace/composites.js +5 -2
- package/dist/workspace/composites.schema.json +22 -0
- package/dist/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
- package/dist/workspace/cross-root-lint.d.ts +56 -0
- package/dist/workspace/cross-root-lint.d.ts.map +1 -0
- package/dist/workspace/cross-root-lint.js +111 -0
- package/dist/workspace/dbt-models-reader.d.ts +44 -0
- package/dist/workspace/dbt-models-reader.d.ts.map +1 -0
- package/dist/workspace/dbt-models-reader.js +356 -0
- package/dist/workspace/dbt-project-common.d.ts +40 -0
- package/dist/workspace/dbt-project-common.d.ts.map +1 -0
- package/dist/workspace/dbt-project-common.js +106 -0
- package/dist/workspace/dbt-project-reader.d.ts +4 -3
- package/dist/workspace/dbt-project-reader.d.ts.map +1 -1
- package/dist/workspace/dbt-project-reader.js +14 -108
- package/dist/workspace/decision-rank.d.ts +26 -0
- package/dist/workspace/decision-rank.d.ts.map +1 -0
- package/dist/workspace/decision-rank.js +16 -0
- package/dist/workspace/declaration.d.ts +50 -2
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +57 -11
- package/dist/workspace/declaration.schema.json +166 -24
- package/dist/workspace/environments.d.ts +26 -2
- package/dist/workspace/environments.d.ts.map +1 -1
- package/dist/workspace/environments.js +26 -2
- package/dist/workspace/exit-cli.d.ts +44 -0
- package/dist/workspace/exit-cli.d.ts.map +1 -0
- package/dist/workspace/exit-cli.js +198 -0
- package/dist/workspace/exit.d.ts +159 -0
- package/dist/workspace/exit.d.ts.map +1 -0
- package/dist/workspace/exit.js +480 -0
- package/dist/workspace/exit.schema.json +257 -0
- package/dist/workspace/flyway-dir.d.ts +39 -0
- package/dist/workspace/flyway-dir.d.ts.map +1 -0
- package/dist/workspace/flyway-dir.js +104 -0
- package/dist/workspace/flyway-layout.d.ts +96 -0
- package/dist/workspace/flyway-layout.d.ts.map +1 -0
- package/dist/workspace/flyway-layout.js +308 -0
- package/dist/workspace/graph-cache.d.ts +3 -1
- package/dist/workspace/graph-cache.d.ts.map +1 -1
- package/dist/workspace/graph-cache.js +12 -1
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/graph-cli.js +26 -8
- package/dist/workspace/graph.schema.json +34 -0
- package/dist/workspace/import-fetch.d.ts +38 -0
- package/dist/workspace/import-fetch.d.ts.map +1 -0
- package/dist/workspace/import-fetch.js +99 -0
- package/dist/workspace/import-init.d.ts +26 -0
- package/dist/workspace/import-init.d.ts.map +1 -0
- package/dist/workspace/import-init.js +130 -0
- package/dist/workspace/import-source.d.ts +147 -0
- package/dist/workspace/import-source.d.ts.map +1 -0
- package/dist/workspace/import-source.js +370 -0
- package/dist/workspace/init.d.ts +4 -0
- package/dist/workspace/init.d.ts.map +1 -1
- package/dist/workspace/init.js +70 -3
- package/dist/workspace/intent.d.ts +2 -3
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +5 -9
- package/dist/workspace/kind-link-readers.d.ts +59 -2
- package/dist/workspace/kind-link-readers.d.ts.map +1 -1
- package/dist/workspace/kind-link-readers.js +17 -1
- package/dist/workspace/kind-readers.d.ts +6 -0
- package/dist/workspace/kind-readers.d.ts.map +1 -1
- package/dist/workspace/kind-readers.js +18 -0
- package/dist/workspace/kinds.d.ts +90 -3
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/kinds.js +161 -7
- package/dist/workspace/ledger.schema.json +158 -0
- package/dist/workspace/lexicon-outputs.d.ts +20 -1
- package/dist/workspace/lexicon-outputs.d.ts.map +1 -1
- package/dist/workspace/lexicon-outputs.js +102 -10
- package/dist/workspace/lineage-check.d.ts +3 -0
- package/dist/workspace/lineage-check.d.ts.map +1 -1
- package/dist/workspace/lineage-check.js +23 -4
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.js +13 -4
- package/dist/workspace/links.d.ts +8 -1
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/links.js +25 -1
- package/dist/workspace/ls.d.ts +8 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +7 -1
- package/dist/workspace/ls.schema.json +7 -0
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/member-commands.js +54 -5
- package/dist/workspace/migrate-links.d.ts +45 -0
- package/dist/workspace/migrate-links.d.ts.map +1 -0
- package/dist/workspace/migrate-links.js +100 -0
- package/dist/workspace/migration-dirs.d.ts +74 -0
- package/dist/workspace/migration-dirs.d.ts.map +1 -0
- package/dist/workspace/migration-dirs.js +254 -0
- package/dist/workspace/new-migrations.d.ts +47 -0
- package/dist/workspace/new-migrations.d.ts.map +1 -0
- package/dist/workspace/new-migrations.js +91 -0
- package/dist/workspace/pr-report.schema.json +46 -0
- package/dist/workspace/promote.d.ts +96 -0
- package/dist/workspace/promote.d.ts.map +1 -0
- package/dist/workspace/promote.js +263 -0
- package/dist/workspace/reason-codes.d.ts +21 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +25 -0
- package/dist/workspace/records-cli.d.ts +11 -0
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-cli.js +57 -5
- package/dist/workspace/records.schema.json +5 -0
- package/dist/workspace/release-candidates.d.ts +2 -0
- package/dist/workspace/release-candidates.d.ts.map +1 -1
- package/dist/workspace/release-candidates.js +5 -2
- package/dist/workspace/release-train.d.ts +53 -0
- package/dist/workspace/release-train.d.ts.map +1 -0
- package/dist/workspace/release-train.js +134 -0
- package/dist/workspace/release-workflow.d.ts.map +1 -1
- package/dist/workspace/release-workflow.js +4 -3
- package/dist/workspace/repo-ref.d.ts +45 -0
- package/dist/workspace/repo-ref.d.ts.map +1 -0
- package/dist/workspace/repo-ref.js +114 -0
- package/dist/workspace/run-event.schema.json +2 -2
- package/dist/workspace/runs-cli.d.ts +9 -0
- package/dist/workspace/runs-cli.d.ts.map +1 -1
- package/dist/workspace/runs-cli.js +48 -3
- package/dist/workspace/runs.schema.json +7 -0
- package/dist/workspace/search-contract.d.ts +1 -1
- package/dist/workspace/search-contract.d.ts.map +1 -1
- package/dist/workspace/status-decisions.d.ts +37 -0
- package/dist/workspace/status-decisions.d.ts.map +1 -0
- package/dist/workspace/status-decisions.js +147 -0
- package/dist/workspace/status-gates.d.ts +9 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +4 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +14 -2
- package/dist/workspace/status.schema.json +6 -0
- package/dist/workspace/work-cli.d.ts.map +1 -1
- package/dist/workspace/work-cli.js +5 -3
- package/dist/workspace/work-evidence-ops.d.ts +83 -0
- package/dist/workspace/work-evidence-ops.d.ts.map +1 -0
- package/dist/workspace/work-evidence-ops.js +169 -0
- package/dist/workspace/work-evidence.d.ts +12 -2
- package/dist/workspace/work-evidence.d.ts.map +1 -1
- package/dist/workspace/work-evidence.js +21 -3
- package/dist/workspace/work-evidence.schema.json +49 -2
- package/dist/workspace/workspace-gates.d.ts +110 -0
- package/dist/workspace/workspace-gates.d.ts.map +1 -0
- package/dist/workspace/workspace-gates.js +140 -0
- package/dist/workspace/workspace-kinds.schema.json +108 -10
- package/dist/workspace/workspace-ledger.d.ts +76 -0
- package/dist/workspace/workspace-ledger.d.ts.map +1 -0
- package/dist/workspace/workspace-ledger.js +91 -0
- package/dist/workspace/workspace-run.d.ts +112 -0
- package/dist/workspace/workspace-run.d.ts.map +1 -0
- package/dist/workspace/workspace-run.js +432 -0
- package/package.json +1 -1
- package/src/__golden__/wave-loop/op-by-apply-after-fails.json +10 -0
- package/src/__golden__/wave-loop/op-by-apply-hold.json +10 -0
- package/src/__golden__/wave-loop/op-by-apply-ignored.json +10 -0
- package/src/__golden__/wave-loop/op-by-apply-pass.json +10 -0
- package/src/__golden__/wave-loop/op-by-apply-skip.json +10 -0
- package/src/__golden__/wave-loop/op-cost-member-clash.json +6 -0
- package/src/__golden__/wave-loop/op-cost-member.json +13 -0
- package/src/__golden__/wave-loop/op-denied-source.json +16 -0
- package/src/__golden__/wave-loop/op-denied.json +16 -0
- package/src/__golden__/wave-loop/op-discover-rest-short.json +6 -0
- package/src/__golden__/wave-loop/op-discover-rest-stops.json +13 -0
- package/src/__golden__/wave-loop/op-discover-rest.json +26 -0
- package/src/__golden__/wave-loop/op-discover-wide.json +26 -0
- package/src/__golden__/wave-loop/op-held-on-again.json +16 -0
- package/src/__golden__/wave-loop/op-held-on-before-apply.json +16 -0
- package/src/__golden__/wave-loop/op-held-on.json +16 -0
- package/src/__golden__/wave-loop/op-hooks-apply-fails.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-fail-after-apply.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-fail-before-apply.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-fail-before-plan.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-held.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-pass.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +13 -0
- package/src/__golden__/wave-loop/op-hooks-share.json +33 -0
- package/src/__golden__/wave-loop/op-outcome-held.json +10 -0
- package/src/__golden__/wave-loop/op-outcome-replan-approved.json +10 -0
- package/src/__golden__/wave-loop/op-outcome-replan-forever.json +10 -0
- package/src/__golden__/wave-loop/op-outcome-replan.json +10 -0
- package/src/__golden__/wave-loop/op-outcome-stood-down.json +10 -0
- package/src/__golden__/wave-loop/op-outcome-unreadable.json +10 -0
- package/src/__golden__/wave-loop/op-overridden.json +16 -0
- package/src/__golden__/wave-loop/op-per-run.json +20 -0
- package/src/__golden__/wave-loop/op-point.json +13 -0
- package/src/__golden__/wave-loop/op-pr-review.json +10 -0
- package/src/__golden__/wave-loop/op-sealed.json +10 -0
- package/src/__golden__/wave-loop/op-spend-change-only.json +10 -0
- package/src/__golden__/wave-loop/op-spend.json +10 -0
- package/src/__golden__/wave-loop/op-wave-commands.json +55 -0
- package/src/__golden__/wave-loop/op-wide-refused.json +26 -0
- package/src/__golden__/wave-loop/op-wide-waiting.json +29 -0
- package/src/__golden__/wave-loop/op-wide.json +39 -0
- package/src/action-pins.test.ts +78 -0
- package/src/action-pins.ts +92 -0
- package/src/cli/commands/carve-score-once.test.ts +99 -0
- package/src/cli/commands/carve.ts +37 -6
- package/src/cli/commands/check-lexicon.test.ts +2 -2
- package/src/cli/commands/import-report.ts +71 -0
- package/src/cli/commands/import.ts +33 -3
- package/src/cli/commands/migrate-dialect.test.ts +145 -0
- package/src/cli/commands/migrate.ts +132 -11
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +4 -4
- package/src/cli/handlers/approve.ts +33 -4
- package/src/cli/handlers/build.ts +7 -1
- package/src/cli/handlers/graph-reader.test.ts +31 -0
- package/src/cli/handlers/graph-reader.ts +51 -0
- package/src/cli/handlers/graph.ts +14 -0
- package/src/cli/handlers/ledger.ts +54 -0
- package/src/cli/handlers/migrate.ts +21 -12
- package/src/cli/handlers/misc.ts +10 -1
- package/src/cli/handlers/pr-env.ts +2 -2
- package/src/cli/handlers/pr.ts +176 -37
- package/src/cli/handlers/query.ts +16 -4
- package/src/cli/handlers/release.ts +6 -3
- package/src/cli/handlers/search.ts +13 -34
- package/src/cli/main.test.ts +17 -0
- package/src/cli/main.ts +87 -14
- package/src/cli/parse-args.ts +35 -2
- package/src/cli/registry.ts +22 -4
- package/src/components/builders.ts +8 -0
- package/src/components/capability-plugin-loader.ts +13 -1
- package/src/components/capability-plugin.ts +16 -0
- package/src/components/pr-env-run-bundle.test.ts +1 -0
- package/src/components/pr-hooks.test.ts +73 -5
- package/src/components/pr-hooks.ts +95 -5
- package/src/components/pr-pipeline.ts +64 -16
- package/src/components/pr-policy.test.ts +64 -1
- package/src/components/pr-policy.ts +53 -0
- package/src/components/pr-run.ts +4 -0
- package/src/components/registry.test.ts +1 -1
- package/src/components/starter-plugin.ts +6 -0
- package/src/components/verbs/index.ts +2 -0
- package/src/components/verbs/publish-artifact.test.ts +60 -0
- package/src/components/verbs/publish-artifact.ts +122 -0
- package/src/components/verbs/publish-image.test.ts +101 -0
- package/src/components/verbs/publish-image.ts +265 -0
- package/src/config-environments.ts +28 -1
- package/src/config.ts +24 -1
- package/src/gate-decision-bundle.test.ts +5 -2
- package/src/gate-decision-identity.test.ts +20 -0
- package/src/gate-decision.ts +46 -3
- package/src/graph-ir.ts +27 -0
- package/src/graph-walk.test.ts +44 -0
- package/src/graph-walk.ts +102 -0
- package/src/import/parser.ts +20 -0
- package/src/lexicon.ts +98 -6
- package/src/lifecycle/gate-ledger.ts +8 -0
- package/src/lifecycle/git.ts +2 -2
- package/src/lifecycle/ledger-read.test.ts +107 -0
- package/src/lifecycle/ledger-read.ts +195 -0
- package/src/lifecycle/release-workstation.ts +24 -0
- package/src/lifecycle/run-ledger.ts +5 -2
- package/src/lint/cross-root.test.ts +122 -0
- package/src/lint/discover.ts +3 -1
- package/src/lint/post-synth.ts +212 -2
- package/src/lint/rules/op/index.ts +2 -0
- package/src/lint/rules/op/ops019-workspace-op-target.ts +89 -0
- package/src/op/activities/activity-contracts.ts +5 -2
- package/src/op/activities/index.ts +2 -2
- package/src/op/activities/review-agent.test.ts +221 -4
- package/src/op/activities/review-agent.ts +265 -22
- package/src/op/activities/work-evidence.ts +9 -1
- package/src/op/builders.ts +9 -5
- package/src/op/composites/apply-op.test.ts +108 -0
- package/src/op/composites/apply-op.ts +37 -2
- package/src/op/gate-approval-at-base.test.ts +97 -0
- package/src/op/gate-approval-base.ts +72 -0
- package/src/op/gate-approval.test.ts +6 -1
- package/src/op/gate-approval.ts +22 -27
- package/src/op/gate-name.ts +16 -1
- package/src/op/gate-quorum-at-base.test.ts +211 -0
- package/src/op/gate-quorum.ts +156 -8
- package/src/op/gate-review.ts +14 -0
- package/src/op/gate.ts +69 -9
- package/src/op/index.ts +3 -2
- package/src/op/local-executor.ts +91 -11
- package/src/op/op-waves-depends-on.test.ts +81 -0
- package/src/op/op-waves-hook-targets.test.ts +4 -2
- package/src/op/op-waves-quorum-product.test.ts +158 -0
- package/src/op/op-waves-run.ts +288 -14
- package/src/op/op-waves.ts +143 -14
- package/src/op/runtime.ts +8 -0
- package/src/op/schedule-overdue.ts +33 -0
- package/src/op/stage-hooks.ts +13 -5
- package/src/op/workspace-run-env.ts +29 -0
- package/src/pr-forge-bundle.test.ts +2 -2
- package/src/pr-forge.test.ts +47 -0
- package/src/pr-forge.ts +68 -0
- package/src/pr-loop-bundle.test.ts +3 -2
- package/src/pr-loop.test.ts +52 -0
- package/src/pr-loop.ts +12 -0
- package/src/pr-migrations.ts +97 -0
- package/src/pr-post-open.e2e.test.ts +36 -0
- package/src/pr-post.test.ts +102 -3
- package/src/pr-post.ts +90 -2
- package/src/read-filter.test.ts +54 -0
- package/src/read-filter.ts +193 -0
- package/src/run-event.ts +1 -1
- package/src/stage-hooks-bundle.test.ts +1 -0
- package/src/terraform/carve.ts +52 -15
- package/src/terraform/cuts.test.ts +173 -0
- package/src/terraform/cuts.ts +188 -0
- package/src/terraform/folds.ts +44 -0
- package/src/terraform/graph.ts +9 -0
- package/src/terraform/radius.test.ts +90 -0
- package/src/terraform/reach.test.ts +146 -0
- package/src/terraform/reach.ts +247 -0
- package/src/terraform/score.ts +27 -29
- package/src/terraform/state.ts +3 -1
- package/src/terraform/types.ts +8 -0
- package/src/wave-blocked.ts +28 -3
- package/src/wave-loop-golden.test.ts +4 -3
- package/src/workspace/__fixtures__/import/spacelift.json +9 -0
- package/src/workspace/__fixtures__/import/tfc.json +13 -0
- package/src/workspace/behold-kinds.test.ts +2 -1
- package/src/workspace/check-contract.test.ts +8 -6
- package/src/workspace/check.schema.json +13 -2
- package/src/workspace/checks/ci-branch.test.ts +128 -0
- package/src/workspace/checks/ci-branch.ts +141 -0
- package/src/workspace/checks/gate-approval.test.ts +77 -0
- package/src/workspace/checks/gate-approval.ts +121 -0
- package/src/workspace/checks/links.ts +12 -1
- package/src/workspace/checks/org.ts +169 -0
- package/src/workspace/checks.ts +68 -7
- package/src/workspace/ci-branches.ts +31 -0
- package/src/workspace/ci-green-branches.test.ts +153 -0
- package/src/workspace/ci-green-cli.ts +7 -7
- package/src/workspace/ci-green-workflow.test.ts +4 -3
- package/src/workspace/ci-green-workflow.ts +13 -9
- package/src/workspace/ci-green.ts +49 -12
- package/src/workspace/ci-run.test.ts +72 -1
- package/src/workspace/ci-run.ts +68 -8
- package/src/workspace/ci-serve.test.ts +95 -0
- package/src/workspace/ci-serve.ts +106 -65
- package/src/workspace/component-environments.test.ts +25 -2
- package/src/workspace/compose-graph.ts +9 -2
- package/src/workspace/composites.schema.json +22 -0
- package/src/workspace/composites.test.ts +39 -8
- package/src/workspace/composites.ts +6 -2
- package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +105 -1
- package/src/workspace/cross-root-lint.test.ts +90 -0
- package/src/workspace/cross-root-lint.ts +126 -0
- package/src/workspace/dbt-kind.test.ts +3 -2
- package/src/workspace/dbt-models-reader.ts +362 -0
- package/src/workspace/dbt-models.test.ts +248 -0
- package/src/workspace/dbt-project-common.ts +113 -0
- package/src/workspace/dbt-project-reader.ts +29 -112
- package/src/workspace/decision-rank.ts +40 -0
- package/src/workspace/declaration.schema.json +166 -24
- package/src/workspace/declaration.ts +101 -15
- package/src/workspace/environments.ts +40 -4
- package/src/workspace/exit-cli.ts +219 -0
- package/src/workspace/exit.schema.json +257 -0
- package/src/workspace/exit.test.ts +185 -0
- package/src/workspace/exit.ts +560 -0
- package/src/workspace/flyway-dir.ts +113 -0
- package/src/workspace/flyway-layout.ts +337 -0
- package/src/workspace/graph-cache.ts +12 -2
- package/src/workspace/graph-cli.ts +25 -10
- package/src/workspace/graph-contract.test.ts +4 -2
- package/src/workspace/graph.schema.json +34 -0
- package/src/workspace/identity.test.ts +29 -2
- package/src/workspace/import-fetch.ts +151 -0
- package/src/workspace/import-init.ts +147 -0
- package/src/workspace/import-source.test.ts +183 -0
- package/src/workspace/import-source.ts +462 -0
- package/src/workspace/init.test.ts +55 -1
- package/src/workspace/init.ts +66 -4
- package/src/workspace/intent.ts +6 -14
- package/src/workspace/kind-link-readers.ts +68 -3
- package/src/workspace/kind-readers.test.ts +36 -0
- package/src/workspace/kind-readers.ts +18 -1
- package/src/workspace/kinds.test.ts +35 -7
- package/src/workspace/kinds.ts +212 -11
- package/src/workspace/ledger.schema.json +158 -0
- package/src/workspace/lexicon-outputs.ts +132 -13
- package/src/workspace/lineage-check.ts +30 -7
- package/src/workspace/lineage-upgrade.test.ts +20 -0
- package/src/workspace/lineage-upgrade.ts +16 -7
- package/src/workspace/links.ts +24 -1
- package/src/workspace/ls-contract.test.ts +1 -1
- package/src/workspace/ls.schema.json +7 -0
- package/src/workspace/ls.ts +10 -1
- package/src/workspace/member-commands.ts +60 -2
- package/src/workspace/member-file-schema.test.ts +124 -0
- package/src/workspace/migrate-links.test.ts +99 -0
- package/src/workspace/migrate-links.ts +108 -0
- package/src/workspace/migration-dirs.test.ts +272 -0
- package/src/workspace/migration-dirs.ts +267 -0
- package/src/workspace/new-migrations.test.ts +98 -0
- package/src/workspace/new-migrations.ts +109 -0
- package/src/workspace/org-scope.test.ts +238 -0
- package/src/workspace/pr-report.schema.json +46 -0
- package/src/workspace/promote.test.ts +114 -0
- package/src/workspace/promote.ts +302 -0
- package/src/workspace/reason-codes.test.ts +10 -1
- package/src/workspace/reason-codes.ts +25 -0
- package/src/workspace/records-cli.ts +58 -5
- package/src/workspace/records.schema.json +5 -0
- package/src/workspace/release-candidates.ts +7 -2
- package/src/workspace/release-train.ts +157 -0
- package/src/workspace/release-workflow.ts +4 -3
- package/src/workspace/repo-ref.ts +113 -0
- package/src/workspace/run-event.schema.json +2 -2
- package/src/workspace/runs-cli.ts +48 -4
- package/src/workspace/runs.schema.json +7 -0
- package/src/workspace/status-contract.test.ts +46 -1
- package/src/workspace/status-decisions.test.ts +100 -0
- package/src/workspace/status-decisions.ts +151 -0
- package/src/workspace/status-gates.ts +9 -0
- package/src/workspace/status.schema.json +6 -0
- package/src/workspace/status.ts +17 -3
- package/src/workspace/where-filters.test.ts +39 -0
- package/src/workspace/work-cli.ts +5 -3
- package/src/workspace/work-evidence-ops.test.ts +176 -0
- package/src/workspace/work-evidence-ops.ts +189 -0
- package/src/workspace/work-evidence.schema.json +49 -2
- package/src/workspace/work-evidence.ts +22 -3
- package/src/workspace/workspace-gates.ts +200 -0
- package/src/workspace/workspace-kinds.schema.json +108 -10
- package/src/workspace/workspace-ledger.ts +130 -0
- package/src/workspace/workspace-run.test.ts +322 -0
- package/src/workspace/workspace-run.ts +461 -0
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"name": {
|
|
47
47
|
"type": "string",
|
|
48
48
|
"pattern": "^[a-z][a-z0-9-]{0,39}$",
|
|
49
|
-
"description": "The kind's name, as a member's kind field names it. chant, workspace, other and examples are built in and can't be supplied."
|
|
49
|
+
"description": "The kind's name, as a member's kind field names it. chant, workspace, repo, design, other and examples are built in and can't be supplied."
|
|
50
50
|
},
|
|
51
51
|
"description": {
|
|
52
52
|
"type": "string",
|
|
@@ -82,7 +82,10 @@
|
|
|
82
82
|
"reader": {
|
|
83
83
|
"type": "string",
|
|
84
84
|
"pattern": "^[a-z][a-z0-9-]{0,39}$",
|
|
85
|
-
"description": "A link reader chant ships, by name, that derives a member's links from the member's own files (#3932): dbt-project reads a dbt project's sources. The reader is chant's code and runs offline, parsing files as data; the package supplies only the name. A name this chant doesn't have keeps the kind without a reader, and the load reports it. A chant older than this field fails a kinds file that uses it."
|
|
85
|
+
"description": "A link reader chant ships, by name, that derives a member's links from the member's own files (#3932): dbt-project reads a dbt project's sources; goose-dir, golang-migrate-dir, clickhouse-migrations-dir and flyway-dir list a migrations directory in order (#4400). The reader is chant's code and runs offline, parsing files as data; the package supplies only the name. A name this chant doesn't have keeps the kind without a reader, and the load reports it. A chant older than this field fails a kinds file that uses it."
|
|
86
|
+
},
|
|
87
|
+
"memberFile": {
|
|
88
|
+
"$ref": "#/$defs/memberFile"
|
|
86
89
|
}
|
|
87
90
|
},
|
|
88
91
|
"patternProperties": {
|
|
@@ -159,33 +162,104 @@
|
|
|
159
162
|
}
|
|
160
163
|
},
|
|
161
164
|
"additionalProperties": false
|
|
165
|
+
},
|
|
166
|
+
"anyLine": {
|
|
167
|
+
"type": "object",
|
|
168
|
+
"description": "The directory passes when a file directly in it, whose name matches one of in, has a line that starts with one of starts, after leading space and ignoring case (#4400). A goose directory is found by -- +goose Up. Added by the chant that adds the goose-dir reader.",
|
|
169
|
+
"required": [
|
|
170
|
+
"in",
|
|
171
|
+
"starts"
|
|
172
|
+
],
|
|
173
|
+
"properties": {
|
|
174
|
+
"in": {
|
|
175
|
+
"type": "array",
|
|
176
|
+
"minItems": 1,
|
|
177
|
+
"uniqueItems": true,
|
|
178
|
+
"items": {
|
|
179
|
+
"$ref": "#/$defs/fileName"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"starts": {
|
|
183
|
+
"type": "array",
|
|
184
|
+
"minItems": 1,
|
|
185
|
+
"uniqueItems": true,
|
|
186
|
+
"items": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"minLength": 1
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"additionalProperties": false
|
|
193
|
+
},
|
|
194
|
+
"anyDocument": {
|
|
195
|
+
"type": "object",
|
|
196
|
+
"description": "The directory passes when a file directly in it, whose name matches one of in, parses as YAML or JSON and one of its documents satisfies an entry of match (#3179). An entry maps JSON Pointers to the values allowed there, all of which must hold; a * in a value matches any run of characters, so [\"*\"] means the pointer has a value. A file that does not parse is no evidence either way.",
|
|
197
|
+
"required": [
|
|
198
|
+
"in",
|
|
199
|
+
"match"
|
|
200
|
+
],
|
|
201
|
+
"properties": {
|
|
202
|
+
"in": {
|
|
203
|
+
"type": "array",
|
|
204
|
+
"minItems": 1,
|
|
205
|
+
"uniqueItems": true,
|
|
206
|
+
"items": {
|
|
207
|
+
"$ref": "#/$defs/fileName"
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"match": {
|
|
211
|
+
"type": "array",
|
|
212
|
+
"minItems": 1,
|
|
213
|
+
"items": {
|
|
214
|
+
"type": "object",
|
|
215
|
+
"minProperties": 1,
|
|
216
|
+
"propertyNames": {
|
|
217
|
+
"pattern": "^(/[^/]+)+$"
|
|
218
|
+
},
|
|
219
|
+
"additionalProperties": {
|
|
220
|
+
"type": "array",
|
|
221
|
+
"minItems": 1,
|
|
222
|
+
"uniqueItems": true,
|
|
223
|
+
"items": {
|
|
224
|
+
"type": "string",
|
|
225
|
+
"minLength": 1
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
"additionalProperties": false
|
|
162
232
|
}
|
|
163
233
|
},
|
|
164
234
|
"additionalProperties": false
|
|
165
235
|
},
|
|
166
236
|
"fileName": {
|
|
167
237
|
"type": "string",
|
|
168
|
-
"description": "A file name, with no directory part. A * matches any run of characters.",
|
|
238
|
+
"description": "A file name, with no directory part. A * matches any run of characters, and [0-9] one digit.",
|
|
169
239
|
"pattern": "^(?!\\.\\.?$)[^/\\\\]+$"
|
|
170
240
|
},
|
|
171
241
|
"graph": {
|
|
172
242
|
"type": "object",
|
|
173
|
-
"description": "How chant workspace graph reads a member of this kind (#2874). chant writes a reader project of its own outside the workspace
|
|
174
|
-
"required": [
|
|
175
|
-
"lexicon",
|
|
176
|
-
"config"
|
|
177
|
-
],
|
|
243
|
+
"description": "How chant workspace graph reads a member of this kind (#2874, #4413). chant writes a reader project of its own outside the workspace and runs the member's chant graph there. The project either declares a lexicon and puts config under that lexicon's key (lexicon), or names a graph reader module the supplying package ships (reader). In every string key and value of config, {member} becomes the member's name, {dir} its absolute directory and {workspace} the absolute workspace root. Without this field a member of the kind is listed with kind-not-run.",
|
|
178
244
|
"properties": {
|
|
179
245
|
"lexicon": {
|
|
180
246
|
"type": "string",
|
|
181
247
|
"pattern": "^[a-z][a-z0-9-]{0,39}$",
|
|
182
|
-
"description": "The lexicon that reads the member, by name. The package that supplies the kind must be @intentius/chant-lexicon-<lexicon>, so the lexicon chant loads is the one the declaration pins."
|
|
248
|
+
"description": "The lexicon that reads the member, by name. The package that supplies the kind must be @intentius/chant-lexicon-<lexicon>, so the lexicon chant loads is the one the declaration pins. Not with reader."
|
|
249
|
+
},
|
|
250
|
+
"reader": {
|
|
251
|
+
"type": "string",
|
|
252
|
+
"pattern": "^(?!/)(?!.*\\.\\.)(?!.*\\\\)[^\\u0000-\\u001f]+$",
|
|
253
|
+
"description": "A graph reader module, as a path inside the supplying package (#4413): an ES module whose default export is (input: { dir, config }) => GraphIR. The member's own chant graph loads it in the reader project, so it runs under the member's toolchain, and it must only read files. Any package may supply it, not only a lexicon. Not with lexicon. A chant older than this field can't read a kinds file that uses it."
|
|
183
254
|
},
|
|
184
255
|
"config": {
|
|
185
256
|
"type": "object",
|
|
186
|
-
"description": "
|
|
257
|
+
"description": "With lexicon, the lexicon's config namespace for the reader project, such as terraform's roots. With reader, the config object the module is called with (optional). Placeholders are substituted; nothing else is interpreted."
|
|
187
258
|
}
|
|
188
259
|
},
|
|
260
|
+
"if": { "properties": { "reader": true }, "required": ["reader"] },
|
|
261
|
+
"then": { "not": { "properties": { "lexicon": true }, "required": ["lexicon"] } },
|
|
262
|
+
"else": { "properties": { "lexicon": true, "config": true }, "required": ["lexicon", "config"] },
|
|
189
263
|
"patternProperties": {
|
|
190
264
|
"^x-": true
|
|
191
265
|
},
|
|
@@ -281,6 +355,30 @@
|
|
|
281
355
|
}
|
|
282
356
|
},
|
|
283
357
|
"additionalProperties": false
|
|
358
|
+
},
|
|
359
|
+
"memberFile": {
|
|
360
|
+
"type": "object",
|
|
361
|
+
"description": "A file every member of this kind has, and the JSON Schema it must pass (#3672). chant workspace check reads the file from the member's directory, parses it as YAML when its name ends in .yml or .yaml and as JSON otherwise, and validates it against the schema (WSP012). The schema is a JSON file inside the supplying package, read as data with draft 2020-12. A chant older than this field fails a kinds file that uses it.",
|
|
362
|
+
"required": [
|
|
363
|
+
"path",
|
|
364
|
+
"schema"
|
|
365
|
+
],
|
|
366
|
+
"properties": {
|
|
367
|
+
"path": {
|
|
368
|
+
"type": "string",
|
|
369
|
+
"description": "The file, relative to the member's directory, such as terragucci.yml. Forward slashes; no .. segment and no leading /.",
|
|
370
|
+
"pattern": "^(?!/)(?!.*(^|/)\\.\\.?(/|$))[^\\\\]+$"
|
|
371
|
+
},
|
|
372
|
+
"schema": {
|
|
373
|
+
"type": "string",
|
|
374
|
+
"description": "The JSON Schema the file must pass, relative to the supplying package's directory, such as schemas/terragucci.schema.json. It must be a .json file inside the package.",
|
|
375
|
+
"pattern": "^(?!/)(?!.*(^|/)\\.\\.?(/|$))[^\\\\]+\\.json$"
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
"patternProperties": {
|
|
379
|
+
"^x-": true
|
|
380
|
+
},
|
|
381
|
+
"additionalProperties": false
|
|
284
382
|
}
|
|
285
383
|
}
|
|
286
384
|
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The workspace's own ledger (#2554, #2524 D17, ws-030): `_members/_workspace/`
|
|
3
|
+
* on the `chant/lifecycle` branch, beside every member's `_members/<member>/`.
|
|
4
|
+
* `_workspace` is a reserved member name, so no member writes here.
|
|
5
|
+
*
|
|
6
|
+
* Three stores, each a JSON Lines file appended with the read-modify-write
|
|
7
|
+
* and CAS retry every ledger here uses:
|
|
8
|
+
*
|
|
9
|
+
* - `<env>/runs.jsonl`: one record per `chant workspace run`, naming every
|
|
10
|
+
* `<member>/<op>` step, how it ended, and the member run it started. Each
|
|
11
|
+
* member run cites the workspace run back (`OpRunRecord.workspaceRun`).
|
|
12
|
+
* - `_gates/<gate>.jsonl`: a workspace gate's pending facts and approvals.
|
|
13
|
+
* Each names the member plans it covers and their digest
|
|
14
|
+
* (`./workspace-gates.ts`).
|
|
15
|
+
* - `<env>/trains.jsonl`: release trains, each pinning one release of every
|
|
16
|
+
* `chant` member (`./release-train.ts`).
|
|
17
|
+
*
|
|
18
|
+
* A malformed line is skipped and counted, never thrown on.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { createHash } from "node:crypto";
|
|
22
|
+
import { readBlobBySha, readBlobFromPath, readPathSha, RefCASConflictError, writeBlobToPath, type LedgerLocation } from "../lifecycle/git";
|
|
23
|
+
import { MEMBERS_DIR } from "../lifecycle/member-ledger";
|
|
24
|
+
import { sortedJsonReplacer } from "../utils";
|
|
25
|
+
|
|
26
|
+
/** The reserved member name the workspace's own ledger sits under. */
|
|
27
|
+
export const WORKSPACE_LEDGER_NAME = "_workspace";
|
|
28
|
+
/** The branch prefix of the workspace's ledger. */
|
|
29
|
+
export const WORKSPACE_LEDGER_PREFIX = `${MEMBERS_DIR}/${WORKSPACE_LEDGER_NAME}/`;
|
|
30
|
+
/** The environment directory a run with no `--env` records under, as a member Op run does. */
|
|
31
|
+
export const DEFAULT_WORKSPACE_ENV = "local";
|
|
32
|
+
export const WORKSPACE_GATES_DIR = "_gates";
|
|
33
|
+
|
|
34
|
+
const APPEND_RETRY_ATTEMPTS = 5;
|
|
35
|
+
|
|
36
|
+
function at(cwd: string): LedgerLocation {
|
|
37
|
+
return { cwd, ledgerPrefix: WORKSPACE_LEDGER_PREFIX };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Append one line to `<dir>/<file>` in the workspace ledger. Returns the ledger commit. */
|
|
41
|
+
export async function appendWorkspaceLine(cwd: string, dir: string, file: string, value: unknown, message: string): Promise<string> {
|
|
42
|
+
const json = JSON.stringify(value, sortedJsonReplacer);
|
|
43
|
+
let lastErr: unknown;
|
|
44
|
+
for (let attempt = 1; attempt <= APPEND_RETRY_ATTEMPTS; attempt++) {
|
|
45
|
+
try {
|
|
46
|
+
const priorSha = await readPathSha(dir, file, at(cwd));
|
|
47
|
+
const existing = priorSha ? await readBlobBySha(priorSha, { cwd }) : null;
|
|
48
|
+
const content = `${existing ? `${existing.replace(/\n$/, "")}\n` : ""}${json}\n`;
|
|
49
|
+
return await writeBlobToPath(dir, file, content, message, { ...at(cwd), expectPriorPathSha: priorSha });
|
|
50
|
+
} catch (err) {
|
|
51
|
+
if (!(err instanceof RefCASConflictError)) throw err;
|
|
52
|
+
lastErr = err;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
throw lastErr;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Read every line of `<dir>/<file>` that `accept` takes, oldest first. */
|
|
59
|
+
export async function readWorkspaceLines<T>(cwd: string, dir: string, file: string, accept: (v: unknown) => v is T): Promise<{ records: T[]; malformed: number }> {
|
|
60
|
+
const content = await readBlobFromPath(dir, file, at(cwd));
|
|
61
|
+
if (!content) return { records: [], malformed: 0 };
|
|
62
|
+
const records: T[] = [];
|
|
63
|
+
let malformed = 0;
|
|
64
|
+
for (const line of content.split("\n")) {
|
|
65
|
+
if (!line.trim()) continue;
|
|
66
|
+
try {
|
|
67
|
+
const v = JSON.parse(line) as unknown;
|
|
68
|
+
if (accept(v)) records.push(v);
|
|
69
|
+
else malformed++;
|
|
70
|
+
} catch {
|
|
71
|
+
malformed++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { records, malformed };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** `sha256:<hex>` over a value's canonical JSON (keys sorted). */
|
|
78
|
+
export function canonicalDigest(value: unknown): string {
|
|
79
|
+
return `sha256:${createHash("sha256").update(JSON.stringify(value, sortedJsonReplacer)).digest("hex")}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Runs ─────────────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
/** How one `<member>/<op>` step of a workspace run ended. */
|
|
85
|
+
export type WorkspaceStepStatus = "ok" | "fail" | "gated" | "not-run";
|
|
86
|
+
|
|
87
|
+
export interface WorkspaceRunStep {
|
|
88
|
+
/** `<member>/<op>`. */
|
|
89
|
+
target: string;
|
|
90
|
+
member: string;
|
|
91
|
+
op: string;
|
|
92
|
+
status: WorkspaceStepStatus;
|
|
93
|
+
/** The member's `chant run` exit code; null for a step that did not run. */
|
|
94
|
+
exitCode: number | null;
|
|
95
|
+
/** The member run's id, read back from the member's run ledger, where it cites this run. Null when none was found. */
|
|
96
|
+
memberRun: string | null;
|
|
97
|
+
/** For a gated step: the member gate it stopped at and the plan it waits on. */
|
|
98
|
+
gate?: { op: string; name: string; planDigest: string | null; environment: string | null };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface WorkspaceRunRecord {
|
|
102
|
+
version: 1;
|
|
103
|
+
id: string;
|
|
104
|
+
started: string;
|
|
105
|
+
ended: string;
|
|
106
|
+
/** `gated` when a step stopped at a gate; `fail` when a step failed. A run stops at the first step that does not pass. */
|
|
107
|
+
status: "ok" | "fail" | "gated";
|
|
108
|
+
/** `--env`, or null. */
|
|
109
|
+
env: string | null;
|
|
110
|
+
/** The workspace gate the run was started under (`--gate`), or null. */
|
|
111
|
+
gate: string | null;
|
|
112
|
+
/** `--dependents-of`, the member whose dependents the run ran, or null. */
|
|
113
|
+
dependentsOf: string | null;
|
|
114
|
+
steps: WorkspaceRunStep[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const RUNS_FILE = "runs.jsonl";
|
|
118
|
+
|
|
119
|
+
function isRunRecord(v: unknown): v is WorkspaceRunRecord {
|
|
120
|
+
const r = v as WorkspaceRunRecord;
|
|
121
|
+
return typeof r === "object" && r !== null && r.version === 1 && typeof r.id === "string" && Array.isArray(r.steps);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function appendWorkspaceRun(cwd: string, record: WorkspaceRunRecord): Promise<string> {
|
|
125
|
+
return appendWorkspaceLine(cwd, record.env ?? DEFAULT_WORKSPACE_ENV, RUNS_FILE, record, `Workspace run record: ${record.id}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export async function readWorkspaceRuns(cwd: string, env: string = DEFAULT_WORKSPACE_ENV): Promise<{ records: WorkspaceRunRecord[]; malformed: number }> {
|
|
129
|
+
return readWorkspaceLines(cwd, env, RUNS_FILE, isRunRecord);
|
|
130
|
+
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Ops (#2554, #2524 D17, ws-030, ws-039): OPS019 over the composed
|
|
3
|
+
* graph, the run's order and dependents, its ledger and the member records
|
|
4
|
+
* that cite it, the workspace gate rule, and release trains.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, test, expect, afterEach } from "vitest";
|
|
7
|
+
import { spawnSync } from "node:child_process";
|
|
8
|
+
import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { runCrossRootChecks } from "../lint/post-synth";
|
|
12
|
+
import { ops019Check, parseWorkspaceOpTarget } from "../lint/rules/op/ops019-workspace-op-target";
|
|
13
|
+
import { evaluateGate, memoryGateLedgerPort } from "../op/gate";
|
|
14
|
+
import { buildRunRecord } from "../lifecycle/run-ledger";
|
|
15
|
+
import { writeLedgerFiles } from "../lifecycle/git";
|
|
16
|
+
import { resolveWorkspaceRun, WORKSPACE_GATE_ENV, WORKSPACE_RUN_ENV } from "../op/workspace-run-env";
|
|
17
|
+
import type { WorkspaceGraph } from "./compose-graph";
|
|
18
|
+
import { crossRootInputs, dependentsOf, orderTargets, workspaceRun, type MemberOpResult } from "./workspace-run";
|
|
19
|
+
import { readWorkspaceRuns } from "./workspace-ledger";
|
|
20
|
+
import { approveWorkspaceGate, readWorkspaceGate, workspaceGateDigest, workspaceGateStandsIn, type WorkspaceGateApproval } from "./workspace-gates";
|
|
21
|
+
import { pickTrain } from "./release-train";
|
|
22
|
+
import { decideRootCommand } from "./root-refusal";
|
|
23
|
+
|
|
24
|
+
const PLAN_A = `sha256:${"a".repeat(64)}`;
|
|
25
|
+
const PLAN_B = `sha256:${"b".repeat(64)}`;
|
|
26
|
+
|
|
27
|
+
function opNode(member: string, name: string, env?: string) {
|
|
28
|
+
return { id: `${member}/${name}`, kind: "Chant::Op", lexicon: "chant", member, attrs: { name, overview: "t", phases: [], ...(env ? { labels: { Env: env } } : {}) } };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function member(name: string, status: "composed" | "skipped" | "failed" = "composed", kind = "chant") {
|
|
32
|
+
return { name, dir: name, kind, status, reason: null, chant: null, irVersion: 1, live: false };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function link(producer: string, consumer: string) {
|
|
36
|
+
return { consumer, producer, output: "out", kind: "depends-on", origin: "declared", resolves: "source", status: "resolved", label: "exact", input: null, reason: null };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** network <- api <- web, and docs (other) composes nothing. */
|
|
40
|
+
function graph(): WorkspaceGraph {
|
|
41
|
+
return {
|
|
42
|
+
version: 1,
|
|
43
|
+
workspace: { name: "acme", root: "." },
|
|
44
|
+
members: [member("network"), member("api"), member("web"), member("docs", "skipped", "other")],
|
|
45
|
+
nodes: [opNode("network", "deploy"), opNode("api", "deploy"), opNode("api", "migrate", "prod"), opNode("web", "deploy"), { id: "api/bucket", kind: "AWS::S3::Bucket", lexicon: "aws", member: "api", attrs: {} }],
|
|
46
|
+
edges: [],
|
|
47
|
+
groups: { byMember: {} },
|
|
48
|
+
exports: [],
|
|
49
|
+
imports: [],
|
|
50
|
+
links: [link("network", "api"), link("api", "web")],
|
|
51
|
+
collectors: [],
|
|
52
|
+
records: [],
|
|
53
|
+
} as unknown as WorkspaceGraph;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
describe("OPS019: workspace Op targets against the composed graph", () => {
|
|
57
|
+
test("parses <member>/<op> at the first slash", () => {
|
|
58
|
+
expect(parseWorkspaceOpTarget("api/deploy")).toEqual({ member: "api", op: "deploy" });
|
|
59
|
+
expect(parseWorkspaceOpTarget("api/ns/op")).toEqual({ member: "api", op: "ns/op" });
|
|
60
|
+
expect(parseWorkspaceOpTarget("deploy")).toBeNull();
|
|
61
|
+
expect(parseWorkspaceOpTarget("/deploy")).toBeNull();
|
|
62
|
+
expect(parseWorkspaceOpTarget("api/")).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("each composed chant member is one root, with its Ops among the entities", () => {
|
|
66
|
+
const roots = crossRootInputs(graph());
|
|
67
|
+
expect(roots.map((r) => r.root)).toEqual(["network", "api", "web"]);
|
|
68
|
+
const api = roots.find((r) => r.root === "api")!;
|
|
69
|
+
expect([...api.buildResult.entities.keys()].sort()).toEqual(["bucket", "deploy", "migrate"]);
|
|
70
|
+
expect(api.buildResult.entities.get("deploy")!.entityType).toBe("Chant::Op");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("a target naming a declared Op passes; an unknown Op or a member not composed fails", () => {
|
|
74
|
+
const roots = crossRootInputs(graph());
|
|
75
|
+
const ok = runCrossRootChecks([ops019Check([{ member: "api", op: "deploy" }])], roots);
|
|
76
|
+
expect(ok).toEqual([]);
|
|
77
|
+
const bad = runCrossRootChecks([ops019Check([{ member: "api", op: "nope" }, { member: "docs", op: "deploy" }, { member: "ghost", op: "x" }])], roots);
|
|
78
|
+
expect(bad.map((d) => d.checkId)).toEqual(["OPS019", "OPS019", "OPS019"]);
|
|
79
|
+
expect(bad[0]!.message).toContain('declares no Op named "nope"');
|
|
80
|
+
expect(bad[0]!.message).toContain("deploy, migrate");
|
|
81
|
+
expect(bad[0]!.root).toBe("api");
|
|
82
|
+
expect(bad[1]!.message).toContain('member "docs" is not in the composed graph');
|
|
83
|
+
expect(bad[2]!.missing).toEqual({ kind: "member", scope: "ghost" });
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("order and dependents", () => {
|
|
88
|
+
test("dependents follow member links transitively, nearest first", () => {
|
|
89
|
+
expect(dependentsOf("network", graph().links)).toEqual(["api", "web"]);
|
|
90
|
+
expect(dependentsOf("web", graph().links)).toEqual([]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("a producer's target runs before its consumers', whatever order they were named in", () => {
|
|
94
|
+
const order = orderTargets([{ member: "web", op: "deploy" }, { member: "network", op: "deploy" }, { member: "api", op: "deploy" }], graph().links);
|
|
95
|
+
expect(order.map((t) => t.member)).toEqual(["network", "api", "web"]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("unlinked targets keep the order given", () => {
|
|
99
|
+
const order = orderTargets([{ member: "web", op: "deploy" }, { member: "docs", op: "x" }], []);
|
|
100
|
+
expect(order.map((t) => t.member)).toEqual(["web", "docs"]);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// ── Runs, against a real chant/lifecycle branch ──────────────────────────────
|
|
105
|
+
|
|
106
|
+
const dirs: string[] = [];
|
|
107
|
+
afterEach(() => {
|
|
108
|
+
for (const d of dirs.splice(0)) rmSync(d, { recursive: true, force: true });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
function git(args: string[], cwd: string): string {
|
|
112
|
+
const r = spawnSync("git", args, { cwd, encoding: "utf-8" });
|
|
113
|
+
if (r.status !== 0) throw new Error(`git ${args.join(" ")}: ${r.stderr}`);
|
|
114
|
+
return r.stdout;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function repo(): string {
|
|
118
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), "chant-ws-run-")));
|
|
119
|
+
dirs.push(dir);
|
|
120
|
+
git(["init", "-q", "-b", "main"], dir);
|
|
121
|
+
git(["config", "user.email", "test@chant.dev"], dir);
|
|
122
|
+
git(["config", "user.name", "Test"], dir);
|
|
123
|
+
writeFileSync(join(dir, "README.md"), "x\n");
|
|
124
|
+
git(["add", "."], dir);
|
|
125
|
+
git(["commit", "-q", "-m", "init"], dir);
|
|
126
|
+
return dir;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* A member runner that behaves like `chant run`: it writes the member's run
|
|
131
|
+
* record, citing the workspace run it found in its environment, and for a
|
|
132
|
+
* gated exit a pending gate fact with a plan digest.
|
|
133
|
+
*/
|
|
134
|
+
function fakeMembers(cwd: string, exits: Record<string, number>, seen: Array<{ member: string; argv: string[]; env: Record<string, string> }>) {
|
|
135
|
+
let n = 0;
|
|
136
|
+
return async (m: { name: string; dir: string }, argv: string[], env: Record<string, string>): Promise<MemberOpResult> => {
|
|
137
|
+
seen.push({ member: m.name, argv, env });
|
|
138
|
+
const op = argv[1]!;
|
|
139
|
+
const exitCode = exits[`${m.name}/${op}`] ?? 0;
|
|
140
|
+
const cite = resolveWorkspaceRun(env)!;
|
|
141
|
+
const id = `run-${++n}`;
|
|
142
|
+
const record = {
|
|
143
|
+
version: 1,
|
|
144
|
+
...buildRunRecord({ name: op, labels: {} }, [], {
|
|
145
|
+
started: "2026-10-23T00:00:00Z",
|
|
146
|
+
ended: "2026-10-23T00:00:01Z",
|
|
147
|
+
status: exitCode === 3 ? "gated" : exitCode === 0 ? "ok" : "fail",
|
|
148
|
+
id,
|
|
149
|
+
workspaceRun: cite,
|
|
150
|
+
...(exitCode === 3 ? { gate: { name: "approve", since: "2026-10-23T00:00:01Z" } } : {}),
|
|
151
|
+
}),
|
|
152
|
+
};
|
|
153
|
+
const files = [{ path: `_members/${m.name}/local/runs__${op}.jsonl`, content: `${JSON.stringify(record)}\n` }];
|
|
154
|
+
if (exitCode === 3) {
|
|
155
|
+
const pending = { version: 1, kind: "pending", op, gate: "approve", timestamp: "2026-10-23T00:00:01Z", expiresAt: "2026-10-30T00:00:01Z", runId: id, planDigest: m.name === "api" ? PLAN_A : PLAN_B };
|
|
156
|
+
files.push({ path: `_members/${m.name}/_gates/${op}.jsonl`, content: `${JSON.stringify(pending)}\n` });
|
|
157
|
+
}
|
|
158
|
+
await writeLedgerFiles(files, `fake run ${id}`, { cwd });
|
|
159
|
+
return { exitCode, stdout: `ran ${op}\n`, stderr: "" };
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
describe("chant workspace run", () => {
|
|
164
|
+
test("refuses before running anything when OPS019 finds a target that doesn't exist", async () => {
|
|
165
|
+
const seen: Array<{ member: string; argv: string[]; env: Record<string, string> }> = [];
|
|
166
|
+
const cwd = repo();
|
|
167
|
+
const out = await workspaceRun({ cwd, targets: ["api/deploy", "api/nope"], readGraph: async () => ({ graph: graph(), failed: false }), runMember: fakeMembers(cwd, {}, seen) });
|
|
168
|
+
expect(out.kind).toBe("refused");
|
|
169
|
+
if (out.kind === "refused") expect(out.findings.map((f) => f.checkId)).toEqual(["OPS019"]);
|
|
170
|
+
expect(seen).toEqual([]);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("runs each target under the workspace run's id, in link order, and records it under _members/_workspace/", async () => {
|
|
174
|
+
const seen: Array<{ member: string; argv: string[]; env: Record<string, string> }> = [];
|
|
175
|
+
const cwd = repo();
|
|
176
|
+
const out = await workspaceRun({ cwd, targets: ["web/deploy", "api/deploy"], readGraph: async () => ({ graph: graph(), failed: false }), runMember: fakeMembers(cwd, {}, seen) });
|
|
177
|
+
expect(out.kind).toBe("ran");
|
|
178
|
+
if (out.kind !== "ran") return;
|
|
179
|
+
expect(seen.map((s) => s.member)).toEqual(["api", "web"]);
|
|
180
|
+
expect(seen[0]!.argv).toEqual(["run", "deploy"]);
|
|
181
|
+
expect(seen.every((s) => s.env[WORKSPACE_RUN_ENV] === out.record.id)).toBe(true);
|
|
182
|
+
expect(out.record.status).toBe("ok");
|
|
183
|
+
// Each member record cites the workspace run, and the workspace record names each member run.
|
|
184
|
+
expect(out.record.steps.map((s) => [s.target, s.status, s.memberRun])).toEqual([["api/deploy", "ok", "run-1"], ["web/deploy", "ok", "run-2"]]);
|
|
185
|
+
expect(out.ledgerError).toBeNull();
|
|
186
|
+
expect(git(["show", "chant/lifecycle:_members/_workspace/local/runs.jsonl"], cwd)).toContain(out.record.id);
|
|
187
|
+
const api = JSON.parse(git(["show", "chant/lifecycle:_members/api/local/runs__deploy.jsonl"], cwd)) as { workspaceRun: { id: string } };
|
|
188
|
+
expect(api.workspaceRun.id).toBe(out.record.id);
|
|
189
|
+
expect((await readWorkspaceRuns(cwd)).records.map((r) => r.id)).toEqual([out.record.id]);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("stops at the first step that fails", async () => {
|
|
193
|
+
const seen: Array<{ member: string; argv: string[]; env: Record<string, string> }> = [];
|
|
194
|
+
const cwd = repo();
|
|
195
|
+
const out = await workspaceRun({ cwd, targets: ["network/deploy", "api/deploy", "web/deploy"], readGraph: async () => ({ graph: graph(), failed: false }), runMember: fakeMembers(cwd, { "api/deploy": 1 }, seen) });
|
|
196
|
+
if (out.kind !== "ran") throw new Error(out.kind);
|
|
197
|
+
expect(out.record.status).toBe("fail");
|
|
198
|
+
expect(out.record.steps.map((s) => s.status)).toEqual(["ok", "fail", "not-run"]);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("--dependents-of runs the Op in every dependent, not in the member itself, and skips a dependent without it", async () => {
|
|
202
|
+
const g = graph();
|
|
203
|
+
g.nodes = g.nodes.filter((n) => n.id !== "web/deploy");
|
|
204
|
+
const seen: Array<{ member: string; argv: string[]; env: Record<string, string> }> = [];
|
|
205
|
+
const cwd = repo();
|
|
206
|
+
const planned = await workspaceRun({ cwd, targets: [], dependentsOf: "network", op: "deploy", dryRun: true, readGraph: async () => ({ graph: g, failed: false }), runMember: fakeMembers(cwd, {}, seen) });
|
|
207
|
+
expect(planned).toMatchObject({ kind: "planned", targets: ["api/deploy"], skipped: [{ member: "web", reason: 'declares no Op named "deploy"' }] });
|
|
208
|
+
expect(seen).toEqual([]);
|
|
209
|
+
const refused = await workspaceRun({ cwd, targets: [], dependentsOf: "network", readGraph: async () => ({ graph: g, failed: false }) });
|
|
210
|
+
expect(refused).toMatchObject({ kind: "refused", message: "--dependents-of needs the Op to run: --op <op>" });
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("with --gate, gated members are gathered into one workspace gate pending fact over their plans", async () => {
|
|
214
|
+
const seen: Array<{ member: string; argv: string[]; env: Record<string, string> }> = [];
|
|
215
|
+
const cwd = repo();
|
|
216
|
+
const out = await workspaceRun({ cwd, targets: ["api/deploy", "web/deploy"], gate: "release", readGraph: async () => ({ graph: graph(), failed: false }), runMember: fakeMembers(cwd, { "api/deploy": 3, "web/deploy": 3 }, seen) });
|
|
217
|
+
if (out.kind !== "ran") throw new Error(out.kind);
|
|
218
|
+
expect(seen.every((s) => s.env[WORKSPACE_GATE_ENV] === "release")).toBe(true);
|
|
219
|
+
expect(out.record.status).toBe("gated");
|
|
220
|
+
expect(out.record.steps.map((s) => s.status)).toEqual(["gated", "gated"]);
|
|
221
|
+
expect(out.pending?.covers).toEqual([
|
|
222
|
+
{ member: "api", op: "deploy", gate: "approve", planDigest: PLAN_A },
|
|
223
|
+
{ member: "web", op: "deploy", gate: "approve", planDigest: PLAN_B },
|
|
224
|
+
]);
|
|
225
|
+
const approval = await approveWorkspaceGate(cwd, { gate: "release", approvedBy: "alice", timestamp: "2026-10-23T01:00:00Z" });
|
|
226
|
+
expect(approval.digest).toBe(out.pending!.digest);
|
|
227
|
+
expect((await readWorkspaceGate(cwd, "release")).approvals).toHaveLength(1);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe("a workspace gate never satisfies a member gate unless it covers the plan and the member opts in at base (ws-039)", () => {
|
|
232
|
+
const covers = [{ member: "api", op: "deploy", gate: "approve", planDigest: PLAN_A }];
|
|
233
|
+
const approval: WorkspaceGateApproval = { version: 1, kind: "approval", gate: "release", digest: workspaceGateDigest(covers), covers, approvedBy: "alice", timestamp: "2026-10-23T01:00:00Z" };
|
|
234
|
+
const q = { op: "deploy", gate: "approve", planDigest: PLAN_A };
|
|
235
|
+
|
|
236
|
+
test("a member that has not opted in is never satisfied", () => {
|
|
237
|
+
expect(workspaceGateStandsIn(q, { member: "api", optedIn: false, approvals: [approval] })).toBeNull();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("an opted-in member is satisfied only for the plan the approval covers", () => {
|
|
241
|
+
expect(workspaceGateStandsIn(q, { member: "api", optedIn: true, approvals: [approval] })).toBe(approval);
|
|
242
|
+
expect(workspaceGateStandsIn({ ...q, planDigest: PLAN_B }, { member: "api", optedIn: true, approvals: [approval] })).toBeNull();
|
|
243
|
+
expect(workspaceGateStandsIn(q, { member: "web", optedIn: true, approvals: [approval] })).toBeNull();
|
|
244
|
+
expect(workspaceGateStandsIn({ ...q, gate: "other" }, { member: "api", optedIn: true, approvals: [approval] })).toBeNull();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("covers edited after the approval no longer hash to its digest, and count for nothing", () => {
|
|
248
|
+
const edited = { ...approval, covers: [{ ...covers[0]!, planDigest: PLAN_B }] };
|
|
249
|
+
expect(workspaceGateStandsIn({ ...q, planDigest: PLAN_B }, { member: "api", optedIn: true, approvals: [edited] })).toBeNull();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("evaluateGate passes a plan-bound gate via the workspace only when the stand-in answers, and writes nothing to the member's ledger", async () => {
|
|
253
|
+
const port = memoryGateLedgerPort();
|
|
254
|
+
const standIn = { standIn: async () => ({ gate: "release", digest: approval.digest, approvedBy: "alice", timestamp: approval.timestamp }) };
|
|
255
|
+
const passed = await evaluateGate(port, { op: "deploy", gate: "approve", planDigest: PLAN_A, now: "2026-10-23T02:00:00Z", workspace: standIn });
|
|
256
|
+
expect(passed).toMatchObject({ satisfied: true, via: "workspace", resolution: { resolvedBy: "alice", workspaceGate: { gate: "release", digest: approval.digest } } });
|
|
257
|
+
expect(port.resolved).toEqual([]);
|
|
258
|
+
|
|
259
|
+
const refused = await evaluateGate(memoryGateLedgerPort(), { op: "deploy", gate: "approve", planDigest: PLAN_A, now: "2026-10-23T02:00:00Z", workspace: { standIn: async () => null } });
|
|
260
|
+
expect(refused.satisfied).toBe(false);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("a gate bound to no plan never asks the workspace", async () => {
|
|
264
|
+
let asked = false;
|
|
265
|
+
const check = await evaluateGate(memoryGateLedgerPort(), { op: "deploy", gate: "approve", now: "2026-10-23T02:00:00Z", workspace: { standIn: async () => { asked = true; return null; } } });
|
|
266
|
+
expect(check.satisfied).toBe(false);
|
|
267
|
+
expect(asked).toBe(false);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe("member run records cite the workspace run", () => {
|
|
272
|
+
test("the environment names the run and its gate; a plain run names neither", () => {
|
|
273
|
+
expect(resolveWorkspaceRun({ [WORKSPACE_RUN_ENV]: "ws-1", [WORKSPACE_GATE_ENV]: "release" })).toEqual({ id: "ws-1", gate: "release" });
|
|
274
|
+
expect(resolveWorkspaceRun({})).toBeUndefined();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("buildRunRecord carries the citation only when given one", () => {
|
|
278
|
+
const base = { started: "a", ended: "b", status: "ok" as const };
|
|
279
|
+
expect(buildRunRecord({ name: "deploy" }, [], { ...base, workspaceRun: { id: "ws-1" } }).workspaceRun).toEqual({ id: "ws-1" });
|
|
280
|
+
expect("workspaceRun" in buildRunRecord({ name: "deploy" }, [], base)).toBe(false);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
describe("release trains", () => {
|
|
285
|
+
const rel = (member: string, digest: string, label: string | null) => ({ member, releaseDigest: digest, commit: "c".repeat(40), label });
|
|
286
|
+
test("pins the newest release of every member", () => {
|
|
287
|
+
const picked = pickTrain([
|
|
288
|
+
{ member: "api", releases: [rel("api", "sha256:2", "v2"), rel("api", "sha256:1", "v1")] },
|
|
289
|
+
{ member: "web", releases: [rel("web", "sha256:9", null)] },
|
|
290
|
+
]);
|
|
291
|
+
expect(picked).toEqual({ members: [rel("api", "sha256:2", "v2"), rel("web", "sha256:9", null)] });
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("a pin picks a release by label or digest", () => {
|
|
295
|
+
const picked = pickTrain([{ member: "api", releases: [rel("api", "sha256:2", "v2"), rel("api", "sha256:1", "v1")] }], new Map([["api", "v1"]]));
|
|
296
|
+
expect(picked).toEqual({ members: [rel("api", "sha256:1", "v1")] });
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("refuses when any member has no release, so a train never leaves one out", () => {
|
|
300
|
+
const picked = pickTrain([{ member: "api", releases: [rel("api", "sha256:2", "v2")] }, { member: "web", releases: [] }], new Map([["ghost", "v1"]]));
|
|
301
|
+
expect(picked).toEqual({ problems: ["--pin ghost: not a chant member of the workspace", "member web has no release; cut one with chant release cut in its directory"] });
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe("chant run at a declared root keeps the root's own Ops", () => {
|
|
306
|
+
test("Op discovery at the root finds the root's Ops and none of a member's", async () => {
|
|
307
|
+
const dir = repo();
|
|
308
|
+
const op = (name: string) => `export default { props: { name: ${JSON.stringify(name)}, overview: "t", phases: [{ name: "Run", steps: [] }] } };\n`;
|
|
309
|
+
writeFileSync(join(dir, "chant.config.json"), JSON.stringify({ lexicons: [] }));
|
|
310
|
+
writeFileSync(join(dir, "chant.workspace.json"), JSON.stringify({ name: "acme", schema: 1, members: [{ name: "api", dir: "api", kind: "chant" }] }));
|
|
311
|
+
mkdirSync(join(dir, "ops"), { recursive: true });
|
|
312
|
+
writeFileSync(join(dir, "ops", "root.op.ts"), op("root-op"));
|
|
313
|
+
mkdirSync(join(dir, "api", "ops"), { recursive: true });
|
|
314
|
+
writeFileSync(join(dir, "api", "chant.config.json"), JSON.stringify({ lexicons: [] }));
|
|
315
|
+
writeFileSync(join(dir, "api", "ops", "deploy.op.ts"), op("deploy"));
|
|
316
|
+
const { discoverOps } = await import("../op/discover");
|
|
317
|
+
const { ops } = await discoverOps({ cwd: dir });
|
|
318
|
+
expect([...ops.keys()]).toEqual(["root-op"]);
|
|
319
|
+
// A root build is guarded (WSP000); `run` is not, so it runs the root's Ops above.
|
|
320
|
+
expect(decideRootCommand({ verb: "build", target: dir, workspaceDir: dir, rootOnly: false }).action).toBe("refuse");
|
|
321
|
+
});
|
|
322
|
+
});
|