worldvm 0.0.0-stage → 0.1.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/AUTHORING.md +833 -0
- package/README.md +154 -2
- package/bin/worldvm.mjs +3 -0
- package/dist/_internal/forma/index.d.ts +1 -0
- package/dist/_internal/forma/index.js +1 -0
- package/dist/_internal/forma/world/authoring-help.d.ts +25 -0
- package/dist/_internal/forma/world/authoring-help.js +130 -0
- package/dist/_internal/forma/world/citations.d.ts +3 -0
- package/dist/_internal/forma/world/citations.js +26 -0
- package/dist/_internal/forma/world/clock-bindings.d.ts +4 -0
- package/dist/_internal/forma/world/clock-bindings.js +45 -0
- package/dist/_internal/forma/world/compile.d.ts +13 -0
- package/dist/_internal/forma/world/compile.js +190 -0
- package/dist/_internal/forma/world/coverage.d.ts +10 -0
- package/dist/_internal/forma/world/coverage.js +242 -0
- package/dist/_internal/forma/world/digest.d.ts +1 -0
- package/dist/_internal/forma/world/digest.js +1 -0
- package/dist/_internal/forma/world/documentation.d.ts +3 -0
- package/dist/_internal/forma/world/documentation.js +38 -0
- package/dist/_internal/forma/world/homes.d.ts +4 -0
- package/dist/_internal/forma/world/homes.js +202 -0
- package/dist/_internal/forma/world/index.d.ts +6 -0
- package/dist/_internal/forma/world/index.js +5 -0
- package/dist/_internal/forma/world/lowering/accounts.d.ts +4 -0
- package/dist/_internal/forma/world/lowering/accounts.js +36 -0
- package/dist/_internal/forma/world/lowering/action-policy.d.ts +5 -0
- package/dist/_internal/forma/world/lowering/action-policy.js +16 -0
- package/dist/_internal/forma/world/lowering/agents.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/agents.js +116 -0
- package/dist/_internal/forma/world/lowering/bundles.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/bundles.js +13 -0
- package/dist/_internal/forma/world/lowering/child-parameters.d.ts +14 -0
- package/dist/_internal/forma/world/lowering/child-parameters.js +36 -0
- package/dist/_internal/forma/world/lowering/collects.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/collects.js +51 -0
- package/dist/_internal/forma/world/lowering/component-operations.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/component-operations.js +26 -0
- package/dist/_internal/forma/world/lowering/components.d.ts +7 -0
- package/dist/_internal/forma/world/lowering/components.js +236 -0
- package/dist/_internal/forma/world/lowering/connectors.d.ts +4 -0
- package/dist/_internal/forma/world/lowering/connectors.js +31 -0
- package/dist/_internal/forma/world/lowering/context.d.ts +80 -0
- package/dist/_internal/forma/world/lowering/context.js +316 -0
- package/dist/_internal/forma/world/lowering/contracts.d.ts +7 -0
- package/dist/_internal/forma/world/lowering/contracts.js +73 -0
- package/dist/_internal/forma/world/lowering/decisions.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/decisions.js +164 -0
- package/dist/_internal/forma/world/lowering/derives.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/derives.js +22 -0
- package/dist/_internal/forma/world/lowering/discretionary.d.ts +7 -0
- package/dist/_internal/forma/world/lowering/discretionary.js +15 -0
- package/dist/_internal/forma/world/lowering/documents.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/documents.js +68 -0
- package/dist/_internal/forma/world/lowering/effective.d.ts +5 -0
- package/dist/_internal/forma/world/lowering/effective.js +26 -0
- package/dist/_internal/forma/world/lowering/entities.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/entities.js +43 -0
- package/dist/_internal/forma/world/lowering/escalations.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/escalations.js +31 -0
- package/dist/_internal/forma/world/lowering/evidence.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/evidence.js +75 -0
- package/dist/_internal/forma/world/lowering/files.d.ts +4 -0
- package/dist/_internal/forma/world/lowering/files.js +56 -0
- package/dist/_internal/forma/world/lowering/holds.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/holds.js +35 -0
- package/dist/_internal/forma/world/lowering/index.d.ts +89 -0
- package/dist/_internal/forma/world/lowering/index.js +115 -0
- package/dist/_internal/forma/world/lowering/interactions.d.ts +7 -0
- package/dist/_internal/forma/world/lowering/interactions.js +190 -0
- package/dist/_internal/forma/world/lowering/library.d.ts +5 -0
- package/dist/_internal/forma/world/lowering/library.js +41 -0
- package/dist/_internal/forma/world/lowering/milestones.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/milestones.js +17 -0
- package/dist/_internal/forma/world/lowering/notices.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/notices.js +33 -0
- package/dist/_internal/forma/world/lowering/param-bounds.d.ts +15 -0
- package/dist/_internal/forma/world/lowering/param-bounds.js +71 -0
- package/dist/_internal/forma/world/lowering/policies.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/policies.js +95 -0
- package/dist/_internal/forma/world/lowering/principals.d.ts +62 -0
- package/dist/_internal/forma/world/lowering/principals.js +32 -0
- package/dist/_internal/forma/world/lowering/recurrences.d.ts +9 -0
- package/dist/_internal/forma/world/lowering/recurrences.js +56 -0
- package/dist/_internal/forma/world/lowering/renewal.d.ts +5 -0
- package/dist/_internal/forma/world/lowering/renewal.js +21 -0
- package/dist/_internal/forma/world/lowering/resources.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/resources.js +19 -0
- package/dist/_internal/forma/world/lowering/reuse.d.ts +5 -0
- package/dist/_internal/forma/world/lowering/reuse.js +75 -0
- package/dist/_internal/forma/world/lowering/sources.d.ts +10 -0
- package/dist/_internal/forma/world/lowering/sources.js +47 -0
- package/dist/_internal/forma/world/lowering/syntax.d.ts +84 -0
- package/dist/_internal/forma/world/lowering/syntax.js +94 -0
- package/dist/_internal/forma/world/lowering/vaults.d.ts +11 -0
- package/dist/_internal/forma/world/lowering/vaults.js +148 -0
- package/dist/_internal/forma/world/prelude.generated.d.ts +1 -0
- package/dist/_internal/forma/world/prelude.generated.js +2 -0
- package/dist/_internal/forma/world/properties.d.ts +9 -0
- package/dist/_internal/forma/world/properties.js +387 -0
- package/dist/_internal/forma/world/scenarios.d.ts +10 -0
- package/dist/_internal/forma/world/scenarios.js +757 -0
- package/dist/_internal/forma/world/sensitivity.d.ts +3 -0
- package/dist/_internal/forma/world/sensitivity.js +118 -0
- package/dist/_internal/forma/world/source-syntax.d.ts +6 -0
- package/dist/_internal/forma/world/source-syntax.js +27 -0
- package/dist/_internal/forma/world/status-handling.d.ts +3 -0
- package/dist/_internal/forma/world/status-handling.js +143 -0
- package/dist/_internal/forma/world/surface.d.ts +30 -0
- package/dist/_internal/forma/world/surface.js +213 -0
- package/dist/_internal/forma/world/table-expressions.d.ts +3 -0
- package/dist/_internal/forma/world/table-expressions.js +41 -0
- package/dist/_internal/forma-tools/index.d.ts +17 -0
- package/dist/_internal/forma-tools/index.js +124 -0
- package/dist/_internal/ir-checks/advances.d.ts +7 -0
- package/dist/_internal/ir-checks/advances.js +6 -0
- package/dist/_internal/ir-checks/agent-reads.d.ts +14 -0
- package/dist/_internal/ir-checks/agent-reads.js +118 -0
- package/dist/_internal/ir-checks/amendments.d.ts +3 -0
- package/dist/_internal/ir-checks/amendments.js +21 -0
- package/dist/_internal/ir-checks/canonical-json.d.ts +3 -0
- package/dist/_internal/ir-checks/canonical-json.js +85 -0
- package/dist/_internal/ir-checks/child-parameters.d.ts +2 -0
- package/dist/_internal/ir-checks/child-parameters.js +43 -0
- package/dist/_internal/ir-checks/clock-bindings.d.ts +3 -0
- package/dist/_internal/ir-checks/clock-bindings.js +43 -0
- package/dist/_internal/ir-checks/collect-types.d.ts +14 -0
- package/dist/_internal/ir-checks/collect-types.js +34 -0
- package/dist/_internal/ir-checks/component-operations.d.ts +4 -0
- package/dist/_internal/ir-checks/component-operations.js +23 -0
- package/dist/_internal/ir-checks/content-digest.d.ts +4 -0
- package/dist/_internal/ir-checks/content-digest.js +78 -0
- package/dist/_internal/ir-checks/contract-validation.d.ts +3 -0
- package/dist/_internal/ir-checks/contract-validation.js +307 -0
- package/dist/_internal/ir-checks/contracts.d.ts +25 -0
- package/dist/_internal/ir-checks/contracts.js +80 -0
- package/dist/_internal/ir-checks/dates.d.ts +2 -0
- package/dist/_internal/ir-checks/dates.js +7 -0
- package/dist/_internal/ir-checks/documents.d.ts +3 -0
- package/dist/_internal/ir-checks/documents.js +45 -0
- package/dist/_internal/ir-checks/evidence.d.ts +20 -0
- package/dist/_internal/ir-checks/evidence.js +56 -0
- package/dist/_internal/ir-checks/files.d.ts +7 -0
- package/dist/_internal/ir-checks/files.js +47 -0
- package/dist/_internal/ir-checks/index.d.ts +59 -0
- package/dist/_internal/ir-checks/index.js +166 -0
- package/dist/_internal/ir-checks/instants.d.ts +7 -0
- package/dist/_internal/ir-checks/instants.js +61 -0
- package/dist/_internal/ir-checks/notices.d.ts +4 -0
- package/dist/_internal/ir-checks/notices.js +58 -0
- package/dist/_internal/ir-checks/policy-bounds.d.ts +5 -0
- package/dist/_internal/ir-checks/policy-bounds.js +28 -0
- package/dist/_internal/ir-checks/principals.d.ts +26 -0
- package/dist/_internal/ir-checks/principals.js +162 -0
- package/dist/_internal/ir-checks/prior-cycle.d.ts +41 -0
- package/dist/_internal/ir-checks/prior-cycle.js +21 -0
- package/dist/_internal/ir-checks/read-classification.d.ts +12 -0
- package/dist/_internal/ir-checks/read-classification.js +67 -0
- package/dist/_internal/ir-checks/read-types.d.ts +2 -0
- package/dist/_internal/ir-checks/read-types.js +40 -0
- package/dist/_internal/ir-checks/resource-proofs.d.ts +4 -0
- package/dist/_internal/ir-checks/resource-proofs.js +98 -0
- package/dist/_internal/ir-checks/resources.d.ts +3 -0
- package/dist/_internal/ir-checks/resources.js +30 -0
- package/dist/_internal/ir-checks/reuse.d.ts +10 -0
- package/dist/_internal/ir-checks/reuse.js +99 -0
- package/dist/_internal/ir-checks/sealing.d.ts +14 -0
- package/dist/_internal/ir-checks/sealing.js +123 -0
- package/dist/_internal/ir-checks/sensitive-flow.d.ts +4 -0
- package/dist/_internal/ir-checks/sensitive-flow.js +240 -0
- package/dist/_internal/ir-checks/sensitivity.d.ts +19 -0
- package/dist/_internal/ir-checks/sensitivity.js +133 -0
- package/dist/_internal/ir-checks/signatures.d.ts +4 -0
- package/dist/_internal/ir-checks/signatures.js +22 -0
- package/dist/_internal/ir-checks/vaults.d.ts +10 -0
- package/dist/_internal/ir-checks/vaults.js +140 -0
- package/dist/_internal/protocol/index.d.ts +1234 -0
- package/dist/_internal/protocol/index.js +215 -0
- package/dist/_internal/reconciler/action-intents.d.ts +17 -0
- package/dist/_internal/reconciler/action-intents.js +161 -0
- package/dist/_internal/reconciler/actions.d.ts +7 -0
- package/dist/_internal/reconciler/actions.js +134 -0
- package/dist/_internal/reconciler/amendments.d.ts +12 -0
- package/dist/_internal/reconciler/amendments.js +52 -0
- package/dist/_internal/reconciler/basis.d.ts +253 -0
- package/dist/_internal/reconciler/basis.js +16 -0
- package/dist/_internal/reconciler/bundles.d.ts +22 -0
- package/dist/_internal/reconciler/bundles.js +110 -0
- package/dist/_internal/reconciler/change-detection.d.ts +5 -0
- package/dist/_internal/reconciler/change-detection.js +82 -0
- package/dist/_internal/reconciler/collection-rules.d.ts +7 -0
- package/dist/_internal/reconciler/collection-rules.js +46 -0
- package/dist/_internal/reconciler/collection-state.d.ts +11 -0
- package/dist/_internal/reconciler/collection-state.js +62 -0
- package/dist/_internal/reconciler/collection-values.d.ts +5 -0
- package/dist/_internal/reconciler/collection-values.js +60 -0
- package/dist/_internal/reconciler/collections.d.ts +35 -0
- package/dist/_internal/reconciler/collections.js +141 -0
- package/dist/_internal/reconciler/commands.d.ts +60 -0
- package/dist/_internal/reconciler/commands.js +468 -0
- package/dist/_internal/reconciler/component-operations.d.ts +48 -0
- package/dist/_internal/reconciler/component-operations.js +256 -0
- package/dist/_internal/reconciler/context.d.ts +38 -0
- package/dist/_internal/reconciler/context.js +44 -0
- package/dist/_internal/reconciler/coverage.d.ts +31 -0
- package/dist/_internal/reconciler/coverage.js +124 -0
- package/dist/_internal/reconciler/dates.d.ts +38 -0
- package/dist/_internal/reconciler/dates.js +121 -0
- package/dist/_internal/reconciler/decide.d.ts +25 -0
- package/dist/_internal/reconciler/decide.js +263 -0
- package/dist/_internal/reconciler/decision-evaluation.d.ts +9 -0
- package/dist/_internal/reconciler/decision-evaluation.js +67 -0
- package/dist/_internal/reconciler/decision-records.d.ts +14 -0
- package/dist/_internal/reconciler/decision-records.js +77 -0
- package/dist/_internal/reconciler/decision-release.d.ts +3 -0
- package/dist/_internal/reconciler/decision-release.js +12 -0
- package/dist/_internal/reconciler/dependencies.d.ts +179 -0
- package/dist/_internal/reconciler/dependencies.js +372 -0
- package/dist/_internal/reconciler/discovery.d.ts +9 -0
- package/dist/_internal/reconciler/discovery.js +65 -0
- package/dist/_internal/reconciler/document-corrections.d.ts +18 -0
- package/dist/_internal/reconciler/document-corrections.js +59 -0
- package/dist/_internal/reconciler/documents.d.ts +26 -0
- package/dist/_internal/reconciler/documents.js +88 -0
- package/dist/_internal/reconciler/effective.d.ts +6 -0
- package/dist/_internal/reconciler/effective.js +17 -0
- package/dist/_internal/reconciler/escalations.d.ts +4 -0
- package/dist/_internal/reconciler/escalations.js +76 -0
- package/dist/_internal/reconciler/evaluation.d.ts +56 -0
- package/dist/_internal/reconciler/evaluation.js +704 -0
- package/dist/_internal/reconciler/evidence-authority.d.ts +6 -0
- package/dist/_internal/reconciler/evidence-authority.js +70 -0
- package/dist/_internal/reconciler/evidence-history.d.ts +5 -0
- package/dist/_internal/reconciler/evidence-history.js +22 -0
- package/dist/_internal/reconciler/evidence-validity.d.ts +17 -0
- package/dist/_internal/reconciler/evidence-validity.js +135 -0
- package/dist/_internal/reconciler/fact-validation.d.ts +5 -0
- package/dist/_internal/reconciler/fact-validation.js +49 -0
- package/dist/_internal/reconciler/files.d.ts +9 -0
- package/dist/_internal/reconciler/files.js +56 -0
- package/dist/_internal/reconciler/graph.d.ts +40 -0
- package/dist/_internal/reconciler/graph.js +298 -0
- package/dist/_internal/reconciler/holds.d.ts +6 -0
- package/dist/_internal/reconciler/holds.js +18 -0
- package/dist/_internal/reconciler/home-definitions.d.ts +11 -0
- package/dist/_internal/reconciler/home-definitions.js +25 -0
- package/dist/_internal/reconciler/homes.d.ts +6 -0
- package/dist/_internal/reconciler/homes.js +32 -0
- package/dist/_internal/reconciler/index.d.ts +42 -0
- package/dist/_internal/reconciler/index.js +58 -0
- package/dist/_internal/reconciler/instants.d.ts +17 -0
- package/dist/_internal/reconciler/instants.js +56 -0
- package/dist/_internal/reconciler/interaction-fields.d.ts +10 -0
- package/dist/_internal/reconciler/interaction-fields.js +30 -0
- package/dist/_internal/reconciler/invariants.d.ts +8 -0
- package/dist/_internal/reconciler/invariants.js +34 -0
- package/dist/_internal/reconciler/journal.d.ts +3 -0
- package/dist/_internal/reconciler/journal.js +4 -0
- package/dist/_internal/reconciler/linking.d.ts +10 -0
- package/dist/_internal/reconciler/linking.js +259 -0
- package/dist/_internal/reconciler/memory.d.ts +32 -0
- package/dist/_internal/reconciler/memory.js +190 -0
- package/dist/_internal/reconciler/milestones.d.ts +7 -0
- package/dist/_internal/reconciler/milestones.js +58 -0
- package/dist/_internal/reconciler/missing.d.ts +18 -0
- package/dist/_internal/reconciler/missing.js +79 -0
- package/dist/_internal/reconciler/model.d.ts +350 -0
- package/dist/_internal/reconciler/model.js +9 -0
- package/dist/_internal/reconciler/notices.d.ts +14 -0
- package/dist/_internal/reconciler/notices.js +80 -0
- package/dist/_internal/reconciler/occurrences.d.ts +3 -0
- package/dist/_internal/reconciler/occurrences.js +30 -0
- package/dist/_internal/reconciler/outputs.d.ts +12 -0
- package/dist/_internal/reconciler/outputs.js +132 -0
- package/dist/_internal/reconciler/parameters.d.ts +4 -0
- package/dist/_internal/reconciler/parameters.js +27 -0
- package/dist/_internal/reconciler/policies.d.ts +22 -0
- package/dist/_internal/reconciler/policies.js +94 -0
- package/dist/_internal/reconciler/policy-edits.d.ts +11 -0
- package/dist/_internal/reconciler/policy-edits.js +42 -0
- package/dist/_internal/reconciler/policy-linking.d.ts +4 -0
- package/dist/_internal/reconciler/policy-linking.js +104 -0
- package/dist/_internal/reconciler/principal-types.d.ts +2 -0
- package/dist/_internal/reconciler/principal-types.js +38 -0
- package/dist/_internal/reconciler/prior-decisions.d.ts +3 -0
- package/dist/_internal/reconciler/prior-decisions.js +34 -0
- package/dist/_internal/reconciler/projections.d.ts +4 -0
- package/dist/_internal/reconciler/projections.js +85 -0
- package/dist/_internal/reconciler/proofs.d.ts +9 -0
- package/dist/_internal/reconciler/proofs.js +24 -0
- package/dist/_internal/reconciler/properties.d.ts +25 -0
- package/dist/_internal/reconciler/properties.js +67 -0
- package/dist/_internal/reconciler/property-eval.d.ts +5 -0
- package/dist/_internal/reconciler/property-eval.js +101 -0
- package/dist/_internal/reconciler/property-generator.d.ts +9 -0
- package/dist/_internal/reconciler/property-generator.js +288 -0
- package/dist/_internal/reconciler/property-shrink.d.ts +14 -0
- package/dist/_internal/reconciler/property-shrink.js +183 -0
- package/dist/_internal/reconciler/provenance.d.ts +8 -0
- package/dist/_internal/reconciler/provenance.js +61 -0
- package/dist/_internal/reconciler/reconcile.d.ts +32 -0
- package/dist/_internal/reconciler/reconcile.js +164 -0
- package/dist/_internal/reconciler/recurrence.d.ts +9 -0
- package/dist/_internal/reconciler/recurrence.js +44 -0
- package/dist/_internal/reconciler/requirements.d.ts +5 -0
- package/dist/_internal/reconciler/requirements.js +331 -0
- package/dist/_internal/reconciler/resource-generation.d.ts +9 -0
- package/dist/_internal/reconciler/resource-generation.js +17 -0
- package/dist/_internal/reconciler/resource-scenarios.d.ts +26 -0
- package/dist/_internal/reconciler/resource-scenarios.js +78 -0
- package/dist/_internal/reconciler/resources.d.ts +24 -0
- package/dist/_internal/reconciler/resources.js +178 -0
- package/dist/_internal/reconciler/result-digest.d.ts +13 -0
- package/dist/_internal/reconciler/result-digest.js +66 -0
- package/dist/_internal/reconciler/reuse.d.ts +16 -0
- package/dist/_internal/reconciler/reuse.js +119 -0
- package/dist/_internal/reconciler/scenarios.d.ts +32 -0
- package/dist/_internal/reconciler/scenarios.js +442 -0
- package/dist/_internal/reconciler/scheduling.d.ts +9 -0
- package/dist/_internal/reconciler/scheduling.js +203 -0
- package/dist/_internal/reconciler/sha256.d.ts +1 -0
- package/dist/_internal/reconciler/sha256.js +1 -0
- package/dist/_internal/reconciler/signatures.d.ts +13 -0
- package/dist/_internal/reconciler/signatures.js +37 -0
- package/dist/_internal/reconciler/temporal.d.ts +10 -0
- package/dist/_internal/reconciler/temporal.js +46 -0
- package/dist/_internal/reconciler/upgrades.d.ts +6 -0
- package/dist/_internal/reconciler/upgrades.js +65 -0
- package/dist/_internal/reconciler/wire.d.ts +2 -0
- package/dist/_internal/reconciler/wire.js +5 -0
- package/dist/_internal/reconciler/work.d.ts +32 -0
- package/dist/_internal/reconciler/work.js +101 -0
- package/dist/_internal/world-ir/schema.generated.d.ts +180 -0
- package/dist/_internal/world-ir/schema.generated.js +1127 -0
- package/dist/_internal/world-ir/types.generated.d.ts +1011 -0
- package/dist/_internal/world-ir/types.generated.js +1 -0
- package/dist/authoring.d.ts +9 -0
- package/dist/authoring.js +10 -0
- package/dist/catalogue.d.ts +20 -0
- package/dist/catalogue.js +531 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +553 -0
- package/dist/codegen.d.ts +6 -0
- package/dist/codegen.js +89 -0
- package/dist/coverage.d.ts +60 -0
- package/dist/coverage.js +128 -0
- package/dist/deploy.d.ts +21 -0
- package/dist/deploy.js +72 -0
- package/dist/diagnostics.d.ts +9 -0
- package/dist/diagnostics.js +37 -0
- package/dist/diff.d.ts +17 -0
- package/dist/diff.js +19 -0
- package/dist/durable-object.d.ts +14 -0
- package/dist/durable-object.js +30 -0
- package/dist/explain.d.ts +113 -0
- package/dist/explain.js +225 -0
- package/dist/fixes.d.ts +4 -0
- package/dist/fixes.js +70 -0
- package/dist/fmt.d.ts +12 -0
- package/dist/fmt.js +39 -0
- package/dist/handle/open.d.ts +18 -0
- package/dist/handle/open.js +28 -0
- package/dist/handle/protocol.d.ts +12 -0
- package/dist/handle/protocol.js +96 -0
- package/dist/handle/store.d.ts +31 -0
- package/dist/handle/store.js +11 -0
- package/dist/handle/types.d.ts +37 -0
- package/dist/handle/types.js +1 -0
- package/dist/handle/world.d.ts +190 -0
- package/dist/handle/world.js +533 -0
- package/dist/http.d.ts +3 -0
- package/dist/http.js +72 -0
- package/dist/impact.d.ts +21 -0
- package/dist/impact.js +57 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +43 -0
- package/dist/init.d.ts +13 -0
- package/dist/init.js +99 -0
- package/dist/ir-schema.json +4213 -0
- package/dist/lints.d.ts +8 -0
- package/dist/lints.js +154 -0
- package/dist/lsp.d.ts +14 -0
- package/dist/lsp.js +380 -0
- package/dist/manual/catalog.d.ts +12 -0
- package/dist/manual/catalog.js +23 -0
- package/dist/manual/examples.d.ts +15 -0
- package/dist/manual/examples.js +39 -0
- package/dist/manual/projections.d.ts +77 -0
- package/dist/manual/projections.js +344 -0
- package/dist/manual/prose.d.ts +19 -0
- package/dist/manual/prose.js +215 -0
- package/dist/manual/render.d.ts +76 -0
- package/dist/manual/render.js +250 -0
- package/dist/manual/text.d.ts +7 -0
- package/dist/manual/text.js +15 -0
- package/dist/mcp.d.ts +32 -0
- package/dist/mcp.js +269 -0
- package/dist/mutate.d.ts +36 -0
- package/dist/mutate.js +123 -0
- package/dist/project.d.ts +26 -0
- package/dist/project.js +80 -0
- package/dist/refine.d.ts +18 -0
- package/dist/refine.js +197 -0
- package/dist/release/diff.d.ts +45 -0
- package/dist/release/diff.js +103 -0
- package/dist/release/impact.d.ts +83 -0
- package/dist/release/impact.js +214 -0
- package/dist/release/nodes.d.ts +20 -0
- package/dist/release/nodes.js +118 -0
- package/dist/render.d.ts +19 -0
- package/dist/render.js +43 -0
- package/dist/serve.d.ts +18 -0
- package/dist/serve.js +140 -0
- package/dist/snapshot.d.ts +19 -0
- package/dist/snapshot.js +45 -0
- package/dist/sqlite.d.ts +16 -0
- package/dist/sqlite.js +151 -0
- package/dist/test.d.ts +54 -0
- package/dist/test.js +131 -0
- package/dist/world.d.ts +7 -0
- package/dist/world.js +5 -0
- package/docs/authoring/vendor-risk/example.forma +11 -0
- package/docs/authoring/vendor-risk/library/vendor.forma +76 -0
- package/docs/authoring/vendor-risk/scenarios/reviews.forma +107 -0
- package/docs/protocol.md +195 -0
- package/docs/reference.md +258 -0
- package/package.json +81 -3
package/AUTHORING.md
ADDED
|
@@ -0,0 +1,833 @@
|
|
|
1
|
+
# Authoring worlds
|
|
2
|
+
|
|
3
|
+
This guide teaches you to write a WorldVM world from a brief: the requirements, who satisfies them
|
|
4
|
+
and how, where people decide, and the scenarios that prove it. Read it top to bottom once, then
|
|
5
|
+
use the [forms reference](#3-forms-reference), [Truth and comparisons](#4-types-truth-and-comparisons)
|
|
6
|
+
and the [diagnostics catalogue](#10-diagnostics-catalogue) while you work. You should not need to
|
|
7
|
+
read the reference worlds or the compiler to start.
|
|
8
|
+
|
|
9
|
+
Every example here is synthetic and illustrative, not legal, compliance or audit advice. Every
|
|
10
|
+
Forma snippet in this file is compiled by a test, and the scenarios in it pass.
|
|
11
|
+
|
|
12
|
+
Contents: [1 Layout and commands](#1-layout-and-commands) ·
|
|
13
|
+
[2 A minimal world](#2-a-minimal-world) · [3 Forms reference](#3-forms-reference) ·
|
|
14
|
+
[4 Types, Truth and comparisons](#4-types-truth-and-comparisons) · [5 Evidence](#5-evidence) ·
|
|
15
|
+
[6 Decisions](#6-decisions) · [7 Outputs and NotApplicable](#7-outputs-and-notapplicable) ·
|
|
16
|
+
[8 Actions, interactions and status changes](#8-actions-interactions-and-status-changes) ·
|
|
17
|
+
[9 Scenarios and properties](#9-scenarios-and-properties) ·
|
|
18
|
+
[10 Diagnostics catalogue](#10-diagnostics-catalogue) · [11 Units and dates](#11-units-and-dates) ·
|
|
19
|
+
[12 Idioms and anti-patterns](#12-idioms-and-anti-patterns) ·
|
|
20
|
+
[13 Recipes and a complete example](#13-recipes-and-a-complete-example)
|
|
21
|
+
|
|
22
|
+
## 1. Layout and commands
|
|
23
|
+
|
|
24
|
+
A world is a directory of `.forma` files. The CLI reads every `.forma` file under the directory
|
|
25
|
+
(skipping `node_modules`, `dist`, `build`, `coverage` and dot-directories), so the split into
|
|
26
|
+
files is yours. A layout that scales:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
my-world/
|
|
30
|
+
library/policy.forma ; (library …), roles, sources, evidence, interactions, components, decisions
|
|
31
|
+
example.forma ; entities that implement the roles, and the (account …) that runs the library
|
|
32
|
+
scenarios/main.forma ; (scenario …) and (property …) examples
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
There are two halves. The **library** says what is required, of whom, and how, against abstract
|
|
36
|
+
**roles** (`training/worker`). The **account package** says which stored **entities** play those
|
|
37
|
+
roles and runs components for them with `(for-each role Component)`. Scenarios and properties
|
|
38
|
+
run against an account.
|
|
39
|
+
|
|
40
|
+
Install the package (`npm install worldvm effect@^4.1.1 @forma-language/forma@^0.6.0`), then
|
|
41
|
+
point the CLI at your directory:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
npx worldvm check my-world # compile, link accounts, report diagnostics
|
|
45
|
+
npx worldvm test my-world --seed 42 # run scenarios and seeded properties
|
|
46
|
+
npx worldvm coverage my-world # which requirements and cited clauses tests reach
|
|
47
|
+
npx worldvm render my-world # the human-readable manual
|
|
48
|
+
npx worldvm explain my-world --scenario <name> --at 2026-11-10
|
|
49
|
+
npx worldvm coverage my-world --mutate # semantic mutants your tests fail to catch
|
|
50
|
+
npx worldvm explain W_NO_SCENARIO # cause, example and fix for a diagnostic code
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
To preview a rule change against a deployed population, export the SQLite database:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npx worldvm deploy my-world --db world.db --account example
|
|
57
|
+
npx worldvm serve --db world.db
|
|
58
|
+
# In another terminal, or after stopping serve:
|
|
59
|
+
npx worldvm snapshot --db world.db --account example --out snapshot.json
|
|
60
|
+
npx worldvm impact my-world proposed-world --world snapshot.json --at 2026-11-10
|
|
61
|
+
npx worldvm explain my-world --world snapshot.json --subject hire-123 --at 2026-11-10
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use the account the server opened. The export reads one committed state while the server
|
|
65
|
+
can keep running, and records the release that last wrote it. Omit `--out` to write JSON
|
|
66
|
+
to stdout. `impact` and `explain` evaluate that population at the explicit `--at` instant.
|
|
67
|
+
|
|
68
|
+
Stop `serve` before deploying changes. Use `worldvm deploy proposed-world --db world.db
|
|
69
|
+
--account example --dry-run --at 2026-11-10` to preview against committed records;
|
|
70
|
+
omit `--dry-run` to activate and retain the release. `serve <directory> --db` is a
|
|
71
|
+
development shortcut for deploy, then serve. A later `--clock` advances evaluation
|
|
72
|
+
without requiring a redeploy. See the [deployment reference](docs/reference.md#cli-impact-on-a-live-world).
|
|
73
|
+
|
|
74
|
+
Start a new world with `npx worldvm init my-world`: it writes a minimal library, account,
|
|
75
|
+
scenario, property and local `REFERENCE.md`. The world passes `check`, `test` and `coverage`. Every command has its own
|
|
76
|
+
`--help`.
|
|
77
|
+
|
|
78
|
+
Each command exits 0 when it passes and 1 when it fails; `--json` prints the machine-readable
|
|
79
|
+
report, where every diagnostic carries `code`, `at` (file, line, column, offsets), `help` and
|
|
80
|
+
`fix` (a mechanical edit with the exact text it replaces, or `null`). Warnings never fail `check`.
|
|
81
|
+
|
|
82
|
+
### Glossary
|
|
83
|
+
|
|
84
|
+
| Term | Meaning |
|
|
85
|
+
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| **Subject** | The entity whose facts and work are being reconciled, such as one vendor. |
|
|
87
|
+
| **Home** | Internal code/IR term for that subject's ownership and routing boundary; diagnostic codes such as `E_NO_HOME` retain this spelling. |
|
|
88
|
+
| **Instance** | A component bound to particular key inputs, such as `(VendorReview acme)`. A subject can have several instances. |
|
|
89
|
+
| **Occurrence** | One evaluation of an instance, including a dated recurrence when the component repeats. |
|
|
90
|
+
|
|
91
|
+
## 2. A minimal world
|
|
92
|
+
|
|
93
|
+
One file is enough. This world requires every worker to complete safety training and proves it
|
|
94
|
+
with one scenario.
|
|
95
|
+
|
|
96
|
+
```forma world=minimal
|
|
97
|
+
;; Synthetic example; illustrative, not legal or audit advice.
|
|
98
|
+
(library example.training "Safety training for each worker." :version "1.0" :subject training/worker)
|
|
99
|
+
|
|
100
|
+
(role training/worker "A person who must complete training." {:name String})
|
|
101
|
+
|
|
102
|
+
(source safety-policy "A synthetic internal safety policy."
|
|
103
|
+
:kind :policy :url "https://example.invalid/safety" :as-of "2026-11-01")
|
|
104
|
+
|
|
105
|
+
(evidence Completion "A signed training completion record." :of Training
|
|
106
|
+
{:course String}
|
|
107
|
+
(attest :signer worker :covers :all :method :otp))
|
|
108
|
+
(layout CompletionLayout "Record and sign the completion."
|
|
109
|
+
(step record "Record" (field course) (sign)))
|
|
110
|
+
(interaction CompletionEntry "The worker records the course they finished."
|
|
111
|
+
:actor worker :produces Completion :layout CompletionLayout)
|
|
112
|
+
|
|
113
|
+
(component Training "Track one worker's safety training."
|
|
114
|
+
:key [worker] :inputs {:worker (Id training/worker)}
|
|
115
|
+
(requirement completed
|
|
116
|
+
:cites [(safety-policy "1: every worker completes safety training")]
|
|
117
|
+
:satisfied-by (attested Completion))
|
|
118
|
+
(action complete :advances completed :assignee worker :via (interaction CompletionEntry))
|
|
119
|
+
(output done (all-of completed)))
|
|
120
|
+
|
|
121
|
+
(entity Person "A synthetic worker." :ns :example.person :display name
|
|
122
|
+
:implements {training/worker {:name name}}
|
|
123
|
+
{:name String})
|
|
124
|
+
(account example "A synthetic account." :requires {example.training "1.0"} :package example
|
|
125
|
+
(for-each training/worker Training))
|
|
126
|
+
|
|
127
|
+
(scenario completes "A worker completes training." :world example
|
|
128
|
+
(given (entity Person ana {:name "Ana"}) (actor ana))
|
|
129
|
+
(at "2026-11-02" (expect (status (Training ana) completed) :Open))
|
|
130
|
+
(do (perform (Training ana) complete :by ana :values {:course "Safety 101"}))
|
|
131
|
+
(at "2026-11-03" (expect (status (Training ana) done) :Satisfied)))
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Read it in this order:
|
|
135
|
+
|
|
136
|
+
1. `library` names the release (`example.training` version `"1.0"`) and the role each component's
|
|
137
|
+
subject plays (`:subject`).
|
|
138
|
+
2. `role` declares the fields components may read. Roles store nothing.
|
|
139
|
+
3. `source` is the authority you cite. Every requirement and decision should `:cites` a clause;
|
|
140
|
+
otherwise you get `W_UNCITED`.
|
|
141
|
+
4. `evidence` is a record kept exactly as signed, owned by a component (`:of Training`). `attest`
|
|
142
|
+
says who signs it; `worker` here is the component input of that name.
|
|
143
|
+
5. `layout` and `interaction` are how a person produces that evidence.
|
|
144
|
+
6. `component` is one requirement scope. `:key` says when two runs are the same (one per worker),
|
|
145
|
+
`:inputs` types the key. It holds `requirement`s (what must be true), `action`s (work that
|
|
146
|
+
advances a requirement) and `output`s (named rollups).
|
|
147
|
+
7. `entity` is stored data in the account package; `:implements` maps role fields to entity
|
|
148
|
+
fields. `:ns` must start with the account's `:package` name.
|
|
149
|
+
8. `account` requires the library release and runs `Training` for every `training/worker`.
|
|
150
|
+
9. `scenario` gives synthetic entities and actors, performs work and expects statuses.
|
|
151
|
+
|
|
152
|
+
## 3. Forms reference
|
|
153
|
+
|
|
154
|
+
The [core reference](docs/reference.md) lists syntax, complete examples and common mistakes
|
|
155
|
+
for the fourteen forms needed to start. `worldvm init` includes it as `REFERENCE.md`.
|
|
156
|
+
Options are keyword/value pairs after the docstring; field records and child forms come last.
|
|
157
|
+
|
|
158
|
+
Advanced forms (`derive`, `calendar`, `agent`, `bundle`, `collect`, `connector`, `machine`,
|
|
159
|
+
`document`, `notice`, `vault`, `component-contract`, `component-import`) and component children
|
|
160
|
+
such as `attach`, `child`, `hold`, `invariant`, `milestone`, `resource` and `retention` are
|
|
161
|
+
not fully documented yet; the [complete example](docs/authoring/vendor-risk/) uses several. `worldvm check` lists top-level forms when you misspell one.
|
|
162
|
+
|
|
163
|
+
Who can act: `:assignee` and `:actor` take a component input of a person role (`worker`), a field
|
|
164
|
+
path to one (`vendor.owner`), or `(members group-field)` for everyone in a `Group`. In `attest`,
|
|
165
|
+
`:signer assignee` means whoever performs the action.
|
|
166
|
+
|
|
167
|
+
**Assignees authorize interaction work.** Ordinary `perform` and live recorded-evidence commands
|
|
168
|
+
require the assigned person or a member of the assigned group; account policy restrictions also
|
|
169
|
+
apply. Attestation additionally requires the evidence's declared signer, resolved in the selected
|
|
170
|
+
component occurrence. To expose actions or decisions through the embedding API, list their names in the component's
|
|
171
|
+
`:operations [action-name decision-name]`. This selects callable operations; authority checks
|
|
172
|
+
still apply.
|
|
173
|
+
|
|
174
|
+
Unknown assignments/signers and mismatched identities fail closed
|
|
175
|
+
(`:not-assignee` in scenarios). Unsigned drafts require assignment but do not claim a signature.
|
|
176
|
+
Decisions separately enforce `:by` and `:distinct-from`.
|
|
177
|
+
|
|
178
|
+
Hosts authenticate identities and memberships; WorldVM checks their domain authority. The pure
|
|
179
|
+
in-memory `perform` API treats `by` as a trusted human ID when no `actor` is supplied (no group
|
|
180
|
+
memberships); it is not a public authentication boundary. Public operations require a trusted
|
|
181
|
+
actor. Scenario `(given (evidence ...))` records are trusted historical setup, not live commands.
|
|
182
|
+
|
|
183
|
+
Use `worldvm check . --core` to see `I_EXTENDED_FORM` information for declarations
|
|
184
|
+
outside the ten core forms in [the short reference](docs/reference.md). This mode
|
|
185
|
+
keeps the usual validation and exit status. Editors can enable it with the LSP
|
|
186
|
+
initialization option `core: true`; the check API takes `{ core: true }`.
|
|
187
|
+
|
|
188
|
+
## 4. Types, Truth and comparisons
|
|
189
|
+
|
|
190
|
+
Field types: `String`, `Int`, `Float`, `Bool`, `Date`, `Instant`, `Keyword`, `Group` (a named set
|
|
191
|
+
of actors), `(Id Role-or-Entity)` (a reference), `(Option T)` (may be absent) and
|
|
192
|
+
`(Union :a :b …)` (one of these keywords, usually through `(type Name (Union …))`). Read fields
|
|
193
|
+
through references with dots: `contractor.sponsor.name`.
|
|
194
|
+
|
|
195
|
+
**Conditions are Truth, not Bool.** A world reads facts that may be missing, so `:applies-when`,
|
|
196
|
+
`:satisfied-by`, `:needs`, `:when` and `:while` are three-valued `Truth`: true, false or Unknown
|
|
197
|
+
(a missing fact). Use the Truth operators there:
|
|
198
|
+
|
|
199
|
+
| You mean | Requirement, action, decision (Truth) | Property (also accepted) |
|
|
200
|
+
| --------------- | ----------------------------------------- | ---------------------------- |
|
|
201
|
+
| equal | `(= a b)` | `(= a b)` |
|
|
202
|
+
| not equal | `(!= a b)` | `(!= a b)` |
|
|
203
|
+
| order | `(< a b)` `(<= a b)` `(> a b)` `(>= a b)` | same |
|
|
204
|
+
| not | `(not x)` | `(not x)` |
|
|
205
|
+
| and | `(and x y …)` | `(and x y …)` |
|
|
206
|
+
| or | `(or x y …)` | `(or x y …)` |
|
|
207
|
+
| implies | | `(implies x y)` |
|
|
208
|
+
| fact is present | `(known? x)` | `(known? x)`, `(unknown? x)` |
|
|
209
|
+
| one of | `(in? x [:a :b])` | |
|
|
210
|
+
| dates | `(before? a b)` `(on-or-after? a b)` | `(< a b)` and friends |
|
|
211
|
+
|
|
212
|
+
`and`, `or` and `not` use the type of their context: Kleene Truth in conditions,
|
|
213
|
+
ordinary Bool in properties and Bool tests. `=` and `!=` compare in either context.
|
|
214
|
+
A Bool fact field already reads as a condition: write `contractor.escorted` or
|
|
215
|
+
`(not contractor.escorted)`. `(and)` is true and `(or)` is false; in Truth
|
|
216
|
+
contexts `(and collection)` and `(or collection)` also fold a collection.
|
|
217
|
+
The retired spellings `all?`, `any?`, `negate` and `not=` are errors for one release,
|
|
218
|
+
with machine-applicable fixes to `and`, `or`, `not` and `!=`.
|
|
219
|
+
|
|
220
|
+
Unknown is not false. If `contractor.risk` is missing, `(!= contractor.risk :none)` is Unknown
|
|
221
|
+
and the requirement's status is `Unknown`, so the gap stays visible instead of silently passing.
|
|
222
|
+
Use `(known? x)` when you need to branch on presence.
|
|
223
|
+
|
|
224
|
+
## 5. Evidence
|
|
225
|
+
|
|
226
|
+
Signal-driven protocols use `(machine Protocol … :signal updated …)` and a
|
|
227
|
+
`(connector provider … :capabilities [] :signals [updated] :owns [Protocol])`.
|
|
228
|
+
Declare the signal and machine ownership even for an inbound-only connector.
|
|
229
|
+
`component-contract` may omit `:capabilities`; it defaults to `[]`.
|
|
230
|
+
|
|
231
|
+
The rest of this guide builds one world: contractors need site access. Its library starts with
|
|
232
|
+
roles, a source and the evidence types.
|
|
233
|
+
|
|
234
|
+
```forma world=site file=library/site.forma
|
|
235
|
+
;; Synthetic site-access policy; illustrative, not legal or audit advice.
|
|
236
|
+
(library example.site-access
|
|
237
|
+
"Safety and insurance requirements before a contractor's badge is active."
|
|
238
|
+
:version "1.0" :subject site/contractor)
|
|
239
|
+
|
|
240
|
+
(type RiskLevel (Union :high :standard :none))
|
|
241
|
+
(type BadgeState (Union :pending :active))
|
|
242
|
+
(role site/person "A sponsor, site manager or badge administrator." {:name String})
|
|
243
|
+
(role site/contractor "A contractor with a sponsor, approver groups and a badge."
|
|
244
|
+
{:name String :sponsor (Id site/person) :managers Group :admins Group
|
|
245
|
+
:risk RiskLevel :badge BadgeState :joined Date})
|
|
246
|
+
|
|
247
|
+
(source site-policy "Synthetic site-access policy; not compliance advice."
|
|
248
|
+
:kind :policy :url "https://example.invalid/site-policy" :as-of "2026-11-01")
|
|
249
|
+
|
|
250
|
+
(evidence Induction "A signed safety induction; valid for twelve months from signing."
|
|
251
|
+
:of SiteAccess
|
|
252
|
+
:expires (months-after signed-on 12)
|
|
253
|
+
{:course String}
|
|
254
|
+
(attest :signer contractor :covers :all :method :otp))
|
|
255
|
+
(evidence InsuranceCertificate "Proof of liability insurance with its own end date."
|
|
256
|
+
:of SiteAccess
|
|
257
|
+
:expires valid-until
|
|
258
|
+
{:insurer String :valid-until Date}
|
|
259
|
+
(attest :signer contractor.sponsor :covers :all :method :otp))
|
|
260
|
+
(evidence HazardPermit "A signed permit for hazardous work." :of SiteAccess
|
|
261
|
+
{:scope String}
|
|
262
|
+
(attest :signer contractor.sponsor :covers :all :method :otp))
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
What to know about evidence:
|
|
266
|
+
|
|
267
|
+
- `:of` names the component that owns it; evidence is always recorded for one occurrence of that
|
|
268
|
+
component. Fields are typed like role fields.
|
|
269
|
+
- `(rule name :when condition :requires fields)` requires filled signed fields before
|
|
270
|
+
attestation. The optional `:when` reads evidence fields and uses three-valued Truth:
|
|
271
|
+
false skips the rule, Unknown prevents attestation, and omission always applies.
|
|
272
|
+
`:requires` accepts `field`, `(one-of a b …)` for at least one filled field, or
|
|
273
|
+
`(all-of a b …)` for every listed field. Groups must be nonempty and name declared
|
|
274
|
+
fields. For example, `(rule documents :when (= document-choice :list-b-and-c)
|
|
275
|
+
:requires (all-of list-b list-c))` requires both documents for that signed choice.
|
|
276
|
+
The interaction layout must show required fields when the rule applies; a `one-of`
|
|
277
|
+
group needs a visible alternative.
|
|
278
|
+
- `(attest :signer who :covers :all :method :otp)` declares who signs. The signature date is the
|
|
279
|
+
date the record is made.
|
|
280
|
+
- `:expires` is a Date computed from the signed record. Write a date field (`valid-until`), or
|
|
281
|
+
date arithmetic over fields and `signed-on`, the date the record was signed:
|
|
282
|
+
`(months-after signed-on 12)`. Expiry is exclusive: a record expiring on 2027-11-07 is valid
|
|
283
|
+
through 2027-11-06. It may not read live facts, other evidence or decisions (`E_EXPIRES_SCOPE`).
|
|
284
|
+
- **`(attested E)` vs `(valid E)`.** `attested` is true once a record was ever signed and ignores
|
|
285
|
+
`:expires`. `valid` is true only while the latest signed record is unexpired. For anything that
|
|
286
|
+
expires you almost always want `valid`; `worldvm check` warns `W_ATTESTED_EXPIRING` and offers
|
|
287
|
+
the fix. Use `attested` for evidence without `:expires` (`HazardPermit`).
|
|
288
|
+
- A requirement satisfied by `(valid E)` can add `:renew-within (days 30)` to open renewal work
|
|
289
|
+
before the record expires.
|
|
290
|
+
|
|
291
|
+
## 6. Decisions
|
|
292
|
+
|
|
293
|
+
A decision is a person's recorded judgment. It names the requirement it `:applies` to, who may
|
|
294
|
+
make it, and its effect. `(decided name)` reads it in a condition.
|
|
295
|
+
|
|
296
|
+
```forma world=site file=library/site.forma
|
|
297
|
+
(decision insurance-waiver
|
|
298
|
+
"A site manager may waive insurance with a reason; the waiver lasts six months."
|
|
299
|
+
:of SiteAccess :applies insurance-or-waiver :by (members contractor.managers)
|
|
300
|
+
:effect :approve :reason String :expires (months 6)
|
|
301
|
+
:cites [(site-policy "2: insurance or a manager's time-limited waiver")])
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
- `:by` is who may decide, usually `(members group-field)`. Anyone else is rejected with
|
|
305
|
+
`:decision-authority` (a scenario can expect that, see [section 9](#9-scenarios-and-properties)).
|
|
306
|
+
- `:effect` is `:approve` (read it with `(decided name)` in `:satisfied-by`), `:waive` (the
|
|
307
|
+
requirement's status becomes `Waived` directly), `:reject`, `:defer`, `:accept` or `:add`.
|
|
308
|
+
- `:reason String` makes a reason mandatory. `:expires (months 6)` ends the decision six months
|
|
309
|
+
after it was made; `:until date` ends it on a date.
|
|
310
|
+
- `:distinct-from [contractor.sponsor]` forbids those people from deciding (separation of
|
|
311
|
+
duties); `:approvals 2` needs two distinct deciders; `:when truth` limits when it may be made.
|
|
312
|
+
- **Inside a decision's `:when`, read facts, requirement and output statuses only.**
|
|
313
|
+
`(met? self.<output>)`, `(satisfied? self.<requirement>)` and fact comparisons work there;
|
|
314
|
+
`(valid E)` and `(attested E)` currently evaluate as false with no diagnostic, so the decision
|
|
315
|
+
is always rejected with `:decision-condition`. Read the requirement that uses them instead.
|
|
316
|
+
|
|
317
|
+
## 7. Outputs and NotApplicable
|
|
318
|
+
|
|
319
|
+
Every requirement occurrence has one status: `Open`, `Overdue` (open past `:due`), `Satisfied`,
|
|
320
|
+
`Waived`, `NotApplicable` (its `:applies-when` is false) or `Unknown` (a fact it needs is
|
|
321
|
+
missing).
|
|
322
|
+
|
|
323
|
+
An `output` rolls requirements and other outputs up with `(all-of …)`. **`all-of` treats
|
|
324
|
+
`NotApplicable` as satisfied**, so an output is `Satisfied` when every member is `Satisfied`,
|
|
325
|
+
`Waived` or `NotApplicable` and at least one applies; an output whose members are all
|
|
326
|
+
NotApplicable is itself `NotApplicable`. `(satisfied? x)` is true only for `Satisfied` or `Waived`, so
|
|
327
|
+
`(satisfied? requirement)` on a NotApplicable requirement is false. To ask "is everything that
|
|
328
|
+
applies done?", put the requirements in an output and read it with `(met? self.<output>)`.
|
|
329
|
+
`(met? x)` is true for `Satisfied`, `Waived` or `NotApplicable`, so it also counts "nothing
|
|
330
|
+
applies" as met; `(satisfied? self.<output>)` would be false when every member is
|
|
331
|
+
NotApplicable. `worldvm check` warns about both traps with `W_SATISFIED_NOT_APPLICABLE`.
|
|
332
|
+
|
|
333
|
+
```forma world=site file=library/site.forma
|
|
334
|
+
(component SiteAccess "Clear one contractor for site access and track their badge."
|
|
335
|
+
:key [contractor] :inputs {:contractor (Id site/contractor)}
|
|
336
|
+
(requirement induction
|
|
337
|
+
:cites [(site-policy "1: safety induction within the last twelve months")]
|
|
338
|
+
:applies-when (!= contractor.risk :none)
|
|
339
|
+
:satisfied-by (valid Induction)
|
|
340
|
+
:due (days-after contractor.joined 14))
|
|
341
|
+
(requirement insurance-or-waiver
|
|
342
|
+
:cites [(site-policy "2: insurance or a manager's time-limited waiver")]
|
|
343
|
+
:applies-when (!= contractor.risk :none)
|
|
344
|
+
:satisfied-by (or (valid InsuranceCertificate) (decided insurance-waiver)))
|
|
345
|
+
(requirement hazard-permit
|
|
346
|
+
:cites [(site-policy "3: high-risk work needs a hazard permit")]
|
|
347
|
+
:applies-when (= contractor.risk :high)
|
|
348
|
+
:satisfied-by (attested HazardPermit))
|
|
349
|
+
(requirement badge-activated
|
|
350
|
+
:cites [(site-policy "4: administrators activate badges after clearance")]
|
|
351
|
+
:applies-when (!= contractor.risk :none)
|
|
352
|
+
:satisfied-by (= contractor.badge :active))
|
|
353
|
+
(requirement badge-only-if-cleared
|
|
354
|
+
:cites [(site-policy "4: administrators activate badges after clearance")]
|
|
355
|
+
:applies-when (= contractor.badge :active)
|
|
356
|
+
:satisfied-by (met? self.cleared))
|
|
357
|
+
(invariant no-badge-before-clearance
|
|
358
|
+
:cites [(site-policy "4: administrators activate badges after clearance")]
|
|
359
|
+
:forbids (action-kind :activate :subject contractor)
|
|
360
|
+
:while (not (met? self.cleared)))
|
|
361
|
+
(action induct :advances induction :assignee contractor :via (interaction InductionEntry))
|
|
362
|
+
(action insure :advances insurance-or-waiver :assignee contractor.sponsor
|
|
363
|
+
:via (interaction InsuranceEntry))
|
|
364
|
+
(action permit :advances hazard-permit :assignee contractor.sponsor
|
|
365
|
+
:via (interaction PermitEntry))
|
|
366
|
+
(action activate-badge :advances badge-activated :dispatch :manual
|
|
367
|
+
:needs [(met? self.cleared)]
|
|
368
|
+
:assignee (members contractor.admins) :via (interaction BadgeActivationEntry))
|
|
369
|
+
(output cleared (all-of induction insurance-or-waiver hazard-permit))
|
|
370
|
+
(output access (all-of cleared badge-activated badge-only-if-cleared)))
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
For a standard-risk contractor `hazard-permit` is NotApplicable, so `cleared` is Satisfied once
|
|
374
|
+
the induction and insurance are. `badge-only-if-cleared` is a _detection_: nothing works on it;
|
|
375
|
+
it turns `Open` when someone activates a badge before clearance or when the induction expires
|
|
376
|
+
(signed on 2026-11-04 in the scenario below, it stops counting on 2027-11-04).
|
|
377
|
+
`worldvm check` reports `W_NO_PRODUCER` for it, which is expected for a detection.
|
|
378
|
+
|
|
379
|
+
## 8. Actions, interactions and status changes
|
|
380
|
+
|
|
381
|
+
An `action` is work: it `:advances` one requirement, is offered to its `:assignee`, and is done
|
|
382
|
+
`:via` an interaction (or a connector). `:needs [truth …]` gates it: until every need holds, the
|
|
383
|
+
work waits and performing it is rejected (`:not-eligible`). `:dispatch :manual` means the action
|
|
384
|
+
is available on request rather than pushed to the assignee as soon as the requirement opens.
|
|
385
|
+
|
|
386
|
+
Use `:by` with the stable assigned identity in scenario commands, not its display name. Live
|
|
387
|
+
`(do (evidence ...))` records must match an eligible producing action, evidence type and current
|
|
388
|
+
requirement occurrence, and pass the same assignment/signer checks as `perform`. Historical
|
|
389
|
+
`given` records remain available for importing synthetic prior proof without replaying its work.
|
|
390
|
+
|
|
391
|
+
An `interaction` produces one evidence type. Its `layout` decides what the form shows. Its
|
|
392
|
+
`:proposes {role.field evidence-field}` writes signed values back into the subject's facts when
|
|
393
|
+
the evidence is recorded; that is how a world changes a status.
|
|
394
|
+
|
|
395
|
+
Use `:fixes {:examination :remote}` when an interaction supplies a signed value
|
|
396
|
+
instead of asking the signer. The evidence must declare that field with a matching
|
|
397
|
+
scalar type, such as `(Union :in-person :remote)`. Omit fixed fields from the layout,
|
|
398
|
+
including nested groups; they count toward show coverage. A fixed field cannot also
|
|
399
|
+
use `:binds`, `:prefill` or `:proposes`. Conflicting submissions are refused for both
|
|
400
|
+
drafts and signatures. The stored evidence includes the fixed value, so evidence
|
|
401
|
+
rules and explanations can read it. In-person and remote actions can share one
|
|
402
|
+
evidence type through separate interactions fixing different values; gate the remote
|
|
403
|
+
action with `:needs [employee.employer.everify-enrolled]`.
|
|
404
|
+
|
|
405
|
+
**Modelling "activate".** There is no `set-fact` action. To let administrators activate a badge
|
|
406
|
+
only when the contractor is cleared, record the activation as evidence whose interaction
|
|
407
|
+
proposes the new status, behind an action that `:needs` the clearance (see `activate-badge`
|
|
408
|
+
above):
|
|
409
|
+
|
|
410
|
+
```forma world=site file=library/site.forma
|
|
411
|
+
(evidence BadgeActivation "An administrator's signed badge activation." :of SiteAccess
|
|
412
|
+
{:badge BadgeState}
|
|
413
|
+
(attest :signer assignee :covers :all :method :otp))
|
|
414
|
+
(layout BadgeActivationLayout "Activate the badge."
|
|
415
|
+
(step activate "Activate" (field badge :options {:active "Active"}) (sign)))
|
|
416
|
+
(interaction BadgeActivationEntry "An administrator activates the contractor's badge."
|
|
417
|
+
:actor assignee :produces BadgeActivation :layout BadgeActivationLayout
|
|
418
|
+
:proposes {contractor.badge badge})
|
|
419
|
+
|
|
420
|
+
(layout InductionLayout "Record the induction." (step record "Record" (field course) (sign)))
|
|
421
|
+
(interaction InductionEntry "The contractor completes the safety induction."
|
|
422
|
+
:actor contractor :produces Induction :layout InductionLayout)
|
|
423
|
+
(layout InsuranceLayout "Record the certificate."
|
|
424
|
+
(step record "Record" (field insurer) (field valid-until) (sign)))
|
|
425
|
+
(interaction InsuranceEntry "The sponsor records the insurance certificate."
|
|
426
|
+
:actor contractor.sponsor :produces InsuranceCertificate :layout InsuranceLayout)
|
|
427
|
+
(layout PermitLayout "Record the permit." (step record "Record" (field scope) (sign)))
|
|
428
|
+
(interaction PermitEntry "The sponsor records the hazard permit."
|
|
429
|
+
:actor contractor.sponsor :produces HazardPermit :layout PermitLayout)
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
`:needs` controls _when_ the activation can happen, not _who_ performs it (see
|
|
433
|
+
[section 3](#3-forms-reference)). When only a group may activate, record the activation as a
|
|
434
|
+
decision instead: `:by` enforces who, and `:when` (over requirement statuses, see
|
|
435
|
+
[section 6](#6-decisions)) enforces when. Early or unauthorised attempts are rejected with
|
|
436
|
+
`:decision-condition` or `:decision-authority`.
|
|
437
|
+
|
|
438
|
+
```forma world=site file=library/site.forma
|
|
439
|
+
(decision approve-badge
|
|
440
|
+
"A badge administrator approves activation once the contractor is cleared."
|
|
441
|
+
:of SiteAccess :applies badge-activated :by (members contractor.admins)
|
|
442
|
+
:when (met? self.cleared)
|
|
443
|
+
:effect :approve :reason String
|
|
444
|
+
:cites [(site-policy "4: administrators activate badges after clearance")])
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Detect vs prevent.** `badge-only-if-cleared` _detects_ an active badge without clearance after
|
|
448
|
+
the fact. To _prevent_ the write, the component also declares an `invariant`:
|
|
449
|
+
`:forbids (action-kind :activate :subject contractor)` rejects any command of kind `:activate`
|
|
450
|
+
against that contractor while `:while` holds. A write must be submitted as a command with that
|
|
451
|
+
kind to be checked: in a scenario, `(set c :badge :active :kind :activate :subject c :by "Admin")`
|
|
452
|
+
is rejected with the invariant's name (see `badge-write-blocked` below). Writes that bypass the
|
|
453
|
+
command path, or carry another kind, are not checked, and an Unknown `:while` admits the write,
|
|
454
|
+
so keep the detection too. `worldvm check` suggests this pair (`I_DETECT_ONLY`) for a
|
|
455
|
+
detection-only requirement whose `:applies-when` compares a fact to a keyword.
|
|
456
|
+
|
|
457
|
+
## 9. Scenarios and properties
|
|
458
|
+
|
|
459
|
+
The account package binds entities to the roles and runs the component:
|
|
460
|
+
|
|
461
|
+
```forma world=site file=example.forma
|
|
462
|
+
(entity Person "A synthetic person." :ns :example.person :display name
|
|
463
|
+
:implements {site/person {:name name}} {:name String})
|
|
464
|
+
(entity Contractor "A synthetic contractor." :ns :example.contractor :display name
|
|
465
|
+
:implements {site/contractor {:name name :sponsor sponsor :managers managers :admins admins
|
|
466
|
+
:risk risk :badge badge :joined joined}}
|
|
467
|
+
{:name String :sponsor (Id Person) :managers Group :admins Group :risk RiskLevel
|
|
468
|
+
:badge BadgeState :joined Date})
|
|
469
|
+
(account example "An isolated synthetic site-access account."
|
|
470
|
+
:requires {example.site-access "1.0"} :package example
|
|
471
|
+
(for-each site/contractor SiteAccess))
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
A scenario has three kinds of steps, in any order:
|
|
475
|
+
|
|
476
|
+
- `(given …)` creates synthetic data: `(entity Type alias {fields})` and `(actor alias
|
|
477
|
+
[:groups ["Group"]])`. A `Group` field is a string naming the group; an actor is a member when
|
|
478
|
+
its `:groups` lists that name.
|
|
479
|
+
- `(do …)` performs commands. **Commands take effect at the date of the next `(at …)`**, so
|
|
480
|
+
expiry counts from that date.
|
|
481
|
+
- `(perform (Component alias) action :by actor :values {field value})` does an action's work
|
|
482
|
+
through its interaction (rejected if the action is not eligible).
|
|
483
|
+
- `(evidence Type :for (Component alias) :requirement name :by actor :values {…})` records
|
|
484
|
+
evidence directly.
|
|
485
|
+
- `(decide (Component alias) decision :by actor :reason "…")` records a decision.
|
|
486
|
+
- `(set alias :field value :by "who")` changes a fact. Add `:kind :k :subject alias` to submit
|
|
487
|
+
it as a command of kind `:k`, which invariants can reject.
|
|
488
|
+
- `(at "YYYY-MM-DD" (expect selector expected) …)` checks the world on that date:
|
|
489
|
+
- `(status (Component alias) requirement-or-output)` against `:Open`, `:Overdue`,
|
|
490
|
+
`:Satisfied`, `:Waived`, `:NotApplicable` or `:Unknown`;
|
|
491
|
+
- `(work (Component alias) action)` against `{:state :eligible}` (also `:waiting`,
|
|
492
|
+
`:dispatched`, `:held`, `:absent`);
|
|
493
|
+
- `(due (Component alias) requirement)` against a date;
|
|
494
|
+
- `(rejected n)` against the reason the n-th command of the preceding `do` was refused (table
|
|
495
|
+
below). A rejection you do not assert fails the scenario with `E_SCENARIO_RUN`.
|
|
496
|
+
|
|
497
|
+
Two `(at …)` steps may share a date, for example to assert a rejection and then retry.
|
|
498
|
+
|
|
499
|
+
| Rejection | Meaning |
|
|
500
|
+
| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
|
|
501
|
+
| `:not-eligible` | The action's `:needs` do not hold. |
|
|
502
|
+
| `:decision-authority` | The actor is not in the decision's `:by`. |
|
|
503
|
+
| `:decision-distinct` | The actor is excluded by `:distinct-from`. |
|
|
504
|
+
| `:decision-reason` | `:reason` is required and missing. |
|
|
505
|
+
| `:decision-condition` | The decision's `:when` does not hold. |
|
|
506
|
+
| `:decision-approvals`, `:decision-duplicate` | An approval count is not met, or the same person approved twice. |
|
|
507
|
+
| `:decision-expiry`, `:decision-until`, `:decision-stale`, `:decision-prior`, `:decision-target`, `:decision-state` | The decision's timing, target or prior state does not allow it. |
|
|
508
|
+
| `:evidence-invalid`, `:signature-required` | The evidence values or signature are missing or invalid. |
|
|
509
|
+
| `:no-occurrence` | The component occurrence does not exist (check the subject and its `:applies-when`). |
|
|
510
|
+
| `:command-date` | The command's date is out of order. |
|
|
511
|
+
| the invariant's name, e.g. `:no-badge-before-clearance` | An invariant forbids this command kind while its `:while` holds. |
|
|
512
|
+
|
|
513
|
+
```forma world=site file=scenarios/access.forma
|
|
514
|
+
;; Synthetic data only; illustrative, not compliance advice.
|
|
515
|
+
(scenario standard-clearance "A standard-risk contractor is cleared, then activated."
|
|
516
|
+
:world example
|
|
517
|
+
(given (entity Person sponsor {:name "Sponsor"}) (entity Person admin {:name "Admin"})
|
|
518
|
+
(entity Contractor c {:name "Rivera Electrical" :sponsor sponsor :managers "Managers"
|
|
519
|
+
:admins "Badge admins" :risk :standard :badge :pending
|
|
520
|
+
:joined "2026-11-02"})
|
|
521
|
+
(actor c) (actor sponsor) (actor admin :groups ["Badge admins"]))
|
|
522
|
+
(at "2026-11-02"
|
|
523
|
+
(expect (status (SiteAccess c) induction) :Open)
|
|
524
|
+
(expect (status (SiteAccess c) hazard-permit) :NotApplicable)
|
|
525
|
+
(expect (work (SiteAccess c) activate-badge) {:state :waiting}))
|
|
526
|
+
(do (perform (SiteAccess c) activate-badge :by admin :values {:badge :active}))
|
|
527
|
+
(at "2026-11-03" (expect (rejected 1) :not-eligible))
|
|
528
|
+
(do (perform (SiteAccess c) induct :by c :values {:course "Site safety"})
|
|
529
|
+
(perform (SiteAccess c) insure :by sponsor
|
|
530
|
+
:values {:insurer "Synthetic Mutual" :valid-until "2027-12-31"}))
|
|
531
|
+
(at "2026-11-04"
|
|
532
|
+
(expect (status (SiteAccess c) cleared) :Satisfied)
|
|
533
|
+
(expect (work (SiteAccess c) activate-badge) {:state :eligible}))
|
|
534
|
+
(do (perform (SiteAccess c) activate-badge :by admin :values {:badge :active}))
|
|
535
|
+
(at "2026-11-05"
|
|
536
|
+
(expect (status (SiteAccess c) badge-activated) :Satisfied)
|
|
537
|
+
(expect (status (SiteAccess c) access) :Satisfied))
|
|
538
|
+
(at "2027-11-03" (expect (status (SiteAccess c) access) :Satisfied))
|
|
539
|
+
(at "2027-11-04"
|
|
540
|
+
(expect (status (SiteAccess c) induction) :Overdue)
|
|
541
|
+
(expect (status (SiteAccess c) badge-only-if-cleared) :Open)))
|
|
542
|
+
|
|
543
|
+
(scenario manager-waiver "Only a manager waives insurance, and the waiver lasts six months."
|
|
544
|
+
:world example
|
|
545
|
+
(given (entity Person sponsor {:name "Sponsor"}) (entity Person manager {:name "Manager"})
|
|
546
|
+
(entity Contractor c {:name "Okafor Scaffolds" :sponsor sponsor :managers "Managers"
|
|
547
|
+
:admins "Badge admins" :risk :high :badge :pending
|
|
548
|
+
:joined "2026-11-02"})
|
|
549
|
+
(actor sponsor) (actor manager :groups ["Managers"]))
|
|
550
|
+
(do (decide (SiteAccess c) insurance-waiver :by sponsor :reason "Trust me"))
|
|
551
|
+
(at "2026-11-02"
|
|
552
|
+
(expect (rejected 1) :decision-authority)
|
|
553
|
+
(expect (status (SiteAccess c) hazard-permit) :Open))
|
|
554
|
+
(do (decide (SiteAccess c) insurance-waiver :by manager :reason "Certificate in transit"))
|
|
555
|
+
(at "2026-11-03" (expect (status (SiteAccess c) insurance-or-waiver) :Satisfied))
|
|
556
|
+
(at "2027-05-02" (expect (status (SiteAccess c) insurance-or-waiver) :Satisfied))
|
|
557
|
+
(at "2027-05-03" (expect (status (SiteAccess c) insurance-or-waiver) :Open)))
|
|
558
|
+
|
|
559
|
+
(scenario no-site-work "A contractor who never comes on site has no requirements." :world example
|
|
560
|
+
(given (entity Person sponsor {:name "Sponsor"})
|
|
561
|
+
(entity Contractor c {:name "Remote Design" :sponsor sponsor :managers "Managers"
|
|
562
|
+
:admins "Badge admins" :risk :none :badge :pending
|
|
563
|
+
:joined "2026-11-02"}))
|
|
564
|
+
(at "2026-11-02" (expect (status (SiteAccess c) access) :NotApplicable)))
|
|
565
|
+
|
|
566
|
+
(scenario badge-write-blocked "An activation submitted before clearance is rejected." :world example
|
|
567
|
+
(given (entity Person sponsor {:name "Sponsor"})
|
|
568
|
+
(entity Contractor c {:name "Rivera Electrical" :sponsor sponsor :managers "Managers"
|
|
569
|
+
:admins "Badge admins" :risk :standard :badge :pending
|
|
570
|
+
:joined "2026-11-02"}))
|
|
571
|
+
(do (set c :badge :active :kind :activate :subject c :by "Admin"))
|
|
572
|
+
(at "2026-11-02"
|
|
573
|
+
(expect (rejected 1) :no-badge-before-clearance)
|
|
574
|
+
(expect (status (SiteAccess c) badge-only-if-cleared) :NotApplicable)))
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
A `property` claims something for every generated entity of a type. `worldvm test` builds
|
|
578
|
+
bounded synthetic worlds from a seed and checks the claim on the `:at` date; false and Unknown
|
|
579
|
+
both fail, and a failure prints a replay scenario. Properties use the property operators from
|
|
580
|
+
[section 4](#4-types-truth-and-comparisons), read facts through the bound name, and select
|
|
581
|
+
statuses with `(status (Component x) name)` and evidence with `(attested (Component x) Type)`.
|
|
582
|
+
|
|
583
|
+
Each `implies` reports how many generated cases made its antecedent true, once per
|
|
584
|
+
case regardless of how many entities matched. Zero produces `W_PROPERTY_VACUOUS`
|
|
585
|
+
in `test` and `coverage`, in text and JSON. Vacuity warns without failing the command;
|
|
586
|
+
false and Unknown results still fail. A passing claim with zero matches has not
|
|
587
|
+
tested its consequent.
|
|
588
|
+
|
|
589
|
+
To reach a claim's preconditions, put an optional scenario-style `(given …)` block
|
|
590
|
+
between `:for-all` and the predicate. It accepts entity, evidence and signal records
|
|
591
|
+
and must declare the quantified alias with its entity type. For example, a property
|
|
592
|
+
quantifying `(p Person)` can seed `(given (entity Person p {:name "Synthetic worker"})
|
|
593
|
+
(evidence Completion :for (Training p) :requirement completed :by p
|
|
594
|
+
:values {:course "Safety 101"}))`. Every case preserves explicit seed facts and
|
|
595
|
+
records while varying unspecified fields; random history cannot replace a seeded
|
|
596
|
+
evidence type or machine on the same occurrence. `given` is trusted history, as in
|
|
597
|
+
scenarios. Use `do` in a scenario to test whether an action is permitted.
|
|
598
|
+
Counterexample shrinking preserves the authored seed facts and records.
|
|
599
|
+
|
|
600
|
+
```forma world=site file=scenarios/access.forma
|
|
601
|
+
(property active-badge-means-inducted
|
|
602
|
+
"A contractor whose access is satisfied with an active badge has an induction on file."
|
|
603
|
+
:world example :at "2026-11-02" :for-all (c Contractor)
|
|
604
|
+
(implies (and (= c.badge :active) (!= c.risk :none)
|
|
605
|
+
(= (status (SiteAccess c) access) :Satisfied))
|
|
606
|
+
(attested (SiteAccess c) Induction)))
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## 10. Diagnostics catalogue
|
|
610
|
+
|
|
611
|
+
Every diagnostic has a code, a location, `help` and, when the repair is mechanical, a `fix`.
|
|
612
|
+
Errors stop `check`; warnings (`W_…`) and info (`I_…`) are advice. The CLI owns the catalogue:
|
|
613
|
+
|
|
614
|
+
```sh
|
|
615
|
+
worldvm explain --list
|
|
616
|
+
worldvm explain W_ATTESTED_EXPIRING
|
|
617
|
+
worldvm explain E_NO_HOME
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
Start with the located help, repair the smallest reported declaration, then rerun `check`.
|
|
621
|
+
Use `valid` for expiring evidence (`W_ATTESTED_EXPIRING`), and `met?` when NotApplicable
|
|
622
|
+
should count as done (`W_SATISFIED_NOT_APPLICABLE`). `W_NO_PRODUCER` asks whether a rule
|
|
623
|
+
needs work to resolve it; a detection-only rule can legitimately have no action. `W_NO_SCENARIO`
|
|
624
|
+
asks for an observed status assertion. A reference alone does not demonstrate the outcome.
|
|
625
|
+
|
|
626
|
+
The catalogue below quotes diagnostic messages verbatim, including internal routing terminology.
|
|
627
|
+
Use `worldvm explain <code>` for details, or `worldvm explain --list` for every code.
|
|
628
|
+
|
|
629
|
+
<!-- catalogue:start -->
|
|
630
|
+
|
|
631
|
+
| Code | Meaning | Usual cause | Fix |
|
|
632
|
+
| ---------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
633
|
+
| `E_ADVANCES` | An action's :advances is malformed | :advances must name one requirement or a vector of distinct requirement names. | Use one declared requirement name or a nonempty vector of distinct local names. |
|
|
634
|
+
| `E_ARTIFACT_UNTYPED_RUNTIME_DECLARATION` | Compiler diagnostic (artifact/untyped-runtime-declaration) | The compiler reported E_ARTIFACT_UNTYPED_RUNTIME_DECLARATION, formerly artifact/untyped-runtime-declaration. | Review this declaration and correct the reported syntax, type or reference. |
|
|
635
|
+
| `E_CHECK_THROW` | The compiler threw | An internal compiler failure, not an authoring error. | Reduce the failing source to the smallest example and report it. |
|
|
636
|
+
| `E_CITATION` | A :cites value is malformed | :cites must be a vector of (source "clause") pairs. | Write :cites [(brief-policy "1: clause text")]. |
|
|
637
|
+
| `E_DECISION_EFFECT` | Unsupported decision effect | Decision :effect must be one of the kernel effects. | Choose :approve, :reject, :waive, :defer, :accept or :add. |
|
|
638
|
+
| `E_DECISION_REASON` | Unsupported decision reason type | :reason must be a scalar, a keyword union, or an Option/alias of one. | Use :reason String (or a keyword union); structured records belong in evidence. |
|
|
639
|
+
| `E_DECISION_TARGET` | A decision applies to something its component does not own | :applies names a requirement not declared in the :of component. | Name a requirement declared in the :of component. |
|
|
640
|
+
| `E_ELABORATE_ACTION_DEFINITION` | Compiler diagnostic (elaborate/action-definition) | The compiler reported E_ELABORATE_ACTION_DEFINITION, formerly elaborate/action-definition. | Review this declaration and correct the reported syntax, type or reference. |
|
|
641
|
+
| `E_ELABORATE_CONSTRUCT_FAILED` | Compiler diagnostic (elaborate/construct-failed) | The compiler reported E_ELABORATE_CONSTRUCT_FAILED, formerly elaborate/construct-failed. | Review this declaration and correct the reported syntax, type or reference. |
|
|
642
|
+
| `E_ELABORATE_DUPLICATE_DECLARATION` | Two declarations share a name | Names are global across every .forma file in the world directory. | Rename or delete one declaration. |
|
|
643
|
+
| `E_ELABORATE_EXPANSION_FAILED` | Compiler diagnostic (elaborate/expansion-failed) | The compiler reported E_ELABORATE_EXPANSION_FAILED, formerly elaborate/expansion-failed. | Review this declaration and correct the reported syntax, type or reference. |
|
|
644
|
+
| `E_ELABORATE_FORM_CHECK` | A declaration failed one of its publish checks | The prelude's per-form checks (key closure, unknown fields, and similar) rejected it. | Follow the message; it names the field or child that failed. |
|
|
645
|
+
| `E_ELABORATE_FORM_DEFINITION` | Compiler diagnostic (elaborate/form-definition) | The compiler reported E_ELABORATE_FORM_DEFINITION, formerly elaborate/form-definition. | Review this declaration and correct the reported syntax, type or reference. |
|
|
646
|
+
| `E_ELABORATE_HOLE_TYPE` | An expression has the wrong type for its slot | Most often a Bool where a three-valued Truth is expected: a bare true/false or (if …) in :applies-when/:satisfied-by. Also unbound names and misspelled fields. | Use and, or, not, = and != in every context; collapse Truth to Bool only with (true? x) inside if. |
|
|
647
|
+
| `E_ELABORATE_INVALID_SLOT_VALUE` | An option has a value of the wrong shape | A keyword, vector or map was expected but something else was supplied. | Use the shape named in the message, for example :key [vendor]. |
|
|
648
|
+
| `E_ELABORATE_MALFORMED_FORM` | A form's parts are out of order or malformed | Positional parts (name, docstring, field map) are missing or in the wrong order. | Write (form name "Doc." :option value ... {fields} children...). |
|
|
649
|
+
| `E_ELABORATE_MISSING_IDENTIFIER` | A form is missing its name | The first argument after the form head must be a symbol naming the declaration. | Insert a name: (component VendorReview "Track one vendor." ...). |
|
|
650
|
+
| `E_ELABORATE_MISSING_SLOT` | A required option or part is missing | The form needs a name, docstring, option or field map that was omitted. | Add the missing part named in the message; compare with the minimal world in AUTHORING.md. |
|
|
651
|
+
| `E_ELABORATE_NO_CONSTRUCT_HOOK` | Compiler diagnostic (elaborate/no-construct-hook) | The compiler reported E_ELABORATE_NO_CONSTRUCT_HOOK, formerly elaborate/no-construct-hook. | Review this declaration and correct the reported syntax, type or reference. |
|
|
652
|
+
| `E_ELABORATE_REFERENCE_TYPE` | A reference names the wrong kind of declaration | For example :produces names a role instead of an evidence type, or :of names an entity instead of a component. | Point the option at a declaration of the expected kind (see the message). |
|
|
653
|
+
| `E_ELABORATE_RESULT_TYPE_FAILED` | Compiler diagnostic (elaborate/result-type-failed) | The compiler reported E_ELABORATE_RESULT_TYPE_FAILED, formerly elaborate/result-type-failed. | Review this declaration and correct the reported syntax, type or reference. |
|
|
654
|
+
| `E_ELABORATE_UNKNOWN_FORM` | A top-level form name is not a world declaration | The head of a top-level list is not one of the world forms, or a component/account/evidence child form was written at top level. | Use one of: account, agent, bundle, calendar, collect, component, component-contract, component-import, connector, decision, derive, document, entity, evidence, interaction, layout, library, machine, notice, property, role, scenario, source, type, vault. Child forms such as requirement, action, output and invariant go inside (component ...). |
|
|
655
|
+
| `E_ELABORATE_UNKNOWN_REFERENCE` | A reference names nothing declared | A role, entity, component, evidence, decision or interaction name is misspelled or not declared in any file of the world. | Declare the target or correct the spelling; names are case-sensitive and global to the world directory. |
|
|
656
|
+
| `E_ELABORATE_UNSUPPORTED_FORM` | Compiler diagnostic (elaborate/unsupported-form) | The compiler reported E_ELABORATE_UNSUPPORTED_FORM, formerly elaborate/unsupported-form. | Review this declaration and correct the reported syntax, type or reference. |
|
|
657
|
+
| `E_ELABORATE_VALIDATION_FAILED` | A child form is not valid inside its parent | Usually "Expected child form X-child": a misspelt child inside component, layout, step or evidence. | Help lists the valid children of that parent; correct the child form's head. |
|
|
658
|
+
| `E_EXPIRES_SCOPE` | An evidence :expires reads outside its signed fields | Expiry may read only the evidence's own signed fields, signed-on (the signing date) and pure date logic. | Compute expiry from the signing date, :expires (months-after signed-on 12), or from a signed date field, :expires valid-until. |
|
|
659
|
+
| `E_FIXES_CONFLICT` | A fixed field has another value source | A field is fixed more than once or also uses binds, prefill or proposes. | Give each fixed field exactly one literal value source. |
|
|
660
|
+
| `E_FIXES_LAYOUT` | A layout shows an interaction fixed field | The interaction supplies this value, so the signer must not be prompted for it. | Remove the fixed field from the layout, including nested groups. |
|
|
661
|
+
| `E_FIXES_SHAPE` | Interaction fixed values need a map | :fixes accepts evidence field names and scalar literals. | Use :fixes {:examination :remote}. |
|
|
662
|
+
| `E_FIXES_TYPE` | A fixed value has the wrong evidence field type | The literal does not fit the declared field, including keyword union membership. | Use a scalar literal of the evidence field's declared type. |
|
|
663
|
+
| `E_KEY_NOT_INPUT` | A :key name is not an input | Every :key entry must also appear in :inputs. | Rename the key or the input so they match. |
|
|
664
|
+
| `E_LINK` | An account does not link against its library | The account's :requires names a library/version that is not declared, or an entity does not :implements every role field the library needs. | Match :requires to the (library … :version …) declaration and map every role field in the entity's :implements. |
|
|
665
|
+
| `E_LOWER` | An expression cannot be lowered to the kernel | A name is not in scope, a dotted path names no field, or a call is neither a kernel function nor a derive. | Use kernel functions only: and/or/not for logic, in? for membership; declare a (derive ...) for reusable logic. |
|
|
666
|
+
| `E_MISSING_DOC` | A published declaration has no docstring | Libraries, roles, entities, components, evidence, decisions, scenarios and properties need a nonempty docstring. | Add a docstring right after the name: (component VendorReview "Track one vendor." ...). |
|
|
667
|
+
| `E_NO_HOME` | A component has no entity home | The first :key input must be an entity reference (Id E), directly or through another component. | Put an (Id role) input first in :key, for example :key [vendor]. |
|
|
668
|
+
| `E_NO_SOURCES` | The directory has no .forma files | check/test/coverage compile every visible .forma file under the directory. | Point at the world directory, or scaffold one with worldvm init <dir>. |
|
|
669
|
+
| `E_OUTPUT` | An output expression is not supported | Outputs combine statuses; they are not general expressions. | Write (output name (all-of a b)), (any-of a b) or (status-of a). |
|
|
670
|
+
| `E_PARSE_SYNTAX` | Compiler diagnostic (parse/syntax) | The compiler reported E_PARSE_SYNTAX, formerly parse/syntax. | Review this declaration and correct the reported syntax, type or reference. |
|
|
671
|
+
| `E_PROJECT_IO` | The directory cannot be read | The path does not exist or is not readable. | Pass a readable directory containing .forma source files. |
|
|
672
|
+
| `E_PROPERTY_ASSERT` | A property was false or Unknown for a generated case | The predicate failed; the report includes a shrunk counterexample scenario. | Replay the counterexample source; then correct the rule, or narrow the property with implies/known?. |
|
|
673
|
+
| `E_PROPERTY_DATE` | A property :at is not a real date | Properties never read the system clock; :at must be a fixed date or zoned instant. | Use YYYY-MM-DD, e.g. :at "2026-11-06". |
|
|
674
|
+
| `E_PROPERTY_REF` | A property names an unknown world, binding, field or clause | The :world account, :for-all entity, a field path or a status/evidence name is not declared. | Use an entity in this account package, its declared fields, and a component requirement/output or evidence type. |
|
|
675
|
+
| `E_PROPERTY_SHAPE` | A property expression uses an unsupported form | Properties are closed finite predicates with their own small operator set; runtime vocabulary (valid, decided, days-after …) is not available there. | Use :world, :at and one :for-all (alias Entity), then implies, and, or, not, =, !=, <, <=, >, >=, known?, unknown?, satisfied?, not-applicable?, (status (C alias) name), (attested (C alias) Evidence), work, due, occurrence-count. |
|
|
676
|
+
| `E_PROPERTY_TYPE` | A property operand has the wrong type | Comparison of incompatible values, or a non-boolean under and/or/implies. | Compare like with like (keywords as :high, dates as "YYYY-MM-DD"); logic needs booleans; status predicates need (status …). |
|
|
677
|
+
| `E_READ_OUTSIDE_KEY` | An expression reads a non-key input | Shared/keyed components and decisions may only read key inputs and their reference paths. | Read only key inputs and their reference paths, or include this input in the component key. |
|
|
678
|
+
| `E_READ_SYNTAX` | The file does not parse | Unbalanced parentheses/brackets, an unterminated string or a stray character. | Balance every ( [ { with its closer and close every string; the location points at the first unreadable token. |
|
|
679
|
+
| `E_RENAMED_OPERATOR` | An operator was renamed | The source uses all?, any?, negate or not=. | Use and, or, not and !=. Apply the diagnostic's machine fix; and/or take positional operands or a collection. |
|
|
680
|
+
| `E_ROLE_TYPE_MISMATCH` | A mapped entity field has the wrong type | The entity field bound to a role field has a different type. | Make the types match, using (Id Entity) where the role has (Id role). |
|
|
681
|
+
| `E_SCENARIO_ASSERT` | A scenario expectation did not hold | The observed status, work, rejection or property differed from (expect …). | Inspect the expected/actual values; correct the rule if the example is right, or the example if the rule is right. |
|
|
682
|
+
| `E_SCENARIO_DATE` | A scenario date is invalid or goes backwards | Observation dates must be real dates and nondecreasing. | Use YYYY-MM-DD dates in nondecreasing order. |
|
|
683
|
+
| `E_SCENARIO_NOT_FOUND` | --scenario names no scenario | The name passed to worldvm test --scenario is not declared. | Use one of the names listed in the help. |
|
|
684
|
+
| `E_SCENARIO_REF` | A scenario names something undeclared | An entity alias, account, component, requirement or property is unknown in this scenario. | Use names declared in this world, and declare local aliases in given. |
|
|
685
|
+
| `E_SCENARIO_RUN` | A scenario command could not run | Most often "Unasserted command rejection": a do command was rejected and no (expect (rejected n) …) follows. Also commands that reference state the scenario did not create. | Assert the rejection with (expect (rejected 1) :decision-authority), or fix the command; AUTHORING.md section 9 lists the rejection codes. |
|
|
686
|
+
| `E_SCENARIO_SHAPE` | A scenario block is malformed | Scenarios are (scenario name "Doc." :world account (given …) (do …) (at "date" (expect …)) …). | Use finite given entity/evidence/signal records, do commands, and dated at/expect blocks. |
|
|
687
|
+
| `E_SCENARIO_TYPE` | A scenario value has the wrong type | A literal does not match the declared field or key type. | Use a literal of the declared type (:high for a keyword union, "YYYY-MM-DD" for Date). |
|
|
688
|
+
| `E_SELF_APPROVAL` | The decision authority is excluded from signing | :by names exactly the principal that :distinct-from excludes. | Choose a different principal or a reviewer group. |
|
|
689
|
+
| `E_SOURCE_DATE` | A source :as-of is not a real YYYY-MM-DD date | Dates are zero-padded ISO calendar dates. | Pad the date ("2026-01-02"); a machine fix is offered when the date is otherwise valid. |
|
|
690
|
+
| `E_SOURCE_KIND` | A source has an unsupported :kind | Only a fixed set of source kinds is accepted. | Choose :law, :regulation, :guidance, :framework, :contract or :policy. |
|
|
691
|
+
| `E_SURFACE_INVALID_FORM` | Compiler diagnostic (surface/invalid-form) | The compiler reported E_SURFACE_INVALID_FORM, formerly surface/invalid-form. | Review this declaration and correct the reported syntax, type or reference. |
|
|
692
|
+
| `E_TABLE_NOT_EXHAUSTIVE` | A table does not cover every keyword | A keyword-union selector has a value with no row. | Cover every value of the union, or add :else. |
|
|
693
|
+
| `E_TABLE_SHAPE` | A (table …) is malformed | Tables select on a keyword parameter of a derive. | Use (table keyword-parameter :value result ... :else fallback) inside a derive. |
|
|
694
|
+
| `E_TEST_RUNTIME` | The test engine threw | An internal failure while running a scenario or property. | Reduce the failing scenario/property to its source example and report it. |
|
|
695
|
+
| `E_UNBOUNDED_TIME` | A time expression has no finite bound | Clock reads must compare against a fact-based cutoff so the reconciler can schedule wakeups. | Shift a fact-based date, not the clock: :due (days-after vendor.opened 3). |
|
|
696
|
+
| `E_UNBOUND_ROLE_FIELD` | An entity does not map a role field | The entity's :implements omits a field the role declares. | Map every role field: {role-field entity-field}. |
|
|
697
|
+
| `E_UNKNOWN_FIELD` | A field name is not declared | A rule, layout or binding names a field missing from the evidence or role. | Use a declared evidence or role field name. |
|
|
698
|
+
| `E_UNKNOWN_REQUIREMENT` | A name refers to a requirement that is not declared here | An action's :advances, an output or a decision's :applies names a requirement not declared in the same component. | Name only requirements declared in the same component. |
|
|
699
|
+
| `E_UNKNOWN_SOURCE` | A citation names an undeclared source | :cites refers to a source that has no (source ...) declaration. | Declare the source with (source name ...), then cite its specific clause. |
|
|
700
|
+
| `E_USAGE` | Invalid command line | Unknown subcommand, missing directory or an option the subcommand does not accept. | Run worldvm --help or worldvm <command> --help. |
|
|
701
|
+
| `I_DETECT_ONLY` | This requirement detects a state it could prevent | The requirement has no producer and applies when a fact field has a particular value, so it only flags the state after the write. Nothing blocks the write itself. | Keep the detection, and add an invariant that rejects the labelled write while the review is incomplete: (invariant no-activation-before-review :forbids (action-kind :activate :subject vendor) :while (not (met? self.review-complete))). Scenarios exercise it with (set v :status :active :kind :activate :subject v :by buyer) and (expect (rejected 1) :no-activation-before-review). Only writes submitted as commands with that :kind are checked, and an Unknown :while admits the write. |
|
|
702
|
+
| `I_EXTENDED_FORM` | This form is outside the core language | check --core found an extended declaration or child form. | Use the ten core forms when possible, or keep this form and consult AUTHORING.md for its extended semantics. |
|
|
703
|
+
| `W_ATTESTED_EXPIRING` | attested ignores this evidence's :expires | (attested E) is true once any record was signed, even after it expired. The evidence declares :expires, which suggests validity was intended. | Use (valid E), which is false once the signed expiry passes. A machine fix replaces attested with valid. Keep attested only when historical signing is what matters. |
|
|
704
|
+
| `W_CROSS_HOME_READ` | A read crosses into a home not proven equal | A parent reads a child's output through a reference whose home may differ. | Usually advisory; route the read through a child declared with :bind on the same home if possible. |
|
|
705
|
+
| `W_ERASURE_CONFLICT` | Erasure may conflict with retention | A vault allows on-request erasure while a cited retention obligation still applies. | Resolve the retention obligation before shredding. |
|
|
706
|
+
| `W_NO_PRODUCER` | No action advances this requirement | Nothing can be done to satisfy the requirement from inside the world: no (action … :advances r) and no machine signal. Common and fine for detection requirements that flag a state. | If people should act, add (action do-r :advances r :assignee … :via (interaction …)). If it is a detection, keep it in an output and accept this warning; to block the state instead of flagging it, add an (invariant …) (see AUTHORING.md, Detect vs prevent). |
|
|
707
|
+
| `W_NO_SCENARIO` | No scenario observes this requirement | No scenario expects its status, an output containing it, or work/evidence advancing it. | Add a scenario with (expect (status (Component alias) requirement) :Open) and a step that satisfies it; worldvm coverage shows branches actually taken. |
|
|
708
|
+
| `W_NS_DEFAULTED` | An entity's attribute namespace was defaulted | Without :ns, renaming the entity changes every stored attribute name. | Pin it: :ns :example.vendor. |
|
|
709
|
+
| `W_PROPERTY_VACUOUS` | An implication was never exercised | No generated case made this implies antecedent true; passing does not establish its consequent. | Add a property given block with facts and evidence that exercise the antecedent. Vacuity warns without failing test or coverage. |
|
|
710
|
+
| `W_SATISFIED_NOT_APPLICABLE` | satisfied? is false for NotApplicable | (satisfied? r) is true only for Satisfied or Waived. Reading a requirement with :applies-when inside and or :needs means the conjunction can never hold when r does not apply. Reading an all-of output whose members can all be NotApplicable has the same effect: the output is then NotApplicable too. | Use (met? r), which is true for Satisfied, Waived or NotApplicable, on the requirement or the output. The machine fix replaces satisfied? with met?. |
|
|
711
|
+
| `W_SHOW_COVERAGE_UNPROVEN` | Layout coverage of a required field cannot be proven | A rule's predicate reads non-enum fields, so the checker cannot enumerate cases. | Use keyword enums in rule predicates, or show the field unconditionally. |
|
|
712
|
+
| `W_UNCITED` | A requirement/invariant/retention has no citation | Every obligation should say which source clause it implements. | Add :cites [(source-name "clause")]. |
|
|
713
|
+
| `W_UNHANDLED_STATUS` | A custom child-output expression ignores a status | The expression does not say what happens for NotApplicable or Unknown. | Handle NotApplicable/Unknown explicitly or use all-of/any-of. |
|
|
714
|
+
| `W_UNREACHABLE_REQUIREMENT` | Nothing reads this requirement's status | The requirement is in no output and no expression reads it, so it never affects a result. | Add it to an output, e.g. (output review-complete (all-of questionnaire soc2 dpa)), or remove it. A machine fix is offered when the component has exactly one all-of output. |
|
|
715
|
+
|
|
716
|
+
### Renamed diagnostic codes
|
|
717
|
+
|
|
718
|
+
| Old code | New code |
|
|
719
|
+
| -------------------------------------- | ---------------------------------------- |
|
|
720
|
+
| `read/syntax` | `E_READ_SYNTAX` |
|
|
721
|
+
| `parse/syntax` | `E_PARSE_SYNTAX` |
|
|
722
|
+
| `surface/invalid-form` | `E_SURFACE_INVALID_FORM` |
|
|
723
|
+
| `artifact/untyped-runtime-declaration` | `E_ARTIFACT_UNTYPED_RUNTIME_DECLARATION` |
|
|
724
|
+
| `elaborate/unknown-form` | `E_ELABORATE_UNKNOWN_FORM` |
|
|
725
|
+
| `elaborate/hole-type` | `E_ELABORATE_HOLE_TYPE` |
|
|
726
|
+
| `elaborate/unknown-reference` | `E_ELABORATE_UNKNOWN_REFERENCE` |
|
|
727
|
+
| `elaborate/reference-type` | `E_ELABORATE_REFERENCE_TYPE` |
|
|
728
|
+
| `elaborate/missing-slot` | `E_ELABORATE_MISSING_SLOT` |
|
|
729
|
+
| `elaborate/invalid-slot-value` | `E_ELABORATE_INVALID_SLOT_VALUE` |
|
|
730
|
+
| `elaborate/malformed-form` | `E_ELABORATE_MALFORMED_FORM` |
|
|
731
|
+
| `elaborate/duplicate-declaration` | `E_ELABORATE_DUPLICATE_DECLARATION` |
|
|
732
|
+
| `elaborate/form-check` | `E_ELABORATE_FORM_CHECK` |
|
|
733
|
+
| `elaborate/missing-identifier` | `E_ELABORATE_MISSING_IDENTIFIER` |
|
|
734
|
+
| `elaborate/validation-failed` | `E_ELABORATE_VALIDATION_FAILED` |
|
|
735
|
+
| `elaborate/action-definition` | `E_ELABORATE_ACTION_DEFINITION` |
|
|
736
|
+
| `elaborate/construct-failed` | `E_ELABORATE_CONSTRUCT_FAILED` |
|
|
737
|
+
| `elaborate/expansion-failed` | `E_ELABORATE_EXPANSION_FAILED` |
|
|
738
|
+
| `elaborate/form-definition` | `E_ELABORATE_FORM_DEFINITION` |
|
|
739
|
+
| `elaborate/no-construct-hook` | `E_ELABORATE_NO_CONSTRUCT_HOOK` |
|
|
740
|
+
| `elaborate/result-type-failed` | `E_ELABORATE_RESULT_TYPE_FAILED` |
|
|
741
|
+
| `elaborate/unsupported-form` | `E_ELABORATE_UNSUPPORTED_FORM` |
|
|
742
|
+
|
|
743
|
+
<!-- catalogue:end -->
|
|
744
|
+
|
|
745
|
+
```forma broken=W_ATTESTED_EXPIRING
|
|
746
|
+
(library example.expiring "attested ignores expiry." :version "1.0" :subject expiring/item)
|
|
747
|
+
(role expiring/item "An item with an owner." {:owner (Id expiring/item)})
|
|
748
|
+
(evidence Licence "A licence with an end date." :of Expiring :expires valid-until
|
|
749
|
+
{:valid-until Date}
|
|
750
|
+
(attest :signer item.owner :covers :all :method :otp))
|
|
751
|
+
(component Expiring "Warns: attested stays true after expiry." :key [item]
|
|
752
|
+
:inputs {:item (Id expiring/item)}
|
|
753
|
+
(requirement licensed :satisfied-by (attested Licence))
|
|
754
|
+
(output done (all-of licensed)))
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
```forma broken=E_ELABORATE_UNKNOWN_FORM
|
|
758
|
+
(requirment stray :satisfied-by true)
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
## 11. Units and dates
|
|
762
|
+
|
|
763
|
+
- Dates are strings `"YYYY-MM-DD"`; instants are zoned RFC 3339 strings such as
|
|
764
|
+
`"2026-11-02T09:00:00Z"`, or `(instant "…")` in expressions. `today` is the evaluation date and
|
|
765
|
+
`now` the evaluation instant; the CLI never reads the system clock.
|
|
766
|
+
- Durations: `(days n)`, `(months n)`, `(years n)`, `(hours n)`, `(minutes n)`.
|
|
767
|
+
- Date arithmetic: `(days-after d n)`, `(days-before d n)`, `(months-after d n)`,
|
|
768
|
+
`(years-after d n)`, `(business-days-after d n :calendar cal)`, `(hours-after i n)`,
|
|
769
|
+
`(minutes-after i n)`, `(earliest [d …])`, `(latest [d …])`.
|
|
770
|
+
- Months keep the day of month and clamp to the end of shorter months: January 31 plus one month
|
|
771
|
+
is February 28 (29 in a leap year). Years clamp February 29 to February 28.
|
|
772
|
+
- Expiry relative to signing: `:expires (months-after signed-on 12)` on evidence. Relative to a
|
|
773
|
+
decision: `:expires (months 6)` on the decision. Both are exclusive: the record or decision
|
|
774
|
+
stops counting on that date.
|
|
775
|
+
- `:recur {:every …}`, `:renew-within`, `:reuse :within`, collection `:confirm-after`, escalation
|
|
776
|
+
delays and policy parameter bounds count exact days, years, hours or minutes and reject months.
|
|
777
|
+
Use `(days 30)` or `(years 1)` there.
|
|
778
|
+
- `:due` is a date (or instant); a requirement open after its due date is `Overdue`.
|
|
779
|
+
- A signal-driven case can use `:due (business-days-after (entered-at case :referred) 10 :calendar us-federal)`
|
|
780
|
+
with a declared calendar. `case` must be a machine declared in the current component;
|
|
781
|
+
connector ownership is allowed and no resource declaration is needed. `entered-at` returns
|
|
782
|
+
the UTC Date of the first observation in the latest visit to the state. Consecutive duplicate
|
|
783
|
+
states keep that date; leaving and re-entering resets it. After departure the last entry date
|
|
784
|
+
remains available. Before an observed entry it is Unknown. Managed-resource machines read only
|
|
785
|
+
the current acquisition generation. `resource-generation` and `attested-for` remain resource-only.
|
|
786
|
+
|
|
787
|
+
## 12. Idioms and anti-patterns
|
|
788
|
+
|
|
789
|
+
- Type the library against roles, never entities. Entities live only in the account package.
|
|
790
|
+
- One component per subject and requirement scope; use `:key` for identity, not a name string.
|
|
791
|
+
- Model flags as keyword unions; avoid comparing a Bool field with `true`.
|
|
792
|
+
- Use `valid` for anything with an expiry; keep `attested` for history.
|
|
793
|
+
- Combine requirements with an output and read it with `(met? self.<output>)`, never by
|
|
794
|
+
conjoining `satisfied?` of requirements that can be NotApplicable.
|
|
795
|
+
- To stop a write rather than flag it, pair the detection with an `invariant` over a labelled
|
|
796
|
+
command (section 8).
|
|
797
|
+
- Cite a clause on every requirement and decision; keep sources synthetic and say so.
|
|
798
|
+
- Every requirement deserves a scenario that shows it Open and one that shows it closed. Run
|
|
799
|
+
`worldvm coverage` to find the ones you missed, then `worldvm coverage --mutate`: each
|
|
800
|
+
surviving mutant (for example `valid` → `attested` still passing) is behaviour no test pins
|
|
801
|
+
down.
|
|
802
|
+
- Decisions are made by people. Agents may draft or recommend, never sign or
|
|
803
|
+
decide.
|
|
804
|
+
|
|
805
|
+
## 13. Recipes and a complete example
|
|
806
|
+
|
|
807
|
+
**Renewal.** `:renew-within (days N)` on a requirement satisfied by `(valid E)` opens replacement
|
|
808
|
+
work N days before the record expires, while the requirement stays Satisfied:
|
|
809
|
+
|
|
810
|
+
```forma excerpt
|
|
811
|
+
(requirement questionnaire
|
|
812
|
+
:cites [(vendor-policy "1: completed security questionnaire, renewed every 12 months")]
|
|
813
|
+
:applies-when (!= vendor.tier :none)
|
|
814
|
+
:satisfied-by (valid SecurityQuestionnaire)
|
|
815
|
+
:renew-within (days 30))
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
**Exception with expiry.** Satisfy a requirement by evidence or by a decision, and give the
|
|
819
|
+
decision `:by`, `:reason` and `:expires`:
|
|
820
|
+
|
|
821
|
+
```forma excerpt
|
|
822
|
+
(decision approve-exception
|
|
823
|
+
"Only a CISO approves a SOC 2 exception, with a reason; it lasts six months."
|
|
824
|
+
:of VendorReview :applies soc2-or-exception :by (members vendor.cisos)
|
|
825
|
+
:effect :approve :reason String :expires (months 6)
|
|
826
|
+
:cites [(vendor-policy "5: CISO-only exceptions with a reason, expiring after six months")])
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
These excerpts come from [`docs/authoring/vendor-risk`](docs/authoring/vendor-risk/), a complete
|
|
830
|
+
vendor-review world: 12-month questionnaires from `signed-on`, six-month CISO exceptions, a DPA
|
|
831
|
+
for high-risk vendors, and an activation invariant next to its detection. It passes `check` and
|
|
832
|
+
`test`, and `coverage --mutate` leaves no surviving mutant. `eval/` grades new cold-author worlds
|
|
833
|
+
against the same brief.
|