@intentius/chant 0.101.0 → 0.102.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/checks.d.ts.map +1 -1
- package/dist/attrref.d.ts.map +1 -1
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/nginx.d.ts.map +1 -1
- package/dist/audit/prior-art.d.ts +48 -0
- package/dist/audit/prior-art.d.ts.map +1 -1
- package/dist/behaviour-kinds.d.ts.map +1 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/cli/commands/carve-emit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +16 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/import.d.ts +8 -0
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +9 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/pinned-upgrade.d.ts +19 -5
- package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +9 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/lsp/server.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/tools/build.d.ts.map +1 -1
- package/dist/cli/mcp/tools/composites.d.ts.map +1 -1
- package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
- package/dist/cli/mcp/tools/import.d.ts.map +1 -1
- package/dist/cli/mcp/tools/lint.d.ts.map +1 -1
- package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -1
- package/dist/cli/mcp/tools/search.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +11 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/codegen/naming.d.ts.map +1 -1
- package/dist/codegen/pinned-upgrade.d.ts +67 -3
- package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
- package/dist/codegen/registry.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/verbs/process-runner.d.ts.map +1 -1
- package/dist/components/verbs/r2-sync.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/components/verbs/sbom-generator.d.ts.map +1 -1
- package/dist/components/verbs/sign.d.ts.map +1 -1
- package/dist/components/verbs/verify.d.ts.map +1 -1
- package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
- package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
- package/dist/components/verbs/wrangler.d.ts.map +1 -1
- package/dist/composite.d.ts.map +1 -1
- package/dist/config.d.ts +19 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/declarable.d.ts +16 -0
- package/dist/declarable.d.ts.map +1 -1
- package/dist/discovery/cache.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +15 -5
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +16 -0
- package/dist/discovery/param-deps.d.ts.map +1 -1
- package/dist/discovery/sandbox/policy-wire.d.ts.map +1 -1
- package/dist/discovery/specifier-resolve.d.ts +37 -0
- package/dist/discovery/specifier-resolve.d.ts.map +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/fold/fold.d.ts +3 -1
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-layout.d.ts.map +1 -1
- package/dist/import/base-parser.d.ts.map +1 -1
- package/dist/import/embedded.d.ts +7 -0
- package/dist/import/embedded.d.ts.map +1 -1
- package/dist/import/parser-options.d.ts +14 -0
- package/dist/import/parser-options.d.ts.map +1 -0
- package/dist/import/parser.d.ts +17 -0
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts.map +1 -1
- package/dist/lexicon-output.d.ts.map +1 -1
- package/dist/lexicon-schema.d.ts +3 -3
- package/dist/lexicon.d.ts +55 -3
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/assert-live.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +15 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/member-ledger.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/plan-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/scenario.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lifecycle/work-lease.d.ts +61 -2
- package/dist/lifecycle/work-lease.d.ts.map +1 -1
- package/dist/lint/config.d.ts +8 -8
- package/dist/lint/engine.d.ts +6 -0
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lsp/lexicon-providers.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +11 -11
- package/dist/op/activities/apply.d.ts +79 -9
- package/dist/op/activities/apply.d.ts.map +1 -1
- package/dist/op/activities/decide.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +1 -1
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/lexicon-upgrade.d.ts +44 -12
- package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -1
- package/dist/op/activity-contract.d.ts +1 -1
- package/dist/op/activity-failure.d.ts.map +1 -1
- package/dist/op/activity-profiles.d.ts.map +1 -1
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/apply-op.d.ts +12 -5
- package/dist/op/composites/apply-op.d.ts.map +1 -1
- package/dist/op/composites/lexicon-upgrade-op.d.ts +22 -6
- package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -1
- package/dist/op/decide-backend.d.ts.map +1 -1
- package/dist/op/decide-read-inputs.d.ts.map +1 -1
- package/dist/op/emulator-lifecycle.d.ts +21 -2
- package/dist/op/emulator-lifecycle.d.ts.map +1 -1
- package/dist/op/gate-approval.d.ts +5 -1
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-wait.d.ts.map +1 -1
- package/dist/op/gate.d.ts +19 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/index.d.ts +2 -0
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +6 -0
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/run-context.d.ts +39 -0
- package/dist/op/run-context.d.ts.map +1 -0
- package/dist/op/run-live.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/source-archive.d.ts.map +1 -1
- package/dist/op/steward-beside.d.ts.map +1 -1
- package/dist/op/steward-points.d.ts.map +1 -1
- package/dist/op/steward.d.ts.map +1 -1
- package/dist/op/types.d.ts +8 -3
- package/dist/op/types.d.ts.map +1 -1
- package/dist/op/work-lease-run.d.ts +24 -4
- package/dist/op/work-lease-run.d.ts.map +1 -1
- package/dist/project-root.d.ts.map +1 -1
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/pseudo-parameter.d.ts.map +1 -1
- package/dist/reconcile.d.ts.map +1 -1
- package/dist/secret-materialization.d.ts.map +1 -1
- package/dist/terraform/emit-source.d.ts.map +1 -1
- package/dist/terraform/parse.d.ts.map +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/decide-workspace.d.ts +1 -1
- package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -1
- package/dist/workspace/agent-cli.d.ts +5 -0
- package/dist/workspace/agent-cli.d.ts.map +1 -1
- package/dist/workspace/box-factory.d.ts +73 -0
- package/dist/workspace/box-factory.d.ts.map +1 -0
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -1
- package/dist/workspace/checks/boxes.d.ts.map +1 -1
- package/dist/workspace/checks.d.ts +5 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/compose-graph.d.ts +10 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/conformance/index.d.ts +67 -6
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/vitest.d.ts +12 -3
- package/dist/workspace/conformance/vitest.d.ts.map +1 -1
- package/dist/workspace/conformance/writer.d.ts +368 -0
- package/dist/workspace/conformance/writer.d.ts.map +1 -0
- package/dist/workspace/decide.d.ts +1 -1
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/declaration.d.ts +91 -4
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +234 -5
- package/dist/workspace/generated-files.d.ts.map +1 -1
- package/dist/workspace/graph-cache.d.ts.map +1 -1
- package/dist/workspace/graph-cli.d.ts +2 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/identity.d.ts +111 -0
- package/dist/workspace/identity.d.ts.map +1 -0
- package/dist/workspace/intent-cli.d.ts +7 -4
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-record.d.ts +8 -1
- package/dist/workspace/intent-record.d.ts.map +1 -1
- package/dist/workspace/intent.d.ts +149 -5
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/kinds.d.ts +18 -4
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/lineage-hash-index.d.ts.map +1 -1
- package/dist/workspace/lineage-lock.d.ts +2 -2
- package/dist/workspace/lineage-lock.d.ts.map +1 -1
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/points.d.ts.map +1 -1
- package/dist/workspace/principal-classes.d.ts +114 -0
- package/dist/workspace/principal-classes.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +21 -1
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/record-assets.d.ts.map +1 -1
- package/dist/workspace/records-checkout.d.ts +54 -0
- package/dist/workspace/records-checkout.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +27 -1
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +1 -1
- package/dist/workspace/records-close.d.ts.map +1 -1
- package/dist/workspace/records-since.d.ts.map +1 -1
- package/dist/workspace/records-write.d.ts +5 -4
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +15 -0
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/runs-cli.d.ts +164 -0
- package/dist/workspace/runs-cli.d.ts.map +1 -0
- package/dist/workspace/runs.d.ts +423 -0
- package/dist/workspace/runs.d.ts.map +1 -0
- package/dist/workspace/source-block.d.ts +1 -1
- package/dist/workspace/source-block.d.ts.map +1 -1
- package/dist/workspace/status-gates.d.ts +15 -3
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +10 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/symbols.d.ts +52 -0
- package/dist/workspace/symbols.d.ts.map +1 -0
- package/dist/workspace/trailer-joins.d.ts +46 -0
- package/dist/workspace/trailer-joins.d.ts.map +1 -0
- package/dist/workspace/trailers.d.ts +89 -0
- package/dist/workspace/trailers.d.ts.map +1 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.d.ts.map +1 -1
- package/dist/workspace/trust/seal.d.ts +33 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/test-repo.d.ts.map +1 -1
- package/dist/workspace/work-cli.d.ts +39 -3
- package/dist/workspace/work-cli.d.ts.map +1 -1
- package/dist/workspace/work-evidence.d.ts +1 -1
- package/dist/workspace/work-evidence.d.ts.map +1 -1
- package/dist/workspace/work.d.ts +26 -1
- package/dist/workspace/work.d.ts.map +1 -1
- package/dist/workspace/workspace-principals.schema.json +60 -0
- package/dist/workspace/write-scope.d.ts +79 -19
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/dist/yaml.d.ts +21 -39
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/audit/prior-art.ts +15 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
- package/src/cli/commands/carve-emit-state.test.ts +4 -0
- package/src/cli/commands/carve-emit.ts +5 -19
- package/src/cli/commands/check-lexicon.test.ts +62 -1
- package/src/cli/commands/check-lexicon.ts +36 -6
- package/src/cli/commands/import-live.test.ts +18 -0
- package/src/cli/commands/import-parser-options.test.ts +133 -0
- package/src/cli/commands/import.ts +21 -3
- package/src/cli/commands/init-lexicon/templates/project.ts +1 -1
- package/src/cli/commands/init.test.ts +8 -1
- package/src/cli/commands/init.ts +23 -8
- package/src/cli/commands/lint.test.ts +37 -0
- package/src/cli/commands/onboard.test.ts +53 -3
- package/src/cli/commands/onboard.ts +52 -4
- package/src/cli/commands/pinned-upgrade.ts +73 -33
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +2 -2
- package/src/cli/handlers/components.ts +2 -2
- package/src/cli/handlers/dev.ts +8 -6
- package/src/cli/handlers/lifecycle.test.ts +67 -0
- package/src/cli/handlers/lifecycle.ts +6 -5
- package/src/cli/handlers/misc.ts +2 -0
- package/src/cli/handlers/operator.ts +87 -3
- package/src/cli/handlers/run.test.ts +111 -0
- package/src/cli/handlers/run.ts +85 -24
- package/src/cli/handlers/scenario.ts +2 -2
- package/src/cli/main.test.ts +1 -0
- package/src/cli/main.ts +65 -11
- package/src/cli/mcp/workspace-tools.test.ts +5 -2
- package/src/cli/mcp/workspace-tools.ts +29 -2
- package/src/cli/registry.ts +11 -2
- package/src/codegen/pinned-upgrade-multi.test.ts +157 -0
- package/src/codegen/pinned-upgrade-tags.test.ts +112 -0
- package/src/codegen/pinned-upgrade.test.ts +104 -0
- package/src/codegen/pinned-upgrade.ts +250 -56
- package/src/codegen/testdata/postgres-tags.json +242 -0
- package/src/config.test.ts +21 -0
- package/src/config.ts +15 -0
- package/src/declarable.test.ts +21 -0
- package/src/declarable.ts +19 -0
- package/src/discovery/fold-composite.test.ts +41 -5
- package/src/discovery/fold-import.test.ts +129 -0
- package/src/discovery/fold-import.ts +643 -131
- package/src/discovery/fold-package-composite.test.ts +264 -0
- package/src/discovery/fold-specifiers.test.ts +191 -0
- package/src/discovery/param-deps.test.ts +66 -1
- package/src/discovery/param-deps.ts +45 -0
- package/src/discovery/specifier-resolve.ts +344 -0
- package/src/fold/fold.test.ts +39 -1
- package/src/fold/fold.ts +83 -9
- package/src/fold-provenance.test.ts +140 -0
- package/src/graph-ir.test.ts +83 -0
- package/src/graph-ir.ts +29 -0
- package/src/import/embedded.test.ts +20 -0
- package/src/import/embedded.ts +13 -0
- package/src/import/parser-options.ts +46 -0
- package/src/import/parser.ts +20 -0
- package/src/lexicon.ts +55 -3
- package/src/lifecycle/gate-ledger.ts +16 -0
- package/src/lifecycle/observe.test.ts +18 -0
- package/src/lifecycle/observe.ts +6 -4
- package/src/lifecycle/snapshot.ts +2 -2
- package/src/lifecycle/work-lease.test.ts +1 -1
- package/src/lifecycle/work-lease.ts +93 -2
- package/src/lint/engine.test.ts +55 -0
- package/src/lint/engine.ts +30 -4
- package/src/lint/output-docs.test.ts +59 -56
- package/src/op/activities/apply.test.ts +189 -5
- package/src/op/activities/apply.ts +230 -8
- package/src/op/activities/index.ts +3 -0
- package/src/op/activities/lexicon-upgrade.test.ts +160 -6
- package/src/op/activities/lexicon-upgrade.ts +154 -51
- package/src/op/composites/apply-op.ts +12 -5
- package/src/op/composites/composites.test.ts +30 -3
- package/src/op/composites/lexicon-upgrade-op.test.ts +7 -2
- package/src/op/composites/lexicon-upgrade-op.ts +29 -8
- package/src/op/emulator-lifecycle.test.ts +23 -0
- package/src/op/emulator-lifecycle.ts +52 -14
- package/src/op/gate-approval.test.ts +74 -0
- package/src/op/gate-approval.ts +5 -1
- package/src/op/gate.ts +31 -5
- package/src/op/index.ts +2 -0
- package/src/op/local-executor.test.ts +15 -0
- package/src/op/local-executor.ts +40 -12
- package/src/op/local-output.ts +1 -1
- package/src/op/operator.ts +4 -0
- package/src/op/run-context.test.ts +141 -0
- package/src/op/run-context.ts +51 -0
- package/src/op/runtimes/local.test.ts +14 -0
- package/src/op/runtimes/local.ts +2 -0
- package/src/op/steward-beside.ts +2 -0
- package/src/op/types.ts +8 -3
- package/src/op/work-lease-run.test.ts +47 -0
- package/src/op/work-lease-run.ts +73 -11
- package/src/provenance.ts +35 -0
- package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
- package/src/workspace/agent-cli.ts +7 -3
- package/src/workspace/agent.schema.json +22 -0
- package/src/workspace/box-factory.test.ts +320 -0
- package/src/workspace/box-factory.ts +103 -0
- package/src/workspace/changes.schema.json +12 -20
- package/src/workspace/changes.ts +4 -1
- package/src/workspace/checks/boxes.test.ts +2 -0
- package/src/workspace/checks.ts +29 -5
- package/src/workspace/compose-graph.ts +7 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +27 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +95 -0
- package/src/workspace/conformance/__writer_fixture__/decisions/points.json +132 -0
- package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +48 -0
- package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +108 -0
- package/src/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
- package/src/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
- package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +578 -0
- package/src/workspace/conformance/conformance.e2e.test.ts +35 -3
- package/src/workspace/conformance/index.mjs +30 -3
- package/src/workspace/conformance/index.ts +103 -15
- package/src/workspace/conformance/vitest.ts +44 -2
- package/src/workspace/conformance/writer-conformance.e2e.test.ts +131 -0
- package/src/workspace/conformance/writer.test.ts +156 -0
- package/src/workspace/conformance/writer.ts +880 -0
- package/src/workspace/decide.ts +7 -1
- package/src/workspace/declaration.schema.json +234 -5
- package/src/workspace/declaration.ts +174 -10
- package/src/workspace/graph-cli.ts +5 -2
- package/src/workspace/graph.schema.json +247 -0
- package/src/workspace/identity.test.ts +196 -0
- package/src/workspace/identity.ts +186 -0
- package/src/workspace/intent-cli.ts +64 -10
- package/src/workspace/intent-record.schema.json +208 -0
- package/src/workspace/intent-record.ts +35 -1
- package/src/workspace/intent-why.test.ts +274 -0
- package/src/workspace/intent.schema.json +718 -7
- package/src/workspace/intent.test.ts +11 -2
- package/src/workspace/intent.ts +495 -32
- package/src/workspace/kinds.ts +44 -37
- package/src/workspace/points-write.schema.json +1 -0
- package/src/workspace/principal-classes.test.ts +175 -0
- package/src/workspace/principal-classes.ts +307 -0
- package/src/workspace/reason-codes.test.ts +11 -3
- package/src/workspace/reason-codes.ts +24 -1
- package/src/workspace/records-amend.schema.json +2 -0
- package/src/workspace/records-checkout.ts +106 -0
- package/src/workspace/records-cli.ts +103 -11
- package/src/workspace/records-close.schema.json +1 -0
- package/src/workspace/records-new.schema.json +2 -0
- package/src/workspace/records-review.schema.json +2 -0
- package/src/workspace/records-uncommitted.test.ts +171 -0
- package/src/workspace/records-write.ts +28 -6
- package/src/workspace/records.schema.json +72 -1
- package/src/workspace/records.ts +27 -0
- package/src/workspace/runs-cli.ts +381 -0
- package/src/workspace/runs-write.schema.json +583 -0
- package/src/workspace/runs.schema.json +810 -0
- package/src/workspace/runs.test.ts +242 -0
- package/src/workspace/runs.ts +709 -0
- package/src/workspace/status-contract.test.ts +5 -0
- package/src/workspace/status-gates.ts +22 -8
- package/src/workspace/status.schema.json +127 -2
- package/src/workspace/status.ts +21 -1
- package/src/workspace/symbols.ts +180 -0
- package/src/workspace/trailer-joins.test.ts +171 -0
- package/src/workspace/trailer-joins.ts +82 -0
- package/src/workspace/trailers.ts +148 -0
- package/src/workspace/trust/seal.ts +52 -1
- package/src/workspace/work-cli.e2e.test.ts +28 -1
- package/src/workspace/work-cli.ts +133 -8
- package/src/workspace/work-evidence.schema.json +2 -0
- package/src/workspace/work-history.schema.json +73 -1
- package/src/workspace/work-history.test.ts +48 -5
- package/src/workspace/work-kind-fields.test.ts +154 -0
- package/src/workspace/work-lease.schema.json +14 -1
- package/src/workspace/work.ts +82 -0
- package/src/workspace/workspace-principals.schema.json +60 -0
- package/src/workspace/write-scope.test.ts +128 -2
- package/src/workspace/write-scope.ts +170 -45
- package/src/yaml.test.ts +111 -1
- package/src/yaml.ts +141 -549
|
@@ -25,59 +25,77 @@
|
|
|
25
25
|
* detection only (ws-002).
|
|
26
26
|
*
|
|
27
27
|
* A principal's class comes from the role grants in the trust policy at base,
|
|
28
|
-
* through {@link principalClass} alone (the agent, runner and service roles
|
|
28
|
+
* through {@link principalClass} alone (the agent, runner and service roles,
|
|
29
|
+
* then the role each domain class a pinned package supplies names, #3080),
|
|
29
30
|
* or from naming an agent session. Claiming a session only ever narrows what
|
|
30
31
|
* a writer may do, so an unverified `CHANT_AGENT` or trailer is safe to honour.
|
|
32
|
+
*
|
|
33
|
+
* A writeScope key no pinned package supplies fails closed: a writer judged
|
|
34
|
+
* `human` might be in that class, so it is refused with
|
|
35
|
+
* `write-scope-class-unknown` until the plugin is installed at the pinned
|
|
36
|
+
* version or the entry is removed.
|
|
31
37
|
*/
|
|
32
38
|
|
|
33
39
|
import { execFileSync } from "node:child_process";
|
|
34
40
|
import { posix, relative, sep } from "node:path";
|
|
41
|
+
import { isDeepStrictEqual } from "node:util";
|
|
42
|
+
// @ts-ignore — picomatch has no types declaration
|
|
43
|
+
import picomatch from "picomatch";
|
|
35
44
|
import {
|
|
36
|
-
PRINCIPAL_CLASSES,
|
|
37
45
|
readDeclaration,
|
|
38
46
|
WorkspaceReadError,
|
|
39
47
|
type AgentDeclaration,
|
|
40
48
|
type ClassScope,
|
|
41
49
|
type Declaration,
|
|
42
50
|
type PrincipalClass,
|
|
51
|
+
type ProtectedPath,
|
|
43
52
|
type WriteVerb,
|
|
44
53
|
} from "./declaration";
|
|
54
|
+
import {
|
|
55
|
+
classesOf,
|
|
56
|
+
coreClassRegistry,
|
|
57
|
+
loadClassRegistry,
|
|
58
|
+
principalClass as classOf,
|
|
59
|
+
roleGrants,
|
|
60
|
+
unknownScopeClasses,
|
|
61
|
+
type ClassRegistry,
|
|
62
|
+
} from "./principal-classes";
|
|
45
63
|
import type { ReasonCode } from "./reason-codes";
|
|
46
64
|
import { memberHolding } from "./record-assets";
|
|
47
65
|
import { gitRoot } from "./record-source";
|
|
48
66
|
import { normalisePrincipal, parseFrontMatter } from "./records";
|
|
67
|
+
import type { WorkspaceTree } from "./tree";
|
|
49
68
|
import { emptyPolicy, type TrustPolicy } from "./trust/policy";
|
|
50
69
|
import { commitProvenance, policyAtBase, resolveBase } from "./trust/provenance";
|
|
70
|
+
import { AGENT_TRAILER } from "./trailers";
|
|
51
71
|
import { locateWorkspace } from "./which-chant";
|
|
52
72
|
|
|
53
73
|
/** The environment variable naming the agent session a write is made in. */
|
|
54
74
|
export const AGENT_ENV = "CHANT_AGENT";
|
|
55
75
|
|
|
56
|
-
/** The commit trailer naming the agent session a commit was made in. */
|
|
57
|
-
export
|
|
76
|
+
/** The commit trailer naming the agent session a commit was made in, one of chant's trailers (`trailers.ts`). */
|
|
77
|
+
export { AGENT_TRAILER };
|
|
58
78
|
|
|
59
79
|
/** Why a write is outside its writer's scope. Closed. */
|
|
60
|
-
export const WRITE_SCOPE_CODES = ["write-scope-member", "write-scope-kind", "agent-unknown"] as const satisfies readonly ReasonCode[];
|
|
80
|
+
export const WRITE_SCOPE_CODES = ["write-scope-member", "write-scope-kind", "write-scope-class-unknown", "agent-unknown"] as const satisfies readonly ReasonCode[];
|
|
61
81
|
export type WriteScopeCode = (typeof WRITE_SCOPE_CODES)[number];
|
|
62
82
|
|
|
63
|
-
/** The classes a role grant puts a principal in, in the order they are tried. Human is the rest. */
|
|
64
|
-
const ROLE_CLASSES = ["agent", "runner", "service"] as const satisfies readonly PrincipalClass[];
|
|
65
|
-
|
|
66
83
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* possibly the declaration, #2547) is decided in one place: the policy.
|
|
84
|
+
* What `check --changes` reports a path with: the record-write codes, and a
|
|
85
|
+
* write to a protected path (#3146), which a record write never is. Closed.
|
|
70
86
|
*/
|
|
71
|
-
export
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
export const SCOPE_FINDING_CODES = [...WRITE_SCOPE_CODES, "write-scope-protected"] as const satisfies readonly ReasonCode[];
|
|
88
|
+
export type ScopeFindingCode = (typeof SCOPE_FINDING_CODES)[number];
|
|
89
|
+
|
|
90
|
+
export { classesOf, roleGrants };
|
|
74
91
|
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
92
|
+
/**
|
|
93
|
+
* The class a principal is in: the first of agent, runner and service whose
|
|
94
|
+
* role it holds at base, then the first domain class in `classes` whose role
|
|
95
|
+
* it holds (#3080), or human.
|
|
96
|
+
*/
|
|
97
|
+
export function principalClass(policy: TrustPolicy, principal: string | null, classes: ClassRegistry = coreClassRegistry()): PrincipalClass {
|
|
98
|
+
return classOf(policy, principal, classes);
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
/** Who is writing, as the scope judges it. */
|
|
@@ -104,7 +122,12 @@ export class WriteScopeError extends Error {
|
|
|
104
122
|
* the declaration declares none by that name), in the session that lists
|
|
105
123
|
* `principal`, or else in the class `principal`'s roles give it.
|
|
106
124
|
*/
|
|
107
|
-
export function resolveWriter(
|
|
125
|
+
export function resolveWriter(
|
|
126
|
+
declaration: Declaration | null,
|
|
127
|
+
policy: TrustPolicy,
|
|
128
|
+
given: { agent?: string | null; principal?: string | null },
|
|
129
|
+
classes: ClassRegistry = coreClassRegistry(),
|
|
130
|
+
): Writer {
|
|
108
131
|
const principal = given.principal ?? null;
|
|
109
132
|
const agents = declaration?.agents ?? [];
|
|
110
133
|
if (given.agent !== undefined && given.agent !== null) {
|
|
@@ -125,19 +148,40 @@ export function resolveWriter(declaration: Declaration | null, policy: TrustPoli
|
|
|
125
148
|
const agent = agents.find((a) => a.principals.some((p) => normalisePrincipal(p) === name));
|
|
126
149
|
if (agent) return { principal, class: "agent", agent };
|
|
127
150
|
}
|
|
128
|
-
return { principal, class: principalClass(policy, principal), agent: null };
|
|
151
|
+
return { principal, class: principalClass(policy, principal, classes), agent: null };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Refuse `writer` when the declaration's writeScope names a class `classes`
|
|
156
|
+
* doesn't know and the writer is judged human (#3080): nothing says which
|
|
157
|
+
* role puts a principal in the unknown class, so this writer may be in it.
|
|
158
|
+
* A writer in another class is judged by that class, which comes first.
|
|
159
|
+
*/
|
|
160
|
+
export function unknownClassVerdict(declaration: Declaration | null, writer: Writer, classes: ClassRegistry): { ok: true } | { ok: false; code: "write-scope-class-unknown"; message: string } {
|
|
161
|
+
if (writer.class !== "human") return OK;
|
|
162
|
+
const unknown = unknownScopeClasses(declaration?.writeScope ?? null, classes);
|
|
163
|
+
if (unknown.length === 0) return OK;
|
|
164
|
+
const list = unknown.map((c) => `writeScope.${c}`).join(", ");
|
|
165
|
+
return {
|
|
166
|
+
ok: false,
|
|
167
|
+
code: "write-scope-class-unknown",
|
|
168
|
+
message: `${who(writer)} may be in ${unknown.length === 1 ? "a class" : "classes"} no pinned package supplies (${list}), so its scope can't be told; install the package that supplies ${unknown.length === 1 ? "it" : "them"} at the pinned version, or remove the entry (known classes: ${classes.names().join(", ")})`,
|
|
169
|
+
};
|
|
129
170
|
}
|
|
130
171
|
|
|
131
172
|
/** The scope that applies to `writer`, or null when its class is not restricted. An agent is always restricted to its member. */
|
|
132
173
|
export function scopeOf(declaration: Declaration | null, writer: Writer): ClassScope | null {
|
|
133
174
|
const entry = declaration?.writeScope?.[writer.class];
|
|
134
|
-
if (writer.class === "agent") return { members: null, records: entry?.records ?? null, pointer: entry?.pointer ?? "/agents" };
|
|
175
|
+
if (writer.class === "agent") return { members: null, records: entry?.records ?? null, protected: entry?.protected ?? [], pointer: entry?.pointer ?? "/agents" };
|
|
135
176
|
return entry ?? null;
|
|
136
177
|
}
|
|
137
178
|
|
|
138
|
-
export type ScopeVerdict = { ok: true } | { ok: false; code: Exclude<
|
|
179
|
+
export type ScopeVerdict = { ok: true } | { ok: false; code: Exclude<ScopeFindingCode, "agent-unknown">; message: string };
|
|
180
|
+
|
|
181
|
+
/** A record write's verdict: never write-scope-protected, since protected paths judge files that aren't records. */
|
|
182
|
+
export type RecordVerdict = { ok: true } | { ok: false; code: Exclude<WriteScopeCode, "agent-unknown">; message: string };
|
|
139
183
|
|
|
140
|
-
const OK
|
|
184
|
+
const OK = { ok: true } as const;
|
|
141
185
|
|
|
142
186
|
function who(writer: Writer): string {
|
|
143
187
|
if (writer.agent) return `agent session ${writer.agent.name}, bound to member ${writer.agent.member},`;
|
|
@@ -164,7 +208,7 @@ export interface ScopedKind {
|
|
|
164
208
|
}
|
|
165
209
|
|
|
166
210
|
/** Whether `writer` may write a record of `kind` with `verb`. A delete is never in a restricted scope. */
|
|
167
|
-
export function judgeRecord(declaration: Declaration | null, writer: Writer, kind: ScopedKind, verb: WriteVerb | "delete"):
|
|
211
|
+
export function judgeRecord(declaration: Declaration | null, writer: Writer, kind: ScopedKind, verb: WriteVerb | "delete"): RecordVerdict {
|
|
168
212
|
const scope = scopeOf(declaration, writer);
|
|
169
213
|
if (scope === null) return OK;
|
|
170
214
|
// The workspace's own kinds are in every member's reach; a member's kinds only in its own.
|
|
@@ -194,17 +238,76 @@ export function judgeRecord(declaration: Declaration | null, writer: Writer, kin
|
|
|
194
238
|
};
|
|
195
239
|
}
|
|
196
240
|
|
|
197
|
-
/**
|
|
198
|
-
|
|
241
|
+
/**
|
|
242
|
+
* The protected entry of `entries` that covers `path` (#3146, ws-077): one
|
|
243
|
+
* whose glob matches the path or a directory above it. Null when none does.
|
|
244
|
+
*/
|
|
245
|
+
export function protectedEntry(entries: readonly ProtectedPath[], path: string): ProtectedPath | null {
|
|
246
|
+
if (entries.length === 0) return null;
|
|
247
|
+
const parts = path.split("/");
|
|
248
|
+
const prefixes = parts.map((_, i) => parts.slice(0, i + 1).join("/"));
|
|
249
|
+
for (const entry of entries) {
|
|
250
|
+
const match = picomatch(entry.path, { dot: true }) as (p: string) => boolean;
|
|
251
|
+
if (prefixes.some((p) => match(p))) return entry;
|
|
252
|
+
}
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Whether JSON text `before` and `after` differ only in the top-level keys
|
|
258
|
+
* `except` names. False when either is missing or isn't a JSON object.
|
|
259
|
+
*/
|
|
260
|
+
export function onlyKeysChanged(before: string | null, after: string | null, except: readonly string[]): boolean {
|
|
261
|
+
if (before === null || after === null || except.length === 0) return false;
|
|
262
|
+
const strip = (text: string): Record<string, unknown> | null => {
|
|
263
|
+
try {
|
|
264
|
+
const value = JSON.parse(text) as unknown;
|
|
265
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
266
|
+
const copy = { ...(value as Record<string, unknown>) };
|
|
267
|
+
for (const key of except) delete copy[key];
|
|
268
|
+
return copy;
|
|
269
|
+
} catch {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
const a = strip(before);
|
|
274
|
+
const b = strip(after);
|
|
275
|
+
return a !== null && b !== null && isDeepStrictEqual(a, b);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** A file's text before and after a write, read only when a protected entry with `except` needs it. */
|
|
279
|
+
export interface PathChange {
|
|
280
|
+
before(): string | null;
|
|
281
|
+
after(): string | null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Whether `writer` may write the file at `path`, from the workspace root:
|
|
286
|
+
* the path is in a member the writer may write, and no protected entry of
|
|
287
|
+
* its scope covers it, unless the entry's `except` allows the change (#3146).
|
|
288
|
+
* Without `change`, a write to a path an `except` entry covers is judged as a
|
|
289
|
+
* change outside the allowed keys.
|
|
290
|
+
*/
|
|
291
|
+
export function judgePath(declaration: Declaration | null, writer: Writer, path: string, change?: PathChange): ScopeVerdict {
|
|
199
292
|
const scope = scopeOf(declaration, writer);
|
|
200
293
|
if (scope === null) return OK;
|
|
201
294
|
const member = memberHolding(path, declaration?.members ?? []);
|
|
202
|
-
if (memberAllowed(writer, scope, member))
|
|
203
|
-
|
|
295
|
+
if (!memberAllowed(writer, scope, member)) {
|
|
296
|
+
const where = member === null ? "is in no member" : `is in member ${member}`;
|
|
297
|
+
return {
|
|
298
|
+
ok: false,
|
|
299
|
+
code: "write-scope-member",
|
|
300
|
+
message: `${who(writer)} may not write ${path}, which ${where}: ${writer.class === "agent" ? "an agent session writes only its own member" : `writeScope.${writer.class}.members leaves it out`}`,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const guarded = protectedEntry(scope.protected, path);
|
|
304
|
+
if (guarded === null) return OK;
|
|
305
|
+
if (guarded.except.length > 0 && change && onlyKeysChanged(change.before(), change.after(), guarded.except)) return OK;
|
|
306
|
+
const allowance = guarded.except.length > 0 ? `, outside the top-level keys it allows (${guarded.except.join(", ")})` : "";
|
|
204
307
|
return {
|
|
205
308
|
ok: false,
|
|
206
|
-
code: "write-scope-
|
|
207
|
-
message: `${who(writer)} may not write ${path}
|
|
309
|
+
code: "write-scope-protected",
|
|
310
|
+
message: `${who(writer)} may not write ${path}${allowance}: writeScope.${writer.class}.protected lists ${guarded.path}`,
|
|
208
311
|
};
|
|
209
312
|
}
|
|
210
313
|
|
|
@@ -220,12 +323,14 @@ export interface ScopeSource {
|
|
|
220
323
|
/** The workspace root from the git root, "." for the git root, or null without a declaration. */
|
|
221
324
|
root: string | null;
|
|
222
325
|
policy: TrustPolicy;
|
|
326
|
+
/** The principal classes: the core four and those the declaration's pins supply, path pins read where the declaration was (#3080). */
|
|
327
|
+
classes: ClassRegistry;
|
|
223
328
|
}
|
|
224
329
|
|
|
225
|
-
function readAt(cwd: string, at?: string): { declaration: Declaration; rootOnDisk: string; root: string } | null {
|
|
330
|
+
function readAt(cwd: string, at?: string): { declaration: Declaration; rootOnDisk: string; root: string; tree: WorkspaceTree } | null {
|
|
226
331
|
try {
|
|
227
332
|
const located = locateWorkspace(cwd, at);
|
|
228
|
-
return { declaration: readDeclaration(located.tree), rootOnDisk: located.rootOnDisk, root: located.root };
|
|
333
|
+
return { declaration: readDeclaration(located.tree), rootOnDisk: located.rootOnDisk, root: located.root, tree: located.tree };
|
|
229
334
|
} catch (err) {
|
|
230
335
|
if (err instanceof WorkspaceReadError) return null;
|
|
231
336
|
throw err;
|
|
@@ -242,12 +347,16 @@ export function scopeSource(cwd: string): ScopeSource {
|
|
|
242
347
|
const top = gitRoot(cwd);
|
|
243
348
|
const base = top ? resolveBase(top) : null;
|
|
244
349
|
const policy = top && base ? policyAtBase(top, base) : emptyPolicy(null);
|
|
350
|
+
const withClasses = (read: NonNullable<ReturnType<typeof readAt>>) => {
|
|
351
|
+
const { tree, ...rest } = read;
|
|
352
|
+
return { ...rest, classes: loadClassRegistry(read.declaration.pins, read.rootOnDisk, { tree }).registry };
|
|
353
|
+
};
|
|
245
354
|
if (base?.commit) {
|
|
246
355
|
const atBase = readAt(cwd, base.commit);
|
|
247
|
-
if (atBase) return { ...atBase, from: "base", policy };
|
|
356
|
+
if (atBase) return { ...withClasses(atBase), from: "base", policy };
|
|
248
357
|
}
|
|
249
358
|
const here = readAt(cwd);
|
|
250
|
-
return here ? { ...here, from: "working-tree", policy } : { declaration: null, from: null, rootOnDisk: null, root: null, policy };
|
|
359
|
+
return here ? { ...withClasses(here), from: "working-tree", policy } : { declaration: null, from: null, rootOnDisk: null, root: null, policy, classes: coreClassRegistry() };
|
|
251
360
|
}
|
|
252
361
|
|
|
253
362
|
const toPosix = (p: string) => (sep === "/" ? p : p.split(sep).join("/"));
|
|
@@ -271,16 +380,20 @@ export function scopedKind(source: ScopeSource, kindName: string, kindFile: stri
|
|
|
271
380
|
* Refuse a record write outside the writer's scope (#2548): throws a
|
|
272
381
|
* {@link WriteScopeError}. `agent` is the session the write names
|
|
273
382
|
* (`CHANT_AGENT`), and `principal` who the write names as its author.
|
|
383
|
+
* Returns what it read at base, for the identity rule (#3163).
|
|
274
384
|
*/
|
|
275
385
|
export function refuseRecordWrite(
|
|
276
386
|
cwd: string,
|
|
277
387
|
write: { kindName: string; kindFile: string; recordsDir: string; verb: WriteVerb; agent?: string | null; principal?: string | null },
|
|
278
|
-
):
|
|
388
|
+
): ScopeSource {
|
|
279
389
|
const source = scopeSource(cwd);
|
|
280
|
-
if (source.declaration === null && (write.agent === undefined || write.agent === null)) return;
|
|
281
|
-
const writer = resolveWriter(source.declaration, source.policy, { agent: write.agent, principal: write.principal });
|
|
390
|
+
if (source.declaration === null && (write.agent === undefined || write.agent === null)) return source;
|
|
391
|
+
const writer = resolveWriter(source.declaration, source.policy, { agent: write.agent, principal: write.principal }, source.classes);
|
|
392
|
+
const unknown = unknownClassVerdict(source.declaration, writer, source.classes);
|
|
393
|
+
if (!unknown.ok) throw new WriteScopeError(unknown.code, unknown.message);
|
|
282
394
|
const verdict = judgeRecord(source.declaration, writer, scopedKind(source, write.kindName, write.kindFile, write.recordsDir), write.verb);
|
|
283
395
|
if (!verdict.ok) throw new WriteScopeError(verdict.code, verdict.message);
|
|
396
|
+
return source;
|
|
284
397
|
}
|
|
285
398
|
|
|
286
399
|
// ── The check over a range ───────────────────────────────────────────────────
|
|
@@ -301,9 +414,9 @@ export interface ScopeCommit {
|
|
|
301
414
|
}
|
|
302
415
|
|
|
303
416
|
export interface ScopeFinding {
|
|
304
|
-
/** `finding:<code>:<commit>:<path>`, or `finding
|
|
417
|
+
/** `finding:<code>:<commit>:<path>`, or `finding:<code>:<commit>` for agent-unknown and write-scope-class-unknown, which judge the whole commit. */
|
|
305
418
|
id: string;
|
|
306
|
-
code:
|
|
419
|
+
code: ScopeFindingCode;
|
|
307
420
|
commit: string;
|
|
308
421
|
/** From the workspace root, or null for a finding on the whole commit. */
|
|
309
422
|
path: string | null;
|
|
@@ -318,7 +431,7 @@ export interface ScopeFinding {
|
|
|
318
431
|
export interface ScopeReport {
|
|
319
432
|
/** The commit the declaration and the trust policy were read at. */
|
|
320
433
|
base: string;
|
|
321
|
-
/** The classes with a writeScope entry, and agent when any session is declared. */
|
|
434
|
+
/** The classes with a writeScope entry, and agent when any session is declared: core first, then domain classes in pin order, then any no pinned package supplies. */
|
|
322
435
|
restricted: PrincipalClass[];
|
|
323
436
|
agents: string[];
|
|
324
437
|
commits: ScopeCommit[];
|
|
@@ -384,13 +497,17 @@ export async function checkWriteScope(q: {
|
|
|
384
497
|
prefix: string;
|
|
385
498
|
declaration: Declaration;
|
|
386
499
|
kinds: CheckKind[];
|
|
500
|
+
/** The principal classes the declaration's pins supply at base (#3080). Without it, the core four. */
|
|
501
|
+
classes?: ClassRegistry;
|
|
387
502
|
}): Promise<ScopeReport | null> {
|
|
388
503
|
const { top, base, head, prefix, declaration } = q;
|
|
504
|
+
const classes = q.classes ?? coreClassRegistry();
|
|
389
505
|
if (declaration.writeScope === null && declaration.agents.length === 0) return null;
|
|
390
506
|
const policy = policyAtBase(top, { commit: base, from: "flag" });
|
|
391
507
|
const { activeAttestors } = await import("./trust/attestor");
|
|
392
508
|
const attestors = await activeAttestors();
|
|
393
|
-
const
|
|
509
|
+
const scoped = (c: string) => declaration.writeScope?.[c] !== undefined || (c === "agent" && declaration.agents.length > 0);
|
|
510
|
+
const restricted = [...classes.names().filter(scoped), ...unknownScopeClasses(declaration.writeScope, classes)];
|
|
394
511
|
const report: ScopeReport = { base, restricted, agents: declaration.agents.map((a) => a.name), commits: [], findings: [] };
|
|
395
512
|
const list = git(top, ["rev-list", "--reverse", "--no-merges", `${base}..${head}`]).split("\n").filter(Boolean);
|
|
396
513
|
for (const commit of list) {
|
|
@@ -410,7 +527,7 @@ export async function checkWriteScope(q: {
|
|
|
410
527
|
}
|
|
411
528
|
let writer: Writer;
|
|
412
529
|
try {
|
|
413
|
-
writer = resolveWriter(declaration, policy, { agent: named, principal });
|
|
530
|
+
writer = resolveWriter(declaration, policy, { agent: named, principal }, classes);
|
|
414
531
|
} catch (err) {
|
|
415
532
|
if (!(err instanceof WriteScopeError)) throw err;
|
|
416
533
|
report.commits.push({ commit, subject, principal, attested, class: "agent", agent: named, paths: paths.length });
|
|
@@ -418,6 +535,11 @@ export async function checkWriteScope(q: {
|
|
|
418
535
|
continue;
|
|
419
536
|
}
|
|
420
537
|
report.commits.push({ commit, subject, principal, attested, class: writer.class, agent: writer.agent?.name ?? null, paths: paths.length });
|
|
538
|
+
const unknown = unknownClassVerdict(declaration, writer, classes);
|
|
539
|
+
if (!unknown.ok) {
|
|
540
|
+
report.findings.push({ id: `finding:${unknown.code}:${commit}`, code: unknown.code, commit, path: null, principal, class: writer.class, agent: null, verb: null, message: `${commit.slice(0, 8)}: ${unknown.message}` });
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
421
543
|
if (scopeOf(declaration, writer) === null) continue;
|
|
422
544
|
for (const p of paths) {
|
|
423
545
|
const inWorkspace = prefix === "" ? p.path : p.path.slice(prefix.length + 1);
|
|
@@ -428,7 +550,10 @@ export async function checkWriteScope(q: {
|
|
|
428
550
|
verb = p.status === "A" ? "new" : p.status === "D" ? "delete" : modifiedVerb(top, commit, p.path, kind);
|
|
429
551
|
verdict = judgeRecord(declaration, writer, kind.scoped, verb);
|
|
430
552
|
} else {
|
|
431
|
-
verdict = judgePath(declaration, writer, inWorkspace
|
|
553
|
+
verdict = judgePath(declaration, writer, inWorkspace, {
|
|
554
|
+
before: () => (p.status === "A" ? null : show(top, `${commit}^`, p.path)),
|
|
555
|
+
after: () => (p.status === "D" ? null : show(top, commit, p.path)),
|
|
556
|
+
});
|
|
432
557
|
}
|
|
433
558
|
if (verdict.ok) continue;
|
|
434
559
|
report.findings.push({
|
package/src/yaml.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "vitest";
|
|
2
2
|
import { load as jsYamlLoad } from "js-yaml";
|
|
3
|
-
import { emitYAML, parseYAML, parseYAMLDocument, parseScalar, splitYAMLDocuments, YAMLParseError } from "./yaml";
|
|
3
|
+
import { emitYAML, emitYAMLEntry, parseYAML, parseYAMLDocument, parseScalar, splitYAMLDocuments, YAMLParseError } from "./yaml";
|
|
4
4
|
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
// emitYAML
|
|
@@ -781,3 +781,113 @@ describe("parseYAML — anchors, aliases and merge keys (#2991)", () => {
|
|
|
781
781
|
expect(() => parseYAML("a: &v 1\nb:\n <<: *v\n")).toThrow(YAMLParseError);
|
|
782
782
|
});
|
|
783
783
|
});
|
|
784
|
+
|
|
785
|
+
// #3006: parseYAML is js-yaml with chant's schema. Before it, an inline
|
|
786
|
+
// comment stayed in the value and a flow collection that was not JSON came
|
|
787
|
+
// back as a string, without a warning.
|
|
788
|
+
describe("parseYAML — comments and flow collections (#3006)", () => {
|
|
789
|
+
const same = (src: string): void => {
|
|
790
|
+
expect(parseYAMLDocument(src)).toEqual(jsYamlLoad(src));
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
test("an inline comment is not part of the value", () => {
|
|
794
|
+
expect(parseYAML("replicas: 1 # comment\n")).toEqual({ replicas: 1 });
|
|
795
|
+
expect(parseYAML("image: nginx:1.27 # pinned\nport: 80\t# tab\n")).toEqual({ image: "nginx:1.27", port: 80 });
|
|
796
|
+
same("items:\n - a # one\n - name: b # two\n url: http://x/#frag\n");
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
test("a # inside a quoted or plain scalar is kept", () => {
|
|
800
|
+
expect(parseYAML("a: 'x # y'\nb: \"x # y\"\nc: x#y\n")).toEqual({ a: "x # y", b: "x # y", c: "x#y" });
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
test("flow sequences and mappings that are not JSON", () => {
|
|
804
|
+
expect(parseYAML("args: [--foo, --bar]\n")).toEqual({ args: ["--foo", "--bar"] });
|
|
805
|
+
same("weekdays: [saturday, sunday]\ndays: ['1:7']\nmeta: { name: gw, labels: { app: x } }\n");
|
|
806
|
+
same("times:\n - { start_time: \"00:00\", end_time: \"24:00\" }\n");
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
test("a flow collection spanning lines", () => {
|
|
810
|
+
same("args: [\n --foo,\n --bar\n]\nmeta: {\n a: 1,\n b: [x, y]\n}\n");
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
test("a nested sequence on one line, as Grafana writes object matchers", () => {
|
|
814
|
+
expect(parseYAML("matchers:\n - - severity\n - =\n - page\n")).toEqual({ matchers: [["severity", "=", "page"]] });
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
test("a folded scalar keeps the line breaks of its more-indented lines (#2925)", () => {
|
|
818
|
+
same("msg: >\n a\n indented\n b\n");
|
|
819
|
+
});
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
describe("parseYAML — what chant keeps from its old parser (#3006)", () => {
|
|
823
|
+
test("yes and no are booleans; on and off stay strings", () => {
|
|
824
|
+
expect(parseYAML("a: yes\nb: no\nc: on\nd: off\n")).toEqual({ a: true, b: false, c: "on", d: "off" });
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
test("a date stays a string", () => {
|
|
828
|
+
expect(parseYAML("date: 2024-01-01\nat: 2024-01-01T00:00:00Z\n")).toEqual({ date: "2024-01-01", at: "2024-01-01T00:00:00Z" });
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
test("a local tag on a scalar or sequence reads as its text", () => {
|
|
832
|
+
expect(parseYAML("s: !reference [.base, script]\nr: !Ref MyApi\n")).toEqual({
|
|
833
|
+
s: "!reference [.base, script]",
|
|
834
|
+
r: "!Ref MyApi",
|
|
835
|
+
});
|
|
836
|
+
expect(parseYAML(emitYAMLEntry("s", { tag: "!reference", value: [".base", "script"] }))).toEqual({
|
|
837
|
+
s: "!reference [.base, script]",
|
|
838
|
+
});
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
test("a duplicate key takes the last value", () => {
|
|
842
|
+
expect(parseYAML("a: 1\na: 2\n")).toEqual({ a: 2 });
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
test("a `---`-separated stream reads as one mapping, as before", () => {
|
|
846
|
+
expect(parseYAML("a: 1\n---\nb: 2\n")).toEqual({ a: 1, b: 2 });
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
test("a scalar document is an error naming its line", () => {
|
|
850
|
+
expect(() => parseYAML("# header\njust text\n")).toThrow(/YAML line 2:/);
|
|
851
|
+
expect(parseYAML("")).toEqual({});
|
|
852
|
+
expect(parseYAML("# only a comment\n")).toEqual({});
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
test("a merged block is a copy, not shared with its anchor", () => {
|
|
856
|
+
const doc = parseYAML("a: &a\n x: { n: 1 }\nb:\n <<: *a\n") as { a: { x: { n: number } }; b: { x: { n: number } } };
|
|
857
|
+
doc.b.x.n = 2;
|
|
858
|
+
expect(doc.a.x.n).toBe(1);
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
describe("emitYAMLEntry (#3006)", () => {
|
|
863
|
+
test("an inline value gets a space after the colon", () => {
|
|
864
|
+
expect(emitYAMLEntry("jobs", {})).toBe("jobs: {}");
|
|
865
|
+
expect(emitYAMLEntry("stages", [])).toBe("stages: []");
|
|
866
|
+
expect(emitYAMLEntry("name", "x")).toBe("name: x");
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
test("a block value follows on the next line", () => {
|
|
870
|
+
expect(emitYAMLEntry("stages", ["a", "b"])).toBe("stages:\n - a\n - b");
|
|
871
|
+
expect(emitYAMLEntry("b", { c: 1 }, 1)).toBe(" b:\n c: 1");
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
test("what it writes parses back", () => {
|
|
875
|
+
const doc = { on: {}, env: { A: "1" }, stages: [], jobs: { build: { script: ["make"] } } };
|
|
876
|
+
const text = Object.entries(doc).map(([k, v]) => emitYAMLEntry(k, v)).join("\n");
|
|
877
|
+
expect(parseYAML(text)).toEqual(doc);
|
|
878
|
+
});
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
describe("emitYAML quotes strings a parser would read as something else (#3006)", () => {
|
|
882
|
+
const values = ["foo:", "- x", "? x", "|x", ">x", "@x", "`x", "%x", ",x", "]x", "}x", "True", "NULL", "~", ".inf", "-.inf", ".NaN", " lead", "trail ", "-1", "+1", ".5", "Yes"];
|
|
883
|
+
|
|
884
|
+
for (const v of values) {
|
|
885
|
+
test(`round-trips ${JSON.stringify(v)}`, () => {
|
|
886
|
+
expect(parseYAML(`k: ${emitYAML(v, 0)}\n`).k).toBe(v);
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
test("strings that were safe stay plain", () => {
|
|
891
|
+
for (const v of ["--flag", "-x", "a:b", "http://a/b", "on"]) expect(emitYAML(v, 0)).toBe(v);
|
|
892
|
+
});
|
|
893
|
+
});
|