@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
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// The entry of `@intentius/chant/workspace/conformance` for plain Node
|
|
2
|
-
// (#2679)
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// (#2679): the reader suite, and the writer suite (#3159). The suites are
|
|
3
|
+
// written in TypeScript like the rest of the package, and Node does not
|
|
4
|
+
// strip types under node_modules, so this file loads them through tsx, a
|
|
5
|
+
// dependency of this package. `node --test` runs a reader's or a writer's
|
|
5
6
|
// test file that imports it with no loader flag. Types come from
|
|
6
7
|
// dist/workspace/conformance/index.d.ts.
|
|
7
8
|
|
|
@@ -15,6 +16,7 @@ export const {
|
|
|
15
16
|
READ_CONTRACT_JSON_FLAGS,
|
|
16
17
|
REFERENCE_READS,
|
|
17
18
|
CONFORMANCE_FIXTURE_DIR,
|
|
19
|
+
UNCOMMITTED_DECISION,
|
|
18
20
|
defaultChantCommand,
|
|
19
21
|
referenceWorkspaceDir,
|
|
20
22
|
readContractSchema,
|
|
@@ -32,4 +34,29 @@ export const {
|
|
|
32
34
|
recordingTransport,
|
|
33
35
|
readAndCheck,
|
|
34
36
|
runWorkspaceReaderConformance,
|
|
37
|
+
runChant,
|
|
38
|
+
WRITE_CONTRACT_ACTIONS,
|
|
39
|
+
WRITE_CONTRACT_SCHEMAS,
|
|
40
|
+
WRITE_CONTRACT_JSON_FLAGS,
|
|
41
|
+
WRITER_KINDS,
|
|
42
|
+
WRITER_PRINCIPALS,
|
|
43
|
+
WRITER_SCRIPT,
|
|
44
|
+
WRITER_FIXTURE_DIR,
|
|
45
|
+
PRIVATE_STATE_CATEGORIES,
|
|
46
|
+
writeArgv,
|
|
47
|
+
writeContractSchema,
|
|
48
|
+
worktreeDigest,
|
|
49
|
+
gitRefs,
|
|
50
|
+
refChanges,
|
|
51
|
+
isReadCall,
|
|
52
|
+
writerCallProblems,
|
|
53
|
+
writerDocumentProblems,
|
|
54
|
+
reportedWrites,
|
|
55
|
+
unreportedChanges,
|
|
56
|
+
buildStep,
|
|
57
|
+
referenceWriter,
|
|
58
|
+
createWriterConformanceWorkspace,
|
|
59
|
+
selectActions,
|
|
60
|
+
undeclaredState,
|
|
61
|
+
runWorkspaceWriterConformance,
|
|
35
62
|
} = suite;
|
|
@@ -35,6 +35,14 @@
|
|
|
35
35
|
* app, and a chant member with one composite and one component), declared
|
|
36
36
|
* by `chant workspace init --yes`, so {@link REFERENCE_READS} resolve in
|
|
37
37
|
* both. `workspaceDir` names another workspace with the same files.
|
|
38
|
+
*
|
|
39
|
+
* The workspace the suite generates for a run also holds one decision,
|
|
40
|
+
* proposed and never committed ({@link UNCOMMITTED_DECISION}), so
|
|
41
|
+
* `records --uncommitted` (#3160) has a record to list with its `worktree`.
|
|
42
|
+
* {@link createConformanceWorkspace} called directly leaves it out unless
|
|
43
|
+
* asked, so a test that writes records there allocates the ids it always
|
|
44
|
+
* did. In the chant repository the reference workspace holds whatever the
|
|
45
|
+
* checkout does, often nothing uncommitted.
|
|
38
46
|
*/
|
|
39
47
|
|
|
40
48
|
import { execFileSync, spawn } from "node:child_process";
|
|
@@ -48,7 +56,7 @@ import { isDeepStrictEqual } from "node:util";
|
|
|
48
56
|
import { READ_CONTRACT_VERSION } from "../reason-codes";
|
|
49
57
|
|
|
50
58
|
/** The read-contract commands, as a reader names them. */
|
|
51
|
-
export const READ_CONTRACT_COMMANDS = ["ls", "graph", "check", "status", "records", "graph --intent", "graph --composites"] as const;
|
|
59
|
+
export const READ_CONTRACT_COMMANDS = ["ls", "graph", "check", "status", "records", "records --uncommitted", "graph --intent", "graph --composites", "runs"] as const;
|
|
52
60
|
export type ReadContractCommand = (typeof READ_CONTRACT_COMMANDS)[number];
|
|
53
61
|
|
|
54
62
|
/**
|
|
@@ -62,8 +70,10 @@ export const READ_CONTRACT_SCHEMAS: Record<ReadContractCommand, string> = {
|
|
|
62
70
|
check: "check.schema.json",
|
|
63
71
|
status: "status.schema.json",
|
|
64
72
|
records: "records.schema.json",
|
|
73
|
+
"records --uncommitted": "records.schema.json",
|
|
65
74
|
"graph --intent": "intent.schema.json",
|
|
66
75
|
"graph --composites": "composites.schema.json",
|
|
76
|
+
runs: "runs.schema.json",
|
|
67
77
|
};
|
|
68
78
|
|
|
69
79
|
/** The flags that ask each command for its JSON document. A reader adds one of these and nothing else. */
|
|
@@ -73,8 +83,10 @@ export const READ_CONTRACT_JSON_FLAGS: Record<ReadContractCommand, readonly (rea
|
|
|
73
83
|
check: [["--format", "json"]],
|
|
74
84
|
status: [["--json"]],
|
|
75
85
|
records: [["--json"]],
|
|
86
|
+
"records --uncommitted": [["--json"]],
|
|
76
87
|
"graph --intent": [["--json"]],
|
|
77
88
|
"graph --composites": [[], ["--json"]],
|
|
89
|
+
runs: [["--json"]],
|
|
78
90
|
};
|
|
79
91
|
|
|
80
92
|
/** The arguments the suite passes each read, on the reference workspace or the generated one. */
|
|
@@ -84,21 +96,30 @@ export const REFERENCE_READS: Record<ReadContractCommand, string[]> = {
|
|
|
84
96
|
check: [],
|
|
85
97
|
status: ["dev"],
|
|
86
98
|
records: ["--kind", "decisions/decision.kind.mjs"],
|
|
99
|
+
"records --uncommitted": ["--kind", "decisions/decision.kind.mjs"],
|
|
87
100
|
"graph --intent": ["app/src/server.mjs:19", "--kind", "decisions/decision.kind.mjs"],
|
|
88
101
|
"graph --composites": [],
|
|
102
|
+
runs: [],
|
|
89
103
|
};
|
|
90
104
|
|
|
91
|
-
/** One chant run: the arguments after `chant`, and what it printed. */
|
|
105
|
+
/** One chant run: the arguments after `chant`, what it was given on stdin, and what it printed. */
|
|
92
106
|
export interface ChantRun {
|
|
93
107
|
argv: string[];
|
|
108
|
+
/** What the caller gave chant on stdin, such as a record's fields for `--from -` (#3159). Absent when nothing was. */
|
|
109
|
+
input?: string;
|
|
94
110
|
status: number | null;
|
|
95
111
|
stdout: string;
|
|
96
112
|
stderr: string;
|
|
97
113
|
}
|
|
98
114
|
|
|
99
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* How a reader or a writer runs chant. It runs in the workspace; the caller
|
|
117
|
+
* never sees the path. A writer passes `input` for a command that reads its
|
|
118
|
+
* fields from stdin (`--from -`, `--set -`, `--inputs -`, #3159); a reader
|
|
119
|
+
* never needs it.
|
|
120
|
+
*/
|
|
100
121
|
export interface ChantTransport {
|
|
101
|
-
run(argv: string[]): Promise<ChantRun>;
|
|
122
|
+
run(argv: string[], options?: { input?: string }): Promise<ChantRun>;
|
|
102
123
|
}
|
|
103
124
|
|
|
104
125
|
export interface WorkspaceReader {
|
|
@@ -316,8 +337,10 @@ export const MCP_READ_TOOLS: Partial<Record<ReadContractCommand, string>> = {
|
|
|
316
337
|
graph: "workspace-graph",
|
|
317
338
|
status: "workspace-status",
|
|
318
339
|
records: "workspace-records",
|
|
340
|
+
"records --uncommitted": "workspace-records",
|
|
319
341
|
"graph --intent": "workspace-graph",
|
|
320
342
|
"graph --composites": "workspace-graph",
|
|
343
|
+
runs: "workspace-runs",
|
|
321
344
|
};
|
|
322
345
|
|
|
323
346
|
/**
|
|
@@ -350,6 +373,10 @@ export function mcpToolCall(argv: readonly string[]): { name: string; arguments:
|
|
|
350
373
|
else if (a === "--work") args.work = value();
|
|
351
374
|
else if (a === "--severity") args.severity = value();
|
|
352
375
|
else if (a === "--current") args.current = true;
|
|
376
|
+
else if (a === "--unit") args.unit = value();
|
|
377
|
+
else if (a === "--decision") args.decision = value();
|
|
378
|
+
else if (a === "--by") args.by = value();
|
|
379
|
+
else if (a === "--uncommitted") args.uncommitted = true;
|
|
353
380
|
else if (a === "--composites") args.composites = true;
|
|
354
381
|
else if (a.startsWith("-")) return undefined;
|
|
355
382
|
else positional.push(a);
|
|
@@ -365,6 +392,8 @@ export function mcpToolCall(argv: readonly string[]): { name: string; arguments:
|
|
|
365
392
|
return positional.length === 0 && args.range !== undefined ? { name: "workspace-changes", arguments: { ...args, ...(kinds.length ? { kind: kinds } : {}) } } : undefined;
|
|
366
393
|
case "graph":
|
|
367
394
|
return positional.length === 0 ? { name: "workspace-graph", arguments: { ...args, ...(kinds.length ? { kind: kinds } : {}) } } : undefined;
|
|
395
|
+
case "runs":
|
|
396
|
+
return positional.length === 0 && kinds.length === 0 ? { name: "workspace-runs", arguments: args } : undefined;
|
|
368
397
|
default:
|
|
369
398
|
return undefined;
|
|
370
399
|
}
|
|
@@ -457,6 +486,47 @@ function git(cwd: string, ...args: string[]): void {
|
|
|
457
486
|
});
|
|
458
487
|
}
|
|
459
488
|
|
|
489
|
+
/**
|
|
490
|
+
* The decision the generated workspace leaves uncommitted (#3160): proposed,
|
|
491
|
+
* about the delivery member only, so the app region the intent read walks
|
|
492
|
+
* is unchanged. Its path from the workspace root, and its text.
|
|
493
|
+
*/
|
|
494
|
+
export const UNCOMMITTED_DECISION = {
|
|
495
|
+
path: "decisions/fix-002-where-kept-records-wait.md",
|
|
496
|
+
text: `---
|
|
497
|
+
schema: 1
|
|
498
|
+
id: "fix-002"
|
|
499
|
+
title: "Where kept records wait"
|
|
500
|
+
state: "proposed"
|
|
501
|
+
area: "delivery"
|
|
502
|
+
source:
|
|
503
|
+
kind: "workspace"
|
|
504
|
+
member: "delivery"
|
|
505
|
+
question: "Where does a record wait between being kept and being applied?"
|
|
506
|
+
options:
|
|
507
|
+
- id: "a"
|
|
508
|
+
label: "uncommitted in the working tree"
|
|
509
|
+
how: "The record stays a file in the checkout until it is committed or applied."
|
|
510
|
+
tradeoff: "A reader lists it with records --uncommitted, and nothing else holds it."
|
|
511
|
+
choice: null
|
|
512
|
+
rejected: []
|
|
513
|
+
supersedes: []
|
|
514
|
+
evidence:
|
|
515
|
+
- title: "INTENTIUS/chant#3160, uncommitted records in the read contract"
|
|
516
|
+
url: "https://github.com/INTENTIUS/chant/issues/3160"
|
|
517
|
+
decided_by: null
|
|
518
|
+
decided_on: null
|
|
519
|
+
reviews: []
|
|
520
|
+
constrains:
|
|
521
|
+
- "member:delivery"
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
# Where kept records wait
|
|
525
|
+
|
|
526
|
+
The reader conformance workspace leaves this decision uncommitted, so \`records --uncommitted\` has one record to list.
|
|
527
|
+
`,
|
|
528
|
+
} as const;
|
|
529
|
+
|
|
460
530
|
/** A workspace generated for a conformance run, and how to remove it. */
|
|
461
531
|
export interface ConformanceWorkspace {
|
|
462
532
|
dir: string;
|
|
@@ -468,9 +538,10 @@ export interface ConformanceWorkspace {
|
|
|
468
538
|
* `__fixture__/`, link this package in as `node_modules/@intentius/chant`
|
|
469
539
|
* so the chant member resolves it, declare the workspace with
|
|
470
540
|
* `chant workspace init --yes --name conformance` and commit it all to a new
|
|
471
|
-
* git repository on `main`.
|
|
541
|
+
* git repository on `main`. With `uncommitted`, then write
|
|
542
|
+
* {@link UNCOMMITTED_DECISION} and leave it uncommitted (#3160).
|
|
472
543
|
*/
|
|
473
|
-
export function createConformanceWorkspace(options: { chantCommand?: string[]; timeoutMs?: number } = {}): ConformanceWorkspace {
|
|
544
|
+
export function createConformanceWorkspace(options: { chantCommand?: string[]; timeoutMs?: number; uncommitted?: boolean } = {}): ConformanceWorkspace {
|
|
474
545
|
const chant = options.chantCommand ?? defaultChantCommand();
|
|
475
546
|
const scratch = realpathSync(mkdtempSync(join(tmpdir(), "chant-reader-conformance-")));
|
|
476
547
|
const dispose = () => rmSync(scratch, { recursive: true, force: true });
|
|
@@ -489,7 +560,18 @@ export function createConformanceWorkspace(options: { chantCommand?: string[]; t
|
|
|
489
560
|
timeout: options.timeoutMs ?? 120_000,
|
|
490
561
|
});
|
|
491
562
|
git(dir, "add", "-A");
|
|
492
|
-
git(dir, "commit", "--quiet", "-m", "the reader conformance workspace");
|
|
563
|
+
git(dir, "commit", "--quiet", "-m", "the reader conformance workspace", "-m", "Chant-Run: conformance-run");
|
|
564
|
+
// One agent run in the run ledger (#3033), which made that commit, so `runs` reads a run with every field.
|
|
565
|
+
const head = execFileSync("git", ["rev-parse", "HEAD"], { cwd: dir, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
566
|
+
const run = { id: "conformance-run", harness: { name: "conformance", version: "1" }, model: "none", provider: "none", by: "conformance", startedAt: "2026-01-01T00:00:00Z", endedAt: "2026-01-01T00:01:00Z", outcome: "done", usage: { turns: 1, inputTokens: 10, outputTokens: 5 }, cost: { amount: 0.01, currency: "USD", source: "conformance" }, transcript: { sha256: "0".repeat(64), ref: "conformance" }, commits: [head] };
|
|
567
|
+
execFileSync(chant[0], [...chant.slice(1), "workspace", "runs", "record", "--from", "-"], {
|
|
568
|
+
cwd: dir,
|
|
569
|
+
input: JSON.stringify(run),
|
|
570
|
+
env: { ...process.env, ...GIT_ENV, NO_COLOR: "1" },
|
|
571
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
572
|
+
timeout: options.timeoutMs ?? 120_000,
|
|
573
|
+
});
|
|
574
|
+
if (options.uncommitted) writeFileSync(join(dir, ...UNCOMMITTED_DECISION.path.split("/")), UNCOMMITTED_DECISION.text);
|
|
493
575
|
return { dir, dispose };
|
|
494
576
|
} catch (e) {
|
|
495
577
|
dispose();
|
|
@@ -503,14 +585,17 @@ export function conformanceTarget(options: WorkspaceReaderConformanceOptions = {
|
|
|
503
585
|
const chantCommand = options.chantCommand ?? defaultChantCommand();
|
|
504
586
|
const given = options.workspaceDir ?? referenceWorkspaceDir();
|
|
505
587
|
if (given) return { workspaceDir: given, chantCommand, dispose: () => {} };
|
|
506
|
-
const ws = createConformanceWorkspace({ chantCommand, timeoutMs: options.timeoutMs });
|
|
588
|
+
const ws = createConformanceWorkspace({ chantCommand, timeoutMs: options.timeoutMs, uncommitted: true });
|
|
507
589
|
return { workspaceDir: ws.dir, chantCommand, dispose: ws.dispose };
|
|
508
590
|
}
|
|
509
591
|
|
|
510
|
-
/** Run `command` in `cwd`, collecting what it printed. Resolves, never rejects. */
|
|
511
|
-
function runChant(command: string[], argv: string[], cwd: string, timeoutMs: number): Promise<ChantRun> {
|
|
592
|
+
/** Run `command` in `cwd`, with `input` on stdin when given, collecting what it printed. Resolves, never rejects. */
|
|
593
|
+
export function runChant(command: string[], argv: string[], cwd: string, timeoutMs: number, input?: string): Promise<ChantRun> {
|
|
512
594
|
return new Promise((settle) => {
|
|
513
|
-
const child = spawn(command[0], [...command.slice(1), ...argv], { cwd, env: { ...process.env, NO_COLOR: "1" }, stdio: ["
|
|
595
|
+
const child = spawn(command[0], [...command.slice(1), ...argv], { cwd, env: { ...process.env, NO_COLOR: "1" }, stdio: ["pipe", "pipe", "pipe"] });
|
|
596
|
+
const given = input === undefined ? {} : { input };
|
|
597
|
+
// Without input, stdin is closed at once, as it was when it was ignored.
|
|
598
|
+
child.stdin.on("error", () => {}).end(input ?? "");
|
|
514
599
|
let stdout = "";
|
|
515
600
|
let stderr = "";
|
|
516
601
|
child.stdout.setEncoding("utf-8").on("data", (s: string) => (stdout += s));
|
|
@@ -518,11 +603,11 @@ function runChant(command: string[], argv: string[], cwd: string, timeoutMs: num
|
|
|
518
603
|
const timer = setTimeout(() => child.kill("SIGKILL"), timeoutMs);
|
|
519
604
|
child.on("error", (e) => {
|
|
520
605
|
clearTimeout(timer);
|
|
521
|
-
settle({ argv: [...argv], status: null, stdout, stderr: `${stderr}${e.message}` });
|
|
606
|
+
settle({ argv: [...argv], ...given, status: null, stdout, stderr: `${stderr}${e.message}` });
|
|
522
607
|
});
|
|
523
608
|
child.on("close", (status) => {
|
|
524
609
|
clearTimeout(timer);
|
|
525
|
-
settle({ argv: [...argv], status, stdout, stderr });
|
|
610
|
+
settle({ argv: [...argv], ...given, status, stdout, stderr });
|
|
526
611
|
});
|
|
527
612
|
});
|
|
528
613
|
}
|
|
@@ -582,10 +667,10 @@ export function recordingTransport(target: () => { workspaceDir: string; chantCo
|
|
|
582
667
|
return { argv: [...argv], status: 0, stdout: JSON.stringify(doc), stderr: "" };
|
|
583
668
|
};
|
|
584
669
|
const transport: ChantTransport = {
|
|
585
|
-
async run(argv) {
|
|
670
|
+
async run(argv, options) {
|
|
586
671
|
calls.push([...argv]);
|
|
587
672
|
const t = target();
|
|
588
|
-
const run = over === "mcp" ? await viaMcp(argv) : await runChant(t.chantCommand, argv, t.workspaceDir, timeoutMs);
|
|
673
|
+
const run = over === "mcp" ? await viaMcp(argv) : await runChant(t.chantCommand, argv, t.workspaceDir, timeoutMs, options?.input);
|
|
589
674
|
printed.push(run);
|
|
590
675
|
return run;
|
|
591
676
|
},
|
|
@@ -656,3 +741,6 @@ export async function runWorkspaceReaderConformance(reader: WorkspaceReaderFacto
|
|
|
656
741
|
target.dispose();
|
|
657
742
|
}
|
|
658
743
|
}
|
|
744
|
+
|
|
745
|
+
// The writer suite (#3159), published from this same entry.
|
|
746
|
+
export * from "./writer";
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
* The workspace reader conformance suite for vitest (#2657, #2679):
|
|
3
3
|
* {@link describeWorkspaceReaderConformance} wraps the runner-neutral checks
|
|
4
4
|
* of `./index` in `describe` and `it`, one test per listed command and one
|
|
5
|
-
* for the workspace's files.
|
|
6
|
-
*
|
|
5
|
+
* for the workspace's files. {@link describeWorkspaceWriterConformance}
|
|
6
|
+
* (#3159) does the same for the writer suite: one test per step of the
|
|
7
|
+
* script, and one per check after it. This is the only module of the suite
|
|
8
|
+
* that imports vitest; `@intentius/chant/workspace/conformance` imports no
|
|
9
|
+
* runner.
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
12
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
@@ -17,7 +20,12 @@ import {
|
|
|
17
20
|
selectCommands,
|
|
18
21
|
treeChanges,
|
|
19
22
|
treeDigest,
|
|
23
|
+
runWorkspaceWriterConformance,
|
|
24
|
+
selectActions,
|
|
25
|
+
WRITER_SCRIPT,
|
|
20
26
|
type WorkspaceReaderConformanceConfig,
|
|
27
|
+
type WorkspaceWriterConformanceConfig,
|
|
28
|
+
type WorkspaceWriterConformanceReport,
|
|
21
29
|
} from "./index";
|
|
22
30
|
|
|
23
31
|
export function describeWorkspaceReaderConformance(config: WorkspaceReaderConformanceConfig): void {
|
|
@@ -68,4 +76,38 @@ export function describeWorkspaceReaderConformance(config: WorkspaceReaderConfor
|
|
|
68
76
|
});
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
/**
|
|
80
|
+
* The writer suite in vitest: the runner-neutral run happens once, before the
|
|
81
|
+
* tests, and each test reports its part of it. A step whose action the writer
|
|
82
|
+
* does not list is skipped, as not applicable; the suite still performed it.
|
|
83
|
+
*/
|
|
84
|
+
export function describeWorkspaceWriterConformance(config: WorkspaceWriterConformanceConfig): void {
|
|
85
|
+
const { checked } = selectActions(config.actions);
|
|
86
|
+
describe(`workspace writer conformance (#3159): ${config.name}`, () => {
|
|
87
|
+
let report: WorkspaceWriterConformanceReport | undefined;
|
|
88
|
+
beforeAll(async () => {
|
|
89
|
+
report = await runWorkspaceWriterConformance(config.writer, config);
|
|
90
|
+
}, 900_000);
|
|
91
|
+
const ran = () => {
|
|
92
|
+
expect(report, "the writer suite did not run").toBeDefined();
|
|
93
|
+
return report!;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
for (const s of WRITER_SCRIPT) {
|
|
97
|
+
if (!checked.includes(s.action)) {
|
|
98
|
+
it.skip(`${s.id}: ${s.action} is not applicable, the writer does not list it in actions`, () => {});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
it(`${s.id}: writes through chant workspace ${s.action} alone, and every change it made is one chant reports`, () => {
|
|
102
|
+
expect(ran().results.find((r) => r.id === s.id)?.problems).toEqual([]);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
it("facts() reads only through the read contract and changes nothing", () => expect(ran().after.facts).toEqual([]));
|
|
106
|
+
it("the state directory holds only what privateState declares", () => expect(ran().after.state).toEqual([]));
|
|
107
|
+
it("amnesia: with its private state deleted, the writer shows the same facts", () => expect(ran().after.amnesia).toEqual([]));
|
|
108
|
+
it("everything the writer holds is in the repository, or one of ws-074's four exceptions", () => expect(ran().after.holds).toEqual([]));
|
|
109
|
+
it("every fact the script produced reads back through the read contract, uncommitted ones included", () => expect(ran().after.readBack).toEqual([]));
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
71
113
|
export * from "./index";
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The writer conformance suite run end to end (#3159): a writer that
|
|
3
|
+
* conforms, with a cache in its state directory, passes every action; a
|
|
4
|
+
* writer that breaks each rule once is caught at the step that broke it. Both
|
|
5
|
+
* run on a workspace generated from the shipped fixtures, at the same time.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { describe, expect, test } from "vitest";
|
|
11
|
+
import {
|
|
12
|
+
referenceWriter,
|
|
13
|
+
runWorkspaceWriterConformance,
|
|
14
|
+
WRITE_CONTRACT_ACTIONS,
|
|
15
|
+
WRITE_CONTRACT_JSON_FLAGS,
|
|
16
|
+
WRITER_KINDS,
|
|
17
|
+
WRITER_SCRIPT,
|
|
18
|
+
type ChantTransport,
|
|
19
|
+
type WorkspaceWriterFactory,
|
|
20
|
+
type WriteStep,
|
|
21
|
+
} from "./index";
|
|
22
|
+
|
|
23
|
+
/** One call of the step's command, as a writer makes it. */
|
|
24
|
+
const perform = async (chant: ChantTransport, step: WriteStep, extra: string[] = []) => {
|
|
25
|
+
const run = await chant.run(["workspace", ...step.action.split(" "), ...step.args, ...WRITE_CONTRACT_JSON_FLAGS[step.action][0], ...extra], step.input === undefined ? undefined : { input: step.input });
|
|
26
|
+
return JSON.parse(run.stdout) as Record<string, unknown>;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** The reference writer with a cache of its facts in its state directory: rebuilt from chant when it is gone. */
|
|
30
|
+
const cachingWriter: WorkspaceWriterFactory = (chant, { stateDir }) => {
|
|
31
|
+
const inner = referenceWriter(chant, { stateDir });
|
|
32
|
+
const cache = join(stateDir, "cache", "facts.json");
|
|
33
|
+
return {
|
|
34
|
+
write: (step) => inner.write(step),
|
|
35
|
+
async facts() {
|
|
36
|
+
if (existsSync(cache)) return JSON.parse(readFileSync(cache, "utf-8"));
|
|
37
|
+
const facts = await inner.facts();
|
|
38
|
+
// A cache is private state the suite allows; the suite deletes it for amnesia.
|
|
39
|
+
const { mkdirSync } = await import("node:fs");
|
|
40
|
+
mkdirSync(join(stateDir, "cache"), { recursive: true });
|
|
41
|
+
writeFileSync(cache, JSON.stringify(facts));
|
|
42
|
+
return facts;
|
|
43
|
+
},
|
|
44
|
+
holds: () => [
|
|
45
|
+
{ record: "fix-002", kind: WRITER_KINDS.decision },
|
|
46
|
+
{ run: "writer-run-1" },
|
|
47
|
+
{ lease: "W-001", kind: WRITER_KINDS.work },
|
|
48
|
+
{ exempt: "cache", what: "cache/facts.json, the facts as last read" },
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** A writer that breaks one rule at each step it performs, and keeps a review only in its own state. */
|
|
54
|
+
const leakyWriter: WorkspaceWriterFactory = (chant, { stateDir }) => {
|
|
55
|
+
const reviews = join(stateDir, "reviews.json");
|
|
56
|
+
return {
|
|
57
|
+
async write(step) {
|
|
58
|
+
if (step.id === "decision") return { ...(await perform(chant, step)), note: "the writer's own field" };
|
|
59
|
+
if (step.id === "amend") {
|
|
60
|
+
const doc = await perform(chant, step);
|
|
61
|
+
// A second write the step did not ask for: another decision.
|
|
62
|
+
const extra = WRITER_SCRIPT.find((s) => s.id === "decision")!;
|
|
63
|
+
await chant.run(["workspace", "records", "new", WRITER_KINDS.decision, "--from", "-"], { input: JSON.stringify({ ...(extra.params({}) as { fields: object }).fields, title: "A decision nobody asked for" }) });
|
|
64
|
+
return doc;
|
|
65
|
+
}
|
|
66
|
+
if (step.id === "review") {
|
|
67
|
+
const doc = await perform(chant, step);
|
|
68
|
+
writeFileSync(reviews, JSON.stringify([{ id: doc.id, by: "conformance-reviewer", verdict: "agree" }]));
|
|
69
|
+
writeFileSync(join(stateDir, "notes.txt"), "kept and not declared");
|
|
70
|
+
return doc;
|
|
71
|
+
}
|
|
72
|
+
if (step.id === "answer") return perform(chant, step, ["--dry-run"]);
|
|
73
|
+
return perform(chant, step);
|
|
74
|
+
},
|
|
75
|
+
async facts() {
|
|
76
|
+
// A write made while reading: refused by chant, still not a read.
|
|
77
|
+
await chant.run(["workspace", "runs", "start", "--from", "-"], { input: "{}" });
|
|
78
|
+
return { reviews: existsSync(reviews) ? JSON.parse(readFileSync(reviews, "utf-8")) : [] };
|
|
79
|
+
},
|
|
80
|
+
holds: () => [
|
|
81
|
+
{ record: "fix-999", kind: WRITER_KINDS.decision },
|
|
82
|
+
{ exempt: "database" as never, what: "events" },
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
describe("the writer conformance suite (#3159)", () => {
|
|
88
|
+
test.concurrent("a writer that writes only through chant passes every action, the amnesia test and its holdings", async () => {
|
|
89
|
+
const report = await runWorkspaceWriterConformance(cachingWriter, { privateState: [{ path: "cache", is: "cache" }] });
|
|
90
|
+
expect(report.problems).toEqual([]);
|
|
91
|
+
expect(report.checked).toEqual([...WRITE_CONTRACT_ACTIONS]);
|
|
92
|
+
expect(report.skipped).toEqual([]);
|
|
93
|
+
expect(report.results.map((r) => [r.id, r.by])).toEqual(WRITER_SCRIPT.map((s) => [s.id, "writer"]));
|
|
94
|
+
expect(report.facts.after).toEqual(report.facts.before);
|
|
95
|
+
expect(report.facts.before).toMatchObject({
|
|
96
|
+
decision: [["fix-001", "decided"], ["fix-002", "proposed"]],
|
|
97
|
+
session: [["S-0001", "closed"]],
|
|
98
|
+
answer: [[expect.stringMatching(/^slice-tier-/), "answered"]],
|
|
99
|
+
work: [["W-001", "open"]],
|
|
100
|
+
runs: [["conformance-run", "ended"], ["writer-run-1", "ended"], ["writer-run-2", "ended"]],
|
|
101
|
+
});
|
|
102
|
+
}, 900_000);
|
|
103
|
+
|
|
104
|
+
test.concurrent("a writer that breaks the rules is caught at each step and after", async () => {
|
|
105
|
+
const report = await runWorkspaceWriterConformance(leakyWriter, {
|
|
106
|
+
actions: ["records new", "records amend", "records review", "points answer"],
|
|
107
|
+
privateState: [{ path: "reviews.json", is: "cache" }],
|
|
108
|
+
});
|
|
109
|
+
expect(report.checked).toEqual(["records new", "records amend", "records review", "points answer"]);
|
|
110
|
+
const by = Object.fromEntries(report.results.map((r) => [r.id, r]));
|
|
111
|
+
expect(by.claim.by).toBe("suite");
|
|
112
|
+
expect(by.session.problems).toEqual([]);
|
|
113
|
+
expect(by.decision.problems).toEqual(["decision (records new): the writer must return the document chant printed, unchanged, and it returned something else"]);
|
|
114
|
+
expect(by.amend.problems).toHaveLength(2);
|
|
115
|
+
expect(by.amend.problems[0]).toMatch(/^amend \(records amend\): made 2 chant calls .* expected exactly one$/);
|
|
116
|
+
expect(by.amend.problems[1]).toMatch(/^amend \(records amend\): files changed that chant did not report writing: decisions\/fix-003-a-decision-nobody-asked-for\.md \(added\)$/);
|
|
117
|
+
expect(by.review.problems).toEqual([]);
|
|
118
|
+
expect(by.answer.problems).toEqual([expect.stringMatching(/^answer \(points answer\): .* it may add only nothing or --json, and it added --dry-run$/)]);
|
|
119
|
+
expect(report.after.facts).toEqual([
|
|
120
|
+
"facts: before amnesia, facts() made calls outside the read contract: workspace runs start --from -",
|
|
121
|
+
"facts: after amnesia, facts() made calls outside the read contract: workspace runs start --from -",
|
|
122
|
+
]);
|
|
123
|
+
expect(report.after.state).toEqual(["state: the writer keeps notes.txt in its state directory, and privateState does not declare it"]);
|
|
124
|
+
expect(report.after.amnesia).toEqual([expect.stringMatching(/^amnesia: after its private state was deleted, the writer shows other facts; before: \{"reviews":\[\{"id":"fix-002".*after: \{"reviews":\[\]\}$/)]);
|
|
125
|
+
expect(report.after.holds).toEqual([
|
|
126
|
+
"holds: the writer holds record fix-999 of decisions/decision.kind.mjs, which the repository does not have",
|
|
127
|
+
'holds: events is held as "database"; outside the repo a tool keeps only cache, telemetry, secret, runtime (ws-074)',
|
|
128
|
+
]);
|
|
129
|
+
expect(report.after.readBack).toEqual([]);
|
|
130
|
+
}, 900_000);
|
|
131
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The writer conformance suite's checks on their own (#3159): the calls a
|
|
3
|
+
* step may make, the changes a step may leave, the state a writer may keep,
|
|
4
|
+
* and the fixture it writes to. No chant runs here; the suite run end to end
|
|
5
|
+
* is writer-conformance.e2e.test.ts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { join, resolve } from "node:path";
|
|
10
|
+
import { describe, expect, test } from "vitest";
|
|
11
|
+
import {
|
|
12
|
+
buildStep,
|
|
13
|
+
isReadCall,
|
|
14
|
+
reportedWrites,
|
|
15
|
+
selectActions,
|
|
16
|
+
undeclaredState,
|
|
17
|
+
unreportedChanges,
|
|
18
|
+
WRITE_CONTRACT_ACTIONS,
|
|
19
|
+
WRITE_CONTRACT_SCHEMAS,
|
|
20
|
+
writeArgv,
|
|
21
|
+
writeContractSchema,
|
|
22
|
+
writerCallProblems,
|
|
23
|
+
writerDocumentProblems,
|
|
24
|
+
WRITER_FIXTURE_DIR,
|
|
25
|
+
WRITER_SCRIPT,
|
|
26
|
+
type ChantRun,
|
|
27
|
+
type WriteStep,
|
|
28
|
+
} from "./index";
|
|
29
|
+
|
|
30
|
+
const repoRoot = resolve(import.meta.dirname, "..", "..", "..", "..", "..");
|
|
31
|
+
|
|
32
|
+
const review = buildStep(WRITER_SCRIPT.find((s) => s.id === "review")!, { decision: { id: "fix-002" } });
|
|
33
|
+
const decision = buildStep(WRITER_SCRIPT.find((s) => s.id === "decision")!, {});
|
|
34
|
+
const run = (argv: string[], input?: string, stdout = ""): ChantRun => ({ argv, ...(input === undefined ? {} : { input }), status: 0, stdout, stderr: "" });
|
|
35
|
+
|
|
36
|
+
describe("the script (#3159)", () => {
|
|
37
|
+
test("covers every write-contract action, and each action has a schema that ships", () => {
|
|
38
|
+
expect(new Set(WRITER_SCRIPT.map((s) => s.action))).toEqual(new Set(WRITE_CONTRACT_ACTIONS));
|
|
39
|
+
for (const a of WRITE_CONTRACT_ACTIONS) expect(writeContractSchema(a).schema.$id, a).toMatch(new RegExp(`/${WRITE_CONTRACT_SCHEMAS[a].replace(".", "\\.")}$`));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("writeArgv: fields go on stdin, everything else is a flag", () => {
|
|
43
|
+
expect(review.args).toEqual(["fix-002", "--kind", "decisions/decision.kind.mjs", "--verdict", "agree", "--by", "conformance-reviewer", "--note", "Written through chant, read back after amnesia."]);
|
|
44
|
+
expect(review.input).toBeUndefined();
|
|
45
|
+
expect(decision.args).toEqual(["decisions/decision.kind.mjs", "--from", "-"]);
|
|
46
|
+
expect(JSON.parse(decision.input!)).toMatchObject({ title: "How a writer writes the workspace", state: "proposed" });
|
|
47
|
+
expect(writeArgv("work evidence", { id: "W-001", kind: "work/work.kind.mjs", holder: "h", token: "t", entry: { criterion: "AC-1" } })).toEqual({
|
|
48
|
+
args: ["W-001", "--holder", "h", "--token", "t", "--from", "-", "--kind", "work/work.kind.mjs"],
|
|
49
|
+
input: '{"criterion":"AC-1"}',
|
|
50
|
+
});
|
|
51
|
+
expect(writeArgv("runs end", { id: "r", fields: {} })).toEqual({ args: ["r", "--from", "-"], input: "{}" });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("a later step's params come from the documents of the steps before it", () => {
|
|
55
|
+
const renew = buildStep(WRITER_SCRIPT.find((s) => s.id === "renew")!, { claim: { lease: { token: "tok-1" } } });
|
|
56
|
+
expect(renew.args).toEqual(["W-001", "--holder", "conformance-writer", "--token", "tok-1", "--kind", "work/work.kind.mjs"]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("selectActions refuses a name that is not an action, and an empty list", () => {
|
|
60
|
+
expect(selectActions(["records review", "points answer"])).toEqual({
|
|
61
|
+
checked: ["records review", "points answer"],
|
|
62
|
+
skipped: WRITE_CONTRACT_ACTIONS.filter((a) => a !== "records review" && a !== "points answer"),
|
|
63
|
+
});
|
|
64
|
+
expect(() => selectActions(["records pin" as never])).toThrow(/not write-contract actions: records pin/);
|
|
65
|
+
expect(() => selectActions([])).toThrow(/actions is empty/);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("writerCallProblems (#3159)", () => {
|
|
70
|
+
test("exactly one call of the action, its arguments in order, its JSON flag, its fields on stdin", () => {
|
|
71
|
+
expect(writerCallProblems(review, [run(["workspace", "records", "review", ...review.args])])).toEqual([]);
|
|
72
|
+
expect(writerCallProblems(review, [run(["workspace", "records", "review", ...review.args, "--json"])])).toEqual([]);
|
|
73
|
+
expect(writerCallProblems(decision, [run(["workspace", "records", "new", ...decision.args], JSON.stringify(JSON.parse(decision.input!), null, 2))])).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("a second call, another command, an extra flag, other fields or stdin for a command that reads none", () => {
|
|
77
|
+
const argv = ["workspace", "records", "review", ...review.args];
|
|
78
|
+
expect(writerCallProblems(review, [run(["workspace", "ls", "--json"]), run(argv)])[0]).toMatch(/^review \(records review\): made 2 chant calls .* expected exactly one$/);
|
|
79
|
+
expect(writerCallProblems(review, [run(["workspace", "records", "amend", ...review.args])])[0]).toMatch(/which is not workspace records review/);
|
|
80
|
+
expect(writerCallProblems(review, [run([...argv, "--dry-run"])])[0]).toMatch(/it may add only nothing or --json, and it added --dry-run/);
|
|
81
|
+
expect(writerCallProblems(review, [run(argv, "{}")])[0]).toMatch(/gave chant .* something on stdin, and the command reads nothing there/);
|
|
82
|
+
expect(writerCallProblems(decision, [run(["workspace", "records", "new", ...decision.args], '{"title":"other"}')])[0]).toMatch(/other fields on stdin than the step's/);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("writerDocumentProblems (#3159)", () => {
|
|
87
|
+
test("a refusal, a changed document and one that does not validate", () => {
|
|
88
|
+
const refusal = { $schema: writeContractSchema("records review").schema.$id, contract: 1, error: { code: "record-not-found", message: "no fix-002" } };
|
|
89
|
+
const printed = run(["workspace", "records", "review"], undefined, JSON.stringify(refusal));
|
|
90
|
+
expect(writerDocumentProblems(review, printed, refusal)).toEqual(["review (records review): chant did not write: record-not-found: no fix-002"]);
|
|
91
|
+
expect(writerDocumentProblems(review, printed, { ...refusal, extra: 1 }).join("\n")).toMatch(/must return the document chant printed, unchanged/);
|
|
92
|
+
const bad = { $schema: refusal.$schema, contract: 2 };
|
|
93
|
+
expect(writerDocumentProblems(review, run([], undefined, JSON.stringify(bad)), bad).join("\n")).toMatch(/does not validate against records-review.schema.json/);
|
|
94
|
+
expect(writerDocumentProblems(review, run(["workspace"], undefined, ""), undefined)).toEqual(["review (records review): chant workspace printed nothing"]);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe("unreportedChanges (#3159)", () => {
|
|
99
|
+
const files = (o: Record<string, string>) => o;
|
|
100
|
+
test("a record write may change only the path it reports, and no ref", () => {
|
|
101
|
+
const doc = { path: "decisions/fix-002-x.md" };
|
|
102
|
+
expect(unreportedChanges(review, doc, { before: files({ a: "1" }), after: files({ a: "1", "decisions/fix-002-x.md": "2" }) }, { before: { HEAD: "m 1" }, after: { HEAD: "m 1" } })).toEqual([]);
|
|
103
|
+
expect(unreportedChanges(review, doc, { before: files({ a: "1" }), after: files({ a: "9", "notes.md": "2" }) }, { before: { HEAD: "m 1" }, after: { HEAD: "m 2" } })).toEqual([
|
|
104
|
+
"review (records review): files changed that chant did not report writing: a (changed), notes.md (added)",
|
|
105
|
+
"review (records review): git refs changed that chant did not report writing: HEAD",
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("a lease may move its ref and chant/lifecycle to the commit it reports; a run only chant/lifecycle", () => {
|
|
110
|
+
const claim = buildStep(WRITER_SCRIPT.find((s) => s.id === "claim")!, {});
|
|
111
|
+
const doc = { ref: "refs/chant/lease/work/W-001", history: { commit: "c2" } };
|
|
112
|
+
expect(reportedWrites("work claim", doc)).toEqual({ paths: [], refs: { "refs/heads/chant/lifecycle": "c2", "refs/chant/lease/work/W-001": "*" } });
|
|
113
|
+
const before = { "refs/heads/chant/lifecycle": "c1", HEAD: "m 1" };
|
|
114
|
+
expect(unreportedChanges(claim, doc, { before: {}, after: {} }, { before, after: { ...before, "refs/heads/chant/lifecycle": "c2", "refs/chant/lease/work/W-001": "b" } })).toEqual([]);
|
|
115
|
+
expect(unreportedChanges(claim, doc, { before: {}, after: {} }, { before, after: { ...before, "refs/heads/chant/lifecycle": "c3" } })[0]).toMatch(/git refs changed that chant did not report writing: refs\/heads\/chant\/lifecycle/);
|
|
116
|
+
const start = { id: "run-start", action: "runs start", params: { run: {} }, args: ["--from", "-"], input: "{}" } as WriteStep;
|
|
117
|
+
expect(unreportedChanges(start, { ledger: { commit: "c2" } }, { before: {}, after: {} }, { before, after: { ...before, "refs/heads/chant/lifecycle": "c2" } })).toEqual([]);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("state and reads (#3159)", () => {
|
|
122
|
+
test("undeclaredState: everything in the state directory is declared, as one of ws-074's four", () => {
|
|
123
|
+
expect(undeclaredState(["hud/events.db", "hud/events.db-wal"], [{ path: "hud", is: "cache" }])).toEqual([]);
|
|
124
|
+
expect(undeclaredState(["events.db", "notes.json"], [{ path: "events.db", is: ["cache", "telemetry"] }])).toEqual([
|
|
125
|
+
"state: the writer keeps notes.json in its state directory, and privateState does not declare it",
|
|
126
|
+
]);
|
|
127
|
+
expect(undeclaredState([], [{ path: "db", is: "database" as never }])[0]).toMatch(/private state is cache, telemetry, secret, runtime/);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("isReadCall: the read contract, never a write verb", () => {
|
|
131
|
+
for (const argv of [["workspace", "records", "--kind", "k", "--json"], ["workspace", "records", "--uncommitted", "--json"], ["workspace", "runs", "--json"], ["workspace", "points", "--open", "--json"], ["workspace", "work", "history", "W-001"], ["workspace", "ls", "--json"]]) {
|
|
132
|
+
expect(isReadCall(argv), argv.join(" ")).toBe(true);
|
|
133
|
+
}
|
|
134
|
+
for (const argv of [["workspace", "records", "new"], ["workspace", "runs", "start"], ["workspace", "points", "answer"], ["workspace", "work", "claim"], ["workspace", "work", "evidence"], ["build"]]) {
|
|
135
|
+
expect(isReadCall(argv), argv.join(" ")).toBe(false);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("the writer fixture (#3159)", () => {
|
|
141
|
+
test("its kinds, schemas and decision points are the reference workspace's", () => {
|
|
142
|
+
const same: [string, string][] = [
|
|
143
|
+
["work/work.kind.mjs", "work/work.kind.mjs"],
|
|
144
|
+
["work/work.schema.json", "work/work.schema.json"],
|
|
145
|
+
["answers/answer.kind.mjs", "answers/answer.kind.mjs"],
|
|
146
|
+
["answers/answer.schema.json", "answers/answer.schema.json"],
|
|
147
|
+
["decisions/points.json", "decisions/points.json"],
|
|
148
|
+
["sessions/session.schema.json", "design/sessions/session.schema.json"],
|
|
149
|
+
];
|
|
150
|
+
for (const [ours, theirs] of same) expect(readFileSync(join(WRITER_FIXTURE_DIR, ours), "utf-8"), ours).toBe(readFileSync(join(repoRoot, "reference-workspace", theirs), "utf-8"));
|
|
151
|
+
// The session kind sits one directory higher here than in the reference
|
|
152
|
+
// workspace's design member, so its subjects path is one level shorter.
|
|
153
|
+
const session = readFileSync(join(repoRoot, "reference-workspace", "design", "sessions", "session.kind.mjs"), "utf-8").replace('"../../decisions/decision.kind.mjs"', '"../decisions/decision.kind.mjs"');
|
|
154
|
+
expect(readFileSync(join(WRITER_FIXTURE_DIR, "sessions", "session.kind.mjs"), "utf-8")).toBe(session);
|
|
155
|
+
});
|
|
156
|
+
});
|