eval-quality 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/LICENSE +190 -0
- package/README.md +355 -0
- package/corpus/dev/README.md +36 -0
- package/corpus/dev/compile-seal-example/brief.json +1 -0
- package/corpus/dev/compile-seal-example/contract.json +1 -0
- package/corpus/dev/contracts/absent-collection-locations.json +1 -0
- package/corpus/dev/contracts/absent-sibling-groups.json +1 -0
- package/corpus/dev/contracts/absent-success-indicator.json +1 -0
- package/corpus/dev/contracts/empty-channel-roles.json +1 -0
- package/corpus/dev/contracts/empty-collection-locations.json +1 -0
- package/corpus/dev/contracts/empty-request-shapes.json +1 -0
- package/corpus/dev/contracts/empty-sibling-groups.json +1 -0
- package/corpus/dev/contracts/no-collection-quantifier.json +1 -0
- package/corpus/dev/contracts/no-operation-inventory.json +1 -0
- package/corpus/dev/contracts/no-read-back-relation.json +1 -0
- package/corpus/dev/contracts/no-state-change-marker.json +1 -0
- package/corpus/dev/contracts/no-type-violating-step.json +1 -0
- package/corpus/dev/contracts/per-key-split-oracles.json +1 -0
- package/corpus/dev/contracts/satisfied-declarations.json +1 -0
- package/corpus/dev/contracts/single-required-response-key.json +1 -0
- package/corpus/dev/contracts/split-indicator-oracle.json +1 -0
- package/corpus/dev/contracts/unaddressed-parameter-sibling.json +1 -0
- package/corpus/dev/contracts/unnamed-reference-set.json +1 -0
- package/corpus/dev/contracts/wrong-cardinality-form.json +1 -0
- package/corpus/dev/index.json +1 -0
- package/dist/adapters/index.d.ts +12 -0
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/local-corpus-adapter.d.ts +7 -0
- package/dist/adapters/local-corpus-adapter.js +78 -0
- package/dist/adapters/node-file-system-adapter.d.ts +7 -0
- package/dist/adapters/node-file-system-adapter.js +39 -0
- package/dist/adapters/port-boundary.d.ts +18 -0
- package/dist/adapters/port-boundary.js +83 -0
- package/dist/adapters/system-clock-adapter.d.ts +10 -0
- package/dist/adapters/system-clock-adapter.js +18 -0
- package/dist/application/compile.d.ts +4 -0
- package/dist/application/compile.js +24 -0
- package/dist/application/diagnostics.d.ts +22 -0
- package/dist/application/diagnostics.js +18 -0
- package/dist/application/index.d.ts +23 -0
- package/dist/application/index.js +17 -0
- package/dist/application/invoke-port.d.ts +2 -0
- package/dist/application/invoke-port.js +45 -0
- package/dist/application/preflight.d.ts +27 -0
- package/dist/application/preflight.js +131 -0
- package/dist/application/seal.d.ts +4 -0
- package/dist/application/seal.js +28 -0
- package/dist/application/serialize.d.ts +6 -0
- package/dist/application/serialize.js +9 -0
- package/dist/cli/arguments.d.ts +28 -0
- package/dist/cli/arguments.js +167 -0
- package/dist/cli/exit-codes.d.ts +36 -0
- package/dist/cli/exit-codes.js +39 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +143 -0
- package/dist/cli/render.d.ts +26 -0
- package/dist/cli/render.js +53 -0
- package/dist/cli/run.d.ts +41 -0
- package/dist/cli/run.js +203 -0
- package/dist/core/canonical/canonicalize.d.ts +1 -0
- package/dist/core/canonical/canonicalize.js +113 -0
- package/dist/core/canonical/digest.d.ts +7 -0
- package/dist/core/canonical/digest.js +81 -0
- package/dist/core/canonical/scan-json.d.ts +1 -0
- package/dist/core/canonical/scan-json.js +298 -0
- package/dist/core/canonical/value-domain.d.ts +4 -0
- package/dist/core/canonical/value-domain.js +113 -0
- package/dist/core/compile/compile.d.ts +24 -0
- package/dist/core/compile/compile.js +67 -0
- package/dist/core/compile/declarations.d.ts +5 -0
- package/dist/core/compile/declarations.js +22 -0
- package/dist/core/compile/expression-legality.d.ts +14 -0
- package/dist/core/compile/expression-legality.js +346 -0
- package/dist/core/compile/forbidden-inputs.d.ts +5 -0
- package/dist/core/compile/forbidden-inputs.js +19 -0
- package/dist/core/compile/interface-inventory.d.ts +10 -0
- package/dist/core/compile/interface-inventory.js +55 -0
- package/dist/core/compile/oracle-alignment.d.ts +14 -0
- package/dist/core/compile/oracle-alignment.js +93 -0
- package/dist/core/compile/reachability.d.ts +17 -0
- package/dist/core/compile/reachability.js +177 -0
- package/dist/core/compile/rubrics.d.ts +55 -0
- package/dist/core/compile/rubrics.js +198 -0
- package/dist/core/compile/scripting-bound.d.ts +5 -0
- package/dist/core/compile/scripting-bound.js +151 -0
- package/dist/core/compile/sensitivity-witness.d.ts +73 -0
- package/dist/core/compile/sensitivity-witness.js +232 -0
- package/dist/core/compile/waivers.d.ts +3 -0
- package/dist/core/compile/waivers.js +21 -0
- package/dist/core/coverage/coverage.d.ts +18 -0
- package/dist/core/coverage/coverage.js +47 -0
- package/dist/core/coverage/relevance.d.ts +75 -0
- package/dist/core/coverage/relevance.js +186 -0
- package/dist/core/coverage/rules.d.ts +7 -0
- package/dist/core/coverage/rules.js +18 -0
- package/dist/core/coverage/satisfaction.d.ts +107 -0
- package/dist/core/coverage/satisfaction.js +415 -0
- package/dist/core/coverage/table.d.ts +26 -0
- package/dist/core/coverage/table.js +222 -0
- package/dist/core/declared-inputs.d.ts +16 -0
- package/dist/core/declared-inputs.js +18 -0
- package/dist/core/evaluate/evidence-resolution.d.ts +54 -0
- package/dist/core/evaluate/evidence-resolution.js +153 -0
- package/dist/core/evaluate/operators.d.ts +87 -0
- package/dist/core/evaluate/operators.js +350 -0
- package/dist/core/evaluate/resolution.d.ts +40 -0
- package/dist/core/evaluate/resolution.js +336 -0
- package/dist/core/evaluate/resolved-value.d.ts +9 -0
- package/dist/core/evaluate/resolved-value.js +6 -0
- package/dist/core/failure-codes.d.ts +18 -0
- package/dist/core/failure-codes.js +50 -0
- package/dist/core/lineage/chain.d.ts +49 -0
- package/dist/core/lineage/chain.js +210 -0
- package/dist/core/lineage/freeze.d.ts +12 -0
- package/dist/core/lineage/freeze.js +35 -0
- package/dist/core/lineage/stage-table.d.ts +43 -0
- package/dist/core/lineage/stage-table.js +106 -0
- package/dist/core/preflight/plan.d.ts +67 -0
- package/dist/core/preflight/plan.js +313 -0
- package/dist/core/preflight/projection.d.ts +40 -0
- package/dist/core/preflight/projection.js +96 -0
- package/dist/core/preflight/reduce.d.ts +8 -0
- package/dist/core/preflight/reduce.js +180 -0
- package/dist/core/preflight/witness-evidence.d.ts +42 -0
- package/dist/core/preflight/witness-evidence.js +88 -0
- package/dist/core/probe/target-policy.d.ts +63 -0
- package/dist/core/probe/target-policy.js +328 -0
- package/dist/core/schemas/artifact-reference.d.ts +19 -0
- package/dist/core/schemas/artifact-reference.js +30 -0
- package/dist/core/schemas/artifact.d.ts +1153 -0
- package/dist/core/schemas/artifact.js +84 -0
- package/dist/core/schemas/constraint-ledger.d.ts +57 -0
- package/dist/core/schemas/constraint-ledger.js +143 -0
- package/dist/core/schemas/eval-contract.d.ts +402 -0
- package/dist/core/schemas/eval-contract.js +147 -0
- package/dist/core/schemas/evaluator-configuration.d.ts +37 -0
- package/dist/core/schemas/evaluator-configuration.js +43 -0
- package/dist/core/schemas/evidence-artifact.d.ts +471 -0
- package/dist/core/schemas/evidence-artifact.js +232 -0
- package/dist/core/schemas/expression.d.ts +197 -0
- package/dist/core/schemas/expression.js +284 -0
- package/dist/core/schemas/faults.d.ts +9 -0
- package/dist/core/schemas/faults.js +32 -0
- package/dist/core/schemas/interface.d.ts +417 -0
- package/dist/core/schemas/interface.js +115 -0
- package/dist/core/schemas/isolation-manifest.d.ts +136 -0
- package/dist/core/schemas/isolation-manifest.js +92 -0
- package/dist/core/schemas/lineage.d.ts +21 -0
- package/dist/core/schemas/lineage.js +28 -0
- package/dist/core/schemas/oracle.d.ts +72 -0
- package/dist/core/schemas/oracle.js +35 -0
- package/dist/core/schemas/plan.d.ts +115 -0
- package/dist/core/schemas/plan.js +58 -0
- package/dist/core/schemas/pointer.d.ts +63 -0
- package/dist/core/schemas/pointer.js +94 -0
- package/dist/core/schemas/port-messages.d.ts +103 -0
- package/dist/core/schemas/port-messages.js +90 -0
- package/dist/core/schemas/preflight-verdict.d.ts +60 -0
- package/dist/core/schemas/preflight-verdict.js +38 -0
- package/dist/core/schemas/primitives.d.ts +78 -0
- package/dist/core/schemas/primitives.js +121 -0
- package/dist/core/schemas/private-artifact-manifest.d.ts +56 -0
- package/dist/core/schemas/private-artifact-manifest.js +46 -0
- package/dist/core/schemas/probe-body.d.ts +25 -0
- package/dist/core/schemas/probe-body.js +23 -0
- package/dist/core/schemas/probe-policy.d.ts +76 -0
- package/dist/core/schemas/probe-policy.js +33 -0
- package/dist/core/schemas/probe.d.ts +189 -0
- package/dist/core/schemas/probe.js +82 -0
- package/dist/core/schemas/publish.d.ts +30 -0
- package/dist/core/schemas/publish.js +135 -0
- package/dist/core/schemas/reference-set.d.ts +18 -0
- package/dist/core/schemas/reference-set.js +27 -0
- package/dist/core/schemas/rubric.d.ts +62 -0
- package/dist/core/schemas/rubric.js +68 -0
- package/dist/core/schemas/scoring-policy.d.ts +28 -0
- package/dist/core/schemas/scoring-policy.js +51 -0
- package/dist/core/schemas/sealed-evaluator-brief.d.ts +80 -0
- package/dist/core/schemas/sealed-evaluator-brief.js +59 -0
- package/dist/core/schemas/sealed-run-record.d.ts +382 -0
- package/dist/core/schemas/sealed-run-record.js +215 -0
- package/dist/core/schemas/sensitivity-witness.d.ts +138 -0
- package/dist/core/schemas/sensitivity-witness.js +86 -0
- package/dist/core/schemas/verdict.d.ts +25 -0
- package/dist/core/schemas/verdict.js +20 -0
- package/dist/core/schemas/waiver.d.ts +17 -0
- package/dist/core/schemas/waiver.js +24 -0
- package/dist/core/seal/derived-reference.d.ts +36 -0
- package/dist/core/seal/derived-reference.js +383 -0
- package/dist/core/seal/direction-prose.d.ts +13 -0
- package/dist/core/seal/direction-prose.js +120 -0
- package/dist/core/seal/plan-index.d.ts +63 -0
- package/dist/core/seal/plan-index.js +155 -0
- package/dist/core/seal/scripting-audit.d.ts +15 -0
- package/dist/core/seal/scripting-audit.js +51 -0
- package/dist/core/seal/seal.d.ts +18 -0
- package/dist/core/seal/seal.js +155 -0
- package/dist/core/stage-contracts.d.ts +30 -0
- package/dist/core/stage-contracts.js +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +22 -0
- package/dist/ports/clock-port.d.ts +13 -0
- package/dist/ports/clock-port.js +7 -0
- package/dist/ports/corpus-port.d.ts +16 -0
- package/dist/ports/corpus-port.js +7 -0
- package/dist/ports/environment-probe-port.d.ts +76 -0
- package/dist/ports/environment-probe-port.js +32 -0
- package/dist/ports/file-system-port.d.ts +27 -0
- package/dist/ports/file-system-port.js +11 -0
- package/dist/ports/port.d.ts +33 -0
- package/dist/ports/port.js +14 -0
- package/dist/testing/conformance.d.ts +88 -0
- package/dist/testing/conformance.js +281 -0
- package/dist/testing/index.d.ts +26 -0
- package/dist/testing/index.js +18 -0
- package/dist/testing/probe-conformance.d.ts +45 -0
- package/dist/testing/probe-conformance.js +186 -0
- package/package.json +130 -0
- package/schemas/artifact-reference.schema.json +64 -0
- package/schemas/eval-contract.schema.json +2145 -0
- package/schemas/evaluator-configuration.schema.json +199 -0
- package/schemas/evidence-artifact.schema.json +1229 -0
- package/schemas/isolation-manifest.schema.json +394 -0
- package/schemas/preflight-verdict.schema.json +114 -0
- package/schemas/private-artifact-manifest.schema.json +103 -0
- package/schemas/probe.schema.json +1151 -0
- package/schemas/rubric.schema.json +148 -0
- package/schemas/scoring-policy.schema.json +90 -0
- package/schemas/sealed-evaluator-brief.schema.json +268 -0
- package/schemas/sealed-run-record.schema.json +875 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { type EvalContract } from '../schemas/eval-contract.ts';
|
|
2
|
+
import type { Expression } from '../schemas/expression.ts';
|
|
3
|
+
import type { Operation } from '../schemas/interface.ts';
|
|
4
|
+
import { type PlanIndex } from '../seal/plan-index.ts';
|
|
5
|
+
import { type DisciplineRule } from './rules.ts';
|
|
6
|
+
export type SatisfactionVerdict = {
|
|
7
|
+
readonly rule: DisciplineRule;
|
|
8
|
+
readonly predicate: string;
|
|
9
|
+
readonly satisfied: boolean;
|
|
10
|
+
/** which site decided it, so a gap is diagnosable. */
|
|
11
|
+
readonly reason: string;
|
|
12
|
+
};
|
|
13
|
+
/** The rule fires on no site, so the universal over its sites holds. */
|
|
14
|
+
export declare const NO_RELEVANT_SITE = "the rule is relevant for no site, so satisfaction holds vacuously";
|
|
15
|
+
/** A contract declaring no operation leaves six of the rules an absent site. */
|
|
16
|
+
export declare const NO_OPERATION_WITNESS = "the contract declares no operation, so the site this rule fires on has no declaration to witness";
|
|
17
|
+
/**
|
|
18
|
+
* One `check` node with its operand pointers resolved through any enclosing
|
|
19
|
+
* quantifier. A connective node (`not`, `all`, `any`) carries neither an
|
|
20
|
+
* operand pointer nor a collection, since its operands are nested expressions
|
|
21
|
+
* that get nodes of their own; it witnesses nothing on its own.
|
|
22
|
+
*/
|
|
23
|
+
type CheckNode = {
|
|
24
|
+
readonly expression: Expression;
|
|
25
|
+
/** one entry per operand position; `null` where the operand carries no pointer. */
|
|
26
|
+
readonly operandPointers: readonly (string | null)[];
|
|
27
|
+
/** a quantifier's collection pointer; `null` on every other node. */
|
|
28
|
+
readonly collection: string | null;
|
|
29
|
+
};
|
|
30
|
+
/** One oracle's two channels, flattened to what the predicates compare. */
|
|
31
|
+
type OracleView = {
|
|
32
|
+
readonly directionTargets: readonly string[];
|
|
33
|
+
readonly nodes: readonly CheckNode[];
|
|
34
|
+
readonly checkPointers: ReadonlySet<string>;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The three derived views the predicates read. `evaluateSatisfaction` builds
|
|
38
|
+
* one and passes it down, so seven predicates do not each rebuild the plan
|
|
39
|
+
* index and re-walk every check tree. A predicate called on its own builds its
|
|
40
|
+
* own, which is what every per-rule fixture does.
|
|
41
|
+
*/
|
|
42
|
+
export type SatisfactionContext = {
|
|
43
|
+
readonly operations: readonly Operation[];
|
|
44
|
+
readonly index: PlanIndex;
|
|
45
|
+
readonly oracles: readonly OracleView[];
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Rule 1: for every operation its relevance predicate fires on, some oracle's
|
|
49
|
+
* direction and check both address that operation's success indicator and a
|
|
50
|
+
* pointer whose declared role is something other than `success-indicator`,
|
|
51
|
+
* read at one step. An operation whose only other roled pointers are themselves
|
|
52
|
+
* indicators is a site with no witness.
|
|
53
|
+
*/
|
|
54
|
+
export declare function successIndicatorSeparationSatisfaction(contract: EvalContract, context?: SatisfactionContext): SatisfactionVerdict;
|
|
55
|
+
/**
|
|
56
|
+
* Rule 2: for every operation declaring more than one distinct required
|
|
57
|
+
* response key, some oracle's direction and check both address every one of
|
|
58
|
+
* those keys at one step invoking that operation. AD-20's denominator is the
|
|
59
|
+
* required-key set of the operation the addressed step invokes.
|
|
60
|
+
*/
|
|
61
|
+
export declare function wholeBodySatisfaction(contract: EvalContract, context?: SatisfactionContext): SatisfactionVerdict;
|
|
62
|
+
/**
|
|
63
|
+
* Rule 3: for every operation declaring a request key, some step invoking it
|
|
64
|
+
* binds AD-39's `type-violating` matcher and some check addresses that step.
|
|
65
|
+
* The matcher is a declaration, so the pair decides the rule without reading
|
|
66
|
+
* prose or a run.
|
|
67
|
+
*/
|
|
68
|
+
export declare function malformedInputSatisfaction(contract: EvalContract, context?: SatisfactionContext): SatisfactionVerdict;
|
|
69
|
+
/**
|
|
70
|
+
* Rule 4: every declared collection location is the collection of some
|
|
71
|
+
* quantifier, read at a step invoking its operation. The comparison is
|
|
72
|
+
* equality: a quantifier one level inside a declared collection ranges over
|
|
73
|
+
* something else. Both quantifiers are admitted, since `not(for-any P)` is the
|
|
74
|
+
* idiomatic spelling of "no element satisfies P".
|
|
75
|
+
*/
|
|
76
|
+
export declare function perRecordSatisfaction(contract: EvalContract, context?: SatisfactionContext): SatisfactionVerdict;
|
|
77
|
+
/**
|
|
78
|
+
* Rule 5: every declared sibling group has an oracle whose direction and check
|
|
79
|
+
* both address two of its members. An operation member is addressed through a
|
|
80
|
+
* step that invokes it; a parameter member through a `call-inputs` pointer on
|
|
81
|
+
* any transport channel. Members are deduplicated first, since `SiblingGroups`
|
|
82
|
+
* carries no uniqueness constraint.
|
|
83
|
+
*/
|
|
84
|
+
export declare function siblingCrossCheckSatisfaction(contract: EvalContract, context?: SatisfactionContext): SatisfactionVerdict;
|
|
85
|
+
/**
|
|
86
|
+
* Rule 6: every collection location naming a reference set is reconciled
|
|
87
|
+
* against it, in the form its declared cardinality mode requires.
|
|
88
|
+
*/
|
|
89
|
+
export declare function omissionAndCompletenessSatisfaction(contract: EvalContract, context?: SatisfactionContext): SatisfactionVerdict;
|
|
90
|
+
/**
|
|
91
|
+
* Rule 7: for every operation declaring `stateChangeMarker: true`, some check
|
|
92
|
+
* node relates a pointer under a step invoking it to a pointer under the
|
|
93
|
+
* response body of a later step whose operation changes no state and whose
|
|
94
|
+
* temporal clause names the write. One node carries the relation: two unrelated
|
|
95
|
+
* assertions under one `all` read nothing back.
|
|
96
|
+
*/
|
|
97
|
+
export declare function stateChangeReadBackSatisfaction(contract: EvalContract, context?: SatisfactionContext): SatisfactionVerdict;
|
|
98
|
+
/**
|
|
99
|
+
* One predicate per rule. The mapped type fails the typecheck when a member of
|
|
100
|
+
* `DISCIPLINE_RULES` has no predicate, matching `RELEVANCE_PREDICATES`.
|
|
101
|
+
*/
|
|
102
|
+
export declare const SATISFACTION_PREDICATES: {
|
|
103
|
+
readonly [Rule in DisciplineRule]: (contract: EvalContract, context?: SatisfactionContext) => SatisfactionVerdict;
|
|
104
|
+
};
|
|
105
|
+
/** All seven verdicts, in `DISCIPLINE_RULES` order, over declarations alone. */
|
|
106
|
+
export declare function evaluateSatisfaction(contract: EvalContract): readonly SatisfactionVerdict[];
|
|
107
|
+
export {};
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AD-31's seven satisfaction predicates: decision procedures over AD-19's
|
|
3
|
+
* declarations and the structured direction and `check` pair. No run record,
|
|
4
|
+
* no probe, no outcome state.
|
|
5
|
+
*
|
|
6
|
+
* Each rule quantifies over the sites its relevance predicate fires on:
|
|
7
|
+
* satisfaction holds when every site is witnessed. A site whose declaration is
|
|
8
|
+
* absent has no witness, so under-declaration costs coverage. A rule firing on
|
|
9
|
+
* no site holds vacuously.
|
|
10
|
+
*
|
|
11
|
+
* Nothing here throws, matching `relevance.ts`: a coverage gap is recorded and
|
|
12
|
+
* the artifact still ships (AD-5), so there is no failure code.
|
|
13
|
+
*/
|
|
14
|
+
import { substitutePointer } from '../compile/oracle-alignment.js';
|
|
15
|
+
import { SIBLING_GROUP_MINIMUM, } from '../schemas/eval-contract.js';
|
|
16
|
+
import { TRANSPORT_CHANNELS } from '../schemas/pointer.js';
|
|
17
|
+
import { buildPlanIndex } from '../seal/plan-index.js';
|
|
18
|
+
import { DISCIPLINE_RULES, satisfactionPredicateId, } from './rules.js';
|
|
19
|
+
const verdict = (rule, satisfied, reason) => ({
|
|
20
|
+
rule,
|
|
21
|
+
predicate: satisfactionPredicateId(rule),
|
|
22
|
+
satisfied,
|
|
23
|
+
reason,
|
|
24
|
+
});
|
|
25
|
+
/** The rule fires on no site, so the universal over its sites holds. */
|
|
26
|
+
export const NO_RELEVANT_SITE = 'the rule is relevant for no site, so satisfaction holds vacuously';
|
|
27
|
+
/** A contract declaring no operation leaves six of the rules an absent site. */
|
|
28
|
+
export const NO_OPERATION_WITNESS = 'the contract declares no operation, so the site this rule fires on has no declaration to witness';
|
|
29
|
+
/** Every declared operation, flattened. `relevance.ts` keeps its own copy. */
|
|
30
|
+
const operationsOf = (contract) => contract.permittedInterfaces.flatMap((declared) => declared.operations);
|
|
31
|
+
/** `unresolved` never throws; a duplicate identifier resolves to nothing. */
|
|
32
|
+
const planIndexOf = (contract) => buildPlanIndex(contract.interactionPlan, contract.permittedInterfaces, {
|
|
33
|
+
duplicateIds: 'unresolved',
|
|
34
|
+
});
|
|
35
|
+
// ---- the join between the two pointer spellings -------------------------
|
|
36
|
+
/** RFC 6901 escaping, `~` before `/`. */
|
|
37
|
+
const encodeToken = (token) => token.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
38
|
+
/** Everything one step produced or was given. */
|
|
39
|
+
const stepRoot = (stepId) => `/interactions/${stepId}`;
|
|
40
|
+
/** A descriptor pointer read at one step, spelled interaction-rooted. */
|
|
41
|
+
const bodyPointer = (stepId, descriptorPointer) => `/interactions/${stepId}/response-body${descriptorPointer}`;
|
|
42
|
+
/** One declared response key at one step. */
|
|
43
|
+
const keyPointer = (stepId, key) => `/interactions/${stepId}/response-body/${encodeToken(key)}`;
|
|
44
|
+
/** One declared parameter on one transport channel at one step. */
|
|
45
|
+
const parameterPointer = (stepId, channel, key) => `/interactions/${stepId}/call-inputs/${channel}/${encodeToken(key)}`;
|
|
46
|
+
/** A pointer addresses a root when it names it or descends into it. */
|
|
47
|
+
const addresses = (pointer, root) => pointer === root || pointer.startsWith(`${root}/`);
|
|
48
|
+
const someAddresses = (pointers, root) => {
|
|
49
|
+
for (const pointer of pointers) {
|
|
50
|
+
if (addresses(pointer, root))
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
};
|
|
55
|
+
const operandPointer = (operand, boundElementRoot) => 'pointer' in operand
|
|
56
|
+
? substitutePointer(operand.pointer, boundElementRoot)
|
|
57
|
+
: null;
|
|
58
|
+
function walkCheck(expression, boundElementRoot, nodes) {
|
|
59
|
+
switch (expression.op) {
|
|
60
|
+
case 'not':
|
|
61
|
+
nodes.push({ expression, operandPointers: [], collection: null });
|
|
62
|
+
walkCheck(expression.operands[0], boundElementRoot, nodes);
|
|
63
|
+
return;
|
|
64
|
+
case 'all':
|
|
65
|
+
case 'any':
|
|
66
|
+
nodes.push({ expression, operandPointers: [], collection: null });
|
|
67
|
+
for (const child of expression.operands) {
|
|
68
|
+
walkCheck(child, boundElementRoot, nodes);
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
case 'for-all':
|
|
72
|
+
case 'for-any': {
|
|
73
|
+
const collection = operandPointer(expression.collection, boundElementRoot);
|
|
74
|
+
nodes.push({ expression, operandPointers: [], collection });
|
|
75
|
+
walkCheck(expression.predicate, collection, nodes);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
default: {
|
|
79
|
+
// Widened before mapping: `.map` over a union of tuple types resolves
|
|
80
|
+
// to a union of call signatures. `set-membership` falls here too: its
|
|
81
|
+
// second operand is a `SetOperand`, which carries no pointer, so
|
|
82
|
+
// `operandPointer` already answers `null` in that position.
|
|
83
|
+
const operands = expression.operands;
|
|
84
|
+
nodes.push({
|
|
85
|
+
expression,
|
|
86
|
+
operandPointers: operands.map((operand) => operandPointer(operand, boundElementRoot)),
|
|
87
|
+
collection: null,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const oracleViewsOf = (contract) => contract.oracles.map((oracle) => {
|
|
93
|
+
const nodes = [];
|
|
94
|
+
if (oracle.check !== null)
|
|
95
|
+
walkCheck(oracle.check, null, nodes);
|
|
96
|
+
const checkPointers = new Set();
|
|
97
|
+
for (const node of nodes) {
|
|
98
|
+
if (node.collection !== null)
|
|
99
|
+
checkPointers.add(node.collection);
|
|
100
|
+
for (const pointer of node.operandPointers) {
|
|
101
|
+
if (pointer !== null)
|
|
102
|
+
checkPointers.add(pointer);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
directionTargets: oracle.direction === null ? [] : oracle.direction.evidenceTargets,
|
|
107
|
+
nodes,
|
|
108
|
+
checkPointers,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
/** AD-20's "in both channels": the direction names the pointer and the check reads it. */
|
|
112
|
+
const bothChannelsAddress = (oracle, root) => someAddresses(oracle.directionTargets, root) &&
|
|
113
|
+
someAddresses(oracle.checkPointers, root);
|
|
114
|
+
const definedPointers = (node) => node.operandPointers.filter((pointer) => pointer !== null);
|
|
115
|
+
const contextOf = (contract) => ({
|
|
116
|
+
operations: operationsOf(contract),
|
|
117
|
+
index: planIndexOf(contract),
|
|
118
|
+
oracles: oracleViewsOf(contract),
|
|
119
|
+
});
|
|
120
|
+
/**
|
|
121
|
+
* Rule 1: for every operation its relevance predicate fires on, some oracle's
|
|
122
|
+
* direction and check both address that operation's success indicator and a
|
|
123
|
+
* pointer whose declared role is something other than `success-indicator`,
|
|
124
|
+
* read at one step. An operation whose only other roled pointers are themselves
|
|
125
|
+
* indicators is a site with no witness.
|
|
126
|
+
*/
|
|
127
|
+
export function successIndicatorSeparationSatisfaction(contract, context = contextOf(contract)) {
|
|
128
|
+
const rule = 'success-indicator-separation';
|
|
129
|
+
const { operations, index, oracles } = context;
|
|
130
|
+
if (operations.length === 0)
|
|
131
|
+
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
132
|
+
let sites = 0;
|
|
133
|
+
for (const operation of operations) {
|
|
134
|
+
const { successIndicator, channelRoles } = operation.responseDescriptor;
|
|
135
|
+
if (successIndicator === null) {
|
|
136
|
+
return verdict(rule, false, `operation ${operation.operationId} nominates no success indicator, so no oracle can separate one from the body`);
|
|
137
|
+
}
|
|
138
|
+
if (channelRoles === null) {
|
|
139
|
+
return verdict(rule, false, `operation ${operation.operationId} declares no channel roles, so no pointer is declared to separate the indicator from`);
|
|
140
|
+
}
|
|
141
|
+
const roles = Object.entries(channelRoles);
|
|
142
|
+
if (!roles.some(([pointer]) => pointer !== successIndicator))
|
|
143
|
+
continue;
|
|
144
|
+
sites += 1;
|
|
145
|
+
const others = roles
|
|
146
|
+
.filter(([pointer, role]) => pointer !== successIndicator && role !== 'success-indicator')
|
|
147
|
+
.map(([pointer]) => pointer);
|
|
148
|
+
const witnessed = index
|
|
149
|
+
.stepsUsing(operation.operationId)
|
|
150
|
+
.some((step) => oracles.some((oracle) => bothChannelsAddress(oracle, bodyPointer(step.stepId, successIndicator)) &&
|
|
151
|
+
others.some((pointer) => bothChannelsAddress(oracle, bodyPointer(step.stepId, pointer)))));
|
|
152
|
+
if (!witnessed) {
|
|
153
|
+
return verdict(rule, false, `no oracle addresses operation ${operation.operationId}'s success indicator beside another roled pointer at one step, in both channels`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return verdict(rule, true, sites === 0
|
|
157
|
+
? NO_RELEVANT_SITE
|
|
158
|
+
: 'every operation the rule fires on has an oracle reading its success indicator beside another roled pointer');
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Rule 2: for every operation declaring more than one distinct required
|
|
162
|
+
* response key, some oracle's direction and check both address every one of
|
|
163
|
+
* those keys at one step invoking that operation. AD-20's denominator is the
|
|
164
|
+
* required-key set of the operation the addressed step invokes.
|
|
165
|
+
*/
|
|
166
|
+
export function wholeBodySatisfaction(contract, context = contextOf(contract)) {
|
|
167
|
+
const rule = 'whole-body';
|
|
168
|
+
const { operations, index, oracles } = context;
|
|
169
|
+
if (operations.length === 0)
|
|
170
|
+
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
171
|
+
let sites = 0;
|
|
172
|
+
for (const operation of operations) {
|
|
173
|
+
const required = [...new Set(operation.responseDescriptor.requiredKeys)];
|
|
174
|
+
if (required.length <= 1)
|
|
175
|
+
continue;
|
|
176
|
+
sites += 1;
|
|
177
|
+
const witnessed = index
|
|
178
|
+
.stepsUsing(operation.operationId)
|
|
179
|
+
.some((step) => oracles.some((oracle) => required.every((key) => bothChannelsAddress(oracle, keyPointer(step.stepId, key)))));
|
|
180
|
+
if (!witnessed) {
|
|
181
|
+
return verdict(rule, false, `no oracle covers every required response key of operation ${operation.operationId} at one addressed step, in both channels`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return verdict(rule, true, sites === 0
|
|
185
|
+
? NO_RELEVANT_SITE
|
|
186
|
+
: 'every operation declaring more than one required response key has an oracle covering all of them at one step');
|
|
187
|
+
}
|
|
188
|
+
/** The site condition rule 3 relevance reads: a key on any of the four channels. */
|
|
189
|
+
const declaresRequestKey = (operation) => TRANSPORT_CHANNELS.some((channel) => {
|
|
190
|
+
const shape = operation.requestShape[channel];
|
|
191
|
+
return (shape.requiredKeys.length > 0 ||
|
|
192
|
+
shape.permittedKeys.length > 0 ||
|
|
193
|
+
Object.keys(shape.types).length > 0);
|
|
194
|
+
});
|
|
195
|
+
/** AD-39's matcher, on any transport channel of one step. */
|
|
196
|
+
const bindsTypeViolating = (step) => TRANSPORT_CHANNELS.some((channel) => {
|
|
197
|
+
const binding = step.inputBinding[channel];
|
|
198
|
+
if (binding === null)
|
|
199
|
+
return false;
|
|
200
|
+
return Object.values(binding).some((value) => 'matcher' in value && value.matcher === 'type-violating');
|
|
201
|
+
});
|
|
202
|
+
/**
|
|
203
|
+
* Rule 3: for every operation declaring a request key, some step invoking it
|
|
204
|
+
* binds AD-39's `type-violating` matcher and some check addresses that step.
|
|
205
|
+
* The matcher is a declaration, so the pair decides the rule without reading
|
|
206
|
+
* prose or a run.
|
|
207
|
+
*/
|
|
208
|
+
export function malformedInputSatisfaction(contract, context = contextOf(contract)) {
|
|
209
|
+
const rule = 'malformed-input';
|
|
210
|
+
const { operations, index, oracles } = context;
|
|
211
|
+
if (operations.length === 0)
|
|
212
|
+
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
213
|
+
let sites = 0;
|
|
214
|
+
for (const operation of operations) {
|
|
215
|
+
if (!declaresRequestKey(operation))
|
|
216
|
+
continue;
|
|
217
|
+
sites += 1;
|
|
218
|
+
const witnessed = index
|
|
219
|
+
.stepsUsing(operation.operationId)
|
|
220
|
+
.some((step) => bindsTypeViolating(step) &&
|
|
221
|
+
oracles.some((oracle) => someAddresses(oracle.checkPointers, stepRoot(step.stepId))));
|
|
222
|
+
if (!witnessed) {
|
|
223
|
+
return verdict(rule, false, `no step invoking operation ${operation.operationId} binds a type-violating matcher under a check that addresses it`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return verdict(rule, true, sites === 0
|
|
227
|
+
? NO_RELEVANT_SITE
|
|
228
|
+
: 'every operation declaring a request key has a type-violating step some check addresses');
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Rule 4: every declared collection location is the collection of some
|
|
232
|
+
* quantifier, read at a step invoking its operation. The comparison is
|
|
233
|
+
* equality: a quantifier one level inside a declared collection ranges over
|
|
234
|
+
* something else. Both quantifiers are admitted, since `not(for-any P)` is the
|
|
235
|
+
* idiomatic spelling of "no element satisfies P".
|
|
236
|
+
*/
|
|
237
|
+
export function perRecordSatisfaction(contract, context = contextOf(contract)) {
|
|
238
|
+
const rule = 'per-record';
|
|
239
|
+
const { operations, index, oracles } = context;
|
|
240
|
+
if (operations.length === 0)
|
|
241
|
+
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
242
|
+
let sites = 0;
|
|
243
|
+
for (const operation of operations) {
|
|
244
|
+
const { collectionLocations } = operation.responseDescriptor;
|
|
245
|
+
if (collectionLocations === null) {
|
|
246
|
+
return verdict(rule, false, `operation ${operation.operationId} declares no collection-location list, so no quantifier can range over a declared collection`);
|
|
247
|
+
}
|
|
248
|
+
const steps = index.stepsUsing(operation.operationId);
|
|
249
|
+
for (const location of collectionLocations) {
|
|
250
|
+
sites += 1;
|
|
251
|
+
const witnessed = steps.some((step) => {
|
|
252
|
+
const collection = bodyPointer(step.stepId, location.pointer);
|
|
253
|
+
return oracles.some((oracle) => oracle.nodes.some((node) => node.collection === collection));
|
|
254
|
+
});
|
|
255
|
+
if (!witnessed) {
|
|
256
|
+
return verdict(rule, false, `no check quantifies over collection ${location.pointer} of operation ${operation.operationId}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return verdict(rule, true, sites === 0
|
|
261
|
+
? NO_RELEVANT_SITE
|
|
262
|
+
: 'every declared collection location is the collection of some quantifier');
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Rule 5: every declared sibling group has an oracle whose direction and check
|
|
266
|
+
* both address two of its members. An operation member is addressed through a
|
|
267
|
+
* step that invokes it; a parameter member through a `call-inputs` pointer on
|
|
268
|
+
* any transport channel. Members are deduplicated first, since `SiblingGroups`
|
|
269
|
+
* carries no uniqueness constraint.
|
|
270
|
+
*/
|
|
271
|
+
export function siblingCrossCheckSatisfaction(contract, context = contextOf(contract)) {
|
|
272
|
+
const rule = 'sibling-cross-check';
|
|
273
|
+
const groups = contract.siblingGroups;
|
|
274
|
+
if (groups === null) {
|
|
275
|
+
return verdict(rule, false, 'the contract declares no sibling groups, so no group is declared to cross-check');
|
|
276
|
+
}
|
|
277
|
+
const { index, oracles } = context;
|
|
278
|
+
let sites = 0;
|
|
279
|
+
for (const group of groups.operations) {
|
|
280
|
+
sites += 1;
|
|
281
|
+
const members = [...new Set(group)];
|
|
282
|
+
const witnessed = oracles.some((oracle) => members.filter((operationId) => index
|
|
283
|
+
.stepsUsing(operationId)
|
|
284
|
+
.some((step) => bothChannelsAddress(oracle, stepRoot(step.stepId)))).length >= SIBLING_GROUP_MINIMUM);
|
|
285
|
+
if (!witnessed) {
|
|
286
|
+
return verdict(rule, false, `no oracle addresses two members of the operation sibling group ${members.join(' and ')} in both channels`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
for (const group of groups.parameters) {
|
|
290
|
+
sites += 1;
|
|
291
|
+
const members = [...new Set(group)];
|
|
292
|
+
const witnessed = oracles.some((oracle) => members.filter((parameter) => contract.interactionPlan.some((step) => TRANSPORT_CHANNELS.some((channel) => bothChannelsAddress(oracle, parameterPointer(step.stepId, channel, parameter))))).length >= SIBLING_GROUP_MINIMUM);
|
|
293
|
+
if (!witnessed) {
|
|
294
|
+
return verdict(rule, false, `no oracle addresses two members of the parameter sibling group ${members.join(' and ')} in both channels`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return verdict(rule, true, sites === 0
|
|
298
|
+
? NO_RELEVANT_SITE
|
|
299
|
+
: 'every declared sibling group has an oracle reading two of its members');
|
|
300
|
+
}
|
|
301
|
+
const isReferenceSetOperand = (operand, identifier) => 'referenceSet' in operand && operand.referenceSet === identifier;
|
|
302
|
+
/**
|
|
303
|
+
* AD-20 rule 6's two forms, selected by `expectedCardinality.mode`. `exact`
|
|
304
|
+
* takes the bijection, `covers-by-key` against the reference set;
|
|
305
|
+
* `page-bounded` and `at-most` take the injection, `for-all` over the page
|
|
306
|
+
* whose predicate is `set-membership` against it. A page of twenty drawn from
|
|
307
|
+
* ninety-seven never equals its reference set, so the bijection resolves false
|
|
308
|
+
* against a correct server (AD-26).
|
|
309
|
+
*/
|
|
310
|
+
function reconciles(node, collection, location, referenceSet) {
|
|
311
|
+
const { expression } = node;
|
|
312
|
+
if (location.expectedCardinality.mode === 'exact') {
|
|
313
|
+
return (expression.op === 'covers-by-key' &&
|
|
314
|
+
isReferenceSetOperand(expression.operands[0], referenceSet) &&
|
|
315
|
+
node.operandPointers[1] === collection);
|
|
316
|
+
}
|
|
317
|
+
if (expression.op !== 'for-all' || node.collection !== collection)
|
|
318
|
+
return false;
|
|
319
|
+
const { predicate } = expression;
|
|
320
|
+
return (predicate.op === 'set-membership' &&
|
|
321
|
+
isReferenceSetOperand(predicate.operands[1], referenceSet));
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Rule 6: every collection location naming a reference set is reconciled
|
|
325
|
+
* against it, in the form its declared cardinality mode requires.
|
|
326
|
+
*/
|
|
327
|
+
export function omissionAndCompletenessSatisfaction(contract, context = contextOf(contract)) {
|
|
328
|
+
const rule = 'omission-and-completeness';
|
|
329
|
+
const { operations, index, oracles } = context;
|
|
330
|
+
if (operations.length === 0)
|
|
331
|
+
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
332
|
+
let sites = 0;
|
|
333
|
+
for (const operation of operations) {
|
|
334
|
+
const { collectionLocations } = operation.responseDescriptor;
|
|
335
|
+
if (collectionLocations === null) {
|
|
336
|
+
return verdict(rule, false, `operation ${operation.operationId} declares no collection-location list, so no location can be reconciled against a reference set`);
|
|
337
|
+
}
|
|
338
|
+
const steps = index.stepsUsing(operation.operationId);
|
|
339
|
+
for (const location of collectionLocations) {
|
|
340
|
+
const referenceSet = location.referenceSet;
|
|
341
|
+
if (referenceSet === null)
|
|
342
|
+
continue;
|
|
343
|
+
sites += 1;
|
|
344
|
+
const witnessed = steps.some((step) => {
|
|
345
|
+
const collection = bodyPointer(step.stepId, location.pointer);
|
|
346
|
+
return oracles.some((oracle) => oracle.nodes.some((node) => reconciles(node, collection, location, referenceSet)));
|
|
347
|
+
});
|
|
348
|
+
if (!witnessed) {
|
|
349
|
+
return verdict(rule, false, `no check reconciles collection ${location.pointer} of operation ${operation.operationId} against reference set ${referenceSet} in the form its ${location.expectedCardinality.mode} cardinality requires`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return verdict(rule, true, sites === 0
|
|
354
|
+
? NO_RELEVANT_SITE
|
|
355
|
+
: 'every collection location naming a reference set is reconciled against it in the declared form');
|
|
356
|
+
}
|
|
357
|
+
/** AD-39: a read-back step names the write in its temporal clause and changes no state itself. */
|
|
358
|
+
const readBackStepsFor = (contract, index, writeStepId) => contract.interactionPlan.filter((step) => {
|
|
359
|
+
if (step.stepId === writeStepId || step.after !== writeStepId)
|
|
360
|
+
return false;
|
|
361
|
+
const operation = index.operationOf(step.operationId);
|
|
362
|
+
return operation !== undefined && !operation.stateChangeMarker;
|
|
363
|
+
});
|
|
364
|
+
/** One node holding a pointer into each side of the read-back relation. */
|
|
365
|
+
const relates = (node, writeStepId, readStepId) => {
|
|
366
|
+
const pointers = definedPointers(node);
|
|
367
|
+
return (someAddresses(pointers, `${stepRoot(writeStepId)}/call-inputs`) &&
|
|
368
|
+
someAddresses(pointers, `${stepRoot(readStepId)}/response-body`));
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* Rule 7: for every operation declaring `stateChangeMarker: true`, some check
|
|
372
|
+
* node relates a pointer under a step invoking it to a pointer under the
|
|
373
|
+
* response body of a later step whose operation changes no state and whose
|
|
374
|
+
* temporal clause names the write. One node carries the relation: two unrelated
|
|
375
|
+
* assertions under one `all` read nothing back.
|
|
376
|
+
*/
|
|
377
|
+
export function stateChangeReadBackSatisfaction(contract, context = contextOf(contract)) {
|
|
378
|
+
const rule = 'state-change-read-back';
|
|
379
|
+
const { operations, index, oracles } = context;
|
|
380
|
+
if (operations.length === 0)
|
|
381
|
+
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
382
|
+
let sites = 0;
|
|
383
|
+
for (const operation of operations) {
|
|
384
|
+
if (!operation.stateChangeMarker)
|
|
385
|
+
continue;
|
|
386
|
+
sites += 1;
|
|
387
|
+
const witnessed = index
|
|
388
|
+
.stepsUsing(operation.operationId)
|
|
389
|
+
.some((writeStep) => readBackStepsFor(contract, index, writeStep.stepId).some((readStep) => oracles.some((oracle) => oracle.nodes.some((node) => relates(node, writeStep.stepId, readStep.stepId)))));
|
|
390
|
+
if (!witnessed) {
|
|
391
|
+
return verdict(rule, false, `no check relates operation ${operation.operationId}'s call inputs to the response body of a later step that changes no state`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return verdict(rule, true, sites === 0
|
|
395
|
+
? NO_RELEVANT_SITE
|
|
396
|
+
: 'every state-changing operation is read back through a later non-state-changing step');
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* One predicate per rule. The mapped type fails the typecheck when a member of
|
|
400
|
+
* `DISCIPLINE_RULES` has no predicate, matching `RELEVANCE_PREDICATES`.
|
|
401
|
+
*/
|
|
402
|
+
export const SATISFACTION_PREDICATES = {
|
|
403
|
+
'success-indicator-separation': successIndicatorSeparationSatisfaction,
|
|
404
|
+
'whole-body': wholeBodySatisfaction,
|
|
405
|
+
'malformed-input': malformedInputSatisfaction,
|
|
406
|
+
'per-record': perRecordSatisfaction,
|
|
407
|
+
'sibling-cross-check': siblingCrossCheckSatisfaction,
|
|
408
|
+
'omission-and-completeness': omissionAndCompletenessSatisfaction,
|
|
409
|
+
'state-change-read-back': stateChangeReadBackSatisfaction,
|
|
410
|
+
};
|
|
411
|
+
/** All seven verdicts, in `DISCIPLINE_RULES` order, over declarations alone. */
|
|
412
|
+
export function evaluateSatisfaction(contract) {
|
|
413
|
+
const context = contextOf(contract);
|
|
414
|
+
return DISCIPLINE_RULES.map((rule) => SATISFACTION_PREDICATES[rule](contract, context));
|
|
415
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AD-31's published predicate table, emitted by the implemented predicates.
|
|
3
|
+
* Pure, so the writer and the drift check cannot disagree about bytes, which
|
|
4
|
+
* is `publish.ts`'s split. Its only input is the corpus argument, so the
|
|
5
|
+
* worked example is unreachable from here, which is what AD-31 requires:
|
|
6
|
+
* publication is exercised against a compile-side contract fixture corpus.
|
|
7
|
+
*/
|
|
8
|
+
import type { EvalContract } from '../schemas/eval-contract.ts';
|
|
9
|
+
import { type DisciplineRule } from './rules.ts';
|
|
10
|
+
/** AD-31's three declaration states, a populated one splitting in two. Column order. */
|
|
11
|
+
export declare const DECLARATION_STATES: readonly ['absent', 'explicitly-empty', 'witnessed', 'unwitnessed'];
|
|
12
|
+
export type DeclarationState = (typeof DECLARATION_STATES)[number];
|
|
13
|
+
/** The pair each state asserts. `absent` and `unwitnessed` share one; the reason separates them. */
|
|
14
|
+
export declare const STATE_VERDICTS: {
|
|
15
|
+
readonly [State in DeclarationState]: {
|
|
16
|
+
readonly relevant: boolean;
|
|
17
|
+
readonly satisfied: boolean;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export type CoverageCell = {
|
|
21
|
+
readonly rule: DisciplineRule;
|
|
22
|
+
readonly state: DeclarationState;
|
|
23
|
+
readonly contractId: string;
|
|
24
|
+
};
|
|
25
|
+
/** The whole document. Throws a worded diagnosis on an unoccupied cell, which would otherwise publish as coverage. */
|
|
26
|
+
export declare function coveragePredicateTable(contracts: readonly EvalContract[], cells: readonly CoverageCell[]): string;
|