@intentius/chant 0.124.13 → 0.124.15
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/bin/chant +11 -0
- package/dist/agents/checks.js +326 -0
- package/dist/agents/discover.js +885 -0
- package/dist/agents/importer.js +17 -0
- package/dist/agents/index.js +12 -0
- package/dist/agents/types.js +19 -0
- package/dist/apply.js +151 -0
- package/dist/attrref.js +70 -0
- package/dist/audit/catalog.js +350 -0
- package/dist/audit/core.js +252 -0
- package/dist/audit/discover.js +633 -0
- package/dist/audit/fetch.js +666 -0
- package/dist/audit/lineage.js +91 -0
- package/dist/audit/nginx.js +366 -0
- package/dist/audit/prior-art.js +132 -0
- package/dist/audit/proof.js +321 -0
- package/dist/audit/report-html.js +194 -0
- package/dist/audit/report-model.js +198 -0
- package/dist/audit/report.js +102 -0
- package/dist/audit/rules-doc.js +144 -0
- package/dist/audit/secrets.js +405 -0
- package/dist/audit/terraform-state.js +140 -0
- package/dist/audit/wrangler.js +262 -0
- package/dist/behaviour-delta.js +401 -0
- package/dist/behaviour-engine.js +357 -0
- package/dist/behaviour-graph.js +83 -0
- package/dist/behaviour-http.js +188 -0
- package/dist/behaviour-kinds.js +172 -0
- package/dist/behaviour-overlay.js +50 -0
- package/dist/behaviour-predict.js +188 -0
- package/dist/behaviour-request.js +194 -0
- package/dist/behaviour.js +1325 -0
- package/dist/build-params.js +108 -0
- package/dist/build.d.ts +6 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +669 -0
- package/dist/builder.js +44 -0
- package/dist/cdk/advise.js +30 -0
- package/dist/cdk/assembly.js +122 -0
- package/dist/cdk/graph.js +444 -0
- package/dist/cdk/tier-map.js +64 -0
- package/dist/cdk/types.js +24 -0
- package/dist/change-set.d.ts +24 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +361 -0
- package/dist/child-project.js +21 -0
- package/dist/ci-jobs.d.ts +227 -0
- package/dist/ci-jobs.d.ts.map +1 -0
- package/dist/ci-jobs.js +448 -0
- package/dist/claimed-fields.js +135 -0
- package/dist/cli/build-options.js +70 -0
- package/dist/cli/build-params-cli.js +139 -0
- package/dist/cli/command-group.js +123 -0
- package/dist/cli/commands/audit-agents.js +326 -0
- package/dist/cli/commands/audit.js +571 -0
- package/dist/cli/commands/build.js +920 -0
- package/dist/cli/commands/carve-apply.js +153 -0
- package/dist/cli/commands/carve-bridge.js +201 -0
- package/dist/cli/commands/carve-emit.js +332 -0
- package/dist/cli/commands/carve-status.js +119 -0
- package/dist/cli/commands/carve.js +204 -0
- package/dist/cli/commands/check-lexicon-docs.js +79 -0
- package/dist/cli/commands/check-lexicon-examples.js +177 -0
- package/dist/cli/commands/check-lexicon-intrinsics.js +291 -0
- package/dist/cli/commands/check-lexicon-mcp.js +82 -0
- package/dist/cli/commands/check-lexicon-plugin.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-plugin.js +104 -0
- package/dist/cli/commands/check-lexicon.js +766 -0
- package/dist/cli/commands/describe.js +85 -0
- package/dist/cli/commands/diff.js +175 -0
- package/dist/cli/commands/doctor.js +350 -0
- package/dist/cli/commands/import-agents.js +155 -0
- package/dist/cli/commands/import.js +670 -0
- package/dist/cli/commands/init-lexicon/templates/codegen.js +194 -0
- package/dist/cli/commands/init-lexicon/templates/docs.js +100 -0
- package/dist/cli/commands/init-lexicon/templates/examples.js +29 -0
- package/dist/cli/commands/init-lexicon/templates/lint.js +34 -0
- package/dist/cli/commands/init-lexicon/templates/lsp.js +37 -0
- package/dist/cli/commands/init-lexicon/templates/plugin.js +108 -0
- package/dist/cli/commands/init-lexicon/templates/project.js +240 -0
- package/dist/cli/commands/init-lexicon/templates/spec.js +56 -0
- package/dist/cli/commands/init-lexicon/templates/tests.js +66 -0
- package/dist/cli/commands/init-lexicon.js +172 -0
- package/dist/cli/commands/init.js +381 -0
- package/dist/cli/commands/lexicon-rolling-upgrade.js +145 -0
- package/dist/cli/commands/lexicon-surface-diff.js +145 -0
- package/dist/cli/commands/licenses.js +125 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +948 -0
- package/dist/cli/commands/list.js +63 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -1
- package/dist/cli/commands/migrate.js +423 -0
- package/dist/cli/commands/onboard.js +403 -0
- package/dist/cli/commands/pinned-upgrade.js +191 -0
- package/dist/cli/commands/update.js +194 -0
- package/dist/cli/commands/vendor.js +217 -0
- package/dist/cli/conflict-check.js +104 -0
- package/dist/cli/debug.js +8 -0
- package/dist/cli/format.js +104 -0
- package/dist/cli/handlers/build.js +267 -0
- package/dist/cli/handlers/carve-apply.js +30 -0
- package/dist/cli/handlers/carve-bridge.js +28 -0
- package/dist/cli/handlers/carve-emit.js +50 -0
- package/dist/cli/handlers/carve-status.js +26 -0
- package/dist/cli/handlers/carve.js +40 -0
- package/dist/cli/handlers/change-set.js +46 -0
- package/dist/cli/handlers/components.js +730 -0
- package/dist/cli/handlers/dev.js +113 -0
- package/dist/cli/handlers/emulator.js +233 -0
- package/dist/cli/handlers/explain.js +54 -0
- package/dist/cli/handlers/fan-out.js +422 -0
- package/dist/cli/handlers/gate-resume.js +275 -0
- package/dist/cli/handlers/generate.js +84 -0
- package/dist/cli/handlers/graph.js +969 -0
- package/dist/cli/handlers/init.js +102 -0
- package/dist/cli/handlers/lifecycle.js +1727 -0
- package/dist/cli/handlers/lint.js +62 -0
- package/dist/cli/handlers/migrate.js +48 -0
- package/dist/cli/handlers/misc.js +391 -0
- package/dist/cli/handlers/operator.js +1162 -0
- package/dist/cli/handlers/override-approve.js +102 -0
- package/dist/cli/handlers/pr.js +503 -0
- package/dist/cli/handlers/promote.js +342 -0
- package/dist/cli/handlers/run-generate.js +206 -0
- package/dist/cli/handlers/run-wave.js +66 -0
- package/dist/cli/handlers/run.js +1191 -0
- package/dist/cli/handlers/scenario.js +325 -0
- package/dist/cli/handlers/search.js +924 -0
- package/dist/cli/handlers/serve.js +32 -0
- package/dist/cli/handlers/vendor.js +154 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/is-entry-point.js +23 -0
- package/dist/cli/lsp/capabilities.js +28 -0
- package/dist/cli/lsp/diagnostics.js +34 -0
- package/dist/cli/lsp/server.js +329 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +2540 -0
- package/dist/cli/mcp/lifecycle-tools.js +80 -0
- package/dist/cli/mcp/op-tools.js +306 -0
- package/dist/cli/mcp/resource-handlers.js +258 -0
- package/dist/cli/mcp/resources/context.js +85 -0
- package/dist/cli/mcp/server.js +353 -0
- package/dist/cli/mcp/tools/build.js +111 -0
- package/dist/cli/mcp/tools/composites.js +95 -0
- package/dist/cli/mcp/tools/explain.js +166 -0
- package/dist/cli/mcp/tools/import.js +49 -0
- package/dist/cli/mcp/tools/lint.js +41 -0
- package/dist/cli/mcp/tools/scaffold.js +111 -0
- package/dist/cli/mcp/tools/search.js +112 -0
- package/dist/cli/mcp/types.js +1 -0
- package/dist/cli/mcp/workspace-plugins.js +91 -0
- package/dist/cli/mcp/workspace-tools.d.ts +1 -1
- package/dist/cli/mcp/workspace-tools.js +564 -0
- package/dist/cli/mcp-config.js +70 -0
- package/dist/cli/plugins.js +313 -0
- package/dist/cli/registry.js +23 -0
- package/dist/cli/reporters/stylish.js +329 -0
- package/dist/cli/version.js +15 -0
- package/dist/cli/watch.js +79 -0
- package/dist/codegen/bound-property-types.js +82 -0
- package/dist/codegen/coverage.js +119 -0
- package/dist/codegen/docs-file-markers.js +60 -0
- package/dist/codegen/docs-pages.js +115 -0
- package/dist/codegen/docs-rule-scanning.js +164 -0
- package/dist/codegen/docs-rules.js +76 -0
- package/dist/codegen/docs-sections.js +117 -0
- package/dist/codegen/docs-sidebar.js +65 -0
- package/dist/codegen/docs-types.js +4 -0
- package/dist/codegen/docs.js +356 -0
- package/dist/codegen/fetch.js +340 -0
- package/dist/codegen/generate-post-synth-barrel.js +63 -0
- package/dist/codegen/generate-registry.js +71 -0
- package/dist/codegen/generate-runtime-index.js +56 -0
- package/dist/codegen/generate-typescript.js +119 -0
- package/dist/codegen/generate.js +115 -0
- package/dist/codegen/json-patch.js +141 -0
- package/dist/codegen/json-schema-to-ts.js +344 -0
- package/dist/codegen/json-schema.js +246 -0
- package/dist/codegen/lexicon-regen.js +412 -0
- package/dist/codegen/naming.js +217 -0
- package/dist/codegen/okf-lexicon.js +245 -0
- package/dist/codegen/package.js +150 -0
- package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
- package/dist/codegen/pinned-upgrade.js +494 -0
- package/dist/codegen/registry.js +60 -0
- package/dist/codegen/rehype-base-url.mjs +68 -0
- package/dist/codegen/rolling-upgrade.js +217 -0
- package/dist/codegen/surface-snapshot.js +537 -0
- package/dist/codegen/testdata/postgres-tags.json +242 -0
- package/dist/codegen/topo-sort.js +36 -0
- package/dist/codegen/typecheck.js +115 -0
- package/dist/codegen/validate.js +200 -0
- package/dist/codegen/version-bump.js +35 -0
- package/dist/components/auto-release.js +143 -0
- package/dist/components/builders.js +47 -0
- package/dist/components/capability-plugin-loader.js +175 -0
- package/dist/components/capability-plugin-schema.js +46 -0
- package/dist/components/capability-plugin.js +99 -0
- package/dist/components/capability.js +60 -0
- package/dist/components/changed-files.js +72 -0
- package/dist/components/cli-support.js +427 -0
- package/dist/components/component.js +120 -0
- package/dist/components/component.schema.json +271 -0
- package/dist/components/config-defaults.js +130 -0
- package/dist/components/deploy-units.js +60 -0
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/components/discover.js +189 -0
- package/dist/components/driver-output.js +86 -0
- package/dist/components/driver.js +728 -0
- package/dist/components/fan-out-output.js +165 -0
- package/dist/components/fan-out-record.js +94 -0
- package/dist/components/fan-out-run.js +373 -0
- package/dist/components/fan-out-support.js +60 -0
- package/dist/components/fan-out.js +273 -0
- package/dist/components/gate-plan.js +41 -0
- package/dist/components/gated-wave-pipeline.js +46 -0
- package/dist/components/index.js +24 -0
- package/dist/components/layers.js +28 -0
- package/dist/components/manifest-persistence.js +108 -0
- package/dist/components/pilots/alb-ecs.pilot.js +59 -0
- package/dist/components/pilots/dynamodb.pilot.js +51 -0
- package/dist/components/pilots/jar-emr.pilot.js +65 -0
- package/dist/components/pilots/lambda.pilot.js +52 -0
- package/dist/components/pilots/neo4j-fanout.pilot.js +68 -0
- package/dist/components/pilots/project.js +12 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +135 -0
- package/dist/components/pr-run.js +363 -0
- package/dist/components/presets/ecs-fargate.js +72 -0
- package/dist/components/presets/index.js +28 -0
- package/dist/components/presets/lambda.js +34 -0
- package/dist/components/presets/single-host-compose.js +41 -0
- package/dist/components/promote.js +549 -0
- package/dist/components/registry.js +34 -0
- package/dist/components/run-progress.js +33 -0
- package/dist/components/sandbox/driver.d.ts.map +1 -1
- package/dist/components/sandbox/driver.js +93 -0
- package/dist/components/sandbox/run.js +115 -0
- package/dist/components/starter-plugin.js +104 -0
- package/dist/components/verbs/__tests__/mock-cloud-executor.js +68 -0
- package/dist/components/verbs/__tests__/mock-process-runner.js +45 -0
- package/dist/components/verbs/__tests__/mock-sbom-generator.js +56 -0
- package/dist/components/verbs/bom-writer.js +287 -0
- package/dist/components/verbs/build-archive.js +188 -0
- package/dist/components/verbs/build.js +142 -0
- package/dist/components/verbs/cloud-executor.js +110 -0
- package/dist/components/verbs/component-bom.js +125 -0
- package/dist/components/verbs/ensure-secret.js +50 -0
- package/dist/components/verbs/index.js +61 -0
- package/dist/components/verbs/license-policy.js +75 -0
- package/dist/components/verbs/lockfile-sbom-generator.js +191 -0
- package/dist/components/verbs/process-runner.js +78 -0
- package/dist/components/verbs/r2-sync.js +93 -0
- package/dist/components/verbs/reproducibility.js +62 -0
- package/dist/components/verbs/run-agent.js +577 -0
- package/dist/components/verbs/sbom-generator.js +119 -0
- package/dist/components/verbs/sbom.js +97 -0
- package/dist/components/verbs/shell.js +32 -0
- package/dist/components/verbs/sign.js +221 -0
- package/dist/components/verbs/stub.js +28 -0
- package/dist/components/verbs/tool-sbom-generator.js +154 -0
- package/dist/components/verbs/verify.js +112 -0
- package/dist/components/verbs/vex.js +131 -0
- package/dist/components/verbs/vuln-gate.js +152 -0
- package/dist/components/verbs/vuln-scan.js +288 -0
- package/dist/components/verbs/wait-verify.js +106 -0
- package/dist/components/verbs/wrangler.js +179 -0
- package/dist/components/wave-plan.js +102 -0
- package/dist/composite.d.ts +10 -2
- package/dist/composite.d.ts.map +1 -1
- package/dist/composite.js +313 -0
- package/dist/config-import.d.ts.map +1 -1
- package/dist/config-import.js +22 -0
- package/dist/config-sandbox.js +122 -0
- package/dist/config-static.js +206 -0
- package/dist/config.js +605 -0
- package/dist/content-digest.js +23 -0
- package/dist/declarable.js +114 -0
- package/dist/deep-observation.js +437 -0
- package/dist/detectLexicon.js +54 -0
- package/dist/discovery/cache.js +70 -0
- package/dist/discovery/collect.d.ts +2 -1
- package/dist/discovery/collect.d.ts.map +1 -1
- package/dist/discovery/collect.js +223 -0
- package/dist/discovery/cycles.js +95 -0
- package/dist/discovery/entity-wire-codec.js +391 -0
- package/dist/discovery/entity-wire.js +42 -0
- package/dist/discovery/files.js +121 -0
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/fold-import.js +4073 -0
- package/dist/discovery/fold-rank.js +292 -0
- package/dist/discovery/graph.js +115 -0
- package/dist/discovery/host-composite.d.ts +35 -0
- package/dist/discovery/host-composite.d.ts.map +1 -0
- package/dist/discovery/host-composite.js +93 -0
- package/dist/discovery/import.d.ts.map +1 -1
- package/dist/discovery/import.js +65 -0
- package/dist/discovery/index.d.ts +9 -0
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/index.js +333 -0
- package/dist/discovery/param-deps.js +487 -0
- package/dist/discovery/resolve.js +39 -0
- package/dist/discovery/sandbox/bundle.js +214 -0
- package/dist/discovery/sandbox/child-errors.js +26 -0
- package/dist/discovery/sandbox/config-run.js +100 -0
- package/dist/discovery/sandbox/config-wire.js +183 -0
- package/dist/discovery/sandbox/driver.d.ts.map +1 -1
- package/dist/discovery/sandbox/driver.js +333 -0
- package/dist/discovery/sandbox/fork.js +229 -0
- package/dist/discovery/sandbox/policy-run.js +142 -0
- package/dist/discovery/sandbox/policy-wire.js +152 -0
- package/dist/discovery/sandbox/run.js +77 -0
- package/dist/discovery/specifier-resolve.js +320 -0
- package/dist/discovery/walk.js +284 -0
- package/dist/effect-receipt.js +340 -0
- package/dist/env.js +55 -0
- package/dist/errors.js +62 -0
- package/dist/fold/fold.js +1434 -0
- package/dist/fold/foldable-helpers.js +229 -0
- package/dist/fold/subset.js +696 -0
- package/dist/fold-provenance.js +414 -0
- package/dist/gate-decision.d.ts +20 -9
- package/dist/gate-decision.d.ts.map +1 -1
- package/dist/gate-decision.js +313 -0
- package/dist/gate-resume-run.js +154 -0
- package/dist/gated-waves.js +165 -0
- package/dist/governance.js +43 -0
- package/dist/graph-declared.js +45 -0
- package/dist/graph-detail.js +207 -0
- package/dist/graph-dot.js +54 -0
- package/dist/graph-effective.js +107 -0
- package/dist/graph-ir.js +623 -0
- package/dist/graph-layout.js +184 -0
- package/dist/graph-lens.js +105 -0
- package/dist/graph-mermaid.js +83 -0
- package/dist/graph-ops.js +39 -0
- package/dist/graph-refs.js +156 -0
- package/dist/held-elsewhere.js +87 -0
- package/dist/identity.js +251 -0
- package/dist/import/base-parser.js +34 -0
- package/dist/import/embedded.js +255 -0
- package/dist/import/generator.js +1 -0
- package/dist/import/ir-utils.js +85 -0
- package/dist/import/parser-options.js +45 -0
- package/dist/import/parser.js +1 -0
- package/dist/index.js +118 -0
- package/dist/intrinsic-interpolation.js +73 -0
- package/dist/intrinsic.js +31 -0
- package/dist/join-key.js +35 -0
- package/dist/kubectl-context.js +150 -0
- package/dist/lexicon-config.js +58 -0
- package/dist/lexicon-integrity.js +47 -0
- package/dist/lexicon-manifest.js +70 -0
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon-module.js +241 -0
- package/dist/lexicon-output.js +218 -0
- package/dist/lexicon-plugin-helpers.js +104 -0
- package/dist/lexicon-schema.js +136 -0
- package/dist/lexicon.js +130 -0
- package/dist/lifecycle/affected.js +224 -0
- package/dist/lifecycle/assert-live.js +110 -0
- package/dist/lifecycle/build-ledger-store.js +160 -0
- package/dist/lifecycle/build-ledger.js +155 -0
- package/dist/lifecycle/change-set.js +356 -0
- package/dist/lifecycle/converge-ledger.js +161 -0
- package/dist/lifecycle/deep-diff.js +233 -0
- package/dist/lifecycle/deep-observe.js +147 -0
- package/dist/lifecycle/digest.js +143 -0
- package/dist/lifecycle/disruption.js +135 -0
- package/dist/lifecycle/gate-applied.js +113 -0
- package/dist/lifecycle/gate-audit.d.ts +69 -2
- package/dist/lifecycle/gate-audit.d.ts.map +1 -1
- package/dist/lifecycle/gate-audit.js +415 -0
- package/dist/lifecycle/gate-ledger-select.js +89 -0
- package/dist/lifecycle/gate-ledger.d.ts +24 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.js +201 -0
- package/dist/lifecycle/gate-origin.js +117 -0
- package/dist/lifecycle/gate-pending.js +58 -0
- package/dist/lifecycle/git.js +1182 -0
- package/dist/lifecycle/identity.js +58 -0
- package/dist/lifecycle/index.js +26 -0
- package/dist/lifecycle/lease.js +283 -0
- package/dist/lifecycle/ledger-port.js +1 -0
- package/dist/lifecycle/legacy-digest.js +91 -0
- package/dist/lifecycle/live-diff.js +274 -0
- package/dist/lifecycle/local-note.js +15 -0
- package/dist/lifecycle/member-ledger.js +122 -0
- package/dist/lifecycle/observation-baseline.js +155 -0
- package/dist/lifecycle/observe.js +375 -0
- package/dist/lifecycle/oras-referrer-lookup.js +82 -0
- package/dist/lifecycle/override-ledger.js +54 -0
- package/dist/lifecycle/plan-digest.js +110 -0
- package/dist/lifecycle/plan-ledger.js +129 -0
- package/dist/lifecycle/receipt-plan.js +192 -0
- package/dist/lifecycle/release-ledger.js +155 -0
- package/dist/lifecycle/release-read.js +64 -0
- package/dist/lifecycle/replay.js +193 -0
- package/dist/lifecycle/rollback.js +102 -0
- package/dist/lifecycle/run-ledger.js +183 -0
- package/dist/lifecycle/scenario-eval.js +204 -0
- package/dist/lifecycle/scenario.js +214 -0
- package/dist/lifecycle/snapshot.js +239 -0
- package/dist/lifecycle/status.js +447 -0
- package/dist/lifecycle/symptoms.js +117 -0
- package/dist/lifecycle/teardown.js +255 -0
- package/dist/lifecycle/types.js +1 -0
- package/dist/lifecycle/unobserved-gate.js +33 -0
- package/dist/lifecycle/wave-plans.js +68 -0
- package/dist/lifecycle/work-item-id.js +36 -0
- package/dist/lifecycle/work-lease.js +393 -0
- package/dist/lint/component-checks.js +87 -0
- package/dist/lint/config.js +483 -0
- package/dist/lint/declarative.js +122 -0
- package/dist/lint/discover.d.ts.map +1 -1
- package/dist/lint/discover.js +138 -0
- package/dist/lint/engine.js +246 -0
- package/dist/lint/knowledge-checks.js +64 -0
- package/dist/lint/named-checks.js +22 -0
- package/dist/lint/output-checks.js +87 -0
- package/dist/lint/output-docs.js +163 -0
- package/dist/lint/parser.js +33 -0
- package/dist/lint/pipeline-change-gate.js +70 -0
- package/dist/lint/policy-import.d.ts +1 -1
- package/dist/lint/policy-import.d.ts.map +1 -1
- package/dist/lint/policy-import.js +62 -0
- package/dist/lint/policy-sandbox.js +86 -0
- package/dist/lint/policy.js +122 -0
- package/dist/lint/post-synth.js +87 -0
- package/dist/lint/presets/relaxed.json +26 -0
- package/dist/lint/presets/strict.json +24 -24
- package/dist/lint/receipt-checks.js +83 -0
- package/dist/lint/rule-loader.d.ts.map +1 -1
- package/dist/lint/rule-loader.js +89 -0
- package/dist/lint/rule-registry.js +86 -0
- package/dist/lint/rule.js +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.js +7 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.js +7 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.js +9 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.js +11 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.js +9 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.js +25 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.js +8 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.js +21 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.js +8 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.js +24 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.js +17 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.js +8 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.js +18 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.js +20 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.js +8 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.js +12 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.js +16 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.js +8 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.js +12 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.js +11 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.js +11 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.js +10 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.js +16 -0
- package/dist/lint/rules/comp/comp001-publishes-never-applies.js +69 -0
- package/dist/lint/rules/comp/comp002-dangling-wiring-ref.js +84 -0
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.js +103 -0
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.js +70 -0
- package/dist/lint/rules/comp/comp005-capability-kind-is-noun.js +109 -0
- package/dist/lint/rules/comp/comp006-shell-needs-reason.js +49 -0
- package/dist/lint/rules/comp/comp007-composition-sprawl.js +64 -0
- package/dist/lint/rules/comp/index.js +33 -0
- package/dist/lint/rules/comp/support.js +129 -0
- package/dist/lint/rules/composite-scope.js +51 -0
- package/dist/lint/rules/cor017-composite-name-match.js +93 -0
- package/dist/lint/rules/cor018-composite-prefer-lexicon-type.js +136 -0
- package/dist/lint/rules/cor021-env-literal-name.js +100 -0
- package/dist/lint/rules/cor022-receipt-leaf.js +118 -0
- package/dist/lint/rules/cor024-receipt-secret-pointer.js +199 -0
- package/dist/lint/rules/declarable-naming-convention.js +62 -0
- package/dist/lint/rules/evl001-non-literal-expression.js +66 -0
- package/dist/lint/rules/evl002-control-flow-resource.js +69 -0
- package/dist/lint/rules/evl003-dynamic-property-access.js +44 -0
- package/dist/lint/rules/evl004-spread-non-const.js +94 -0
- package/dist/lint/rules/evl005-resource-block-body.js +41 -0
- package/dist/lint/rules/evl007-invalid-siblings.js +105 -0
- package/dist/lint/rules/evl009-composite-no-constant.js +158 -0
- package/dist/lint/rules/evl010-composite-no-transform.js +59 -0
- package/dist/lint/rules/evl011-symbolic-in-template.js +106 -0
- package/dist/lint/rules/export-required.js +115 -0
- package/dist/lint/rules/file-declarable-limit.js +88 -0
- package/dist/lint/rules/flat-declarations.js +62 -0
- package/dist/lint/rules/index.js +90 -0
- package/dist/lint/rules/no-cyclic-declarable-ref.js +138 -0
- package/dist/lint/rules/no-redundant-type-import.js +76 -0
- package/dist/lint/rules/no-redundant-value-cast.js +41 -0
- package/dist/lint/rules/no-string-ref.js +59 -0
- package/dist/lint/rules/no-unused-declarable-import.js +75 -0
- package/dist/lint/rules/no-unused-declarable.js +142 -0
- package/dist/lint/rules/op/index.js +28 -0
- package/dist/lint/rules/op/ops012-activity-contract.js +54 -0
- package/dist/lint/rules/op/ops013-step-output-ref.js +88 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.js +114 -0
- package/dist/lint/rules/op/ops015-gate-approval.js +45 -0
- package/dist/lint/rules/op/support.js +11 -0
- package/dist/lint/rules/op/sys010-key-over-plain-http.js +100 -0
- package/dist/lint/rules/property-kind.js +56 -0
- package/dist/lint/rules/single-concern-file.js +71 -0
- package/dist/lint/rules/sys001-no-literal-key.js +60 -0
- package/dist/lint/selectors.js +168 -0
- package/dist/lint/suppressions.js +129 -0
- package/dist/lint/typescript.js +17 -0
- package/dist/live-endpoint.js +113 -0
- package/dist/live-read-session.js +76 -0
- package/dist/lsp/lexicon-providers.js +163 -0
- package/dist/lsp/types.js +5 -0
- package/dist/managed-fields.js +293 -0
- package/dist/mcp/types.js +4 -0
- package/dist/notify-verify.js +0 -1
- package/dist/notify.js +0 -1
- package/dist/notify.schema.json +72 -0
- package/dist/observation.js +266 -0
- package/dist/okf-read.js +134 -0
- package/dist/okf.js +275 -0
- package/dist/op/__fixtures__/decide-stub-backend.js +125 -0
- package/dist/op/activities/activity-contracts.d.ts +1 -1
- package/dist/op/activities/activity-contracts.js +313 -0
- package/dist/op/activities/apply.js +582 -0
- package/dist/op/activities/build.js +18 -0
- package/dist/op/activities/change-coverage.js +40 -0
- package/dist/op/activities/change-set.d.ts +8 -3
- package/dist/op/activities/change-set.d.ts.map +1 -1
- package/dist/op/activities/change-set.js +67 -0
- package/dist/op/activities/converge.js +492 -0
- package/dist/op/activities/decide.js +211 -0
- package/dist/op/activities/env-teardown.js +110 -0
- package/dist/op/activities/factory.js +591 -0
- package/dist/op/activities/forge-pull-request.js +82 -0
- package/dist/op/activities/gh-host.js +78 -0
- package/dist/op/activities/guard-validate.js +165 -0
- package/dist/op/activities/http-check.js +40 -0
- package/dist/op/activities/index.js +59 -0
- package/dist/op/activities/lexicon-upgrade.js +629 -0
- package/dist/op/activities/lifecycle.js +109 -0
- package/dist/op/activities/pipeline-audit.js +147 -0
- package/dist/op/activities/policy.js +26 -0
- package/dist/op/activities/predict-behaviour.js +341 -0
- package/dist/op/activities/propose-upgrade.js +165 -0
- package/dist/op/activities/reconcile.d.ts +28 -16
- package/dist/op/activities/reconcile.d.ts.map +1 -1
- package/dist/op/activities/reconcile.js +1011 -0
- package/dist/op/activities/shell.js +118 -0
- package/dist/op/activities/source-release.js +97 -0
- package/dist/op/activities/source-rollback.js +165 -0
- package/dist/op/activities/teardown.js +17 -0
- package/dist/op/activities/util.js +6 -0
- package/dist/op/activities/wait.js +51 -0
- package/dist/op/activities/work-evidence.js +35 -0
- package/dist/op/activities/workflow-audit.js +133 -0
- package/dist/op/activity-contract-registry.js +106 -0
- package/dist/op/activity-contract.js +232 -0
- package/dist/op/activity-failure.js +31 -0
- package/dist/op/activity-profiles.js +129 -0
- package/dist/op/activity-registry.js +105 -0
- package/dist/op/activity-runtime.js +28 -0
- package/dist/op/builders.d.ts +1 -1
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +801 -0
- package/dist/op/change-signal.js +118 -0
- package/dist/op/composites/apply-op.js +151 -0
- package/dist/op/composites/behaviour-op.js +65 -0
- package/dist/op/composites/converge-op.js +141 -0
- package/dist/op/composites/index.js +19 -0
- package/dist/op/composites/lexicon-upgrade-op.js +107 -0
- package/dist/op/composites/pipeline-audit-op.js +50 -0
- package/dist/op/composites/reconcile-op.js +84 -0
- package/dist/op/composites/watch-op.js +69 -0
- package/dist/op/composites/workflow-audit-op.js +57 -0
- package/dist/op/converge-rule.js +205 -0
- package/dist/op/cron.js +146 -0
- package/dist/op/decide-backend.js +181 -0
- package/dist/op/decide-config.js +68 -0
- package/dist/op/decide-read-inputs.js +141 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/discover.js +213 -0
- package/dist/op/duration.js +21 -0
- package/dist/op/emulator-freshness.js +80 -0
- package/dist/op/emulator-lifecycle.js +269 -0
- package/dist/op/factory.js +83 -0
- package/dist/op/gate-approval.js +172 -0
- package/dist/op/gate-name.js +86 -0
- package/dist/op/gate-plan-context.js +167 -0
- package/dist/op/gate-point-run.js +67 -0
- package/dist/op/gate-point.js +111 -0
- package/dist/op/gate-resume-plan.js +155 -0
- package/dist/op/gate-resume.js +31 -0
- package/dist/op/gate-review.js +290 -0
- package/dist/op/gate-run-locator.js +85 -0
- package/dist/op/gate-summary.js +100 -0
- package/dist/op/gate-wait.js +56 -0
- package/dist/op/gate.js +449 -0
- package/dist/op/generate-pipeline.js +124 -0
- package/dist/op/index.js +51 -0
- package/dist/op/lifecycle-receipt-store.js +45 -0
- package/dist/op/local-executor.js +1164 -0
- package/dist/op/local-output.js +140 -0
- package/dist/op/op-ir.js +317 -0
- package/dist/op/op-verb-class.js +131 -0
- package/dist/op/op-waves-notify.js +136 -0
- package/dist/op/op-waves-run.d.ts +1 -1
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +1657 -0
- package/dist/op/op-waves.js +409 -0
- package/dist/op/operator.js +743 -0
- package/dist/op/receipt-store.js +94 -0
- package/dist/op/resource.js +23 -0
- package/dist/op/run-context.js +28 -0
- package/dist/op/run-live.js +256 -0
- package/dist/op/runtime.js +21 -0
- package/dist/op/runtimes/local.js +195 -0
- package/dist/op/source-archive.js +131 -0
- package/dist/op/step-output-ref.js +397 -0
- package/dist/op/steward-beside.js +169 -0
- package/dist/op/steward-points.js +138 -0
- package/dist/op/steward-turn.js +59 -0
- package/dist/op/steward.js +317 -0
- package/dist/op/types.js +25 -0
- package/dist/op/work-lease-decl.js +81 -0
- package/dist/op/work-lease-run.js +393 -0
- package/dist/override-gate.js +284 -0
- package/dist/owner-chain.js +74 -0
- package/dist/ownership.js +92 -0
- package/dist/params.js +61 -0
- package/dist/plan-summary.js +711 -0
- package/dist/point-decision.js +211 -0
- package/dist/pr-forge.js +810 -0
- package/dist/pr-loop.js +203 -0
- package/dist/project-codegen.js +191 -0
- package/dist/project-root.js +74 -0
- package/dist/project-validation.js +50 -0
- package/dist/provenance.js +157 -0
- package/dist/pseudo-parameter.js +40 -0
- package/dist/reconcile.js +450 -0
- package/dist/resource-attributes.js +33 -0
- package/dist/runtime-adapter.js +77 -0
- package/dist/runtime.js +66 -0
- package/dist/secret-materialization.js +153 -0
- package/dist/secret-provenance.js +250 -0
- package/dist/serializer-walker.js +112 -0
- package/dist/serializer.js +1 -0
- package/dist/sort.js +45 -0
- package/dist/stack-output.js +115 -0
- package/dist/telemetry-attribution.js +149 -0
- package/dist/terraform/__fixtures__/build-graph.js +63 -0
- package/dist/terraform/adopt-state.js +40 -0
- package/dist/terraform/aws-resources.js +384 -0
- package/dist/terraform/bridge.js +204 -0
- package/dist/terraform/carve-provider.js +155 -0
- package/dist/terraform/carve.js +138 -0
- package/dist/terraform/data-source-shape.js +57 -0
- package/dist/terraform/emit-source.js +36 -0
- package/dist/terraform/excise.js +123 -0
- package/dist/terraform/graduate.js +128 -0
- package/dist/terraform/graph.js +435 -0
- package/dist/terraform/hcl2json.js +60 -0
- package/dist/terraform/manifest.js +149 -0
- package/dist/terraform/parse.js +81 -0
- package/dist/terraform/providers/aws.js +146 -0
- package/dist/terraform/providers/gcp.js +294 -0
- package/dist/terraform/providers/index.js +17 -0
- package/dist/terraform/providers/kubernetes.js +253 -0
- package/dist/terraform/score.js +151 -0
- package/dist/terraform/state.js +67 -0
- package/dist/terraform/tier-map.js +74 -0
- package/dist/terraform/types.js +13 -0
- package/dist/terraform/unified-diff.js +132 -0
- package/dist/testing.js +295 -0
- package/dist/toml-emit.js +153 -0
- package/dist/toml-parse.js +355 -0
- package/dist/toml-utils.js +60 -0
- package/dist/toml.js +428 -0
- package/dist/ts-loader.d.ts +46 -0
- package/dist/ts-loader.d.ts.map +1 -0
- package/dist/ts-loader.js +103 -0
- package/dist/types.js +1 -0
- package/dist/utils.js +61 -0
- package/dist/validation.js +20 -0
- package/dist/vendor/js-yaml-load.js +1662 -0
- package/dist/workspace/__fixtures__/contract-repo.d.ts +7 -0
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.js +119 -0
- package/dist/workspace/__fixtures__/decide-workspace.js +112 -0
- package/dist/workspace/__fixtures__/sessions.d.ts +1 -1
- package/dist/workspace/__fixtures__/sessions.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/sessions.js +91 -0
- package/dist/workspace/__fixtures__/two-member-workspace.js +34 -0
- package/dist/workspace/agent-cli.js +99 -0
- package/dist/workspace/agent.schema.json +390 -0
- package/dist/workspace/answers-ledger.js +89 -0
- package/dist/workspace/box-cli.js +142 -0
- package/dist/workspace/box-factory-write.js +196 -0
- package/dist/workspace/box-factory-write.schema.json +342 -0
- package/dist/workspace/box-factory.js +88 -0
- package/dist/workspace/box-intent.js +126 -0
- package/dist/workspace/box-isolation.js +169 -0
- package/dist/workspace/box-listing-write.schema.json +204 -0
- package/dist/workspace/box-listing.js +298 -0
- package/dist/workspace/box-publish.js +352 -0
- package/dist/workspace/box-publish.schema.json +498 -0
- package/dist/workspace/box-services.js +39 -0
- package/dist/workspace/broker-protocol.js +359 -0
- package/dist/workspace/broker-protocol.schema.json +228 -0
- package/dist/workspace/change-set.schema.json +462 -0
- package/dist/workspace/changes-cli.js +56 -0
- package/dist/workspace/changes.js +446 -0
- package/dist/workspace/changes.schema.json +642 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-fly-site.js +198 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-live-names.js +155 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-rollback.js +214 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-inputs.js +180 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-policy.js +99 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit.js +847 -0
- package/dist/workspace/chant-migrations.js +113 -0
- package/dist/workspace/check.schema.json +433 -0
- package/dist/workspace/checks/box-isolation.js +62 -0
- package/dist/workspace/checks/boxes.js +491 -0
- package/dist/workspace/checks/diagrams.js +104 -0
- package/dist/workspace/checks/generated.js +419 -0
- package/dist/workspace/checks/ledgers.js +212 -0
- package/dist/workspace/checks/links.js +191 -0
- package/dist/workspace/checks/live.js +100 -0
- package/dist/workspace/checks/pipelines.js +188 -0
- package/dist/workspace/checks/records.js +169 -0
- package/dist/workspace/checks.js +426 -0
- package/dist/workspace/ci-green-cli.js +125 -0
- package/dist/workspace/ci-green-forge.js +83 -0
- package/dist/workspace/ci-green-workflow.d.ts +3 -1
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +202 -0
- package/dist/workspace/ci-green.js +325 -0
- package/dist/workspace/ci-last-green.schema.json +158 -0
- package/dist/workspace/class-data.d.ts +84 -0
- package/dist/workspace/class-data.d.ts.map +1 -0
- package/dist/workspace/class-data.js +238 -0
- package/dist/workspace/compose-graph.js +235 -0
- package/dist/workspace/compose-reports.js +146 -0
- package/dist/workspace/composites.js +264 -0
- package/dist/workspace/composites.schema.json +617 -0
- package/dist/workspace/conformance/__fixture__/app/package.json +7 -0
- package/dist/workspace/conformance/__fixture__/app/src/server.mjs +29 -0
- package/dist/workspace/conformance/__fixture__/decisions/decision.kind.mjs +54 -0
- package/dist/workspace/conformance/__fixture__/decisions/decision.schema.json +512 -0
- package/dist/workspace/conformance/__fixture__/decisions/fix-001-how-the-app-is-deployed.md +40 -0
- package/dist/workspace/conformance/__fixture__/delivery/chant.config.ts +7 -0
- package/dist/workspace/conformance/__fixture__/delivery/lexicon/index.ts +26 -0
- package/dist/workspace/conformance/__fixture__/delivery/package.json +7 -0
- package/dist/workspace/conformance/__fixture__/delivery/src/app.component.ts +14 -0
- package/dist/workspace/conformance/__fixture__/delivery/src/app.ts +4 -0
- package/dist/workspace/conformance/__fixtures__/reference-broker.js +205 -0
- package/dist/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +32 -0
- package/dist/workspace/conformance/__writer_fixture__/answers/answer.schema.json +135 -0
- package/dist/workspace/conformance/__writer_fixture__/contracts/contract.kind.mjs +37 -0
- package/dist/workspace/conformance/__writer_fixture__/contracts/contract.schema.json +213 -0
- package/dist/workspace/conformance/__writer_fixture__/decisions/points.json +228 -0
- package/dist/workspace/conformance/__writer_fixture__/drivers/driver.kind.mjs +15 -0
- package/dist/workspace/conformance/__writer_fixture__/drivers/driver.schema.json +38 -0
- package/dist/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +54 -0
- package/dist/workspace/conformance/__writer_fixture__/sessions/session.schema.json +289 -0
- package/dist/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
- package/dist/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
- package/dist/workspace/conformance/__writer_fixture__/work/work.schema.json +603 -0
- package/dist/workspace/conformance/broker.js +736 -0
- package/dist/workspace/conformance/index.js +673 -0
- package/dist/workspace/conformance/vitest.js +108 -0
- package/dist/workspace/conformance/writer.js +1080 -0
- package/dist/workspace/decide.js +879 -0
- package/dist/workspace/decision-points.schema.json +135 -135
- package/dist/workspace/declaration-write-cli.js +74 -0
- package/dist/workspace/declaration-write.js +238 -0
- package/dist/workspace/declaration.js +938 -0
- package/dist/workspace/declaration.schema.json +1413 -1413
- package/dist/workspace/declared-kinds.js +106 -0
- package/dist/workspace/environments.js +164 -0
- package/dist/workspace/evidence.schema.json +279 -0
- package/dist/workspace/export-cli.js +185 -0
- package/dist/workspace/export.js +499 -0
- package/dist/workspace/factory-rules.js +175 -0
- package/dist/workspace/generated-files.js +92 -0
- package/dist/workspace/glob.js +45 -0
- package/dist/workspace/graph-cache.js +352 -0
- package/dist/workspace/graph-cli.js +348 -0
- package/dist/workspace/graph.schema.json +1338 -0
- package/dist/workspace/identity-gate-rule.js +75 -0
- package/dist/workspace/identity.js +126 -0
- package/dist/workspace/import.js +283 -0
- package/dist/workspace/init.js +620 -0
- package/dist/workspace/intent-cli.js +263 -0
- package/dist/workspace/intent-joins.js +201 -0
- package/dist/workspace/intent-record.js +352 -0
- package/dist/workspace/intent-record.schema.json +864 -0
- package/dist/workspace/intent.js +1582 -0
- package/dist/workspace/intent.schema.json +2420 -0
- package/dist/workspace/json-edit.js +221 -0
- package/dist/workspace/jsonc.js +235 -0
- package/dist/workspace/kind-readers.js +103 -0
- package/dist/workspace/kinds.d.ts +3 -7
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/kinds.js +553 -0
- package/dist/workspace/lexicon-symbols.js +112 -0
- package/dist/workspace/lineage-adopt-cli.js +102 -0
- package/dist/workspace/lineage-adopt.js +360 -0
- package/dist/workspace/lineage-check.js +282 -0
- package/dist/workspace/lineage-cli.js +121 -0
- package/dist/workspace/lineage-hash-index.js +284 -0
- package/dist/workspace/lineage-init.js +507 -0
- package/dist/workspace/lineage-lock.js +387 -0
- package/dist/workspace/lineage-merge.js +45 -0
- package/dist/workspace/lineage-migrations.d.ts.map +1 -1
- package/dist/workspace/lineage-migrations.js +329 -0
- package/dist/workspace/lineage-provenance.js +36 -0
- package/dist/workspace/lineage-update.js +138 -0
- package/dist/workspace/lineage-upgrade-cli.js +131 -0
- package/dist/workspace/lineage-upgrade.js +889 -0
- package/dist/workspace/lineage-vendor.js +171 -0
- package/dist/workspace/lineage-version.js +128 -0
- package/dist/workspace/lineage-versions.js +275 -0
- package/dist/workspace/links.js +330 -0
- package/dist/workspace/ls-generated.js +95 -0
- package/dist/workspace/ls.js +277 -0
- package/dist/workspace/ls.schema.json +333 -0
- package/dist/workspace/member-commands.js +508 -0
- package/dist/workspace/member-handles.js +216 -0
- package/dist/workspace/member-pipeline.js +258 -0
- package/dist/workspace/member-run.js +117 -0
- package/dist/workspace/member-write.schema.json +193 -0
- package/dist/workspace/nested-graph.js +156 -0
- package/dist/workspace/nesting.js +38 -0
- package/dist/workspace/patch.js +301 -0
- package/dist/workspace/patch.schema.json +321 -0
- package/dist/workspace/pin-cli.js +55 -0
- package/dist/workspace/pin-integrity.js +100 -0
- package/dist/workspace/plan-summary.schema.json +155 -0
- package/dist/workspace/point-answer.schema.json +129 -129
- package/dist/workspace/points-cli.js +291 -0
- package/dist/workspace/points-write.schema.json +660 -0
- package/dist/workspace/points.js +521 -0
- package/dist/workspace/points.schema.json +916 -0
- package/dist/workspace/pr-report.schema.json +108 -0
- package/dist/workspace/principal-classes.d.ts +4 -11
- package/dist/workspace/principal-classes.d.ts.map +1 -1
- package/dist/workspace/principal-classes.js +162 -0
- package/dist/workspace/reason-codes.js +326 -0
- package/dist/workspace/record-assets.js +151 -0
- package/dist/workspace/record-decided.js +105 -0
- package/dist/workspace/record-sessions.js +96 -0
- package/dist/workspace/record-source.js +114 -0
- package/dist/workspace/records-amend.schema.json +284 -0
- package/dist/workspace/records-checkout.js +76 -0
- package/dist/workspace/records-cli.js +707 -0
- package/dist/workspace/records-close.js +116 -0
- package/dist/workspace/records-close.schema.json +278 -0
- package/dist/workspace/records-new.schema.json +202 -0
- package/dist/workspace/records-review.schema.json +367 -0
- package/dist/workspace/records-since.js +317 -0
- package/dist/workspace/records-since.schema.json +218 -0
- package/dist/workspace/records-write.js +1229 -0
- package/dist/workspace/records.js +1345 -0
- package/dist/workspace/records.schema.json +604 -0
- package/dist/workspace/reference-kinds/app/package.json +10 -0
- package/dist/workspace/reference-kinds/app/workspace-kinds.json +45 -0
- package/dist/workspace/returns.js +303 -0
- package/dist/workspace/root-refusal.js +75 -0
- package/dist/workspace/run-statement.schema.json +622 -0
- package/dist/workspace/runs-cli.js +584 -0
- package/dist/workspace/runs-write.schema.json +994 -0
- package/dist/workspace/runs.js +708 -0
- package/dist/workspace/runs.schema.json +1257 -0
- package/dist/workspace/runtimes.js +76 -0
- package/dist/workspace/session-kinds.js +83 -0
- package/dist/workspace/signers.schema.json +206 -0
- package/dist/workspace/source-block.js +125 -0
- package/dist/workspace/squash.js +177 -0
- package/dist/workspace/status-gates.js +162 -0
- package/dist/workspace/status-stewards.js +330 -0
- package/dist/workspace/status.js +514 -0
- package/dist/workspace/status.schema.json +998 -0
- package/dist/workspace/steward-index.js +191 -0
- package/dist/workspace/symbols.js +188 -0
- package/dist/workspace/template-manifest.js +227 -0
- package/dist/workspace/template-pins.js +115 -0
- package/dist/workspace/trailer-joins.js +65 -0
- package/dist/workspace/trailers.js +111 -0
- package/dist/workspace/tree.js +136 -0
- package/dist/workspace/trust/attestor.js +63 -0
- package/dist/workspace/trust/dsse.js +108 -0
- package/dist/workspace/trust/evidence-cli.js +175 -0
- package/dist/workspace/trust/evidence.js +161 -0
- package/dist/workspace/trust/gate-seal.js +226 -0
- package/dist/workspace/trust/policy.js +190 -0
- package/dist/workspace/trust/provenance.js +180 -0
- package/dist/workspace/trust/rotation.js +298 -0
- package/dist/workspace/trust/run-commits.js +101 -0
- package/dist/workspace/trust/run-statement.js +182 -0
- package/dist/workspace/trust/seal.js +272 -0
- package/dist/workspace/trust/signers-cli.js +152 -0
- package/dist/workspace/trust/ssh-commit.js +178 -0
- package/dist/workspace/trust/test-repo.js +118 -0
- package/dist/workspace/trust/trust-files.js +193 -0
- package/dist/workspace/trust/verify-cli.js +63 -0
- package/dist/workspace/trust/verify.js +192 -0
- package/dist/workspace/which-chant.js +141 -0
- package/dist/workspace/wip-cli.js +95 -0
- package/dist/workspace/wip-write.schema.json +504 -0
- package/dist/workspace/wip.js +631 -0
- package/dist/workspace/wip.schema.json +334 -0
- package/dist/workspace/work-cli.js +448 -0
- package/dist/workspace/work-evidence.js +122 -0
- package/dist/workspace/work-evidence.schema.json +222 -0
- package/dist/workspace/work-history.schema.json +421 -0
- package/dist/workspace/work-lease.schema.json +246 -0
- package/dist/workspace/work-size.js +77 -0
- package/dist/workspace/work.js +283 -0
- package/dist/workspace/workspace-kinds.schema.json +259 -259
- package/dist/workspace/workspace-principals.schema.json +53 -53
- package/dist/workspace/write-lock-cli.js +105 -0
- package/dist/workspace/write-lock.js +424 -0
- package/dist/workspace/write-lock.schema.json +172 -0
- package/dist/workspace/write-scope.js +484 -0
- package/dist/yaml.js +459 -0
- package/package.json +2 -2
- package/src/build.ts +8 -0
- package/src/change-set.test.ts +48 -0
- package/src/change-set.ts +52 -2
- package/src/ci-jobs.test.ts +49 -0
- package/src/ci-jobs.ts +515 -0
- package/src/cli/commands/check-lexicon-plugin.ts +2 -0
- package/src/cli/commands/lint.ts +3 -0
- package/src/cli/commands/migrate.ts +2 -0
- package/src/cli/compiled-cli.e2e.test.ts +165 -0
- package/src/cli/main.ts +121 -27
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/codegen/pinned-upgrade.ts +2 -0
- package/src/components/discover.ts +2 -0
- package/src/components/pr-pipeline.ts +3 -2
- package/src/components/sandbox/driver.ts +3 -1
- package/src/composite.ts +15 -3
- package/src/config-import.ts +3 -0
- package/src/discovery/collect.ts +15 -1
- package/src/discovery/fold-build-parity.test.ts +71 -0
- package/src/discovery/fold-import.ts +25 -127
- package/src/discovery/fold-project-provenance.test.ts +21 -4
- package/src/discovery/host-composite.ts +101 -0
- package/src/discovery/import.ts +2 -0
- package/src/discovery/index.ts +11 -1
- package/src/discovery/sandbox/driver.ts +3 -1
- package/src/gate-decision-bundle.test.ts +5 -4
- package/src/gate-decision-identity.test.ts +59 -6
- package/src/gate-decision.ts +81 -21
- package/src/lexicon-module.ts +3 -0
- package/src/lifecycle/gate-audit.test.ts +55 -1
- package/src/lifecycle/gate-audit.ts +116 -3
- package/src/lifecycle/gate-ledger.test.ts +20 -1
- package/src/lifecycle/gate-ledger.ts +33 -1
- package/src/lint/config.ts +1 -1
- package/src/lint/discover.ts +28 -8
- package/src/lint/policy-import.ts +4 -1
- package/src/lint/rule-loader.ts +2 -0
- package/src/op/activities/activity-contracts.ts +1 -1
- package/src/op/activities/change-set.ts +11 -3
- package/src/op/activities/lexicon-upgrade.test.ts +2 -0
- package/src/op/activities/reconcile.test.ts +62 -2
- package/src/op/activities/reconcile.ts +46 -22
- package/src/op/builders.ts +1 -1
- package/src/op/discover.ts +3 -0
- package/src/op/op-waves-run.ts +4 -3
- package/src/op/op-waves.test.ts +2 -0
- package/src/op/work-lease-run.test.ts +12 -4
- package/src/ts-loader.test.ts +23 -0
- package/src/ts-loader.ts +122 -0
- package/src/workspace/__fixtures__/contract-repo.ts +17 -1
- package/src/workspace/__fixtures__/sessions.ts +3 -3
- package/src/workspace/change-set.schema.json +5 -3
- package/src/workspace/chant-migrations/chud-lexicon-exit.ts +1 -1
- package/src/workspace/ci-green-workflow.test.ts +4 -4
- package/src/workspace/ci-green-workflow.ts +5 -2
- package/src/workspace/class-data.test.ts +64 -0
- package/src/workspace/class-data.ts +236 -0
- package/src/workspace/declaration.ts +1 -1
- package/src/workspace/kinds.ts +7 -36
- package/src/workspace/lineage-migrations.ts +2 -0
- package/src/workspace/points.ts +1 -1
- package/src/workspace/principal-classes.ts +12 -95
- package/src/workspace/records-formats.test.ts +7 -2
- package/src/workspace/records-sessions-write.test.ts +3 -3
- package/src/workspace/status-contract.e2e.test.ts +37 -0
- package/src/workspace/status-contract.test.ts +23 -21
|
@@ -0,0 +1,924 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { build } from "../../build.js";
|
|
3
|
+
import { buildGraphIr, buildLiveGraphIr, collectUnobserved, sourceOverlayGraphs } from "../../graph-ir.js";
|
|
4
|
+
import { buildDeclaredPerStack } from "../../graph-declared.js";
|
|
5
|
+
import { enrichEffectiveTopology } from "../../graph-effective.js";
|
|
6
|
+
import { reconstructEdges, mergeCatalogs } from "../../graph-refs.js";
|
|
7
|
+
import { discover } from "../../discovery/index.js";
|
|
8
|
+
import { observeResources } from "../../lifecycle/observe.js";
|
|
9
|
+
import { replaySnapshots, hasSnapshot } from "../../lifecycle/replay.js";
|
|
10
|
+
import { diffLive } from "../../lifecycle/live-diff.js";
|
|
11
|
+
import { formatUnobserved } from "../../observation.js";
|
|
12
|
+
import { loadChantConfig, matchesDeclaredEnvironment } from "../../config.js";
|
|
13
|
+
import { loadPlugins, resolveProjectLexicons } from "../plugins.js";
|
|
14
|
+
import { formatError, formatWarning } from "../format.js";
|
|
15
|
+
/**
|
|
16
|
+
* `chant search <query> [--live --env <name>]` — answer an estate question with
|
|
17
|
+
* a COMPACT result instead of the whole graph. The point (measured on aws-bench,
|
|
18
|
+
* #1139): a small model shouldn't ingest a multi-thousand-token IR dump to answer
|
|
19
|
+
* "which instances are in public subnets" — it should query and get a few rows.
|
|
20
|
+
*
|
|
21
|
+
* Query grammar (space-separated terms, all must match — AND):
|
|
22
|
+
* bare word case-insensitive substring over id, kind, and attrs
|
|
23
|
+
* kind:<substr> node kind contains <substr> (e.g. kind:EC2::Instance)
|
|
24
|
+
* tag:<key>=<val> a Tags entry with Key=key and Value containing val
|
|
25
|
+
* attr:<name>=<val> attribute <name> equals/contains <val>
|
|
26
|
+
* ->kind:X / ->attr:.. this node has an edge TO a node matching the right side
|
|
27
|
+
* <-kind:X / <-attr:.. this node has an edge FROM a node matching the right side
|
|
28
|
+
*
|
|
29
|
+
* The edge operators are the point of "edge-aware" search (#1139): a small
|
|
30
|
+
* model shouldn't hand-join instance→subnet→public across many results — one
|
|
31
|
+
* query does the traversal. `kind:Instance ->attr:MapPublicIpOnLaunch=true`
|
|
32
|
+
* = instances that reference a public subnet.
|
|
33
|
+
*
|
|
34
|
+
* Output: one line per match — `<id> <kind> <key=val ...>` — with only the
|
|
35
|
+
* physical id and any attributes named in `attr:`/`--show`. Tens of tokens, not
|
|
36
|
+
* thousands.
|
|
37
|
+
*/
|
|
38
|
+
export async function runSearch(ctx) {
|
|
39
|
+
const { args } = ctx;
|
|
40
|
+
const query = (args.path ?? "").trim();
|
|
41
|
+
if (!query) {
|
|
42
|
+
console.error(formatError({ message: "chant search needs a query: chant search \"<terms>\" [--live --env <name>]" }));
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
let terms;
|
|
46
|
+
try {
|
|
47
|
+
terms = parseQuery(query);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (err instanceof QueryError) {
|
|
51
|
+
console.error(formatError({ message: err.message, hint: err.hint }));
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
const show = parseShow(args);
|
|
57
|
+
// Query-scoped drift (#1268): --check-live checks a snapshot answer against
|
|
58
|
+
// a live read, --check-snapshot checks a live answer against the recorded
|
|
59
|
+
// snapshot — the direction each needs is the source it does NOT already have.
|
|
60
|
+
if (args.checkLive && !args.at) {
|
|
61
|
+
console.error(formatError({
|
|
62
|
+
message: "chant search --check-live needs --at <ref>",
|
|
63
|
+
hint: "answer from a snapshot and check the matched rows against a live read: --at latest --check-live --env <name>",
|
|
64
|
+
}));
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
if (args.checkSnapshot && !args.live) {
|
|
68
|
+
console.error(formatError({
|
|
69
|
+
message: "chant search --check-snapshot needs --live",
|
|
70
|
+
hint: "answer live and check the matched rows against the recorded snapshot: --live --check-snapshot --env <name>",
|
|
71
|
+
}));
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
if (args.failOnDrift && !args.checkLive && !args.checkSnapshot) {
|
|
75
|
+
console.error(formatError({
|
|
76
|
+
message: "chant search --fail-on-drift needs --check-live or --check-snapshot",
|
|
77
|
+
hint: "it fails the check those flags run, so it is meaningless without one",
|
|
78
|
+
}));
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
const projectPath = resolve(".");
|
|
82
|
+
const { config } = await loadChantConfig(projectPath);
|
|
83
|
+
let ir;
|
|
84
|
+
let source = { kind: "declared" };
|
|
85
|
+
// Lexicons whose live read threw (#1263). "Nothing observed" and "could not
|
|
86
|
+
// observe" are different claims; this is what carries the second one.
|
|
87
|
+
const liveFailures = [];
|
|
88
|
+
// Run-level notes from the read (#1265) — "ownership could not be filtered
|
|
89
|
+
// on this path" — printed with the provenance footer, after the rows, so the
|
|
90
|
+
// answer is not preceded by what qualifies it.
|
|
91
|
+
let liveNotes = [];
|
|
92
|
+
// Kinds that can exist in the account without being declared (#1278). Known
|
|
93
|
+
// without a scan, so it costs nothing to mention.
|
|
94
|
+
let ambientKinds = [];
|
|
95
|
+
// Set only on a replay: whether the recording itself holds ambient resources.
|
|
96
|
+
let replayAmbient;
|
|
97
|
+
// Query-scoped drift (#1268): the OTHER observation, read only when
|
|
98
|
+
// --check-live/--check-snapshot asked for one — the declared canvas to
|
|
99
|
+
// classify against, and whether the exit code should reflect what it found.
|
|
100
|
+
let checkObservations;
|
|
101
|
+
let primaryObservations;
|
|
102
|
+
let declaredForDrift;
|
|
103
|
+
let snapshotDepth;
|
|
104
|
+
let driftFailure = false;
|
|
105
|
+
if (args.live || args.at) {
|
|
106
|
+
const environment = args.env;
|
|
107
|
+
if (!environment) {
|
|
108
|
+
const flag = args.at ? "--at" : "--live";
|
|
109
|
+
console.error(formatError({ message: `chant search ${flag} needs an environment: ${flag} --env <name>` }));
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
if (args.live && args.at) {
|
|
113
|
+
// Two different observations of the same estate, and no rule for which
|
|
114
|
+
// wins. Comparing them is a real question (#1268) but it is not this one.
|
|
115
|
+
console.error(formatError({
|
|
116
|
+
message: "chant search takes --live or --at, not both",
|
|
117
|
+
hint: "--live reads the estate now; --at answers from a recorded snapshot",
|
|
118
|
+
}));
|
|
119
|
+
return 1;
|
|
120
|
+
}
|
|
121
|
+
// Membership via matchesDeclaredEnvironment (#1221): handles `{ name,
|
|
122
|
+
// endpoint }` entries (#1166) — a plain `.includes` never matched those —
|
|
123
|
+
// and glob-pattern entries like `"pr-*"`.
|
|
124
|
+
if (config.environments && config.environments.length > 0 && !matchesDeclaredEnvironment(config.environments, environment)) {
|
|
125
|
+
console.error(formatError({ message: `Unknown environment "${environment}"` }));
|
|
126
|
+
return 1;
|
|
127
|
+
}
|
|
128
|
+
const plugins = ctx.plugins.length > 0 ? ctx.plugins : await loadPlugins(await resolveProjectLexicons(projectPath));
|
|
129
|
+
const buildResult = await build(resolve(args.src ?? config.sourceDir ?? "."), plugins.map((p) => p.serializer));
|
|
130
|
+
if (buildResult.errors.length > 0) {
|
|
131
|
+
console.error(formatError({ message: "Build failed — fix errors before searching live state" }));
|
|
132
|
+
return 1;
|
|
133
|
+
}
|
|
134
|
+
const observing = plugins.filter((p) => p.describeResources);
|
|
135
|
+
ambientKinds = observing.flatMap((p) => p.ambientKinds?.() ?? []);
|
|
136
|
+
const stacks = (config.stacks ?? []).map((s) => ({ name: s.name, region: s.region, src: s.src }));
|
|
137
|
+
let observations;
|
|
138
|
+
const liveAttrs = {};
|
|
139
|
+
if (args.at) {
|
|
140
|
+
// Answer from a recorded observation (#1266). Everything downstream is
|
|
141
|
+
// the live path's — the point is that a snapshot replays into the same
|
|
142
|
+
// shape a live read produces, so one pipeline serves both and a fold
|
|
143
|
+
// improvement reaches an old snapshot for free.
|
|
144
|
+
const scoped = new Set(stacks.filter((st) => st.src).map((st) => st.name));
|
|
145
|
+
const replay = await replaySnapshots(environment, String(args.at), scoped);
|
|
146
|
+
if ("error" in replay) {
|
|
147
|
+
console.error(formatError({ message: replay.error, ...(replay.hint ? { hint: replay.hint } : {}) }));
|
|
148
|
+
return 1;
|
|
149
|
+
}
|
|
150
|
+
observations = replay.observations;
|
|
151
|
+
replayAmbient = {
|
|
152
|
+
recordedAmbient: replay.observations.some((o) => Object.values(o.resources).some((m) => m.ambient === true)),
|
|
153
|
+
};
|
|
154
|
+
source = { kind: "snapshot", commit: replay.commit, timestamp: replay.timestamp };
|
|
155
|
+
snapshotDepth = replay.depth;
|
|
156
|
+
if (args.checkLive) {
|
|
157
|
+
// A fresh live read, additional to the snapshot the answer itself came
|
|
158
|
+
// from — this is what the matched rows get checked against.
|
|
159
|
+
const liveCheck = await observeResources(environment, observing, buildResult, {
|
|
160
|
+
owned: true,
|
|
161
|
+
stacks,
|
|
162
|
+
ambient: args.ambient === true,
|
|
163
|
+
});
|
|
164
|
+
for (const e of liveCheck.errors) {
|
|
165
|
+
liveFailures.push(e);
|
|
166
|
+
console.error(formatError({ message: `live read failed — ${e}` }));
|
|
167
|
+
}
|
|
168
|
+
checkObservations = liveCheck.observations;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
const observed = await observeResources(environment, observing, buildResult, {
|
|
173
|
+
owned: true,
|
|
174
|
+
stacks,
|
|
175
|
+
ambient: args.ambient === true,
|
|
176
|
+
});
|
|
177
|
+
// A thrown read is not a warning. Printed as one, it sat between the
|
|
178
|
+
// ownership-filter notices that also print on a working run and carried
|
|
179
|
+
// no signal (#1263). Name the lexicon and the cause, as an error.
|
|
180
|
+
for (const e of observed.errors) {
|
|
181
|
+
liveFailures.push(e);
|
|
182
|
+
console.error(formatError({ message: `live read failed — ${e}` }));
|
|
183
|
+
}
|
|
184
|
+
observations = observed.observations;
|
|
185
|
+
liveNotes = observed.notes ?? [];
|
|
186
|
+
source = { kind: "live" };
|
|
187
|
+
if (args.checkSnapshot) {
|
|
188
|
+
// The reverse direction: answer live, check the matched rows against
|
|
189
|
+
// the most recently recorded snapshot. Missing entirely is not a
|
|
190
|
+
// failure of THIS command — the live answer already stands — so it is
|
|
191
|
+
// a note, not an error.
|
|
192
|
+
const scoped = new Set(stacks.filter((st) => st.src).map((st) => st.name));
|
|
193
|
+
const checkReplay = await replaySnapshots(environment, "latest", scoped);
|
|
194
|
+
if ("error" in checkReplay) {
|
|
195
|
+
console.error(formatWarning({ message: `--check-snapshot: ${checkReplay.error}` }));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
checkObservations = checkReplay.observations;
|
|
199
|
+
snapshotDepth = checkReplay.depth;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
primaryObservations = observations;
|
|
204
|
+
let live = buildLiveGraphIr(observations);
|
|
205
|
+
// Containment edges, kept aside until after the overlay (see below).
|
|
206
|
+
let containmentEdges = [];
|
|
207
|
+
// Live-only: enrichment is a provider call, so it has no place in a replay.
|
|
208
|
+
// A recorded answer that quietly reached for the API would stop being one.
|
|
209
|
+
if (!args.at) {
|
|
210
|
+
for (const p of observing) {
|
|
211
|
+
if (!p.enrichLiveAttrs)
|
|
212
|
+
continue;
|
|
213
|
+
try {
|
|
214
|
+
const enriched = await p.enrichLiveAttrs({ environment, owned: true, stacks });
|
|
215
|
+
for (const [id, a] of Object.entries(enriched))
|
|
216
|
+
liveAttrs[id] = { ...liveAttrs[id], ...a };
|
|
217
|
+
live = { ...live, nodes: live.nodes.map((n) => (enriched[n.id] ? { ...n, attrs: { ...n.attrs, ...enriched[n.id] } } : n)) };
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
/* enrichment is best-effort; search still works on describe attrs */
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Reconstruct edges from live references (#778), the same way `graph --live`
|
|
225
|
+
// does (#1271). `buildLiveGraphIr` projects nodes only, so without this the
|
|
226
|
+
// live side of the graph has no relationships at all — and a fold over
|
|
227
|
+
// topology has nothing to traverse on anything the declared graph does not
|
|
228
|
+
// already model.
|
|
229
|
+
const catalogs = observing.map((p) => p.referenceCatalog).filter((c) => !!c);
|
|
230
|
+
// Held back until after the overlay — see where it is merged for why.
|
|
231
|
+
if (catalogs.length > 0) {
|
|
232
|
+
// Merge, never replace. A lexicon reports relationships a catalog cannot
|
|
233
|
+
// reconstruct (#1273) — an instance placed in a subnet it did not declare
|
|
234
|
+
// carries a template `Ref` in its attributes, not the physical subnet id,
|
|
235
|
+
// so no identity index resolves it. Overwriting here dropped exactly those
|
|
236
|
+
// edges and left the fold with a chain missing its first hop.
|
|
237
|
+
// Containment travels with the references here, and only here. `->`/`<-`
|
|
238
|
+
// asks which nodes reach which, and being inside something is a way of
|
|
239
|
+
// reaching it: "which subnets have no network interfaces IN them" and
|
|
240
|
+
// "which VPCs have no instances IN them" are containment questions, and
|
|
241
|
+
// with only peer references to walk the negation matched everything.
|
|
242
|
+
//
|
|
243
|
+
// `graph` still reads `.containment` on its own and draws boundary boxes,
|
|
244
|
+
// so nothing gains a line it did not have.
|
|
245
|
+
const rebuilt = reconstructEdges(live.nodes, mergeCatalogs(catalogs));
|
|
246
|
+
containmentEdges = rebuilt.containmentEdges;
|
|
247
|
+
const reconstructed = rebuilt.edges;
|
|
248
|
+
const seen = new Set((live.edges ?? []).map((e) => `${e.from}|${e.to}|${e.viaAttr ?? ""}`));
|
|
249
|
+
live = {
|
|
250
|
+
...live,
|
|
251
|
+
edges: [
|
|
252
|
+
...(live.edges ?? []),
|
|
253
|
+
...reconstructed.filter((e) => !seen.has(`${e.from}|${e.to}|${e.viaAttr ?? ""}`)),
|
|
254
|
+
],
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
// Overlay live identity onto the SOURCE graph (same as `graph --overlay`):
|
|
258
|
+
// the declared graph is the canvas — its edges carry the topology so ->/<-
|
|
259
|
+
// resolves, while the live side supplies physical ids.
|
|
260
|
+
//
|
|
261
|
+
// Multi-stack (#1162): build the declared graph PER STACK (scoped to each
|
|
262
|
+
// stack's src, the way it deploys) and stack-qualify node ids + edges as
|
|
263
|
+
// `${stack}::${id}` — matching how observation qualifies. A flat whole-
|
|
264
|
+
// project discovery would disambiguate colliding names by module path
|
|
265
|
+
// (UsEast1Src…), which never matches the observed bare LogicalResourceIds.
|
|
266
|
+
const declared = stacks.length > 0
|
|
267
|
+
? await buildDeclaredPerStack(stacks, projectPath)
|
|
268
|
+
: buildGraphIr((await discover(resolve(args.src ?? config.sourceDir ?? "."))).entities, projectPath);
|
|
269
|
+
declaredForDrift = declared;
|
|
270
|
+
// Carry the NOT-OBSERVED half of the tri-state (#1089) onto the rows, so a
|
|
271
|
+
// declared entity nobody could read is painted `_unobserved` and a row can
|
|
272
|
+
// say so instead of printing blank where a physical id would go (#1263).
|
|
273
|
+
ir = sourceOverlayGraphs(declared, live, { unobserved: collectUnobserved(observations) });
|
|
274
|
+
// Containment goes on AFTER the overlay, not through it.
|
|
275
|
+
//
|
|
276
|
+
// `sourceOverlayGraphs` admits a live edge only when one end is foreign,
|
|
277
|
+
// because "declared edges already cover every declared relationship". That
|
|
278
|
+
// is true of the references it was written for and false of containment:
|
|
279
|
+
// the declared canvas carries what the source *says*, and no source says
|
|
280
|
+
// "this instance is in this VPC" — the instance declares a subnet, and the
|
|
281
|
+
// VPC is implied by the account.
|
|
282
|
+
//
|
|
283
|
+
// So an instance and its VPC, both declared, had their containment dropped
|
|
284
|
+
// between them, while an instance in the account's DEFAULT VPC kept it
|
|
285
|
+
// because that VPC is foreign. "Which VPCs have no instances" answered
|
|
286
|
+
// correctly for the one VPC nobody declared and wrongly for every VPC
|
|
287
|
+
// somebody did.
|
|
288
|
+
if (containmentEdges.length > 0) {
|
|
289
|
+
const present = new Set(ir.nodes.map((n) => n.id));
|
|
290
|
+
const seen = new Set(ir.edges.map((e) => `${e.from}|${e.to}|${e.viaAttr ?? ""}`));
|
|
291
|
+
ir = {
|
|
292
|
+
...ir,
|
|
293
|
+
edges: [
|
|
294
|
+
...ir.edges,
|
|
295
|
+
...containmentEdges.filter((e) => present.has(e.from) &&
|
|
296
|
+
present.has(e.to) &&
|
|
297
|
+
!seen.has(`${e.from}|${e.to}|${e.viaAttr ?? ""}`)),
|
|
298
|
+
],
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
// Carry live-derived attrs onto the declared canvas — some facts only exist
|
|
302
|
+
// in live account state (e.g. `internetFacing` for an instance in the
|
|
303
|
+
// account's default VPC, whose route table chant does not model). The
|
|
304
|
+
// overlay copies physical identity but not attrs, so merge them here.
|
|
305
|
+
ir = { ...ir, nodes: ir.nodes.map((n) => (liveAttrs[n.id] ? { ...n, attrs: { ...n.attrs, ...liveAttrs[n.id] } } : n)) };
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
const discovered = await discover(resolve(args.src ?? config.sourceDir ?? "."));
|
|
309
|
+
ir = buildGraphIr(discovered.entities);
|
|
310
|
+
}
|
|
311
|
+
// Fold derived reachability facts (effectiveIngress, internetFacing) onto
|
|
312
|
+
// instance nodes so multi-hop/launch-template joins are one node predicate (#1139).
|
|
313
|
+
ir = enrichEffectiveTopology(ir);
|
|
314
|
+
// Resolve `kind:` terms against what is actually in this graph, before any
|
|
315
|
+
// node is tested against them.
|
|
316
|
+
resolveKindTerms(terms, ir.nodes);
|
|
317
|
+
const nodeById = new Map(ir.nodes.map((n) => [n.id, n]));
|
|
318
|
+
const matches = ir.nodes.filter((n) => terms.every((t) => matchTerm(n, t, ir, nodeById)));
|
|
319
|
+
if (matches.length === 0) {
|
|
320
|
+
console.log("(no matches)");
|
|
321
|
+
availableAttrs(terms, ir);
|
|
322
|
+
if (args.explain)
|
|
323
|
+
explain(terms, matches, ir, nodeById, query);
|
|
324
|
+
for (const n of liveNotes)
|
|
325
|
+
console.error(formatWarning({ message: n }));
|
|
326
|
+
return 0;
|
|
327
|
+
}
|
|
328
|
+
for (const n of matches) {
|
|
329
|
+
console.log(formatRow(n, show));
|
|
330
|
+
}
|
|
331
|
+
const backed = source.kind === "declared" || matches.some((n) => n.physicalId);
|
|
332
|
+
// Only worth asking when the live read came back empty — that is the one case
|
|
333
|
+
// where a recording changes what the caller should do next.
|
|
334
|
+
const recorded = source.kind === "live" && !matches.some((n) => n.physicalId) && args.env
|
|
335
|
+
? (await hasSnapshot(String(args.env))) ? "yes" : undefined
|
|
336
|
+
: undefined;
|
|
337
|
+
provenance(matches, source, recorded, liveFailures);
|
|
338
|
+
// Qualifies the provenance line, so it sits with it: one line per distinct
|
|
339
|
+
// note for the whole run, not one per stack, and after the rows (#1265).
|
|
340
|
+
for (const n of liveNotes)
|
|
341
|
+
console.error(formatWarning({ message: n }));
|
|
342
|
+
// Query-scoped drift (#1268): the matched rows, and only the matched rows,
|
|
343
|
+
// checked against the observation the primary answer did NOT use. Scoping
|
|
344
|
+
// both sides of diffLive() to the matched ids is what keeps a query-scoped-
|
|
345
|
+
// out resource from reading as `missing` or `unobserved` — it was never
|
|
346
|
+
// asked about, which is a third thing from both.
|
|
347
|
+
if (checkObservations && declaredForDrift && primaryObservations) {
|
|
348
|
+
const declaredIds = new Set(declaredForDrift.nodes.map((n) => n.id));
|
|
349
|
+
const matchedIds = new Set(matches.map((n) => n.id));
|
|
350
|
+
const nowObservations = args.checkLive ? checkObservations : primaryObservations;
|
|
351
|
+
const thenObservations = args.checkLive ? primaryObservations : checkObservations;
|
|
352
|
+
const driftCount = renderDrift(diffLive({
|
|
353
|
+
declared: new Set([...matchedIds].filter((id) => declaredIds.has(id))),
|
|
354
|
+
observedNow: pickResources(mergeResources(nowObservations), matchedIds),
|
|
355
|
+
observedThen: pickResources(mergeResources(thenObservations), matchedIds),
|
|
356
|
+
unobserved: pickResources(collectUnobserved(nowObservations), matchedIds),
|
|
357
|
+
}), matches.length, args.checkLive ? "live" : "snapshot", snapshotDepth);
|
|
358
|
+
if (args.failOnDrift && driftCount > 0)
|
|
359
|
+
driftFailure = true;
|
|
360
|
+
}
|
|
361
|
+
ambientHint(matches, ambientKinds, args.ambient === true, replayAmbient);
|
|
362
|
+
showMiss(matches, show);
|
|
363
|
+
regionSpread(terms, matches, show);
|
|
364
|
+
derivedSurface(terms, matches, ir, backed);
|
|
365
|
+
if (args.explain)
|
|
366
|
+
explain(terms, matches, ir, nodeById, query);
|
|
367
|
+
// The caller asked for a live answer and at least one lexicon could not be
|
|
368
|
+
// read. The rows above are still printed — they are what the source declares,
|
|
369
|
+
// labelled as such — but the command did not do what it was asked, and a
|
|
370
|
+
// script or agent needs to see that without parsing the footer (#1263).
|
|
371
|
+
if (liveFailures.length > 0) {
|
|
372
|
+
console.error(formatError({
|
|
373
|
+
message: `live read failed for ${liveFailures.length} lexicon${liveFailures.length === 1 ? "" : "s"} — answer is declared-only`,
|
|
374
|
+
hint: recorded
|
|
375
|
+
? "answer from the recorded snapshot with --at latest, or drop --live for a declared-only query"
|
|
376
|
+
: "drop --live for a declared-only query, or record a snapshot with chant lifecycle snapshot",
|
|
377
|
+
}));
|
|
378
|
+
return 1;
|
|
379
|
+
}
|
|
380
|
+
if (driftFailure)
|
|
381
|
+
return 1;
|
|
382
|
+
return 0;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Name the `--show` columns nothing carries (#1279).
|
|
386
|
+
*
|
|
387
|
+
* A requested column that no matched resource has simply did not appear, so the
|
|
388
|
+
* result looked like a resource with no such value rather than a name that was
|
|
389
|
+
* never going to match. Combined with case sensitivity that made `--show
|
|
390
|
+
* Region` an invisible no-op on an estate where every resource carries
|
|
391
|
+
* `region`.
|
|
392
|
+
*/
|
|
393
|
+
function showMiss(matches, show) {
|
|
394
|
+
if (show.length === 0 || matches.length === 0)
|
|
395
|
+
return;
|
|
396
|
+
const present = new Set(matches.flatMap((n) => Object.keys((n.attrs ?? {})).map((k) => k.toLowerCase())));
|
|
397
|
+
const missing = show.filter((k) => !present.has(k.toLowerCase()));
|
|
398
|
+
if (missing.length === 0)
|
|
399
|
+
return;
|
|
400
|
+
console.log(`— no matched resource carries ${missing.join(", ")}`);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Say when the answer spans more than one region (#1279).
|
|
404
|
+
*
|
|
405
|
+
* A result is a list of resources with no shape to it, and region is the one
|
|
406
|
+
* dimension of this estate that is invisible in a row unless asked for. Asked
|
|
407
|
+
* to list instances "in all regions", an agent printed six correct ids with no
|
|
408
|
+
* region against any of them — a complete answer to a question about regions
|
|
409
|
+
* that never mentions one, and it was judged wrong.
|
|
410
|
+
*
|
|
411
|
+
* Stated only when the matched set actually spans several and the caller has
|
|
412
|
+
* not already asked: a fact about the result, in the same family as the
|
|
413
|
+
* provenance line. It names the regions and no resource, so it cannot stand in
|
|
414
|
+
* for the answer — it says the answer has a dimension, not what to say about it.
|
|
415
|
+
*/
|
|
416
|
+
function regionSpread(terms, matches, show) {
|
|
417
|
+
if (show.includes("region") || terms.some((t) => t.a === "region"))
|
|
418
|
+
return;
|
|
419
|
+
const regions = [
|
|
420
|
+
...new Set(matches
|
|
421
|
+
.map((n) => n.attrs?.region)
|
|
422
|
+
.filter((r) => typeof r === "string" && r.length > 0)),
|
|
423
|
+
].sort();
|
|
424
|
+
if (regions.length < 2)
|
|
425
|
+
return;
|
|
426
|
+
console.log(`— these span ${regions.length} regions: ${regions.join(", ")} · add --show region to see which`);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Point out that `--ambient` is relevant to the kind just queried (#1278).
|
|
430
|
+
*
|
|
431
|
+
* A resource nothing declares and nothing references is invisible to every
|
|
432
|
+
* other observation path, so an answer about "my security groups" can be
|
|
433
|
+
* complete for the declared estate and still not be the answer the question
|
|
434
|
+
* wanted. The caller cannot know that from the result — it looks like the whole
|
|
435
|
+
* set. An agent asked which groups were unused queried the three declared ones,
|
|
436
|
+
* never learned three more existed, and spent twenty-five turns trying to
|
|
437
|
+
* reconcile the shortfall from the graph.
|
|
438
|
+
*
|
|
439
|
+
* Says only that the flag applies to this kind, which is knowable without a
|
|
440
|
+
* scan. It reports no count and names no resource, so it cannot stand in for
|
|
441
|
+
* the answer.
|
|
442
|
+
*/
|
|
443
|
+
function ambientHint(matches, ambientKinds, asked, replay) {
|
|
444
|
+
if (asked || ambientKinds.length === 0 || matches.length === 0)
|
|
445
|
+
return;
|
|
446
|
+
// On a replay the flag cannot change the answer: what is ambient in a
|
|
447
|
+
// recording was fixed when it was recorded. Telling a caller to add
|
|
448
|
+
// `--ambient` to `--at` is advice that does nothing — and when the snapshot
|
|
449
|
+
// already holds ambient resources it is worse than nothing, because the
|
|
450
|
+
// answer is complete and the hint says it is not. An agent read "6 of 6
|
|
451
|
+
// matched" alongside it, went looking for a seventh, and hand-built a wrong
|
|
452
|
+
// answer from the raw graph over twelve turns.
|
|
453
|
+
if (replay) {
|
|
454
|
+
if (!replay.recordedAmbient) {
|
|
455
|
+
console.log(`— this snapshot recorded no ambient resources · re-record with \`chant lifecycle snapshot <env> --ambient\` to include them`);
|
|
456
|
+
}
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const relevant = [...new Set(ambientKinds.filter((k) => matches.some((n) => n.kind === k)))];
|
|
460
|
+
if (relevant.length === 0)
|
|
461
|
+
return;
|
|
462
|
+
const label = relevant.map((k) => k.split("::").slice(-1)[0]).join(", ");
|
|
463
|
+
console.log(`— ${label} can also exist in the account without being declared or referenced; --ambient includes those`);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Say what backed this answer (#1266).
|
|
467
|
+
*
|
|
468
|
+
* Two things went wrong without it. A `--live` read that failed entirely
|
|
469
|
+
* returned the declared graph, exit 0, with no physical ids and nothing to say
|
|
470
|
+
* so — indistinguishable from a working live answer (#1263). And the derived
|
|
471
|
+
* surface below named folds like `internetFacing` whether or not the
|
|
472
|
+
* observation could support them, which is worse than saying nothing.
|
|
473
|
+
*
|
|
474
|
+
* It is also the most direct thing the tool can say to a caller deciding
|
|
475
|
+
* whether to re-check with a raw provider sweep: the API has already been read,
|
|
476
|
+
* and this many resources were bound to what it returned. A sweep repeats work
|
|
477
|
+
* already done. That is a fact about the query, printed for every query, and it
|
|
478
|
+
* encodes no expected answer.
|
|
479
|
+
*/
|
|
480
|
+
function provenance(matches, source, recorded, liveFailures = []) {
|
|
481
|
+
if (source.kind === "declared") {
|
|
482
|
+
console.log("— declared only · no observation · physical ids unavailable");
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const bound = matches.filter((n) => n.physicalId).length;
|
|
486
|
+
const what = source.kind === "live" ? "live read" : "snapshot";
|
|
487
|
+
if (liveFailures.length > 0) {
|
|
488
|
+
// Could not observe, as distinct from observed nothing. The rows carry the
|
|
489
|
+
// per-entity verdict; this names the lexicons that failed in one line.
|
|
490
|
+
const lexicons = liveFailures.map((e) => e.split(":")[0]).join(", ");
|
|
491
|
+
const unobserved = matches.filter((n) => n.attrs?._unobserved).length;
|
|
492
|
+
const rest = recorded
|
|
493
|
+
? "a snapshot of this environment is recorded — answer from it with --at latest"
|
|
494
|
+
: "answered from the declared graph · physical ids unavailable";
|
|
495
|
+
console.log(`— live read failed (${lexicons}) · ${unobserved}/${matches.length} rows unobserved · ${rest}`);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (bound === 0) {
|
|
499
|
+
// The estate was asked for and nothing came back bound. Naming it is the
|
|
500
|
+
// difference between "these do not exist" and "nobody could see them".
|
|
501
|
+
// A snapshot sitting unused is the actionable half of this. Denied network,
|
|
502
|
+
// agents read six declared rows as a live answer and spent their turns
|
|
503
|
+
// retrying `--live` — the tool knew the estate was unreachable AND that a
|
|
504
|
+
// recording of it was on disk, and said only the first half.
|
|
505
|
+
if (recorded) {
|
|
506
|
+
console.log(`— ${what} returned no bound resources · a snapshot of this environment is recorded — answer from it with --at latest`);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
console.log(`— ${what} returned no bound resources · answered from the declared graph · physical ids unavailable`);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
if (source.kind === "live") {
|
|
513
|
+
console.log(`— observed live · bound ${bound}/${matches.length}`);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
// Time is the whole risk of a recorded answer, so it leads. A caller can see
|
|
517
|
+
// how old this is and decide, rather than discovering staleness later.
|
|
518
|
+
const taken = source.timestamp ? ` taken ${source.timestamp}` : "";
|
|
519
|
+
const at = source.commit ? ` ${source.commit.slice(0, 7)}` : "";
|
|
520
|
+
console.log(`— observed from snapshot${at}${taken} · bound ${bound}/${matches.length}`);
|
|
521
|
+
}
|
|
522
|
+
/** Union `LiveObservation.resources` across lexicons into one name-keyed map. */
|
|
523
|
+
function mergeResources(observations) {
|
|
524
|
+
const out = {};
|
|
525
|
+
for (const o of observations)
|
|
526
|
+
Object.assign(out, o.resources);
|
|
527
|
+
return out;
|
|
528
|
+
}
|
|
529
|
+
/** Restrict a name-keyed map to the matched ids — the whole of "query-scoped". */
|
|
530
|
+
function pickResources(map, ids) {
|
|
531
|
+
const out = {};
|
|
532
|
+
for (const id of ids)
|
|
533
|
+
if (id in map)
|
|
534
|
+
out[id] = map[id];
|
|
535
|
+
return out;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Render the query-scoped drift check (#1268) — one line per matched resource
|
|
539
|
+
* whose {@link diffLive} verdict is not `unchanged`, then a summary.
|
|
540
|
+
*
|
|
541
|
+
* Reuses `diffLive` unmodified, so every category means exactly what it means
|
|
542
|
+
* in `lifecycle diff --live`: `missing`/`orphan`/`disappeared`/`drifted` count
|
|
543
|
+
* as drift, the same set `lifecycle diff` totals; `newlyObserved` and runtime
|
|
544
|
+
* children are reported but do not, and `unobserved` — a read that could not
|
|
545
|
+
* look — stays distinct from both, the same #1089 tri-state everywhere else.
|
|
546
|
+
*/
|
|
547
|
+
function renderDrift(diff, checked, direction, depth) {
|
|
548
|
+
for (const name of diff.missing)
|
|
549
|
+
console.log(`⚠ ${name} — missing (declared, not found by this check)`);
|
|
550
|
+
for (const name of diff.orphan)
|
|
551
|
+
console.log(`⚠ ${name} — orphan (observed, not declared)`);
|
|
552
|
+
for (const name of diff.disappeared)
|
|
553
|
+
console.log(`⚠ ${name} — disappeared since the recorded snapshot`);
|
|
554
|
+
for (const drift of diff.driftedSinceSnapshot) {
|
|
555
|
+
for (const change of drift.changes) {
|
|
556
|
+
console.log(`⚠ ${drift.name} ${change.path}: ${formatValue(change.oldValue)} → ${formatValue(change.newValue)} — drifted`);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
for (const name of diff.newlyObserved)
|
|
560
|
+
console.log(` · ${name} — newly observed since the recorded snapshot`);
|
|
561
|
+
for (const r of diff.runtimeChildren)
|
|
562
|
+
console.log(` · ${r.name} (${r.type}) — runtime, owned by ${r.owner}`);
|
|
563
|
+
for (const u of diff.unobserved)
|
|
564
|
+
console.log(` ? ${formatUnobserved(u.name, u)}`);
|
|
565
|
+
const driftCount = diff.missing.length + diff.orphan.length + diff.disappeared.length + diff.driftedSinceSnapshot.length;
|
|
566
|
+
const label = direction === "live" ? "a live read" : "the recorded snapshot";
|
|
567
|
+
const depthNote = depth === "deep" ? " · snapshot recorded at deep depth, compared here at identity" : "";
|
|
568
|
+
console.log(`— checked against ${label} · ${driftCount > 0 ? `${driftCount} of ${checked} matched drifted` : `no drift across ${checked} matched`}${depthNote}`);
|
|
569
|
+
return driftCount;
|
|
570
|
+
}
|
|
571
|
+
function formatValue(v) {
|
|
572
|
+
if (v === undefined)
|
|
573
|
+
return "<unset>";
|
|
574
|
+
if (typeof v === "string")
|
|
575
|
+
return v.length > 60 ? v.slice(0, 57) + "..." : v;
|
|
576
|
+
const json = JSON.stringify(v);
|
|
577
|
+
return json.length > 60 ? json.slice(0, 57) + "..." : json;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Name the facts chant computed for the kinds in this result that the query did not use.
|
|
581
|
+
*
|
|
582
|
+
* A provider API can only return what it stores; chant additionally folds multi-hop topology
|
|
583
|
+
* onto a node, and a caller has no way to know that surface exists. Reporting it turns a
|
|
584
|
+
* one-shot query into a conversation with the graph — ask something, learn what else is
|
|
585
|
+
* knowable about the same resources, refine.
|
|
586
|
+
*
|
|
587
|
+
* The names come from {@link GraphIR.derivedAttrs}, recorded by whichever enrichment pass
|
|
588
|
+
* produced them. Nothing here knows what any attribute means or which question it answers;
|
|
589
|
+
* add a pass and its facts appear, remove one and they stop.
|
|
590
|
+
*/
|
|
591
|
+
function derivedSurface(terms, matches, ir, backed = true) {
|
|
592
|
+
const derived = ir.derivedAttrs;
|
|
593
|
+
// A fold over live topology has nothing to report when the observation came
|
|
594
|
+
// back empty (#1263). Naming the surface anyway advertises facts this answer
|
|
595
|
+
// could not have computed, which is worse than saying nothing at all.
|
|
596
|
+
if (!derived || matches.length === 0 || !backed)
|
|
597
|
+
return;
|
|
598
|
+
const used = new Set(terms.filter((t) => t.kind === "attr").map((t) => t.a));
|
|
599
|
+
const unused = new Set();
|
|
600
|
+
for (const n of matches) {
|
|
601
|
+
for (const [kind, names] of Object.entries(derived)) {
|
|
602
|
+
if (!n.kind?.includes(kind))
|
|
603
|
+
continue;
|
|
604
|
+
for (const name of names)
|
|
605
|
+
if (!used.has(name))
|
|
606
|
+
unused.add(name);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (unused.size === 0)
|
|
610
|
+
return;
|
|
611
|
+
console.log(`— also derived for these resources: ${[...unused].sort().join(", ")}`);
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* `--explain` footer (#1139): a compact, model-DERIVED summary that gives a
|
|
615
|
+
* small model a reason to trust the result instead of re-deriving it with a
|
|
616
|
+
* lossy CLI sweep. It reports the universe count ("4 of 6 Instances") — chant's
|
|
617
|
+
* structural edge, since the typed graph knows the denominator a live sweep
|
|
618
|
+
* doesn't — and, for the near-miss set, WHY each was excluded (which query term
|
|
619
|
+
* it fails). Everything here is a property of the query over the graph, not of
|
|
620
|
+
* any expected answer, so it stays a fair, question-agnostic capability.
|
|
621
|
+
*/
|
|
622
|
+
function explain(terms, matches, ir, byId, query) {
|
|
623
|
+
const kinds = new Set(matches.map((n) => n.kind).filter((k) => !!k));
|
|
624
|
+
const universe = kinds.size > 0 ? ir.nodes.filter((n) => n.kind && kinds.has(n.kind)) : ir.nodes;
|
|
625
|
+
const matched = new Set(matches.map((n) => n.id));
|
|
626
|
+
const excluded = universe.filter((n) => !matched.has(n.id));
|
|
627
|
+
const kindLabel = kinds.size > 0 ? [...kinds].join("/") : "nodes";
|
|
628
|
+
console.log(`— ${matches.length} of ${universe.length} ${kindLabel} matched (query: ${query})`);
|
|
629
|
+
// Inclusion evidence: for a derived fact a CLI can't easily re-verify
|
|
630
|
+
// (internetFacing, resolved across the default VPC's routing), name WHY each
|
|
631
|
+
// match qualifies, so the agent trusts the result instead of dropping it.
|
|
632
|
+
for (const t of terms) {
|
|
633
|
+
if (t.kind !== "attr")
|
|
634
|
+
continue;
|
|
635
|
+
for (const n of matches) {
|
|
636
|
+
const via = n.attrs?.[`${t.a}Via`];
|
|
637
|
+
const id = n.id.includes("::") ? n.id.slice(n.id.lastIndexOf("::") + 2) : n.id;
|
|
638
|
+
if (typeof via === "string")
|
|
639
|
+
console.log(` ✓ ${id} ${t.a} via ${via}`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
const shown = excluded.slice(0, 8);
|
|
643
|
+
for (const n of shown) {
|
|
644
|
+
const failing = terms.find((t) => !matchTerm(n, t, ir, byId));
|
|
645
|
+
const id = n.id.includes("::") ? n.id.slice(n.id.lastIndexOf("::") + 2) : n.id;
|
|
646
|
+
console.log(` · excluded ${id} — fails ${failing ? describeTerm(failing) : "(query)"}`);
|
|
647
|
+
}
|
|
648
|
+
if (excluded.length > shown.length)
|
|
649
|
+
console.log(` · …and ${excluded.length - shown.length} more excluded`);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* On a miss, name the attributes the queried kind actually carries. A graph knows
|
|
653
|
+
* its own schema, so a caller who guessed an attribute name — or did not know a
|
|
654
|
+
* derived one existed — can see what is queryable instead of falling back to a
|
|
655
|
+
* lossy CLI sweep. Read off the nodes present, so it stays a property of the
|
|
656
|
+
* graph rather than of any expected answer: whatever the estate holds is what
|
|
657
|
+
* this lists, and it says nothing about which attribute answers a question.
|
|
658
|
+
*/
|
|
659
|
+
function availableAttrs(terms, ir) {
|
|
660
|
+
const kindTerm = terms.find((t) => t.kind === "kind");
|
|
661
|
+
if (!kindTerm)
|
|
662
|
+
return;
|
|
663
|
+
const of = ir.nodes.filter((n) => n.kind?.includes(kindTerm.a));
|
|
664
|
+
if (of.length === 0)
|
|
665
|
+
return;
|
|
666
|
+
const names = new Set();
|
|
667
|
+
for (const n of of)
|
|
668
|
+
for (const k of Object.keys(n.attrs ?? {}))
|
|
669
|
+
names.add(k);
|
|
670
|
+
const queried = new Set(terms.filter((t) => t.kind === "attr").map((t) => t.a));
|
|
671
|
+
const unused = [...names].filter((k) => !queried.has(k)).sort();
|
|
672
|
+
if (unused.length > 0) {
|
|
673
|
+
console.log(` · ${of.length} ${kindTerm.a} node(s) carry: ${unused.join(", ")}`);
|
|
674
|
+
}
|
|
675
|
+
// A queried attribute that EXISTS but matched nothing is the more useful
|
|
676
|
+
// miss to explain, and it was the one left silent: the list above omits
|
|
677
|
+
// anything the caller asked about, so querying a real attribute with an
|
|
678
|
+
// unmatchable value taught nothing at all. A caller reaching for a wildcard —
|
|
679
|
+
// `attr:effectiveIngress=*tcp:22:0.0.0.0/0`, which this grammar has no
|
|
680
|
+
// operator for — got "(no matches)" and concluded the tool had nothing.
|
|
681
|
+
for (const term of terms) {
|
|
682
|
+
if (term.kind !== "attr" || term.b === undefined || !names.has(term.a))
|
|
683
|
+
continue;
|
|
684
|
+
const values = new Set();
|
|
685
|
+
for (const n of of) {
|
|
686
|
+
const v = n.attrs?.[term.a];
|
|
687
|
+
for (const one of Array.isArray(v) ? v : [v]) {
|
|
688
|
+
if (one !== undefined && one !== null)
|
|
689
|
+
values.add(String(one));
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
if (values.size === 0)
|
|
693
|
+
continue;
|
|
694
|
+
const sample = [...values].sort().slice(0, 8);
|
|
695
|
+
const more = values.size > sample.length ? `, … ${values.size - sample.length} more` : "";
|
|
696
|
+
console.log(` · ${term.a} is present but no value matched "${term.b}" — values seen: ${sample.join(", ")}${more}`);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
function describeTerm(t) {
|
|
700
|
+
// `--explain` has to say a negated term was negated, or an exclusion reads as
|
|
701
|
+
// the opposite of what it is.
|
|
702
|
+
if (t.negated)
|
|
703
|
+
return `!${describeTerm({ ...t, negated: false })}`;
|
|
704
|
+
const leaf = (x) => x.kind === "kind" ? `kind:${x.a}` : x.kind === "attr" ? `attr:${x.a}${x.b !== undefined ? "=" + x.b : ""}`
|
|
705
|
+
: x.kind === "tag" ? `tag:${x.a}${x.b !== undefined ? "=" + x.b : ""}` : `"${x.a}"`;
|
|
706
|
+
if (t.kind === "edge" && t.sub)
|
|
707
|
+
return `${t.dir === "out" ? "→" : "←"}${leaf(t.sub)} (no such edge)`;
|
|
708
|
+
return leaf(t);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Decide which kinds a `kind:` term means, given what is in the graph.
|
|
712
|
+
*
|
|
713
|
+
* `kind:` is documented as a substring, and substrings of a CloudFormation type
|
|
714
|
+
* cross kind boundaries: `kind:EC2::VPC` also matches
|
|
715
|
+
* `AWS::EC2::VPCGatewayAttachment`, and `kind:EC2::Subnet` also matches
|
|
716
|
+
* `AWS::EC2::SubnetRouteTableAssociation`. An estate with 6 VPCs answers 9, and
|
|
717
|
+
* the caller has no reason to suspect the number they were given.
|
|
718
|
+
*
|
|
719
|
+
* That cost real answers. Asked how many VPCs have no instances, agents named
|
|
720
|
+
* the right VPCs and reported "9 VPCs in the estate" beside them; the grader
|
|
721
|
+
* failed the answer for contradicting the estate, which it did.
|
|
722
|
+
*
|
|
723
|
+
* So: a term that lines up with `::` boundaries means those kinds and only
|
|
724
|
+
* those. `EC2::VPC` is the last two segments of `AWS::EC2::VPC` and is not any
|
|
725
|
+
* run of segments in `AWS::EC2::VPCGatewayAttachment`, because
|
|
726
|
+
* `VPCGatewayAttachment` is not `VPC`.
|
|
727
|
+
*
|
|
728
|
+
* Substring is kept as the fallback, so `kind:Gateway` still finds both
|
|
729
|
+
* `InternetGateway` and `VPCGatewayAttachment` — a genuine substring search
|
|
730
|
+
* that no segment rule would serve. Most specific wins; nothing else changes.
|
|
731
|
+
*/
|
|
732
|
+
export function resolveKindTerms(terms, nodes) {
|
|
733
|
+
const present = [...new Set(nodes.map((n) => n.kind).filter((k) => !!k))];
|
|
734
|
+
const walk = (t) => {
|
|
735
|
+
if (t.sub)
|
|
736
|
+
walk(t.sub);
|
|
737
|
+
if (t.kind !== "kind")
|
|
738
|
+
return;
|
|
739
|
+
const want = t.a.toLowerCase().split("::").filter(Boolean);
|
|
740
|
+
if (want.length === 0)
|
|
741
|
+
return;
|
|
742
|
+
const onBoundary = present.filter((k) => {
|
|
743
|
+
const have = k.toLowerCase().split("::");
|
|
744
|
+
for (let i = 0; i + want.length <= have.length; i++) {
|
|
745
|
+
if (want.every((w, j) => have[i + j] === w))
|
|
746
|
+
return true;
|
|
747
|
+
}
|
|
748
|
+
return false;
|
|
749
|
+
});
|
|
750
|
+
if (onBoundary.length > 0)
|
|
751
|
+
t.kinds = new Set(onBoundary);
|
|
752
|
+
};
|
|
753
|
+
terms.forEach(walk);
|
|
754
|
+
}
|
|
755
|
+
function parseLeaf(tok) {
|
|
756
|
+
const m = /^(kind|tag|attr):(.*)$/i.exec(tok);
|
|
757
|
+
if (m) {
|
|
758
|
+
const key = m[1].toLowerCase();
|
|
759
|
+
const rest = m[2];
|
|
760
|
+
const eq = rest.indexOf("=");
|
|
761
|
+
if (eq >= 0)
|
|
762
|
+
return { kind: key, a: rest.slice(0, eq), b: rest.slice(eq + 1) };
|
|
763
|
+
return { kind: key, a: rest };
|
|
764
|
+
}
|
|
765
|
+
// `name:value` with a prefix the grammar does not have. This parsed as a
|
|
766
|
+
// free-text word and matched nothing, which is the worst available outcome:
|
|
767
|
+
// an agent looking for SSH reachability wrote
|
|
768
|
+
// `effectiveIngress:tcp:22:0.0.0.0/0` — the right idea, the right attribute,
|
|
769
|
+
// the wrong spelling — got a clean empty result, concluded chant did not hold
|
|
770
|
+
// the fact, and rebuilt the answer by hand from security-group rows. An empty
|
|
771
|
+
// result must never be the reply to a question the grammar could not read.
|
|
772
|
+
//
|
|
773
|
+
// `::` and `://` are excluded so a genuine word search for `AWS::EC2::Instance`
|
|
774
|
+
// or a URL still works — a real prefix is one colon, not two.
|
|
775
|
+
const bad = /^([A-Za-z][A-Za-z0-9_]*):(?![:/])/.exec(tok);
|
|
776
|
+
if (bad) {
|
|
777
|
+
const name = bad[1];
|
|
778
|
+
const value = tok.slice(name.length + 1);
|
|
779
|
+
throw new QueryError(`"${tok}" is not a term — there is no "${name}:" prefix`, `for an attribute, say attr:${name}=${value || "<value>"}; the prefixes are kind:, attr:, tag:, and ->/<- for edges`);
|
|
780
|
+
}
|
|
781
|
+
return { kind: "word", a: tok };
|
|
782
|
+
}
|
|
783
|
+
/** A query the grammar cannot accept, carrying the correction to print. */
|
|
784
|
+
class QueryError extends Error {
|
|
785
|
+
constructor(message, hint) {
|
|
786
|
+
super(message);
|
|
787
|
+
this.hint = hint;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function parseQuery(query) {
|
|
791
|
+
// Split on whitespace but keep quoted phrases together.
|
|
792
|
+
const tokens = query.match(/"[^"]*"|\S+/g) ?? [];
|
|
793
|
+
return tokens.map((raw) => {
|
|
794
|
+
let tok = raw.replace(/^"|"$/g, "");
|
|
795
|
+
// A leading `!` negates the term (#1280). Absence is a real estate
|
|
796
|
+
// question — "which security groups does nothing reference", "which
|
|
797
|
+
// subnets hold no instances" — and the grammar could only express
|
|
798
|
+
// presence, so the one question a graph is uniquely good at needed a
|
|
799
|
+
// provider sweep and a hand-built set difference.
|
|
800
|
+
const negated = tok.startsWith("!");
|
|
801
|
+
if (negated)
|
|
802
|
+
tok = tok.slice(1);
|
|
803
|
+
// An edge term needs a target. A bare `<-` used to parse to an empty leaf
|
|
804
|
+
// and quietly match something arbitrary — an agent wrote
|
|
805
|
+
// `kind:EC2::SecurityGroup !<-` meaning "referenced by nothing" and got a
|
|
806
|
+
// silently wrong set. Refusing it is right beyond the parse bug too:
|
|
807
|
+
// "referenced by nothing at all" and "referenced by no Instance" are
|
|
808
|
+
// different questions, and on any estate whose declared graph carries
|
|
809
|
+
// references they give different answers.
|
|
810
|
+
// A bare edge term is refused, and the refusal names the correction.
|
|
811
|
+
//
|
|
812
|
+
// It first parsed to an empty leaf and matched arbitrarily. The fix was to
|
|
813
|
+
// refuse it; then, because agents kept writing `!<-` for "what is unused",
|
|
814
|
+
// it was made to mean "no edge in this direction" — which is a coherent
|
|
815
|
+
// query and still the wrong one to answer that question with. It counts
|
|
816
|
+
// every reference in the project, and a stack output that publishes a
|
|
817
|
+
// resource's id is one, so `kind:EC2::SecurityGroup !<-` omits precisely
|
|
818
|
+
// the unattached group the question was about.
|
|
819
|
+
//
|
|
820
|
+
// Measured both ways: refusing it, agents wrote `!<-kind:EC2::Instance` and
|
|
821
|
+
// got the right answer 3/3; accepting it, they wrote `!<-` and got a wrong
|
|
822
|
+
// one 2 runs out of 3. A query whose plain reading is reliably not what the
|
|
823
|
+
// caller means is worth refusing, and the correction below is what makes
|
|
824
|
+
// the refusal useful rather than merely strict.
|
|
825
|
+
if (/^(->|<-)\s*$/.test(tok)) {
|
|
826
|
+
throw new QueryError(`"${negated ? "!" : ""}${tok}" needs a target`, `say what the edge reaches: ${negated ? "!" : ""}${tok}kind:EC2::Instance, or ${negated ? "!" : ""}${tok}attr:Name=web`);
|
|
827
|
+
}
|
|
828
|
+
const term = tok.startsWith("->")
|
|
829
|
+
? { kind: "edge", a: "", dir: "out", sub: parseLeaf(tok.slice(2)) }
|
|
830
|
+
: tok.startsWith("<-")
|
|
831
|
+
? { kind: "edge", a: "", dir: "in", sub: parseLeaf(tok.slice(2)) }
|
|
832
|
+
: parseLeaf(tok);
|
|
833
|
+
return negated ? { ...term, negated: true } : term;
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
function parseShow(args) {
|
|
837
|
+
return args.show ? args.show.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
838
|
+
}
|
|
839
|
+
function attrString(v) {
|
|
840
|
+
if (v == null)
|
|
841
|
+
return "";
|
|
842
|
+
if (typeof v === "object") {
|
|
843
|
+
// AttrRef placeholder ({$ref}) or nested — stringify shallowly.
|
|
844
|
+
return JSON.stringify(v);
|
|
845
|
+
}
|
|
846
|
+
return String(v);
|
|
847
|
+
}
|
|
848
|
+
function matchTerm(n, t, ir, byId) {
|
|
849
|
+
if (t.negated)
|
|
850
|
+
return !matchTerm(n, { ...t, negated: false }, ir, byId);
|
|
851
|
+
const attrs = n.attrs ?? {};
|
|
852
|
+
if (t.kind === "edge") {
|
|
853
|
+
if (!ir || !byId || !t.sub)
|
|
854
|
+
return false;
|
|
855
|
+
// A node matches if it has an edge (out or in) to a node satisfying `sub`.
|
|
856
|
+
const edges = ir.edges ?? [];
|
|
857
|
+
const neighbors = edges
|
|
858
|
+
.filter((e) => (t.dir === "out" ? e.from === n.id : e.to === n.id))
|
|
859
|
+
.map((e) => byId.get(t.dir === "out" ? e.to : e.from))
|
|
860
|
+
.filter((x) => !!x);
|
|
861
|
+
return neighbors.some((m) => matchTerm(m, t.sub, ir, byId));
|
|
862
|
+
}
|
|
863
|
+
if (t.kind === "kind") {
|
|
864
|
+
// Resolved kinds when the term lined up with `::` boundaries; the
|
|
865
|
+
// documented substring otherwise.
|
|
866
|
+
if (t.kinds)
|
|
867
|
+
return t.kinds.has(n.kind ?? "");
|
|
868
|
+
return (n.kind ?? "").toLowerCase().includes(t.a.toLowerCase());
|
|
869
|
+
}
|
|
870
|
+
if (t.kind === "attr") {
|
|
871
|
+
const val = attrString(attrs[t.a]);
|
|
872
|
+
return t.b === undefined ? t.a in attrs : val.toLowerCase().includes(t.b.toLowerCase());
|
|
873
|
+
}
|
|
874
|
+
if (t.kind === "tag") {
|
|
875
|
+
const tags = attrs["Tags"];
|
|
876
|
+
if (!Array.isArray(tags))
|
|
877
|
+
return false;
|
|
878
|
+
return tags.some((tag) => {
|
|
879
|
+
const key = attrString(tag?.Key);
|
|
880
|
+
const val = attrString(tag?.Value);
|
|
881
|
+
return key.toLowerCase() === t.a.toLowerCase() && (t.b === undefined || val.toLowerCase().includes(t.b.toLowerCase()));
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
// bare word: substring over id, kind, and all attr values
|
|
885
|
+
const hay = [n.id, n.kind, ...Object.values(attrs).map(attrString)].join(" ").toLowerCase();
|
|
886
|
+
return hay.includes(t.a.toLowerCase());
|
|
887
|
+
}
|
|
888
|
+
function formatRow(n, show) {
|
|
889
|
+
const attrs = (n.attrs ?? {});
|
|
890
|
+
// Display the bare logical id, not the `${stack}::` qualification (#1162).
|
|
891
|
+
const displayId = n.id.includes("::") ? n.id.slice(n.id.lastIndexOf("::") + 2) : n.id;
|
|
892
|
+
const parts = [displayId, n.kind ?? ""];
|
|
893
|
+
// Prefer the node-level live physicalId (set by the overlay), then attrs;
|
|
894
|
+
// skip source-mode AttrRef placeholders (objects).
|
|
895
|
+
const physical = n.physicalId ?? attrs["physicalId"] ?? attrs["InstanceId"] ?? attrs["Id"];
|
|
896
|
+
if (physical != null && typeof physical !== "object")
|
|
897
|
+
parts.push(String(physical));
|
|
898
|
+
// A row nobody could read says so where its physical id would go (#1263). A
|
|
899
|
+
// blank there reads as "declared, not provisioned", which the read never
|
|
900
|
+
// established; the reason is the tri-state's (#1089).
|
|
901
|
+
else if (typeof attrs["_unobserved"] === "string")
|
|
902
|
+
parts.push(`(unobserved: ${attrs["_unobserved"]})`);
|
|
903
|
+
for (const key of show) {
|
|
904
|
+
// Match the name case-insensitively, and report what was actually found.
|
|
905
|
+
// AWS attribute names are PascalCase and chant's derived ones are not, so a
|
|
906
|
+
// caller mixing them is normal: `--show region` and `--show Region` are the
|
|
907
|
+
// same request, and one of them silently printed nothing. Seven of the
|
|
908
|
+
// `--show` names in one benchmark run missed on case alone.
|
|
909
|
+
const actual = key in attrs ? key : Object.keys(attrs).find((k) => k.toLowerCase() === key.toLowerCase());
|
|
910
|
+
const v = actual == null ? undefined : attrs[actual];
|
|
911
|
+
if (v == null)
|
|
912
|
+
continue;
|
|
913
|
+
// A column the caller explicitly asked for is shown whatever shape it is.
|
|
914
|
+
// Skipping non-scalars silently meant `--show effectiveIngress` — the
|
|
915
|
+
// derived reachability fact, and the reason to reach for chant at all —
|
|
916
|
+
// printed a blank column, because it is a list. The agent read that as
|
|
917
|
+
// "chant does not have this" and hand-rolled the answer from raw
|
|
918
|
+
// security-group rows, which is exactly the work the fold exists to avoid.
|
|
919
|
+
parts.push(`${actual}=${typeof v === "object" ? JSON.stringify(v) : attrString(v)}`);
|
|
920
|
+
}
|
|
921
|
+
return parts.filter(Boolean).join(" ");
|
|
922
|
+
}
|
|
923
|
+
/** Internals exposed for unit tests. */
|
|
924
|
+
export const __searchInternals = { parseQuery, matchTerm, formatRow, explain, describeTerm, derivedSurface, availableAttrs, ambientHint, regionSpread, showMiss, provenance, renderDrift, mergeResources, pickResources };
|