@intentius/chant 0.124.51 → 0.124.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/discover.js +3 -1
- package/dist/behaviour.d.ts +1 -1
- package/dist/behaviour.d.ts.map +1 -1
- package/dist/capabilities/templates.d.ts +65 -0
- package/dist/capabilities/templates.d.ts.map +1 -0
- package/dist/capabilities/templates.js +193 -0
- package/dist/capabilities/templates.json +145 -0
- package/dist/change-set-graph.d.ts +35 -3
- package/dist/change-set-graph.d.ts.map +1 -1
- package/dist/change-set-graph.js +60 -9
- package/dist/change-set.d.ts +65 -7
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +23 -12
- package/dist/ci-identity.d.ts +7 -2
- package/dist/ci-identity.d.ts.map +1 -1
- package/dist/ci-identity.js +4 -1
- package/dist/cli/commands/import-index.d.ts +52 -0
- package/dist/cli/commands/import-index.d.ts.map +1 -0
- package/dist/cli/commands/import-index.js +226 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +4 -2
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/components.js +11 -2
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +16 -3
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +22 -4
- package/dist/cli/handlers/index-cloud.d.ts +14 -0
- package/dist/cli/handlers/index-cloud.d.ts.map +1 -0
- package/dist/cli/handlers/index-cloud.js +22 -0
- package/dist/cli/handlers/index-diff.d.ts +18 -0
- package/dist/cli/handlers/index-diff.d.ts.map +1 -0
- package/dist/cli/handlers/index-diff.js +142 -0
- package/dist/cli/handlers/lifecycle-drift.d.ts +25 -0
- package/dist/cli/handlers/lifecycle-drift.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-drift.js +64 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.js +188 -28
- package/dist/cli/handlers/misc.d.ts +9 -0
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/misc.js +77 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +17 -3
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +89 -15
- package/dist/cli/handlers/query.d.ts.map +1 -1
- package/dist/cli/handlers/query.js +28 -4
- package/dist/cli/handlers/scenario.d.ts.map +1 -1
- package/dist/cli/handlers/scenario.js +8 -7
- package/dist/cli/handlers/search.d.ts +34 -4
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/handlers/search.js +216 -36
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +55 -1
- package/dist/cli/mcp/lifecycle-tools.d.ts.map +1 -1
- package/dist/cli/mcp/lifecycle-tools.js +3 -2
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.js +4 -3
- package/dist/cli/mcp/workspace-tools.js +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +55 -2
- package/dist/cli/registry.d.ts +19 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cloud-lister/aws.d.ts +35 -0
- package/dist/cloud-lister/aws.d.ts.map +1 -0
- package/dist/cloud-lister/aws.js +203 -0
- package/dist/cloud-lister/azure.d.ts +34 -0
- package/dist/cloud-lister/azure.d.ts.map +1 -0
- package/dist/cloud-lister/azure.js +129 -0
- package/dist/cloud-lister/common.d.ts +40 -0
- package/dist/cloud-lister/common.d.ts.map +1 -0
- package/dist/cloud-lister/common.js +83 -0
- package/dist/cloud-lister/credentials.d.ts +50 -0
- package/dist/cloud-lister/credentials.d.ts.map +1 -0
- package/dist/cloud-lister/credentials.js +86 -0
- package/dist/cloud-lister/fakes.d.ts +27 -0
- package/dist/cloud-lister/fakes.d.ts.map +1 -0
- package/dist/cloud-lister/fakes.js +50 -0
- package/dist/cloud-lister/gcp.d.ts +42 -0
- package/dist/cloud-lister/gcp.d.ts.map +1 -0
- package/dist/cloud-lister/gcp.js +143 -0
- package/dist/cloud-lister/guard.d.ts +49 -0
- package/dist/cloud-lister/guard.d.ts.map +1 -0
- package/dist/cloud-lister/guard.js +103 -0
- package/dist/cloud-lister/ids.d.ts +23 -0
- package/dist/cloud-lister/ids.d.ts.map +1 -0
- package/dist/cloud-lister/ids.js +42 -0
- package/dist/cloud-lister/index.d.ts +67 -0
- package/dist/cloud-lister/index.d.ts.map +1 -0
- package/dist/cloud-lister/index.js +102 -0
- package/dist/cloud-lister/kubernetes.d.ts +64 -0
- package/dist/cloud-lister/kubernetes.d.ts.map +1 -0
- package/dist/cloud-lister/kubernetes.js +276 -0
- package/dist/cloud-lister/markers.d.ts +31 -0
- package/dist/cloud-lister/markers.d.ts.map +1 -0
- package/dist/cloud-lister/markers.js +73 -0
- package/dist/cloud-lister/mask.d.ts +31 -0
- package/dist/cloud-lister/mask.d.ts.map +1 -0
- package/dist/cloud-lister/mask.js +74 -0
- package/dist/cloud-lister/sigv4.d.ts +29 -0
- package/dist/cloud-lister/sigv4.d.ts.map +1 -0
- package/dist/cloud-lister/sigv4.js +54 -0
- package/dist/cloud-lister/transport.d.ts +55 -0
- package/dist/cloud-lister/transport.d.ts.map +1 -0
- package/dist/cloud-lister/transport.js +86 -0
- package/dist/cloud-lister/types.d.ts +121 -0
- package/dist/cloud-lister/types.d.ts.map +1 -0
- package/dist/cloud-lister/types.js +24 -0
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.js +6 -0
- package/dist/components/capability-plugin.d.ts +9 -0
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability.d.ts +224 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +21 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +58 -14
- package/dist/components/covered-round.d.ts +173 -0
- package/dist/components/covered-round.d.ts.map +1 -0
- package/dist/components/covered-round.js +200 -0
- package/dist/components/declared-reads.d.ts +38 -0
- package/dist/components/declared-reads.d.ts.map +1 -0
- package/dist/components/declared-reads.js +131 -0
- package/dist/components/deploy-units.d.ts.map +1 -1
- package/dist/components/deploy-units.js +4 -0
- package/dist/components/driver.d.ts +42 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +74 -3
- package/dist/components/fan-out-run.d.ts +23 -1
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +82 -14
- package/dist/components/fan-out-support.d.ts +6 -0
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/fan-out-support.js +4 -1
- package/dist/components/fan-out.d.ts +28 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/groups.d.ts +229 -0
- package/dist/components/groups.d.ts.map +1 -0
- package/dist/components/groups.js +760 -0
- package/dist/components/index.d.ts +7 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +5 -0
- package/dist/components/plan-shape-conformance.d.ts +48 -0
- package/dist/components/plan-shape-conformance.d.ts.map +1 -0
- package/dist/components/plan-shape-conformance.js +122 -0
- package/dist/components/pr-run.d.ts +62 -7
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +354 -53
- package/dist/components/promote-rounds.d.ts +9 -0
- package/dist/components/promote-rounds.d.ts.map +1 -1
- package/dist/components/promote-rounds.js +23 -2
- package/dist/components/promote.d.ts +17 -0
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +36 -6
- package/dist/components/release-gate.d.ts +23 -1
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +97 -33
- package/dist/components/run-groups.d.ts +58 -0
- package/dist/components/run-groups.d.ts.map +1 -0
- package/dist/components/run-groups.js +78 -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 +6 -1
- package/dist/components/verbs/index.d.ts +1 -0
- package/dist/components/verbs/index.d.ts.map +1 -1
- package/dist/components/verbs/index.js +1 -0
- package/dist/components/verbs/member-outputs.d.ts +187 -0
- package/dist/components/verbs/member-outputs.d.ts.map +1 -0
- package/dist/components/verbs/member-outputs.js +218 -0
- package/dist/components/wave-plan.d.ts +2 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +4 -1
- package/dist/config.d.ts +60 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +27 -0
- package/dist/deep-observation.d.ts +1 -13
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/deep-observation.js +5 -22
- package/dist/drift-record.d.ts +542 -0
- package/dist/drift-record.d.ts.map +1 -0
- package/dist/drift-record.js +886 -0
- package/dist/engine-facts.d.ts +144 -0
- package/dist/engine-facts.d.ts.map +1 -0
- package/dist/engine-facts.js +14 -0
- package/dist/graph-ir.d.ts +153 -2
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ir.js +224 -6
- package/dist/graph-refs.d.ts +145 -0
- package/dist/graph-refs.d.ts.map +1 -1
- package/dist/graph-refs.js +305 -0
- package/dist/graph-schema/fixtures/adopt.json +15 -0
- package/dist/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
- package/dist/graph-schema/fixtures/index.json +68 -0
- package/dist/graph-schema/fixtures/plunge.engine-graph.json +158 -0
- package/dist/graph-schema/fixtures.d.ts +23 -0
- package/dist/graph-schema/fixtures.d.ts.map +1 -0
- package/dist/graph-schema/fixtures.js +246 -0
- package/dist/graph-schema/generate.d.ts +15 -0
- package/dist/graph-schema/generate.d.ts.map +1 -0
- package/dist/graph-schema/generate.js +267 -0
- package/dist/graph-schema/graph.schema.json +1387 -0
- package/dist/health.schema.json +2 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/instance-nodes.d.ts +159 -0
- package/dist/instance-nodes.d.ts.map +1 -0
- package/dist/instance-nodes.js +231 -0
- package/dist/lexicon.d.ts +72 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/adoption-file.d.ts +56 -0
- package/dist/lifecycle/adoption-file.d.ts.map +1 -0
- package/dist/lifecycle/adoption-file.js +123 -0
- package/dist/lifecycle/cloud-index.d.ts +186 -0
- package/dist/lifecycle/cloud-index.d.ts.map +1 -0
- package/dist/lifecycle/cloud-index.js +618 -0
- package/dist/lifecycle/derive-edges.d.ts +59 -0
- package/dist/lifecycle/derive-edges.d.ts.map +1 -0
- package/dist/lifecycle/derive-edges.js +126 -0
- package/dist/lifecycle/git.d.ts +10 -9
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +7 -1
- package/dist/lifecycle/identity-match.d.ts +133 -0
- package/dist/lifecycle/identity-match.d.ts.map +1 -0
- package/dist/lifecycle/identity-match.js +147 -0
- package/dist/lifecycle/index-diff.d.ts +224 -0
- package/dist/lifecycle/index-diff.d.ts.map +1 -0
- package/dist/lifecycle/index-diff.js +327 -0
- package/dist/lifecycle/index-document.d.ts +130 -0
- package/dist/lifecycle/index-document.d.ts.map +1 -0
- package/dist/lifecycle/index-document.js +189 -0
- package/dist/lifecycle/index-drift.d.ts +121 -0
- package/dist/lifecycle/index-drift.d.ts.map +1 -0
- package/dist/lifecycle/index-drift.js +178 -0
- package/dist/lifecycle/index-import.d.ts +168 -0
- package/dist/lifecycle/index-import.d.ts.map +1 -0
- package/dist/lifecycle/index-import.js +515 -0
- package/dist/lifecycle/index-join.d.ts +129 -0
- package/dist/lifecycle/index-join.d.ts.map +1 -0
- package/dist/lifecycle/index-join.js +248 -0
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/observation-baseline.d.ts +61 -21
- package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
- package/dist/lifecycle/observation-baseline.js +144 -19
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/observe.js +6 -1
- package/dist/lifecycle/query.d.ts +45 -4
- package/dist/lifecycle/query.d.ts.map +1 -1
- package/dist/lifecycle/query.js +89 -7
- package/dist/lifecycle/replay.d.ts +10 -3
- package/dist/lifecycle/replay.d.ts.map +1 -1
- package/dist/lifecycle/replay.js +10 -6
- package/dist/lifecycle/scenario.d.ts +4 -3
- package/dist/lifecycle/scenario.d.ts.map +1 -1
- package/dist/lifecycle/snapshot-store-fake.d.ts +9 -0
- package/dist/lifecycle/snapshot-store-fake.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store-fake.js +21 -0
- package/dist/lifecycle/snapshot-store-s3.d.ts +102 -0
- package/dist/lifecycle/snapshot-store-s3.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store-s3.js +254 -0
- package/dist/lifecycle/snapshot-store.d.ts +170 -0
- package/dist/lifecycle/snapshot-store.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store.js +832 -0
- package/dist/lifecycle/snapshot.d.ts +11 -0
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.js +18 -5
- package/dist/lifecycle/type-listing.d.ts +148 -0
- package/dist/lifecycle/type-listing.d.ts.map +1 -0
- package/dist/lifecycle/type-listing.js +166 -0
- package/dist/lint/component-checks.d.ts +15 -1
- package/dist/lint/component-checks.d.ts.map +1 -1
- package/dist/lint/component-checks.js +1 -0
- package/dist/lint/presets/relaxed.json +2 -1
- package/dist/lint/presets/strict.json +2 -1
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.js +13 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.js +8 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts +33 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.js +123 -0
- package/dist/lint/rules/comp/index.d.ts +2 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/comp/index.js +4 -1
- package/dist/observation.d.ts +19 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +18 -1
- package/dist/op/activities/activity-contracts.d.ts +1 -1
- package/dist/op/activities/activity-contracts.js +1 -1
- package/dist/op/activities/change-set.d.ts +6 -4
- package/dist/op/activities/change-set.d.ts.map +1 -1
- package/dist/op/activities/change-set.js +4 -2
- package/dist/op/activity-failure.d.ts +27 -0
- package/dist/op/activity-failure.d.ts.map +1 -1
- package/dist/op/activity-failure.js +31 -0
- package/dist/op/index.d.ts +2 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +1 -1
- package/dist/op/local-executor.d.ts +5 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +22 -8
- package/dist/pr-loop.d.ts +92 -3
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +62 -3
- package/dist/pulumi-urn.d.ts +101 -0
- package/dist/pulumi-urn.d.ts.map +1 -0
- package/dist/pulumi-urn.js +131 -0
- package/dist/release-floors.d.ts +23 -0
- package/dist/release-floors.d.ts.map +1 -0
- package/dist/release-floors.js +22 -0
- package/dist/sensitive-key.d.ts +23 -0
- package/dist/sensitive-key.d.ts.map +1 -0
- package/dist/sensitive-key.js +31 -0
- package/dist/wave-runtime.mjs +234 -234
- package/dist/workspace/adoption-report.d.ts +53 -0
- package/dist/workspace/adoption-report.d.ts.map +1 -0
- package/dist/workspace/adoption-report.js +76 -0
- package/dist/workspace/capabilities-contract.d.ts +7 -0
- package/dist/workspace/capabilities-contract.d.ts.map +1 -1
- package/dist/workspace/capabilities-contract.js +2 -1
- package/dist/workspace/capabilities.schema.json +98 -0
- package/dist/workspace/change-set.schema.json +42 -3
- package/dist/workspace/ci-cloud-identity.d.ts +13 -1
- package/dist/workspace/ci-cloud-identity.d.ts.map +1 -1
- package/dist/workspace/ci-cloud-identity.js +30 -1
- package/dist/workspace/compose-graph.d.ts +10 -1
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/compose-graph.js +6 -1
- package/dist/workspace/conformance/index.d.ts +1 -1
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/index.js +6 -1
- package/dist/workspace/drift-cli.d.ts +81 -0
- package/dist/workspace/drift-cli.d.ts.map +1 -0
- package/dist/workspace/drift-cli.js +317 -0
- package/dist/workspace/drift.schema.json +672 -0
- package/dist/workspace/graph-cli.d.ts +2 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/graph-cli.js +3 -0
- package/dist/workspace/graph.schema.json +72 -0
- package/dist/workspace/init.d.ts +5 -1
- package/dist/workspace/init.d.ts.map +1 -1
- package/dist/workspace/init.js +55 -9
- package/dist/workspace/ledger.schema.json +1 -1
- package/dist/workspace/nested-graph.d.ts.map +1 -1
- package/dist/workspace/nested-graph.js +2 -0
- package/dist/workspace/pr-report.schema.json +76 -3
- package/dist/workspace/promote-waves.d.ts +112 -0
- package/dist/workspace/promote-waves.d.ts.map +1 -0
- package/dist/workspace/promote-waves.js +290 -0
- package/dist/workspace/promote.d.ts +84 -2
- package/dist/workspace/promote.d.ts.map +1 -1
- package/dist/workspace/promote.js +347 -61
- package/dist/workspace/reason-codes.d.ts +12 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +14 -0
- package/dist/workspace/search-contract.d.ts +3 -3
- package/dist/workspace/search-contract.d.ts.map +1 -1
- package/dist/workspace/search-contract.js +2 -0
- package/dist/workspace/search.schema.json +103 -5
- package/package.json +14 -1
- package/src/__fixtures__/drift-record/README.md +58 -0
- package/src/__fixtures__/drift-record/choudoufu-cone.ndjson +4 -0
- package/src/__fixtures__/drift-record/choudoufu-resource.ndjson +3 -0
- package/src/__fixtures__/drift-record/component-status-ledger.json +65 -0
- package/src/__fixtures__/drift-record/component-status.json +91 -0
- package/src/__fixtures__/drift-record/deep-diff.json +259 -0
- package/src/__fixtures__/drift-record/lifecycle-diff-live.json +825 -0
- package/src/__fixtures__/drift-record/live-diff.json +125 -0
- package/src/audit/discover.e2e.test.ts +14 -0
- package/src/audit/discover.ts +3 -1
- package/src/behaviour.ts +1 -1
- package/src/capabilities/templates.json +145 -0
- package/src/capabilities/templates.test.ts +212 -0
- package/src/capabilities/templates.ts +204 -0
- package/src/change-set-bundle.test.ts +83 -0
- package/src/change-set-graph.ts +80 -9
- package/src/change-set.test.ts +24 -0
- package/src/change-set.ts +77 -14
- package/src/ci-identity-targets.test.ts +23 -0
- package/src/ci-identity.ts +10 -4
- package/src/cli/commands/import-index.test.ts +117 -0
- package/src/cli/commands/import-index.ts +265 -0
- package/src/cli/commands/lint.ts +4 -2
- package/src/cli/handlers/components.test.ts +9 -0
- package/src/cli/handlers/components.ts +12 -2
- package/src/cli/handlers/fan-out.ts +17 -3
- package/src/cli/handlers/graph.test.ts +42 -0
- package/src/cli/handlers/graph.ts +26 -4
- package/src/cli/handlers/index-cloud.ts +32 -0
- package/src/cli/handlers/index-diff.test.ts +68 -0
- package/src/cli/handlers/index-diff.ts +146 -0
- package/src/cli/handlers/ledger-root-member.test.ts +161 -0
- package/src/cli/handlers/lifecycle-drift.ts +68 -0
- package/src/cli/handlers/lifecycle.test.ts +33 -11
- package/src/cli/handlers/lifecycle.ts +205 -30
- package/src/cli/handlers/misc.ts +73 -0
- package/src/cli/handlers/pr-waves-blocked.test.ts +37 -2
- package/src/cli/handlers/pr.ts +16 -3
- package/src/cli/handlers/promote.test.ts +64 -0
- package/src/cli/handlers/promote.ts +81 -6
- package/src/cli/handlers/query.ts +27 -4
- package/src/cli/handlers/run-declared-reads-cli.test.ts +187 -0
- package/src/cli/handlers/run-groups-cli.test.ts +229 -0
- package/src/cli/handlers/scenario.test.ts +13 -0
- package/src/cli/handlers/scenario.ts +8 -7
- package/src/cli/handlers/search-index.test.ts +255 -0
- package/src/cli/handlers/search-json.test.ts +1 -1
- package/src/cli/handlers/search-ownership.test.ts +194 -0
- package/src/cli/handlers/search.ts +271 -38
- package/src/cli/main.test.ts +7 -0
- package/src/cli/main.ts +56 -1
- package/src/cli/mcp/lifecycle-tools.ts +3 -2
- package/src/cli/mcp/resource-handlers.ts +4 -3
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/cli/parse-args.ts +40 -2
- package/src/cli/registry.ts +19 -1
- package/src/cloud-lister/aws.test.ts +202 -0
- package/src/cloud-lister/aws.ts +226 -0
- package/src/cloud-lister/azure.test.ts +132 -0
- package/src/cloud-lister/azure.ts +142 -0
- package/src/cloud-lister/common.ts +115 -0
- package/src/cloud-lister/credentials.ts +110 -0
- package/src/cloud-lister/fakes.ts +66 -0
- package/src/cloud-lister/gcp.test.ts +121 -0
- package/src/cloud-lister/gcp.ts +159 -0
- package/src/cloud-lister/guard.test.ts +198 -0
- package/src/cloud-lister/guard.ts +111 -0
- package/src/cloud-lister/ids.ts +54 -0
- package/src/cloud-lister/index.ts +134 -0
- package/src/cloud-lister/kubernetes.test.ts +160 -0
- package/src/cloud-lister/kubernetes.ts +284 -0
- package/src/cloud-lister/markers.ts +74 -0
- package/src/cloud-lister/mask.ts +77 -0
- package/src/cloud-lister/shape.test.ts +81 -0
- package/src/cloud-lister/sigv4.ts +70 -0
- package/src/cloud-lister/transport.ts +118 -0
- package/src/cloud-lister/types.ts +135 -0
- package/src/components/capability-plugin-loader.ts +4 -0
- package/src/components/capability-plugin.ts +10 -0
- package/src/components/capability.ts +238 -0
- package/src/components/cli-support.ts +70 -14
- package/src/components/covered-round.test.ts +166 -0
- package/src/components/covered-round.ts +293 -0
- package/src/components/declared-reads.test.ts +103 -0
- package/src/components/declared-reads.ts +134 -0
- package/src/components/deploy-units.ts +4 -0
- package/src/components/driver.ts +101 -4
- package/src/components/fan-out-run.ts +95 -15
- package/src/components/fan-out-support.ts +10 -1
- package/src/components/fan-out.ts +30 -0
- package/src/components/groups.test.ts +320 -0
- package/src/components/groups.ts +880 -0
- package/src/components/index.ts +59 -0
- package/src/components/plan-shape-conformance.ts +130 -0
- package/src/components/pr-env-run-bundle.test.ts +2 -0
- package/src/components/pr-run.test.ts +728 -4
- package/src/components/pr-run.ts +395 -55
- package/src/components/promote-rounds.ts +25 -2
- package/src/components/promote.ts +55 -5
- package/src/components/registry.test.ts +1 -1
- package/src/components/release-gate.ts +115 -30
- package/src/components/release-groups.test.ts +362 -0
- package/src/components/run-groups.ts +113 -0
- package/src/components/starter-plugin.ts +6 -0
- package/src/components/verbs/index.ts +1 -0
- package/src/components/verbs/member-outputs.test.ts +205 -0
- package/src/components/verbs/member-outputs.ts +308 -0
- package/src/components/wave-plan.ts +5 -2
- package/src/config.ts +44 -0
- package/src/deep-observation.ts +5 -23
- package/src/drift-record.test.ts +596 -0
- package/src/drift-record.ts +1206 -0
- package/src/engine-facts.guard.test.ts +111 -0
- package/src/engine-facts.test.ts +178 -0
- package/src/engine-facts.ts +154 -0
- package/src/gated-waves-bundle.test.ts +2 -0
- package/src/graph-ir-live.test.ts +92 -0
- package/src/graph-ir.ts +317 -7
- package/src/graph-refs.ts +416 -0
- package/src/graph-schema/fixtures/adopt.json +15 -0
- package/src/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
- package/src/graph-schema/fixtures/index.json +68 -0
- package/src/graph-schema/fixtures/plunge.engine-graph.json +158 -0
- package/src/graph-schema/fixtures.ts +267 -0
- package/src/graph-schema/generate.ts +263 -0
- package/src/graph-schema/graph-schema.test.ts +153 -0
- package/src/graph-schema/graph.schema.json +1387 -0
- package/src/health.schema.json +2 -1
- package/src/identity.ts +2 -0
- package/src/index.ts +2 -0
- package/src/instance-nodes.ts +288 -0
- package/src/lexicon.ts +76 -0
- package/src/lifecycle/__fixtures__/index-diff/20261026T120000Z-aaaaaaaaaaaa.json +149 -0
- package/src/lifecycle/__fixtures__/index-diff/20261026T130000Z-bbbbbbbbbbbb.json +127 -0
- package/src/lifecycle/adoption-file.test.ts +100 -0
- package/src/lifecycle/adoption-file.ts +154 -0
- package/src/lifecycle/cloud-index.test.ts +517 -0
- package/src/lifecycle/cloud-index.ts +736 -0
- package/src/lifecycle/derive-edges.test.ts +193 -0
- package/src/lifecycle/derive-edges.ts +182 -0
- package/src/lifecycle/git.ts +10 -4
- package/src/lifecycle/identity-match.test.ts +308 -0
- package/src/lifecycle/identity-match.ts +257 -0
- package/src/lifecycle/index-diff.test.ts +198 -0
- package/src/lifecycle/index-diff.ts +476 -0
- package/src/lifecycle/index-document.test.ts +64 -0
- package/src/lifecycle/index-document.ts +299 -0
- package/src/lifecycle/index-drift.test.ts +191 -0
- package/src/lifecycle/index-drift.ts +304 -0
- package/src/lifecycle/index-import.test.ts +255 -0
- package/src/lifecycle/index-import.ts +634 -0
- package/src/lifecycle/index-join.test.ts +250 -0
- package/src/lifecycle/index-join.ts +304 -0
- package/src/lifecycle/index-store-load-bearing.test.ts +187 -0
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/member-ledger.test.ts +25 -7
- package/src/lifecycle/observation-baseline.test.ts +72 -1
- package/src/lifecycle/observation-baseline.ts +190 -27
- package/src/lifecycle/observe.ts +6 -1
- package/src/lifecycle/query.test.ts +69 -1
- package/src/lifecycle/query.ts +103 -10
- package/src/lifecycle/replay.test.ts +9 -5
- package/src/lifecycle/replay.ts +9 -5
- package/src/lifecycle/scenario.ts +4 -3
- package/src/lifecycle/snapshot-store-fake.ts +29 -0
- package/src/lifecycle/snapshot-store-s3.ts +316 -0
- package/src/lifecycle/snapshot-store.test.ts +385 -0
- package/src/lifecycle/snapshot-store.ts +981 -0
- package/src/lifecycle/snapshot.test.ts +13 -0
- package/src/lifecycle/snapshot.ts +23 -6
- package/src/lifecycle/type-listing.test.ts +166 -0
- package/src/lifecycle/type-listing.ts +309 -0
- package/src/lint/component-checks.ts +17 -1
- package/src/lint/config.test.ts +3 -0
- package/src/lint/presets/relaxed.json +2 -1
- package/src/lint/presets/strict.json +2 -1
- package/src/lint/rules/__fixtures__/comp/comp008/fail/dns.component.ts +15 -0
- package/src/lint/rules/__fixtures__/comp/comp008/pass/dns.component.ts +10 -0
- package/src/lint/rules/comp/comp.test.ts +2 -1
- package/src/lint/rules/comp/comp008-applies-signal-only-root.ts +121 -0
- package/src/lint/rules/comp/comp008.test.ts +77 -0
- package/src/lint/rules/comp/index.ts +4 -1
- package/src/observation-edges.test.ts +33 -0
- package/src/observation.ts +36 -1
- package/src/op/activities/activity-contracts.ts +1 -1
- package/src/op/activities/change-set.ts +8 -5
- package/src/op/activity-failure.ts +36 -0
- package/src/op/index.ts +2 -1
- package/src/op/local-executor.test.ts +80 -0
- package/src/op/local-executor.ts +27 -9
- package/src/pr-loop-bundle.test.ts +5 -2
- package/src/pr-loop.test.ts +57 -1
- package/src/pr-loop.ts +154 -6
- package/src/pulumi-urn.ts +151 -0
- package/src/release-floors.ts +22 -0
- package/src/sensitive-key.ts +34 -0
- package/src/workspace/adoption-report.test.ts +61 -0
- package/src/workspace/adoption-report.ts +103 -0
- package/src/workspace/capabilities-contract.test.ts +4 -0
- package/src/workspace/capabilities-contract.ts +9 -1
- package/src/workspace/capabilities.schema.json +98 -0
- package/src/workspace/change-set.schema.json +42 -3
- package/src/workspace/ci-cloud-identity.ts +31 -1
- package/src/workspace/compose-graph.test.ts +12 -0
- package/src/workspace/compose-graph.ts +12 -1
- package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
- package/src/workspace/conformance/index.ts +6 -1
- package/src/workspace/drift-cli.test.ts +65 -0
- package/src/workspace/drift-cli.ts +365 -0
- package/src/workspace/drift.schema.json +672 -0
- package/src/workspace/graph-cli.ts +5 -0
- package/src/workspace/graph.schema.json +72 -0
- package/src/workspace/init.test.ts +38 -1
- package/src/workspace/init.ts +50 -8
- package/src/workspace/intent-gaps.test.ts +18 -2
- package/src/workspace/ledger.schema.json +1 -1
- package/src/workspace/nested-graph.ts +1 -0
- package/src/workspace/pr-report.schema.json +76 -3
- package/src/workspace/promote-waves.test.ts +296 -0
- package/src/workspace/promote-waves.ts +340 -0
- package/src/workspace/promote.test.ts +158 -4
- package/src/workspace/promote.ts +398 -57
- package/src/workspace/reason-codes.test.ts +4 -1
- package/src/workspace/reason-codes.ts +14 -0
- package/src/workspace/search-contract.ts +2 -0
- package/src/workspace/search.schema.json +103 -5
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { GraphIR } from "../graph-ir.js";
|
|
2
|
+
import type { UnobservedReason } from "../observation.js";
|
|
3
|
+
/** The first chant that writes an index document to the store. */
|
|
4
|
+
export declare const INDEX_DOCUMENT_FLOOR = "0.124.54";
|
|
5
|
+
/** Who produced an index. */
|
|
6
|
+
export type IndexSourceKind = "cloud" | "engine" | "chant";
|
|
7
|
+
/** A direct cloud read (behold#1029). Lists are sorted and deduplicated by {@link canonicalScope}. */
|
|
8
|
+
export interface CloudIndexScope {
|
|
9
|
+
kind: "cloud";
|
|
10
|
+
substrate: "aws" | "azure" | "gcp" | "kubernetes";
|
|
11
|
+
/** The account, subscription, project or cluster. */
|
|
12
|
+
scopeId: string;
|
|
13
|
+
/** Regions or locations, or `"*"` for every one. */
|
|
14
|
+
locations: readonly string[] | "*";
|
|
15
|
+
/** Listed types, or `"*"` for every one. */
|
|
16
|
+
types: readonly string[] | "*";
|
|
17
|
+
/** A tag or label filter, when the read had one. */
|
|
18
|
+
tagFilter?: Readonly<Record<string, string>>;
|
|
19
|
+
}
|
|
20
|
+
/** A graph an engine printed (choudoufu, plunge). */
|
|
21
|
+
export interface EngineIndexScope {
|
|
22
|
+
kind: "engine";
|
|
23
|
+
engine: string;
|
|
24
|
+
/** choudoufu's estate. */
|
|
25
|
+
estate?: string;
|
|
26
|
+
/** plunge's project and stack. */
|
|
27
|
+
project?: string;
|
|
28
|
+
stack?: string;
|
|
29
|
+
/** The backend URL, with any credentials stripped before it reaches here. */
|
|
30
|
+
backend?: string;
|
|
31
|
+
}
|
|
32
|
+
/** A chant source: a snapshot or `chant graph --format ir`. Today's snapshot key, unchanged. */
|
|
33
|
+
export interface ChantIndexScope {
|
|
34
|
+
kind: "chant";
|
|
35
|
+
/** `.` for a project outside a workspace. */
|
|
36
|
+
member: string;
|
|
37
|
+
env: string;
|
|
38
|
+
stack?: string;
|
|
39
|
+
lexicon: string;
|
|
40
|
+
}
|
|
41
|
+
export type IndexScope = CloudIndexScope | EngineIndexScope | ChantIndexScope;
|
|
42
|
+
export interface IndexSource {
|
|
43
|
+
kind: IndexSourceKind;
|
|
44
|
+
/** `behold-lister`, `choudoufu`, `plunge`, `chant`. */
|
|
45
|
+
name: string;
|
|
46
|
+
version?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface IndexStamp {
|
|
49
|
+
source: IndexSource;
|
|
50
|
+
scope: IndexScope;
|
|
51
|
+
/** The store path the scope maps to ({@link indexScopeKey}): a reader refuses a mismatch by comparing it. */
|
|
52
|
+
scopeKey: string;
|
|
53
|
+
/** When the read started, ISO UTC. */
|
|
54
|
+
observedAt: string;
|
|
55
|
+
/** When the last call returned, ISO UTC. */
|
|
56
|
+
completedAt?: string;
|
|
57
|
+
/** The source repository commit, when there is one. */
|
|
58
|
+
commit?: string;
|
|
59
|
+
/** sha256 hex of the canonical `graph` and `coverage` ({@link indexDocumentDigest}). */
|
|
60
|
+
digest: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* What a read looked at, per type and location. A node can be absent only
|
|
64
|
+
* where this says `listed`. `"*"` in `listedType` or `location` means every
|
|
65
|
+
* type or every location (behold#1029's lister writes it for a run with
|
|
66
|
+
* `types` or `locations` of `"*"`); {@link indexCoverageFor} honours it.
|
|
67
|
+
*/
|
|
68
|
+
export interface CoverageEntry {
|
|
69
|
+
listedType: string;
|
|
70
|
+
location: string;
|
|
71
|
+
verdict: "listed" | UnobservedReason;
|
|
72
|
+
detail?: string;
|
|
73
|
+
}
|
|
74
|
+
/** A type with no lister, a lexicon with no catalog, a disabled API. */
|
|
75
|
+
export interface IndexNote {
|
|
76
|
+
kind: string;
|
|
77
|
+
message: string;
|
|
78
|
+
listedType?: string;
|
|
79
|
+
location?: string;
|
|
80
|
+
/** A command the person can run themselves, never run by the source (a GCP API to enable). */
|
|
81
|
+
command?: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The coverage entry that speaks for one (type, location), or undefined when
|
|
85
|
+
* none does (not observed). An exact entry wins over a wildcard one, and a
|
|
86
|
+
* wildcard on one side over a wildcard on both.
|
|
87
|
+
*/
|
|
88
|
+
export declare function indexCoverageFor<E extends Pick<CoverageEntry, "listedType" | "location"> = CoverageEntry>(coverage: readonly E[], listedType: string, location: string): E | undefined;
|
|
89
|
+
/** One index version. */
|
|
90
|
+
export interface IndexDocument {
|
|
91
|
+
index: "v1";
|
|
92
|
+
stamp: IndexStamp;
|
|
93
|
+
coverage: CoverageEntry[];
|
|
94
|
+
graph: GraphIR;
|
|
95
|
+
notes?: IndexNote[];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The scope with every list sorted and deduplicated and every absent field
|
|
99
|
+
* left out, so two reads with the same flags give the same digest.
|
|
100
|
+
*/
|
|
101
|
+
export declare function canonicalScope(scope: IndexScope): IndexScope;
|
|
102
|
+
/** JSON with object keys sorted at every level. Arrays keep their order. */
|
|
103
|
+
export declare function canonicalIndexJson(value: unknown): string;
|
|
104
|
+
/** The first 12 hex of sha256 over the canonical scope. */
|
|
105
|
+
export declare function scopeDigest(scope: IndexScope): string;
|
|
106
|
+
/** sha256 hex over the canonical `graph` and `coverage`, so an unchanged re-read is cheap to recognise. */
|
|
107
|
+
export declare function indexDocumentDigest(doc: Pick<IndexDocument, "graph" | "coverage">): string;
|
|
108
|
+
/** One path segment, percent-encoded so `/`, `:` and `%` in a name survive a git path and an S3 key. */
|
|
109
|
+
export declare function encodeIndexSegment(segment: string): string;
|
|
110
|
+
/** `cloud/<substrate>`, `engine/<engine>` or `chant`. */
|
|
111
|
+
export declare function indexSourcePath(scope: IndexScope): string;
|
|
112
|
+
/** The scope's identity segments, then its digest. */
|
|
113
|
+
export declare function indexScopePath(scope: IndexScope): string;
|
|
114
|
+
/** `<source path>/<scope path>`: what `stamp.scopeKey` holds and what the store lists under. */
|
|
115
|
+
export declare function indexScopeKey(scope: IndexScope): string;
|
|
116
|
+
/** `2026-10-26T09:14:03.120Z` as `20261026T091403Z`. */
|
|
117
|
+
export declare function compactObservedAt(observedAt: string): string;
|
|
118
|
+
/** `<observedAt>-<digest12>.json`. Names sort in time order. */
|
|
119
|
+
export declare function indexVersionName(observedAt: string, digest: string): string;
|
|
120
|
+
/** Whether a name in a scope path is a version (and not `scope.json`). */
|
|
121
|
+
export declare function isIndexVersionName(name: string): boolean;
|
|
122
|
+
/** The file beside the versions that holds the scope in full, so a person or `list` can read what a digest means. */
|
|
123
|
+
export declare const INDEX_SCOPE_FILE = "scope.json";
|
|
124
|
+
/**
|
|
125
|
+
* Refuse a document whose stamp disagrees with itself: a `scopeKey` that is
|
|
126
|
+
* not its scope's, or a source kind that is not its scope's kind. The store
|
|
127
|
+
* writes only a document that passes, so a path always means its scope.
|
|
128
|
+
*/
|
|
129
|
+
export declare function validateIndexDocument(doc: IndexDocument): void;
|
|
130
|
+
//# sourceMappingURL=index-document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-document.d.ts","sourceRoot":"","sources":["../../src/lifecycle/index-document.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An index document and where the index store keeps it (#4519, layout ruled
|
|
3
|
+
* on INTENTIUS/behold#1025, 2026-10-26).
|
|
4
|
+
*
|
|
5
|
+
* An index is an observed graph: a chant snapshot, a cloud listing typed
|
|
6
|
+
* through the lexicons (behold#1029, #4521), or a graph an engine prints
|
|
7
|
+
* (choudoufu, plunge). Every index is derived, stamped, never read for a
|
|
8
|
+
* decision and safe to lose. A document is a `GraphIR` inside a stamped
|
|
9
|
+
* envelope, never a second graph format.
|
|
10
|
+
*
|
|
11
|
+
* This module is pure: types, the canonical scope digest and the store key.
|
|
12
|
+
* ./snapshot-store.ts maps the keys onto a medium.
|
|
13
|
+
*
|
|
14
|
+
* Store key of an index version, relative to the store's `index` family:
|
|
15
|
+
*
|
|
16
|
+
* <source path>/<scope path>/<observedAt>-<digest12>.json
|
|
17
|
+
*
|
|
18
|
+
* - source path: `cloud/<substrate>`, `engine/<engine>`, or `chant`;
|
|
19
|
+
* - scope path: the scope's identity segments, then the first 12 hex of the
|
|
20
|
+
* scope digest: `<scopeId>/<scope digest>` for a cloud read,
|
|
21
|
+
* `<estate>/<scope digest>` or `<project>/<stack>/<scope digest>` for an
|
|
22
|
+
* engine, `<member>/<env>/<stack or ->/<lexicon>/<scope digest>` for chant.
|
|
23
|
+
* Each segment is percent-encoded, so `/` and `:` in a name survive a git
|
|
24
|
+
* path and an S3 key;
|
|
25
|
+
* - version: the read's start as `yyyymmddThhmmssZ` and the first 12 hex of
|
|
26
|
+
* the document digest. A version is written once and never overwritten.
|
|
27
|
+
*
|
|
28
|
+
* Two reads with the same scope write under one scope path, and a read with
|
|
29
|
+
* other regions, types or filters writes under another, so a reader refuses a
|
|
30
|
+
* scope mismatch by comparing one string (`stamp.scopeKey`).
|
|
31
|
+
*/
|
|
32
|
+
import { createHash } from "node:crypto";
|
|
33
|
+
import { NEXT_RELEASE } from "../release-floors.js";
|
|
34
|
+
/** The first chant that writes an index document to the store. */
|
|
35
|
+
export const INDEX_DOCUMENT_FLOOR = NEXT_RELEASE;
|
|
36
|
+
/**
|
|
37
|
+
* The coverage entry that speaks for one (type, location), or undefined when
|
|
38
|
+
* none does (not observed). An exact entry wins over a wildcard one, and a
|
|
39
|
+
* wildcard on one side over a wildcard on both.
|
|
40
|
+
*/
|
|
41
|
+
export function indexCoverageFor(coverage, listedType, location) {
|
|
42
|
+
let best;
|
|
43
|
+
let bestScore = -1;
|
|
44
|
+
for (const e of coverage) {
|
|
45
|
+
const typeHit = e.listedType === listedType ? 2 : e.listedType === "*" ? 0 : -1;
|
|
46
|
+
const locHit = e.location === location ? 1 : e.location === "*" ? 0 : -1;
|
|
47
|
+
if (typeHit < 0 || locHit < 0)
|
|
48
|
+
continue;
|
|
49
|
+
const score = typeHit + locHit;
|
|
50
|
+
if (score > bestScore) {
|
|
51
|
+
best = e;
|
|
52
|
+
bestScore = score;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return best;
|
|
56
|
+
}
|
|
57
|
+
const sortUnique = (xs) => [...new Set(xs)].sort();
|
|
58
|
+
/**
|
|
59
|
+
* The scope with every list sorted and deduplicated and every absent field
|
|
60
|
+
* left out, so two reads with the same flags give the same digest.
|
|
61
|
+
*/
|
|
62
|
+
export function canonicalScope(scope) {
|
|
63
|
+
switch (scope.kind) {
|
|
64
|
+
case "cloud":
|
|
65
|
+
return {
|
|
66
|
+
kind: "cloud",
|
|
67
|
+
substrate: scope.substrate,
|
|
68
|
+
scopeId: scope.scopeId,
|
|
69
|
+
locations: scope.locations === "*" ? "*" : sortUnique(scope.locations),
|
|
70
|
+
types: scope.types === "*" ? "*" : sortUnique(scope.types),
|
|
71
|
+
...(scope.tagFilter && Object.keys(scope.tagFilter).length > 0
|
|
72
|
+
? { tagFilter: Object.fromEntries(Object.entries(scope.tagFilter).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) }
|
|
73
|
+
: {}),
|
|
74
|
+
};
|
|
75
|
+
case "engine":
|
|
76
|
+
return {
|
|
77
|
+
kind: "engine",
|
|
78
|
+
engine: scope.engine,
|
|
79
|
+
...(scope.estate !== undefined ? { estate: scope.estate } : {}),
|
|
80
|
+
...(scope.project !== undefined ? { project: scope.project } : {}),
|
|
81
|
+
...(scope.stack !== undefined ? { stack: scope.stack } : {}),
|
|
82
|
+
...(scope.backend !== undefined ? { backend: scope.backend } : {}),
|
|
83
|
+
};
|
|
84
|
+
case "chant":
|
|
85
|
+
return {
|
|
86
|
+
kind: "chant",
|
|
87
|
+
member: scope.member,
|
|
88
|
+
env: scope.env,
|
|
89
|
+
...(scope.stack !== undefined ? { stack: scope.stack } : {}),
|
|
90
|
+
lexicon: scope.lexicon,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** JSON with object keys sorted at every level. Arrays keep their order. */
|
|
95
|
+
export function canonicalIndexJson(value) {
|
|
96
|
+
return JSON.stringify(value, (_k, v) => {
|
|
97
|
+
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
98
|
+
return Object.fromEntries(Object.entries(v).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
|
|
99
|
+
}
|
|
100
|
+
return v;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const sha256 = (s) => createHash("sha256").update(s, "utf8").digest("hex");
|
|
104
|
+
/** The first 12 hex of sha256 over the canonical scope. */
|
|
105
|
+
export function scopeDigest(scope) {
|
|
106
|
+
return sha256(canonicalIndexJson(canonicalScope(scope))).slice(0, 12);
|
|
107
|
+
}
|
|
108
|
+
/** sha256 hex over the canonical `graph` and `coverage`, so an unchanged re-read is cheap to recognise. */
|
|
109
|
+
export function indexDocumentDigest(doc) {
|
|
110
|
+
return sha256(canonicalIndexJson({ coverage: doc.coverage, graph: doc.graph }));
|
|
111
|
+
}
|
|
112
|
+
/** One path segment, percent-encoded so `/`, `:` and `%` in a name survive a git path and an S3 key. */
|
|
113
|
+
export function encodeIndexSegment(segment) {
|
|
114
|
+
if (segment === "")
|
|
115
|
+
throw new Error("an index key segment is never empty");
|
|
116
|
+
const encoded = encodeURIComponent(segment).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
117
|
+
// `.` and `..` are valid components to encodeURIComponent and meaningful to a filesystem.
|
|
118
|
+
return encoded === "." ? "%2E" : encoded === ".." ? "%2E%2E" : encoded;
|
|
119
|
+
}
|
|
120
|
+
/** `cloud/<substrate>`, `engine/<engine>` or `chant`. */
|
|
121
|
+
export function indexSourcePath(scope) {
|
|
122
|
+
switch (scope.kind) {
|
|
123
|
+
case "cloud":
|
|
124
|
+
return `cloud/${encodeIndexSegment(scope.substrate)}`;
|
|
125
|
+
case "engine":
|
|
126
|
+
return `engine/${encodeIndexSegment(scope.engine)}`;
|
|
127
|
+
case "chant":
|
|
128
|
+
return "chant";
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** The scope's identity segments, then its digest. */
|
|
132
|
+
export function indexScopePath(scope) {
|
|
133
|
+
const digest = scopeDigest(scope);
|
|
134
|
+
let segments;
|
|
135
|
+
switch (scope.kind) {
|
|
136
|
+
case "cloud":
|
|
137
|
+
segments = [scope.scopeId];
|
|
138
|
+
break;
|
|
139
|
+
case "engine":
|
|
140
|
+
segments = scope.estate !== undefined ? [scope.estate] : [scope.project ?? "-", scope.stack ?? "-"];
|
|
141
|
+
break;
|
|
142
|
+
case "chant":
|
|
143
|
+
segments = [scope.member, scope.env, scope.stack ?? "-", scope.lexicon];
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
return [...segments.map(encodeIndexSegment), digest].join("/");
|
|
147
|
+
}
|
|
148
|
+
/** `<source path>/<scope path>`: what `stamp.scopeKey` holds and what the store lists under. */
|
|
149
|
+
export function indexScopeKey(scope) {
|
|
150
|
+
return `${indexSourcePath(scope)}/${indexScopePath(scope)}`;
|
|
151
|
+
}
|
|
152
|
+
/** `2026-10-26T09:14:03.120Z` as `20261026T091403Z`. */
|
|
153
|
+
export function compactObservedAt(observedAt) {
|
|
154
|
+
const t = new Date(observedAt);
|
|
155
|
+
if (Number.isNaN(t.getTime()))
|
|
156
|
+
throw new Error(`observedAt "${observedAt}" is not a time`);
|
|
157
|
+
return t.toISOString().replace(/[-:]|\.\d{3}/g, "");
|
|
158
|
+
}
|
|
159
|
+
/** `<observedAt>-<digest12>.json`. Names sort in time order. */
|
|
160
|
+
export function indexVersionName(observedAt, digest) {
|
|
161
|
+
const hex = digest.replace(/^sha256:/, "");
|
|
162
|
+
if (!/^[0-9a-f]{12,}$/.test(hex))
|
|
163
|
+
throw new Error(`digest "${digest}" is not sha256 hex`);
|
|
164
|
+
return `${compactObservedAt(observedAt)}-${hex.slice(0, 12)}.json`;
|
|
165
|
+
}
|
|
166
|
+
/** Whether a name in a scope path is a version (and not `scope.json`). */
|
|
167
|
+
export function isIndexVersionName(name) {
|
|
168
|
+
return /^\d{8}T\d{6}Z-[0-9a-f]{12}\.json$/.test(name);
|
|
169
|
+
}
|
|
170
|
+
/** The file beside the versions that holds the scope in full, so a person or `list` can read what a digest means. */
|
|
171
|
+
export const INDEX_SCOPE_FILE = "scope.json";
|
|
172
|
+
/**
|
|
173
|
+
* Refuse a document whose stamp disagrees with itself: a `scopeKey` that is
|
|
174
|
+
* not its scope's, or a source kind that is not its scope's kind. The store
|
|
175
|
+
* writes only a document that passes, so a path always means its scope.
|
|
176
|
+
*/
|
|
177
|
+
export function validateIndexDocument(doc) {
|
|
178
|
+
if (doc.index !== "v1")
|
|
179
|
+
throw new Error(`index document version "${String(doc.index)}" is not v1`);
|
|
180
|
+
const { stamp } = doc;
|
|
181
|
+
if (stamp.source.kind !== stamp.scope.kind) {
|
|
182
|
+
throw new Error(`index stamp source kind "${stamp.source.kind}" does not match its scope kind "${stamp.scope.kind}"`);
|
|
183
|
+
}
|
|
184
|
+
const expected = indexScopeKey(stamp.scope);
|
|
185
|
+
if (stamp.scopeKey !== expected) {
|
|
186
|
+
throw new Error(`index stamp scopeKey "${stamp.scopeKey}" is not its scope's key "${expected}"`);
|
|
187
|
+
}
|
|
188
|
+
indexVersionName(stamp.observedAt, stamp.digest);
|
|
189
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drift without a deploy (#4526, tier 2 of INTENTIUS/behold#1025): the deep
|
|
3
|
+
* diff of declared source against an index version, for source chant never
|
|
4
|
+
* applied. `chant lifecycle diff <env> --index <ref>` is the command.
|
|
5
|
+
*
|
|
6
|
+
* Three steps, all pure:
|
|
7
|
+
*
|
|
8
|
+
* 1. Every declared entity is joined to the index through its lexicon's
|
|
9
|
+
* index match, with {@link joinDeclaredToIndex} (./index-join.ts, #4525).
|
|
10
|
+
* `chant search --index` joins the same way, so a row that binds there
|
|
11
|
+
* binds here.
|
|
12
|
+
* 2. A bound row's declared property tree is compared with its indexed
|
|
13
|
+
* node's `attrs` by the live deep diff ({@link diffDeepObservation},
|
|
14
|
+
* ./deep-observe.ts): both sides normalized with the lexicon's own hooks,
|
|
15
|
+
* and the observation baseline (`.chant/observation-baseline/<env>.json`,
|
|
16
|
+
* #4519) subtracted, value-bound. An accepted deviation stays quiet until
|
|
17
|
+
* the indexed value moves again.
|
|
18
|
+
* 3. Every row becomes one drift record (`drift: "v1"`, ../drift-record.ts).
|
|
19
|
+
*
|
|
20
|
+
* A row that is not bound is reported with its join verdict and never
|
|
21
|
+
* diffed. The verdict rides in `evidence.engineCode`:
|
|
22
|
+
*
|
|
23
|
+
* - `absent`: the index's coverage listed the type and location and holds
|
|
24
|
+
* no resource with the identity the declaration spells. The record is
|
|
25
|
+
* `pending` (declared, confirmed absent). The rule is
|
|
26
|
+
* {@link indexAbsence}, the one `chant index diff` reads "removed" by
|
|
27
|
+
* (./index-diff.ts, #4533);
|
|
28
|
+
* - `not-observed`: the index did not look there (filtered, not reached, a
|
|
29
|
+
* read failure), or the lexicon has no index match. An uncovered entity is
|
|
30
|
+
* not observed, never drifted and never in sync;
|
|
31
|
+
* - `no-binding`: the declaration spells no identity to join on;
|
|
32
|
+
* - `ambiguous`: two or more indexed resources carry the identity, and chant
|
|
33
|
+
* will not pick one.
|
|
34
|
+
*
|
|
35
|
+
* The last three are status `not-observed` with a reason from the
|
|
36
|
+
* observation contract's closed list. `not-observed` is also the index
|
|
37
|
+
* diff's word ({@link IndexChangeKind}), so the two comparisons against an
|
|
38
|
+
* index use one vocabulary.
|
|
39
|
+
*
|
|
40
|
+
* Every record's read is `live-read`, observed at the index's
|
|
41
|
+
* `stamp.observedAt`: an index version is a read of the provider, taken
|
|
42
|
+
* then. `evidence.command` names the index version that answered.
|
|
43
|
+
*/
|
|
44
|
+
import type { ChangeSetGraphNodes } from "../change-set-graph.js";
|
|
45
|
+
import type { DeepNormalizationHooks } from "../deep-observation.js";
|
|
46
|
+
import { type DriftDocument, type DriftRecord, type DriftStatus } from "../drift-record.js";
|
|
47
|
+
import { type DeepDiffResult } from "./deep-diff.js";
|
|
48
|
+
import { type DeclaredEntities } from "./deep-observe.js";
|
|
49
|
+
import { type IndexChangeKind } from "./index-diff.js";
|
|
50
|
+
import type { IndexDocument } from "./index-document.js";
|
|
51
|
+
import { type IndexJoin, type JoinLexicon } from "./index-join.js";
|
|
52
|
+
import { type ObservationBaseline } from "./observation-baseline.js";
|
|
53
|
+
/** The status an unbound row takes when the index could not say: the index diff's word and the drift record's. */
|
|
54
|
+
export declare const INDEX_NOT_OBSERVED: Extract<IndexChangeKind, DriftStatus>;
|
|
55
|
+
/** What the drift read needs of a lexicon: its index match and its deep hooks. A `LexiconPlugin` is one. */
|
|
56
|
+
export interface IndexDriftLexicon extends JoinLexicon {
|
|
57
|
+
deepNormalizationHooks?: DeepNormalizationHooks;
|
|
58
|
+
}
|
|
59
|
+
export interface IndexDriftInput {
|
|
60
|
+
/** The index version to compare against, as {@link readIndexRef} returned it. */
|
|
61
|
+
doc: IndexDocument;
|
|
62
|
+
/** Its version name, with or without `.json`. */
|
|
63
|
+
version: string;
|
|
64
|
+
/** Declared entities per lexicon, as the live deep diff takes them. */
|
|
65
|
+
entities: ReadonlyMap<string, DeclaredEntities>;
|
|
66
|
+
lexicons: readonly IndexDriftLexicon[];
|
|
67
|
+
/** The environment's accepted deviations, or null when none are recorded. */
|
|
68
|
+
baseline?: ObservationBaseline | null;
|
|
69
|
+
/** The drift record's `member`. */
|
|
70
|
+
member: string;
|
|
71
|
+
/** A stack of a multi-stack project: record addresses are `<stack>::<name>`, as search ids are. */
|
|
72
|
+
stack?: string;
|
|
73
|
+
/** The region the entities live in, from the stack (or the single stack) they belong to. */
|
|
74
|
+
region?: string;
|
|
75
|
+
/** The chant version, for `evidence.version` and the document's `chant`. */
|
|
76
|
+
chant?: string;
|
|
77
|
+
/** The member's graph, to place each record. Omit to leave `node` null. */
|
|
78
|
+
graph?: ChangeSetGraphNodes | null;
|
|
79
|
+
}
|
|
80
|
+
/** One lexicon's half of the read. */
|
|
81
|
+
export interface IndexDriftLexiconResult {
|
|
82
|
+
/** Every declared entity's join verdict, by entity name. */
|
|
83
|
+
joins: Record<string, IndexJoin>;
|
|
84
|
+
/** The deep diff over the bound rows only. Unbound rows are never in it. */
|
|
85
|
+
deep: DeepDiffResult;
|
|
86
|
+
}
|
|
87
|
+
export interface IndexDriftCounts {
|
|
88
|
+
/** Declared entities compared. */
|
|
89
|
+
declared: number;
|
|
90
|
+
bound: number;
|
|
91
|
+
/** Reported property differences across every bound row. */
|
|
92
|
+
properties: number;
|
|
93
|
+
/** Bound rows with at least one reported difference. */
|
|
94
|
+
drifted: number;
|
|
95
|
+
/** Differences the baseline suppressed. */
|
|
96
|
+
accepted: number;
|
|
97
|
+
/** Rows the index listed the place of and does not hold. */
|
|
98
|
+
pending: number;
|
|
99
|
+
/** Rows the index could not say anything about. */
|
|
100
|
+
notObserved: number;
|
|
101
|
+
}
|
|
102
|
+
export interface IndexDriftResult {
|
|
103
|
+
/** `<scope key>@<version>`. */
|
|
104
|
+
ref: string;
|
|
105
|
+
observedAt: string;
|
|
106
|
+
byLexicon: Record<string, IndexDriftLexiconResult>;
|
|
107
|
+
/** One record per declared entity, sorted by address. */
|
|
108
|
+
records: DriftRecord[];
|
|
109
|
+
counts: IndexDriftCounts;
|
|
110
|
+
}
|
|
111
|
+
/** The command an index read's records name. */
|
|
112
|
+
export declare function indexDriftCommand(ref: string): string;
|
|
113
|
+
/**
|
|
114
|
+
* Compare every declared entity with the index version, lexicon by lexicon.
|
|
115
|
+
* Deterministic: the same index, declaration and baseline give the same
|
|
116
|
+
* records.
|
|
117
|
+
*/
|
|
118
|
+
export declare function diffDeclaredAgainstIndex(input: IndexDriftInput): IndexDriftResult;
|
|
119
|
+
/** The records as one drift document. */
|
|
120
|
+
export declare function indexDriftDocument(results: Iterable<IndexDriftResult>, chant?: string): DriftDocument;
|
|
121
|
+
//# sourceMappingURL=index-drift.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-drift.d.ts","sourceRoot":"","sources":["../../src/lifecycle/index-drift.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { deepDiffDriftRecords, driftDocument, driftRecord, placeDriftRecord, } from "../drift-record.js";
|
|
2
|
+
import { isUnobservedReason } from "../observation.js";
|
|
3
|
+
import { countPropertyDrift } from "./deep-diff.js";
|
|
4
|
+
import { diffDeepObservation } from "./deep-observe.js";
|
|
5
|
+
import { indexAbsence } from "./index-diff.js";
|
|
6
|
+
import { formatIndexRef, joinDeclaredToIndex } from "./index-join.js";
|
|
7
|
+
import { baselineForLexicon } from "./observation-baseline.js";
|
|
8
|
+
/** The status an unbound row takes when the index could not say: the index diff's word and the drift record's. */
|
|
9
|
+
export const INDEX_NOT_OBSERVED = "not-observed";
|
|
10
|
+
/** The command an index read's records name. */
|
|
11
|
+
export function indexDriftCommand(ref) {
|
|
12
|
+
return `chant lifecycle diff --index ${ref}`;
|
|
13
|
+
}
|
|
14
|
+
function declaredGraph(lexicon, entities) {
|
|
15
|
+
const nodes = [...entities.entries()]
|
|
16
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
17
|
+
.map(([name, e]) => ({ id: name, kind: e.entityType, lexicon, attrs: e.props ?? {} }));
|
|
18
|
+
return { nodes, edges: [], groups: { byLexicon: { [lexicon]: nodes.map((n) => n.id) } } };
|
|
19
|
+
}
|
|
20
|
+
function queriedOf(j) {
|
|
21
|
+
if (!j.listedType)
|
|
22
|
+
return undefined;
|
|
23
|
+
return `${j.listedType}${j.identifier !== undefined ? ` ${j.identifier}` : ""}${j.location ? ` @ ${j.location}` : ""}`;
|
|
24
|
+
}
|
|
25
|
+
/** An unbound row's status and why, from its verdict. Never diffed. */
|
|
26
|
+
function unboundStatus(doc, j) {
|
|
27
|
+
const queried = queriedOf(j);
|
|
28
|
+
const q = queried ? { queried } : {};
|
|
29
|
+
switch (j.verdict) {
|
|
30
|
+
case "absent": {
|
|
31
|
+
// The join said absent from the index's coverage. Read it again with the
|
|
32
|
+
// index diff's rule, so "absent" means one thing in both comparisons.
|
|
33
|
+
const a = j.listedType && j.location ? indexAbsence(doc.coverage, j.listedType, j.location) : { kind: "removed" };
|
|
34
|
+
if (a.kind === "removed")
|
|
35
|
+
return { status: "pending" };
|
|
36
|
+
return {
|
|
37
|
+
status: INDEX_NOT_OBSERVED,
|
|
38
|
+
unobserved: {
|
|
39
|
+
reason: isUnobservedReason(a.verdict) ? a.verdict : "not-reached",
|
|
40
|
+
detail: a.detail ?? `the index's coverage says ${a.verdict} for ${j.listedType} at ${j.location}`,
|
|
41
|
+
...q,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
case "not-observed":
|
|
46
|
+
return { status: INDEX_NOT_OBSERVED, unobserved: { reason: j.reason, detail: j.detail, ...q } };
|
|
47
|
+
case "no-binding":
|
|
48
|
+
return { status: INDEX_NOT_OBSERVED, unobserved: { reason: "no-binding", detail: j.detail, ...q } };
|
|
49
|
+
case "ambiguous":
|
|
50
|
+
return {
|
|
51
|
+
status: INDEX_NOT_OBSERVED,
|
|
52
|
+
unobserved: { reason: "no-binding", detail: `${j.detail} (candidates: ${j.candidates.join(", ")})`, ...q },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Compare every declared entity with the index version, lexicon by lexicon.
|
|
58
|
+
* Deterministic: the same index, declaration and baseline give the same
|
|
59
|
+
* records.
|
|
60
|
+
*/
|
|
61
|
+
export function diffDeclaredAgainstIndex(input) {
|
|
62
|
+
const { doc } = input;
|
|
63
|
+
const ref = formatIndexRef(doc.stamp.scopeKey, input.version.endsWith(".json") ? input.version : `${input.version}.json`);
|
|
64
|
+
const observedAt = doc.stamp.observedAt;
|
|
65
|
+
const command = indexDriftCommand(ref);
|
|
66
|
+
const address = (name) => (input.stack ? `${input.stack}::${name}` : name);
|
|
67
|
+
const indexed = new Map(doc.graph.nodes.map((n) => [n.id, n]));
|
|
68
|
+
const byLexicon = {};
|
|
69
|
+
const records = [];
|
|
70
|
+
const counts = { declared: 0, bound: 0, properties: 0, drifted: 0, accepted: 0, pending: 0, notObserved: 0 };
|
|
71
|
+
const adapter = {
|
|
72
|
+
member: input.member,
|
|
73
|
+
observedAt,
|
|
74
|
+
...(input.graph !== undefined ? { graph: input.graph } : {}),
|
|
75
|
+
...(input.chant ? { version: input.chant } : {}),
|
|
76
|
+
};
|
|
77
|
+
const evidence = (engineCode) => ({ tool: "chant", ...(input.chant ? { version: input.chant } : {}), command, ...(engineCode ? { engineCode } : {}) });
|
|
78
|
+
for (const lexicon of [...input.entities.keys()].sort()) {
|
|
79
|
+
const entities = input.entities.get(lexicon);
|
|
80
|
+
if (entities.size === 0)
|
|
81
|
+
continue;
|
|
82
|
+
const plugin = input.lexicons.find((l) => l.name === lexicon) ?? { name: lexicon };
|
|
83
|
+
const { joins } = joinDeclaredToIndex(declaredGraph(lexicon, entities), doc, [plugin], {
|
|
84
|
+
regionOf: () => input.region,
|
|
85
|
+
});
|
|
86
|
+
// The bound rows are the observed side, each its indexed node's tree.
|
|
87
|
+
const bound = new Map();
|
|
88
|
+
const resources = {};
|
|
89
|
+
for (const [name, entity] of entities) {
|
|
90
|
+
const j = joins[name];
|
|
91
|
+
if (j?.verdict !== "bound")
|
|
92
|
+
continue;
|
|
93
|
+
const hit = indexed.get(j.node);
|
|
94
|
+
if (!hit)
|
|
95
|
+
continue;
|
|
96
|
+
bound.set(name, entity);
|
|
97
|
+
resources[name] = { type: entity.entityType, physicalId: hit.physicalId ?? j.nativeId, properties: (hit.attrs ?? {}) };
|
|
98
|
+
}
|
|
99
|
+
const deep = diffDeepObservation(bound, { resources, unobserved: {} }, plugin.deepNormalizationHooks, baselineForLexicon(input.baseline, lexicon));
|
|
100
|
+
byLexicon[lexicon] = { joins, deep };
|
|
101
|
+
// Bound rows: the deep diff's records, and in-sync for a row whose every
|
|
102
|
+
// difference is accepted (the deep diff lists it under neither).
|
|
103
|
+
const deepRecords = new Map(deepDiffDriftRecords({ ...adapter, diff: deep, command }).map((r) => [r.address, r]));
|
|
104
|
+
for (const name of [...entities.keys()].sort()) {
|
|
105
|
+
const entity = entities.get(name);
|
|
106
|
+
const j = joins[name];
|
|
107
|
+
counts.declared++;
|
|
108
|
+
const at = { ...placeDriftRecord(adapter, "chant", address(name)) };
|
|
109
|
+
if (j?.verdict === "bound" && bound.has(name)) {
|
|
110
|
+
counts.bound++;
|
|
111
|
+
const r = deepRecords.get(name);
|
|
112
|
+
records.push(driftRecord({
|
|
113
|
+
...(r ?? {
|
|
114
|
+
member: input.member,
|
|
115
|
+
engine: "chant",
|
|
116
|
+
address: name,
|
|
117
|
+
observedAt,
|
|
118
|
+
read: "live-read",
|
|
119
|
+
status: "in-sync",
|
|
120
|
+
evidence: evidence(),
|
|
121
|
+
}),
|
|
122
|
+
address: address(name),
|
|
123
|
+
...at,
|
|
124
|
+
resourceType: entity.entityType,
|
|
125
|
+
}));
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
// Unbound: the verdict, never a diff.
|
|
129
|
+
const verdict = j ?? { verdict: "not-observed", reason: "not-reached", detail: "the join gave this row no verdict" };
|
|
130
|
+
if (verdict.verdict === "bound") {
|
|
131
|
+
// Bound to a node the index does not hold: the document disagrees with itself.
|
|
132
|
+
records.push(driftRecord({
|
|
133
|
+
member: input.member,
|
|
134
|
+
engine: "chant",
|
|
135
|
+
address: address(name),
|
|
136
|
+
...at,
|
|
137
|
+
resourceType: entity.entityType,
|
|
138
|
+
observedAt,
|
|
139
|
+
read: "live-read",
|
|
140
|
+
status: INDEX_NOT_OBSERVED,
|
|
141
|
+
unobserved: { reason: "read-failed", detail: `the index binds ${name} to ${verdict.node}, which its graph does not hold` },
|
|
142
|
+
evidence: evidence("bound"),
|
|
143
|
+
}));
|
|
144
|
+
counts.notObserved++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const s = unboundStatus(doc, verdict);
|
|
148
|
+
if (s.status === "pending")
|
|
149
|
+
counts.pending++;
|
|
150
|
+
else
|
|
151
|
+
counts.notObserved++;
|
|
152
|
+
records.push(driftRecord({
|
|
153
|
+
member: input.member,
|
|
154
|
+
engine: "chant",
|
|
155
|
+
address: address(name),
|
|
156
|
+
...at,
|
|
157
|
+
resourceType: entity.entityType,
|
|
158
|
+
observedAt,
|
|
159
|
+
read: "live-read",
|
|
160
|
+
status: s.status,
|
|
161
|
+
...(s.unobserved ? { unobserved: s.unobserved } : {}),
|
|
162
|
+
evidence: evidence(verdict.verdict),
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
counts.properties += countPropertyDrift(deep);
|
|
166
|
+
counts.drifted += deep.drifted.length;
|
|
167
|
+
counts.accepted += deep.accepted.reduce((n, e) => n + e.changes.length, 0);
|
|
168
|
+
}
|
|
169
|
+
records.sort((a, b) => (a.address < b.address ? -1 : a.address > b.address ? 1 : 0));
|
|
170
|
+
return { ref, observedAt, byLexicon, records, counts };
|
|
171
|
+
}
|
|
172
|
+
/** The records as one drift document. */
|
|
173
|
+
export function indexDriftDocument(results, chant) {
|
|
174
|
+
const records = [];
|
|
175
|
+
for (const r of results)
|
|
176
|
+
records.push(...r.records);
|
|
177
|
+
return driftDocument(records, chant ? { chant } : {});
|
|
178
|
+
}
|