@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
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evidence that cites an ops result (#3171): a run on the Op run ledger, a
|
|
3
|
+
* deploy on an environment's release ledger, a gate answer on a gate ledger,
|
|
4
|
+
* or a release plan in `_plans/`. `attachWorkEvidence` resolves the citation
|
|
5
|
+
* here before it writes anything, so a criterion is only ticked by a record
|
|
6
|
+
* the `chant/lifecycle` branch really holds.
|
|
7
|
+
*
|
|
8
|
+
* The citation names the record the way `chant workspace graph --intent`'s
|
|
9
|
+
* `trace` names its links (#4023): `ref` is the ledger file on the branch,
|
|
10
|
+
* `field` and `value` are what picks the record out of it, and `sha256` is
|
|
11
|
+
* the hash of the record as stored, a ledger line or a plan file. A citation
|
|
12
|
+
* that comes with a `sha256` is refused when the record no longer hashes to
|
|
13
|
+
* it.
|
|
14
|
+
*
|
|
15
|
+
* A run says what it showed: an `ok` run is passing evidence and a `fail`
|
|
16
|
+
* run failing evidence, and the result given must agree. A run that is
|
|
17
|
+
* still gated or waiting has no outcome yet and is refused. A deploy, a gate
|
|
18
|
+
* answer and a plan show what they record; whether that meets the criterion
|
|
19
|
+
* is the citer's `result`.
|
|
20
|
+
*
|
|
21
|
+
* Everything is read from the local `chant/lifecycle` branch. Nothing is
|
|
22
|
+
* fetched, the stance `graph --intent` takes.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { createHash } from "node:crypto";
|
|
26
|
+
import { ledgerDir, readBlobFromPath } from "../lifecycle/git";
|
|
27
|
+
import type { EvidenceResult } from "./work";
|
|
28
|
+
|
|
29
|
+
/** The ledgers evidence can cite. */
|
|
30
|
+
export const OPS_EVIDENCE_LEDGERS = ["run", "deploy", "gate", "plan"] as const;
|
|
31
|
+
export type OpsEvidenceLedger = (typeof OPS_EVIDENCE_LEDGERS)[number];
|
|
32
|
+
|
|
33
|
+
/** What a caller cites: which record, in which ledger, and optionally the hash it expects. */
|
|
34
|
+
export type OpsCitation = {
|
|
35
|
+
/** The workspace member whose ledger holds the record, when it is not the flat layout. */
|
|
36
|
+
member?: string;
|
|
37
|
+
/** The hash of the record as stored, when the caller already has it. */
|
|
38
|
+
sha256?: string;
|
|
39
|
+
} & (
|
|
40
|
+
| { ledger: "run"; env: string; op: string; id: string }
|
|
41
|
+
| { ledger: "deploy"; env: string; runId: string; component?: string }
|
|
42
|
+
| { ledger: "gate"; op: string; gate: string; at: string }
|
|
43
|
+
| { ledger: "plan"; digest: string }
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
/** The citation as the evidence entry stores it, once it resolved. */
|
|
47
|
+
export interface OpsEvidence {
|
|
48
|
+
ledger: OpsEvidenceLedger;
|
|
49
|
+
/** The ledger file, as `chant/lifecycle:<path>`. */
|
|
50
|
+
ref: string;
|
|
51
|
+
/** The field of the record that picked it out, and its value. */
|
|
52
|
+
field: string;
|
|
53
|
+
value: string;
|
|
54
|
+
/** Hex SHA-256 of the record as stored. */
|
|
55
|
+
sha256: string;
|
|
56
|
+
/** The environment, for a run or a deploy. */
|
|
57
|
+
env?: string;
|
|
58
|
+
/** The run's status, for a run. */
|
|
59
|
+
outcome?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type OpsCitationError = { code: "write-input-invalid" | "work-evidence-ops-missing" | "work-evidence-ops-mismatch" | "work-evidence-ops-unfinished"; message: string };
|
|
63
|
+
|
|
64
|
+
const NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
65
|
+
const HEX64 = /^[0-9a-f]{64}$/;
|
|
66
|
+
|
|
67
|
+
const sha256 = (s: string): string => createHash("sha256").update(s).digest("hex");
|
|
68
|
+
|
|
69
|
+
function lines(text: string): { line: string; rec: Record<string, unknown> }[] {
|
|
70
|
+
const out: { line: string; rec: Record<string, unknown> }[] = [];
|
|
71
|
+
for (const raw of text.split("\n")) {
|
|
72
|
+
const line = raw.trim();
|
|
73
|
+
if (line === "") continue;
|
|
74
|
+
try {
|
|
75
|
+
const rec: unknown = JSON.parse(line);
|
|
76
|
+
if (rec !== null && typeof rec === "object" && !Array.isArray(rec)) out.push({ line, rec: rec as Record<string, unknown> });
|
|
77
|
+
} catch {
|
|
78
|
+
// A malformed line is skipped, as every ledger reader skips it.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Check a citation's shape, before anything is read. Returns the message for a bad one. */
|
|
85
|
+
export function opsCitationProblem(cite: unknown): string | null {
|
|
86
|
+
if (cite === null || typeof cite !== "object" || Array.isArray(cite)) return "ops is an object naming a ledger";
|
|
87
|
+
const c = cite as Record<string, unknown>;
|
|
88
|
+
const str = (k: string) => typeof c[k] === "string" && (c[k] as string) !== "";
|
|
89
|
+
const name = (k: string) => typeof c[k] === "string" && NAME.test(c[k] as string);
|
|
90
|
+
if (!(OPS_EVIDENCE_LEDGERS as readonly string[]).includes(c.ledger as string)) return `ops.ledger is one of ${OPS_EVIDENCE_LEDGERS.join(", ")}, not ${JSON.stringify(c.ledger)}`;
|
|
91
|
+
if (c.member !== undefined && !name("member")) return "ops.member is a member name";
|
|
92
|
+
if (c.sha256 !== undefined && !(typeof c.sha256 === "string" && HEX64.test(c.sha256))) return "ops.sha256 is a hex SHA-256";
|
|
93
|
+
const allowed: Record<OpsEvidenceLedger, { names: string[]; strings: string[]; optional: string[] }> = {
|
|
94
|
+
run: { names: ["env", "op"], strings: ["id"], optional: [] },
|
|
95
|
+
deploy: { names: ["env"], strings: ["runId"], optional: ["component"] },
|
|
96
|
+
gate: { names: ["op"], strings: ["gate", "at"], optional: [] },
|
|
97
|
+
plan: { names: [], strings: ["digest"], optional: [] },
|
|
98
|
+
};
|
|
99
|
+
const spec = allowed[c.ledger as OpsEvidenceLedger];
|
|
100
|
+
for (const k of spec.names) if (!name(k)) return `a ${c.ledger} citation names ops.${k}`;
|
|
101
|
+
for (const k of spec.strings) if (!str(k)) return `a ${c.ledger} citation names ops.${k}`;
|
|
102
|
+
for (const k of spec.optional) if (c[k] !== undefined && !str(k)) return `ops.${k} is a string`;
|
|
103
|
+
const known = new Set(["ledger", "member", "sha256", ...spec.names, ...spec.strings, ...spec.optional]);
|
|
104
|
+
const extra = Object.keys(c).filter((k) => !known.has(k));
|
|
105
|
+
if (extra.length > 0) return `a ${c.ledger} citation takes ${[...known].join(", ")}, not ${extra.join(", ")}`;
|
|
106
|
+
if (c.ledger === "plan" && /[/\\]/.test(c.digest as string)) return "ops.digest is a plan digest, not a path";
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Find the cited record on the local `chant/lifecycle` branch and check it
|
|
112
|
+
* against the citation and the result given. Never throws for a refusal.
|
|
113
|
+
*/
|
|
114
|
+
export async function resolveOpsCitation(cite: OpsCitation, result: EvidenceResult, cwd: string): Promise<{ ops: OpsEvidence } | { error: OpsCitationError }> {
|
|
115
|
+
const problem = opsCitationProblem(cite);
|
|
116
|
+
if (problem) return { error: { code: "write-input-invalid", message: problem } };
|
|
117
|
+
const loc = { cwd, ...(cite.member !== undefined ? { member: cite.member } : {}) };
|
|
118
|
+
const missing = (what: string): { error: OpsCitationError } => ({ error: { code: "work-evidence-ops-missing", message: `the local chant/lifecycle branch has no ${what}` } });
|
|
119
|
+
|
|
120
|
+
let found: { dir: string; file: string; text: string; field: string; value: string; env?: string; outcome?: string };
|
|
121
|
+
switch (cite.ledger) {
|
|
122
|
+
case "run": {
|
|
123
|
+
const file = `runs__${cite.op}.jsonl`;
|
|
124
|
+
const content = await readBlobFromPath(cite.env, file, loc);
|
|
125
|
+
const hit = content === null ? undefined : lines(content).find((l) => l.rec.id === cite.id);
|
|
126
|
+
if (!hit) return missing(`run ${cite.id} of ${cite.op} in ${cite.env}`);
|
|
127
|
+
const status = String(hit.rec.status);
|
|
128
|
+
if (status !== "ok" && status !== "fail") {
|
|
129
|
+
return { error: { code: "work-evidence-ops-unfinished", message: `run ${cite.id} of ${cite.op} is ${status}: it has no outcome yet to cite` } };
|
|
130
|
+
}
|
|
131
|
+
const shows: EvidenceResult = status === "ok" ? "pass" : "fail";
|
|
132
|
+
if (shows !== result) {
|
|
133
|
+
return { error: { code: "work-evidence-ops-mismatch", message: `run ${cite.id} of ${cite.op} in ${cite.env} ended ${status}, so it is ${shows} evidence, not ${result}` } };
|
|
134
|
+
}
|
|
135
|
+
found = { dir: cite.env, file, text: hit.line, field: "id", value: cite.id, env: cite.env, outcome: status };
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case "deploy": {
|
|
139
|
+
const file = "releases.jsonl";
|
|
140
|
+
const content = await readBlobFromPath(cite.env, file, loc);
|
|
141
|
+
const hits = content === null ? [] : lines(content).filter((l) => l.rec.runId === cite.runId && (cite.component === undefined || l.rec.component === cite.component));
|
|
142
|
+
if (hits.length === 0) return missing(`deploy by run ${cite.runId}${cite.component ? ` of ${cite.component}` : ""} in ${cite.env}`);
|
|
143
|
+
if (hits.length > 1) {
|
|
144
|
+
return { error: { code: "write-input-invalid", message: `run ${cite.runId} deployed ${hits.length} components in ${cite.env}: name the one cited in ops.component` } };
|
|
145
|
+
}
|
|
146
|
+
found = { dir: cite.env, file, text: hits[0].line, field: "runId", value: cite.runId, env: cite.env };
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case "gate": {
|
|
150
|
+
const file = `${cite.op}.jsonl`;
|
|
151
|
+
const content = await readBlobFromPath("_gates", file, loc);
|
|
152
|
+
const hit = content === null ? undefined : lines(content).find((l) => (l.rec.kind ?? "resolution") === "resolution" && l.rec.gate === cite.gate && l.rec.timestamp === cite.at);
|
|
153
|
+
if (!hit) return missing(`answer to gate ${cite.gate} of ${cite.op} at ${cite.at}`);
|
|
154
|
+
found = { dir: "_gates", file, text: hit.line, field: "timestamp", value: cite.at };
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
case "plan": {
|
|
158
|
+
const file = `${cite.digest.replace(/:/g, "_")}.json`;
|
|
159
|
+
const content = await readBlobFromPath("_plans", file, loc);
|
|
160
|
+
if (content === null) return missing(`release plan ${cite.digest}`);
|
|
161
|
+
let digest: unknown;
|
|
162
|
+
try {
|
|
163
|
+
digest = (JSON.parse(content) as { digest?: unknown }).digest;
|
|
164
|
+
} catch {
|
|
165
|
+
digest = undefined;
|
|
166
|
+
}
|
|
167
|
+
if (digest !== cite.digest) return { error: { code: "work-evidence-ops-mismatch", message: `the plan stored for ${cite.digest} names another digest` } };
|
|
168
|
+
found = { dir: "_plans", file, text: content, field: "digest", value: cite.digest };
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const hash = sha256(found.text);
|
|
174
|
+
if (cite.sha256 !== undefined && cite.sha256 !== hash) {
|
|
175
|
+
return { error: { code: "work-evidence-ops-mismatch", message: `the ${cite.ledger} record cited hashes to ${hash}, not the ${cite.sha256} given: it is another record` } };
|
|
176
|
+
}
|
|
177
|
+
const ref = `chant/lifecycle:${await ledgerDir(found.dir, loc)}/${found.file}`;
|
|
178
|
+
return {
|
|
179
|
+
ops: {
|
|
180
|
+
ledger: cite.ledger,
|
|
181
|
+
ref,
|
|
182
|
+
field: found.field,
|
|
183
|
+
value: found.value,
|
|
184
|
+
sha256: hash,
|
|
185
|
+
...(found.env !== undefined ? { env: found.env } : {}),
|
|
186
|
+
...(found.outcome !== undefined ? { outcome: found.outcome } : {}),
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"evidence": {
|
|
47
47
|
"type": "object",
|
|
48
|
-
"description": "The entry appended: a title, a url
|
|
48
|
+
"description": "The entry appended: a title, a url, a path pinned by sha256 or an ops result verified against its ledger (#3171), as_of, and the criterion, its verification, the result and by, the lease holder.",
|
|
49
49
|
"required": [
|
|
50
50
|
"title",
|
|
51
51
|
"criterion",
|
|
@@ -91,6 +91,50 @@
|
|
|
91
91
|
"by": {
|
|
92
92
|
"type": "string",
|
|
93
93
|
"description": "The lease holder that attached it."
|
|
94
|
+
},
|
|
95
|
+
"ops": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"description": "An ops result on the chant/lifecycle branch (#3171), named the way graph --intent's trace names a link: the ledger file in ref, the field and value that pick the record out of it, and the record's sha256 as stored. A run carries its env and outcome, a deploy its env.",
|
|
98
|
+
"required": [
|
|
99
|
+
"ledger",
|
|
100
|
+
"ref",
|
|
101
|
+
"field",
|
|
102
|
+
"value",
|
|
103
|
+
"sha256"
|
|
104
|
+
],
|
|
105
|
+
"properties": {
|
|
106
|
+
"ledger": {
|
|
107
|
+
"enum": [
|
|
108
|
+
"run",
|
|
109
|
+
"deploy",
|
|
110
|
+
"gate",
|
|
111
|
+
"plan"
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
"ref": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"pattern": "^chant/lifecycle:"
|
|
117
|
+
},
|
|
118
|
+
"field": {
|
|
119
|
+
"type": "string"
|
|
120
|
+
},
|
|
121
|
+
"value": {
|
|
122
|
+
"type": "string"
|
|
123
|
+
},
|
|
124
|
+
"sha256": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
127
|
+
},
|
|
128
|
+
"env": {
|
|
129
|
+
"type": "string"
|
|
130
|
+
},
|
|
131
|
+
"outcome": {
|
|
132
|
+
"enum": [
|
|
133
|
+
"ok",
|
|
134
|
+
"fail"
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
94
138
|
}
|
|
95
139
|
}
|
|
96
140
|
},
|
|
@@ -207,7 +251,10 @@
|
|
|
207
251
|
"lease-held",
|
|
208
252
|
"lease-not-held",
|
|
209
253
|
"lease-token-mismatch",
|
|
210
|
-
"not-a-git-repository"
|
|
254
|
+
"not-a-git-repository",
|
|
255
|
+
"work-evidence-ops-missing",
|
|
256
|
+
"work-evidence-ops-mismatch",
|
|
257
|
+
"work-evidence-ops-unfinished"
|
|
211
258
|
]
|
|
212
259
|
},
|
|
213
260
|
"message": {
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
* one: the run holding the lease is the item's implementer, and a manual
|
|
18
18
|
* verdict comes from someone else. A person records that verdict with
|
|
19
19
|
* `records amend`, naming themselves in `by`.
|
|
20
|
+
*
|
|
21
|
+
* Evidence is a public link, a workspace file pinned by hash, or an ops
|
|
22
|
+
* result (#3171): a run, a deploy, a gate answer or a release plan on the
|
|
23
|
+
* `chant/lifecycle` branch, cited the way `graph --intent`'s trace cites it.
|
|
24
|
+
* The citation is resolved against the ledger it names before anything is
|
|
25
|
+
* written, and a run's outcome must agree with the result
|
|
26
|
+
* (`./work-evidence-ops.ts`).
|
|
20
27
|
*/
|
|
21
28
|
|
|
22
29
|
import { relative, sep } from "node:path";
|
|
@@ -26,6 +33,7 @@ import { pinFile } from "./records-cli";
|
|
|
26
33
|
import { AMEND_ERROR_CODES, amendRecord } from "./records-write";
|
|
27
34
|
import { acceptanceCriteria, EVIDENCE_RESULTS, workAcceptance, type EvidenceResult, type VerificationType, type WorkAcceptance } from "./work";
|
|
28
35
|
import { findWorkItem, WorkError } from "./work-cli";
|
|
36
|
+
import { resolveOpsCitation, type OpsCitation } from "./work-evidence-ops";
|
|
29
37
|
|
|
30
38
|
/** Why evidence was not attached. Closed. Nothing is written with any of them. */
|
|
31
39
|
export const WORK_EVIDENCE_ERROR_CODES = [
|
|
@@ -40,6 +48,9 @@ export const WORK_EVIDENCE_ERROR_CODES = [
|
|
|
40
48
|
"lease-not-held",
|
|
41
49
|
"lease-token-mismatch",
|
|
42
50
|
"not-a-git-repository",
|
|
51
|
+
"work-evidence-ops-missing",
|
|
52
|
+
"work-evidence-ops-mismatch",
|
|
53
|
+
"work-evidence-ops-unfinished",
|
|
43
54
|
] as const satisfies readonly ReasonCode[];
|
|
44
55
|
export type WorkEvidenceErrorCode = (typeof WORK_EVIDENCE_ERROR_CODES)[number];
|
|
45
56
|
|
|
@@ -59,8 +70,10 @@ export interface AttachWorkEvidenceRequest {
|
|
|
59
70
|
title: string;
|
|
60
71
|
/** A public link, or */
|
|
61
72
|
url?: string;
|
|
62
|
-
/** a workspace file, pinned by the hash of its bytes now
|
|
73
|
+
/** a workspace file, pinned by the hash of its bytes now, or */
|
|
63
74
|
path?: string;
|
|
75
|
+
/** an ops result on the `chant/lifecycle` branch (#3171), verified against its ledger. */
|
|
76
|
+
ops?: OpsCitation;
|
|
64
77
|
/** When the evidence was taken, as an ISO 8601 timestamp. Now by default. */
|
|
65
78
|
asOf?: string;
|
|
66
79
|
}
|
|
@@ -111,7 +124,9 @@ export async function attachWorkEvidence(req: AttachWorkEvidenceRequest): Promis
|
|
|
111
124
|
throw new EvidenceError("write-usage-invalid", `result is pass or fail, not ${JSON.stringify(req.result)}`);
|
|
112
125
|
}
|
|
113
126
|
if (typeof req.title !== "string" || req.title.trim() === "") throw new EvidenceError("write-usage-invalid", "the evidence has no title");
|
|
114
|
-
if (
|
|
127
|
+
if ([req.url, req.path, req.ops].filter((v) => v !== undefined).length !== 1) {
|
|
128
|
+
throw new EvidenceError("write-usage-invalid", "the evidence is a url, a path or an ops result, exactly one of them");
|
|
129
|
+
}
|
|
115
130
|
if (req.url !== undefined && !req.url.startsWith("https://")) throw new EvidenceError("write-input-invalid", `evidence links are https://, not ${JSON.stringify(req.url)}`);
|
|
116
131
|
|
|
117
132
|
const found = await findWorkItem(req.item, req.cwd, req.kind);
|
|
@@ -139,7 +154,11 @@ export async function attachWorkEvidence(req: AttachWorkEvidenceRequest): Promis
|
|
|
139
154
|
}
|
|
140
155
|
|
|
141
156
|
let pin: Record<string, unknown>;
|
|
142
|
-
if (req.
|
|
157
|
+
if (req.ops !== undefined) {
|
|
158
|
+
const resolved = await resolveOpsCitation(req.ops, req.result, req.cwd);
|
|
159
|
+
if ("error" in resolved) throw new EvidenceError(resolved.error.code, resolved.error.message);
|
|
160
|
+
pin = { ops: resolved.ops };
|
|
161
|
+
} else if (req.path !== undefined) {
|
|
143
162
|
const pinned = pinFile(req.path, req.cwd);
|
|
144
163
|
if ("error" in pinned) throw new EvidenceError("write-input-invalid", pinned.error);
|
|
145
164
|
pin = { path: pinned.path, sha256: pinned.sha256 };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace gates (#2554, #2524 D17, ws-039).
|
|
3
|
+
*
|
|
4
|
+
* A workspace gate is one approval for a change across members. A run of
|
|
5
|
+
* `chant workspace run --gate <name>` whose member runs stop at their own
|
|
6
|
+
* gates records a pending fact under `_members/_workspace/_gates/<name>.jsonl`
|
|
7
|
+
* that lists each stopped member gate and the plan it waits on (a cover),
|
|
8
|
+
* with the digest of that list. `chant workspace run approve <name>` records
|
|
9
|
+
* an approval of one such digest.
|
|
10
|
+
*
|
|
11
|
+
* The rule (ws-039): a workspace gate never satisfies a member gate, unless
|
|
12
|
+
* both hold:
|
|
13
|
+
*
|
|
14
|
+
* 1. its approval's digest covers the member's plan: the approval lists the
|
|
15
|
+
* member, the Op, the gate and the plan digest the member run reached it
|
|
16
|
+
* with, and the list still hashes to the approval's digest;
|
|
17
|
+
* 2. the member opts in at base: its entry in `chant.workspace.json`, read
|
|
18
|
+
* from the base revision as other policy is, sets
|
|
19
|
+
* `acceptWorkspaceGates: true`. A declaration read only from the working
|
|
20
|
+
* tree never opts a member in.
|
|
21
|
+
*
|
|
22
|
+
* A member run started under the gate (`CHANT_WORKSPACE_GATE`) asks this
|
|
23
|
+
* module through `GateCheckInput.workspace` after its own approvals have
|
|
24
|
+
* not passed the gate. Nothing is written to the member's gate ledger: the
|
|
25
|
+
* run's step record says `via: "workspace"` and names the workspace gate.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { samePlanDigest } from "../lifecycle/plan-digest";
|
|
29
|
+
import { resolveMemberLedger } from "../lifecycle/member-ledger";
|
|
30
|
+
import type { WorkspaceGateStood } from "../op/gate";
|
|
31
|
+
import { appendWorkspaceLine, canonicalDigest, readWorkspaceLines, WORKSPACE_GATES_DIR } from "./workspace-ledger";
|
|
32
|
+
|
|
33
|
+
/** One member gate a workspace gate covers. */
|
|
34
|
+
export interface WorkspaceGateCover {
|
|
35
|
+
member: string;
|
|
36
|
+
op: string;
|
|
37
|
+
gate: string;
|
|
38
|
+
planDigest: string;
|
|
39
|
+
/** The member gate's environment, when it is bound to one. */
|
|
40
|
+
environment?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface WorkspaceGatePending {
|
|
44
|
+
version: 1;
|
|
45
|
+
kind: "pending";
|
|
46
|
+
gate: string;
|
|
47
|
+
/** The workspace run that recorded it. */
|
|
48
|
+
run: string;
|
|
49
|
+
timestamp: string;
|
|
50
|
+
digest: string;
|
|
51
|
+
covers: WorkspaceGateCover[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface WorkspaceGateApproval {
|
|
55
|
+
version: 1;
|
|
56
|
+
kind: "approval";
|
|
57
|
+
gate: string;
|
|
58
|
+
digest: string;
|
|
59
|
+
covers: WorkspaceGateCover[];
|
|
60
|
+
approvedBy: string;
|
|
61
|
+
timestamp: string;
|
|
62
|
+
note?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type WorkspaceGateLine = WorkspaceGatePending | WorkspaceGateApproval;
|
|
66
|
+
|
|
67
|
+
/** Covers in one order, so the same set always has the same digest. */
|
|
68
|
+
export function sortCovers(covers: readonly WorkspaceGateCover[]): WorkspaceGateCover[] {
|
|
69
|
+
const key = (c: WorkspaceGateCover) => `${c.member}\u0000${c.op}\u0000${c.gate}\u0000${c.environment ?? ""}\u0000${c.planDigest}`;
|
|
70
|
+
return [...covers]
|
|
71
|
+
.map((c) => ({ member: c.member, op: c.op, gate: c.gate, planDigest: c.planDigest, ...(c.environment !== undefined ? { environment: c.environment } : {}) }))
|
|
72
|
+
.sort((a, b) => (key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : 0));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The digest a workspace gate's pending fact and approval carry: the member plans it covers. */
|
|
76
|
+
export function workspaceGateDigest(covers: readonly WorkspaceGateCover[]): string {
|
|
77
|
+
return canonicalDigest(sortCovers(covers));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** What a member gate asks about. */
|
|
81
|
+
export interface MemberGateQuery {
|
|
82
|
+
op: string;
|
|
83
|
+
gate: string;
|
|
84
|
+
planDigest: string;
|
|
85
|
+
environment?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The rule of ws-039, pure: the newest approval that stands in for the
|
|
90
|
+
* member gate `q`, or null. Null whenever the member has not opted in.
|
|
91
|
+
*/
|
|
92
|
+
export function workspaceGateStandsIn(
|
|
93
|
+
q: MemberGateQuery,
|
|
94
|
+
ctx: { member: string; optedIn: boolean; approvals: readonly WorkspaceGateApproval[] },
|
|
95
|
+
): WorkspaceGateApproval | null {
|
|
96
|
+
if (!ctx.optedIn) return null;
|
|
97
|
+
const covering = ctx.approvals.filter(
|
|
98
|
+
(a) =>
|
|
99
|
+
// The covers must still hash to the digest that was approved.
|
|
100
|
+
workspaceGateDigest(a.covers) === a.digest &&
|
|
101
|
+
a.covers.some(
|
|
102
|
+
(c) =>
|
|
103
|
+
c.member === ctx.member && c.op === q.op && c.gate === q.gate &&
|
|
104
|
+
samePlanDigest(c.planDigest, q.planDigest) &&
|
|
105
|
+
(c.environment ?? null) === (q.environment ?? null),
|
|
106
|
+
),
|
|
107
|
+
);
|
|
108
|
+
return covering.sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0)).at(-1) ?? null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const fileFor = (gate: string) => `${gate}.jsonl`;
|
|
112
|
+
|
|
113
|
+
function isCover(v: unknown): v is WorkspaceGateCover {
|
|
114
|
+
const c = v as WorkspaceGateCover;
|
|
115
|
+
return typeof c === "object" && c !== null && typeof c.member === "string" && typeof c.op === "string" && typeof c.gate === "string" && typeof c.planDigest === "string";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function isGateLine(v: unknown): v is WorkspaceGateLine {
|
|
119
|
+
const l = v as WorkspaceGateLine;
|
|
120
|
+
if (typeof l !== "object" || l === null || l.version !== 1 || typeof l.gate !== "string" || typeof l.digest !== "string" || typeof l.timestamp !== "string") return false;
|
|
121
|
+
if (!Array.isArray(l.covers) || !l.covers.every(isCover)) return false;
|
|
122
|
+
if (l.kind === "pending") return typeof l.run === "string";
|
|
123
|
+
if (l.kind === "approval") return typeof l.approvedBy === "string";
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** A workspace gate's ledger, oldest first. */
|
|
128
|
+
export async function readWorkspaceGate(cwd: string, gate: string): Promise<{ pending: WorkspaceGatePending[]; approvals: WorkspaceGateApproval[]; malformed: number }> {
|
|
129
|
+
const { records, malformed } = await readWorkspaceLines(cwd, WORKSPACE_GATES_DIR, fileFor(gate), isGateLine);
|
|
130
|
+
return {
|
|
131
|
+
pending: records.filter((r): r is WorkspaceGatePending => r.kind === "pending"),
|
|
132
|
+
approvals: records.filter((r): r is WorkspaceGateApproval => r.kind === "approval"),
|
|
133
|
+
malformed,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function appendWorkspaceGatePending(cwd: string, input: Omit<WorkspaceGatePending, "version" | "kind" | "digest" | "covers"> & { covers: readonly WorkspaceGateCover[] }): Promise<WorkspaceGatePending> {
|
|
138
|
+
const covers = sortCovers(input.covers);
|
|
139
|
+
const record: WorkspaceGatePending = { version: 1, kind: "pending", gate: input.gate, run: input.run, timestamp: input.timestamp, digest: workspaceGateDigest(covers), covers };
|
|
140
|
+
await appendWorkspaceLine(cwd, WORKSPACE_GATES_DIR, fileFor(input.gate), record, `Workspace gate pending: ${input.gate}`);
|
|
141
|
+
return record;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Approve a workspace gate's pending fact: the newest one, or the one whose
|
|
146
|
+
* digest is `digest`. The approval copies its covers. Throws when there is no
|
|
147
|
+
* such fact.
|
|
148
|
+
*/
|
|
149
|
+
export async function approveWorkspaceGate(
|
|
150
|
+
cwd: string,
|
|
151
|
+
input: { gate: string; digest?: string; approvedBy: string; timestamp: string; note?: string },
|
|
152
|
+
): Promise<WorkspaceGateApproval> {
|
|
153
|
+
const { pending } = await readWorkspaceGate(cwd, input.gate);
|
|
154
|
+
const candidates = input.digest === undefined ? pending : pending.filter((p) => p.digest === input.digest);
|
|
155
|
+
const fact = candidates.at(-1);
|
|
156
|
+
if (!fact) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
input.digest === undefined
|
|
159
|
+
? `workspace gate "${input.gate}" has no pending fact to approve; a chant workspace run --gate ${input.gate} whose members stop at their gates records one`
|
|
160
|
+
: `workspace gate "${input.gate}" has no pending fact with digest ${input.digest}`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
const record: WorkspaceGateApproval = {
|
|
164
|
+
version: 1,
|
|
165
|
+
kind: "approval",
|
|
166
|
+
gate: input.gate,
|
|
167
|
+
digest: fact.digest,
|
|
168
|
+
covers: fact.covers,
|
|
169
|
+
approvedBy: input.approvedBy,
|
|
170
|
+
timestamp: input.timestamp,
|
|
171
|
+
...(input.note ? { note: input.note } : {}),
|
|
172
|
+
};
|
|
173
|
+
await appendWorkspaceLine(cwd, WORKSPACE_GATES_DIR, fileFor(input.gate), record, `Workspace gate approval: ${input.gate}`);
|
|
174
|
+
return record;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Whether the member holding `cwd` opts in to workspace gates at base, and
|
|
179
|
+
* its name. A member of a nested workspace, the root member and a directory
|
|
180
|
+
* no member owns never opt in.
|
|
181
|
+
*/
|
|
182
|
+
export async function memberAcceptsWorkspaceGates(cwd: string): Promise<{ member: string | null; accepts: boolean }> {
|
|
183
|
+
const ledger = await resolveMemberLedger(cwd);
|
|
184
|
+
if (ledger.members.length !== 1) return { member: ledger.members.join("/") || null, accepts: false };
|
|
185
|
+
const member = ledger.members[0]!;
|
|
186
|
+
const { scopeSource } = await import("./write-scope");
|
|
187
|
+
const source = scopeSource(cwd);
|
|
188
|
+
if (source.from !== "base" || !source.declaration) return { member, accepts: false };
|
|
189
|
+
const entry = source.declaration.members.find((m) => m.name === member);
|
|
190
|
+
return { member, accepts: entry?.acceptWorkspaceGates === true };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** The stand-in a member run started under workspace gate `gate` asks (`GateCheckInput.workspace`). */
|
|
194
|
+
export async function memberGateStandIn(cwd: string, gate: string, q: MemberGateQuery): Promise<WorkspaceGateStood | null> {
|
|
195
|
+
const { member, accepts } = await memberAcceptsWorkspaceGates(cwd);
|
|
196
|
+
if (!member || !accepts) return null;
|
|
197
|
+
const { approvals } = await readWorkspaceGate(cwd, gate);
|
|
198
|
+
const found = workspaceGateStandsIn(q, { member, optedIn: accepts, approvals });
|
|
199
|
+
return found ? { gate, digest: found.digest, approvedBy: found.approvedBy, timestamp: found.timestamp } : null;
|
|
200
|
+
}
|